BLE Write Request, Write Command, Signed Write Command & Prepare Write

Summary

In my article entitled “PSoC4 BLE Central Custom Profile w/LED & CapSense” a reader named Doug posted a question which asked about BLE Writes and the events that are created in PSoC 4 BLE.  This article attempts to answer those questions.  The bottom line is that the BLE Spec specifies four ways for a GATT Client to write to a GATT Server.  Each of those write types either have a response or not.  Inside of the PSoC BLE Stack each of them generate a different event.

Here is a summary of the BLE Write Events from my WICED BLE textbook chapter 4D.4

[EDIT: The above table is incorrect.  3.4.5.1 is ATT_WRITE_REQ which requires a 3.4.5.2 an ATT_WRITE_RSP.   And 3.4.5.3 is ATT_WRITE_CMD which requires no response.]

Inside of the PSoC Stack it will generate the following events:

Request Event PSoC 4 Response API
Write Request CYBLE_EVT_GATTS_WRITE_REQ CyBle_GattsWriteRsp or CyBle_GattsErrorRsp
Write Command CYBLE_EVT_GATTS_WRITE_CMD_REQ No Response
Signed Write CYBLE_EVT_GATTS_DATA_SIGNED_CMD_REQ CyBle_GattsWriteRsp or CyBle_GattsErrorRsp
Prepare Write CYBLE_EVT_GATTS_PREP_WRITE_REQ Out of scope for today
Execute Write CYBLE_EVT_GATTS_EXEC_WRITE_REQ Out of scope for today

Question(s)

Here are the original question(s):

Hi, I am having some problems understanding “write without response” setup.
In the “capsenseled” server project “CYBLE_EVT_GATTS_WRITE_REQ” is used instead of “CYBLE_EVT_GATTS_WRITE_CMD_REQ” in the event handler, but “write without response” is selected in the component setup.
According to this other project I am looking at ( https://www.digikey.com/eewiki/pages/viewpage.action?pageId=55574662 ) both are used with comments as follows

case CYBLE_EVT_GATTS_WRITE_REQ: //Write with response
case CYBLE_EVT_GATTS_WRITE_CMD_REQ: //Write without response

Why is “CYBLE_EVT_GATTS_WRITE_CMD_REQ” NOT used in the capsenseled project, and does “CYBLE_EVT_GATTS_WRITE_REQ” still function in a write without response setup?
Am I misunderstanding the definition of a response?
Is the “response” referring to an event that occurs within the server device, or is a response defined as a reply from the client device?

I am trying to set up a low power single button remote to report the state of the button, to be handled by the client which is connected to a larger power source and will possibly count edges, measure button press duration, or both, etc. Ideally the remote would be asleep except for when it needs to tell the client that the button has changed states, and would not spend any energy processing a response from the client.

BLE Spec

When you declare a Characteristic, you have a 1-byte bit field to specify the Characteristic Properties, that tells the GATT Client how it can write to the Characteristic.  Here is a screenshot from section 3.3.1 of the Bluetooth core spec:

And the legal values for the Characteristic Properties are in section 3.3.1.1

This table says that it is perfectly legal to perform a “Write without response” (which is really called “Write Command” in section 3.4.5.3) or a “Write” (which is really called “Write Request” in section 3.4.5.1) as long as you send a legal response (or none).  If you look at the project which is referenced in the Digikey article, you can see that the author did just that for the Characteristic “Number_Write”.

Write Command a.k.a. Write Without Response”

As I looked around at the spec for this article I discovered something which I did not previously know.  Specifically, a Write Command must fit in one packet… which means that the data written to the Characteristic must be no more than the ATT MTU – 3.  Here is the spec:

Write Request

A Write Request is a two sided request, meaning the GATT Server must either send a “Write Response” or an “Error Response”  Here is a screenshot from the spec.

Write Response

A Write Response is a stupid simple packet with just one possible value… 0x13 meaning that the write worked.  In the PSoC, you generate this packet by calling “CyBle_GattsWriteRsp” with the connection handle as the only parameter.

Error Response

The more interesting response is an error where you are allowed to send more information.  To generate an error response in PSoC you call CyBle_GattsErrorRsp with the connection handle and the error code.

The spec gives a list of legal error codes.  Here is a snapshot from the spec with a FEW of them.

The interesting thing about these error codes is that you are allowed to create you own “Application Errors” which will have your application semantics.

PSoC Creator has an enumerated list for the error codes (here is a short section from the BLE Component Datasheet)

Answers

OK… so that leaves me with giving specific answers to his questions.  My answer are embedded in bold:

Hi, I am having some problems understanding “write without response” setup.
In the “capsenseled” server project “CYBLE_EVT_GATTS_WRITE_REQ” is used instead of “CYBLE_EVT_GATTS_WRITE_CMD_REQ” in the event handler, but “write without response” is selected in the component setup.
According to this other project I am looking at ( https://www.digikey.com/eewiki/pages/viewpage.action?pageId=55574662 ) both are used with comments as follows

case CYBLE_EVT_GATTS_WRITE_REQ: //Write with response
case CYBLE_EVT_GATTS_WRITE_CMD_REQ: //Write without response

Why is “CYBLE_EVT_GATTS_WRITE_CMD_REQ” NOT used in the capsenseled project,

When I wrote that project originally I chose to only support no responses to writes.  There is no need to support both.  As the application developer you can choose to do either or both.

and does “CYBLE_EVT_GATTS_WRITE_REQ” still function in a write without response setup?

If the GATT Client sends you a CYBLE_EVT_GATTS_WRITE_REQ and you send a response it might turn out badly and you should not do that.
Am I misunderstanding the definition of a response?
Is the “response” referring to an event that occurs within the server device, or is a response defined as a reply from the client device?

Well..  sounds like the answer was yes… but hopefully it is no now.

I am trying to set up a low power single button remote to report the state of the button, to be handled by the client which is connected to a larger power source and will possibly count edges, measure button press duration, or both, etc. Ideally the remote would be asleep except for when it needs to tell the client that the button has changed states, and would not spend any energy processing a response from the client.

Sounds like “Write without response” is perfect for your application.

Mouser PSoC 6-WiFi-BT L8: Integrate WiFi and AWS Into the Game

Designing low-power, cloud-connected IoT devices with PSoC® 6 MCU’s and WICED® Wi-Fi/Bluetooth

Summary

In this lesson we will move the subscriber app functionality into the main GameBle project (now called GameBleAws).  In order to do this, you need to turn the subscriber into a thread, and fix it so that messages that are sent to the PADDLE topic get turned into messages that can be sent to the paddleQueue.

To implement this lesson I will follow these steps:

  1. Create a New Project starting from L6GameBle
  2. Copy over subscriber.c and wifi_config_dct.h
  3. Update the Makefile
  4. Create subscriber.h
  5. Update main.c
  6. Update subscriber.c
  7. Test

Create a New Project

Copy and paste the L6GameBle project into a new project called L8GameBleAws using Copy/Paste

Create a new Make target for the GameBleAws project

Copy subscriber.c & wifi_config_dct.h

Use copy/paste to make a copy of the subscriber application and paste it into the GameBleAws project.  Once done your folder should look like this:

Update the Makefile

NAME := App_WStudio_L8GameBleAws

$(NAME)_SOURCES := main.c \
    CapSenseThread.c \
	GameThread.c \
	cy_tft_display.c \
	GoBleThread.c \
	GoBle_db.c \
	wiced_bt_cfg.c \
	subscriber.c

$(NAME)_COMPONENTS := graphics/ugui \
                      libraries/drivers/bluetooth/low_energy \
                      protocols/AWS

WIFI_CONFIG_DCT_H := wifi_config_dct.h

$(NAME)_RESOURCES  := apps/aws/iot/rootca.cer \
                      apps/aws/iot/subscriber/client.cer \
                      apps/aws/iot/subscriber/privkey.cer
                      
# To support Low memory platforms, disabling components which are not required
GLOBAL_DEFINES += WICED_CONFIG_DISABLE_SSL_SERVER \
                  WICED_CONFIG_DISABLE_DTLS \
                  WICED_CONFIG_DISABLE_ENTERPRISE_SECURITY \
                  WICED_CONFIG_DISABLE_DES \
                  WICED_CONFIG_DISABLE_ADVANCED_SECURITY_CURVES

Create subscriber.h

In order for the main.c to know about the awsThread (which is the former application_start of subscriber.c) you should create a file called subscriber.h.  Then you should define the awsThread function to match the wiced_thread_t function prototype (just a function that takes a wiced_thread_arg and returns void).

#pragma once
extern void awsThread( wiced_thread_arg_t arg );

Update main.c

To integrate the awsThread thread into your main project you need to add the “subscriber.h” to the includes:

#include "GameThread.h"
#include "GoBleThread.h"
#include "CapSenseThread.h"
#include "subscriber.h"
#include "wiced.h"

Add a new variable to hold the awsThreadHandle.

wiced_thread_t awsThreadHandle;

Finally you should launch the AWS thread by creating it with wiced_rtos_create_thread.

void application_start( )
{
    wiced_init( );
    wiced_rtos_init_queue(&paddleQueue,"paddleQueue",sizeof(game_msg_t),10);
    wiced_rtos_create_thread(&blinkThreadHandle,7,"Blink Thread",pdlBlinkThread,500,0);
    wiced_rtos_create_thread(&capsenseThreadHandle,7,"CapSense Thread",capSenseThread,1024,0);
    wiced_rtos_create_thread(&gameThreadHandle,7,"game Thread",gameThread,4096,0);
    GoBleThread_start();
    wiced_rtos_create_thread(&awsThreadHandle,7,"AWS Thread",awsThread,4096,0);
 }

Update subscriber.c

In order for subscriber.c to be useful you need to fix the includes, send the messages from the PADDLE topic to the game thread, and turn application_start into a thread.  Start by fixing the includes (just like we did in the BLE Example in Lesson 6)

#include "SystemGlobal.h"
#include "GameThread.h"

When you get a message to the PADDLE topic, you should parse it, then send a message to the game thread to move the paddle.  You do this exactly the same way as you did in the BLE project.  Notice that I protect the game thread by making sure it send a value less than 100.

        case WICED_AWS_EVENT_PAYLOAD_RECEIVED:
        {
            uint32_t val;
            WPRINT_APP_INFO( ("[Application/AWS] Payload Received[ Topic: %.*s ]:\n", (int)data->message.topic_length, data->message.topic ) );

            if(strncmp(WICED_TOPIC,(const char *)data->message.topic,strlen(WICED_TOPIC)) == 0 && data->message.data_length < 4)
            {
                sscanf((const char *)data->message.data,"%d",(int *)&val);
                if(val>100)
                    val = 100;

                WPRINT_APP_INFO(("Val = %d\n",(int)val));
                game_msg_t msg;
                msg.evt = MSG_POSITION;
                msg.val = val;
                wiced_rtos_push_to_queue(&paddleQueue,&msg,0);
            }
        }
        break;

Test

I don’t really have a good program (like the GoBle) to test moving the paddle live.  But you can see that when the game is over, you can still move the paddle using AWS console.  In the screen shot below you can see that I moved the paddle to position 0.  And you can see that the BLE started at the same time as AWS.  Score.

Mouser PSoC 6-WiFi-BT L7 : Implement WiFi and AWS

Designing low-power, cloud-connected IoT devices with PSoC® 6 MCU’s and WICED® Wi-Fi/Bluetooth

Summary

In this lesson I am going to update one of the predefined application which we deliver as part of WICED Studio which knows how to connect to the AWS IoT Cloud to work on my network.  This will demonstrate WiFi and MQTT.  I will also fix the application so that it knows about a new MQTT Topic called “PADDLE” which will be used to send messages to move the game paddle.

To implement this project I will:

  1. Copy the subscriber from demo/aws/iot/pub_sub/subscriber
  2. Setup a “thing” on AWS and download the certificates
  3. Update the certificates in the WICED installation
  4. Update the project with my AWS configuration
  5. Test
  6. Update the project to be compatible with the Game
  7. Test

Copy Subscriber & Fix the Makefile

The first thing that I will do is copy/paste the subscriber application which can be found at demo/aws/iot/pub_sub/subscriber into my folder.  I will also rename the project to L7subscriber. Once that is done it should look like this:

Then you need to edit the Makefile to update the name of the project.

NAME := App_WStudio_L7subscriber

Go to Amazon.com and Create a “thing”

In order to connect to the AWS cloud you need to create a “thing”.  Go to the AWS console and click “Create”

We are just going to create one thing.  So, click “Create a single thing”

Give it a name, in this case Mouser.

AWS has device level security which is implemented with RSA certificates for each device.  So,  you need to let AWS create a certificate for you.

Once that is done download the device certificate and the two keys.  You need to 100% make sure you do this now, because you wont get another chance. I’m also going to activate the certificate once I have the certificate and keys.

Now attach a policy to your thing.  I already have the “ww101_policy” setup.

The policy looks like this.  Basically, things are wide open.

Update the Certificates

The last thing that you need is the most recent AWS certificate.  WICED expects that you use the RSA2048 bit key.

Now that you have the four security documents., you need to integrate them into your WICED installation.  To do this, change directories to ~/Documents/WICED-Studio-6.2/43xxx_Wi-Fi/resources/apps/aws/iot  Then copy the files, and then make symbolic links.  If you are not on a Mac or Linux then just copy the files and rename them appropriately.

Update the Project

AWS creates a virtual machine and runs an MQTT server on that machine.  You can find out the DNS name of that machine on the settings screen.  Here you can see my endpoint.  You will need to configure the WICED project to talk to your server.

You need to change the actual endpoint of your MQTT server in the AWS cloud on line 119

static wiced_aws_endpoint_info_t my_subscriber_aws_iot_endpoint = {
    .transport           = WICED_AWS_TRANSPORT_MQTT_NATIVE,
    .uri                 = "amk6m51qrxr2u-ats.iot.us-east-1.amazonaws.com",
    .peer_common_name    = NULL,
    .ip_addr             = {0},
    .port                = WICED_AWS_IOT_DEFAULT_MQTT_PORT,
    .root_ca_certificate = NULL,
    .root_ca_length      = 0,
};

In the file wifi_config_dct.h you will need to change the CLIENT_AP_SSID and CLIENT_AP_PASSPHRASE for your network

/* This is the default AP the device will connect to (as a client)*/
#define CLIENT_AP_SSID       "WW101WPA"
#define CLIENT_AP_PASSPHRASE "yourpassword"
#define CLIENT_AP_BSS_TYPE   WICED_BSS_TYPE_INFRASTRUCTURE
#define CLIENT_AP_SECURITY   WICED_SECURITY_WPA2_MIXED_PSK
#define CLIENT_AP_CHANNEL    1
#define CLIENT_AP_BAND       WICED_802_11_BAND_2_4GHZ

Test

In order to test the project you will need to create a make target and program your development kit.

Once that is done you can go to the Amazon.com test console and send MQTT messages to the correct topic.

Here you can see the project attached to my network.  And successfully received the LIGHT ON message.

Update the Project for the Game

We know that the game doesn’t care about the LIGHT topic so, let’s change it to PADDLE.

#define WICED_TOPIC                                 "PADDLE"

And let’s assume that messages to the paddle topic are ASCII numbers <100.  So, we can parse the message and print it out.  Notice that I used sscanf to parse the message and we all know that is super dangerous.

        case WICED_AWS_EVENT_PAYLOAD_RECEIVED:
        {
            char buff[10];
            uint32_t val;
            WPRINT_APP_INFO( ("[Application/AWS] Payload Received[ Topic: %.*s ]:\n", (int)data->message.topic_length, data->message.topic ) );

            if(strncmp(WICED_TOPIC,data->message.topic,strlen(WICED_TOPIC)) == 0 && data->message.data_length < 4)
            {
                sscanf(data->message.data,"%d",&val);
                WPRINT_APP_INFO(("Val = %d\n",val));
            }
        }
        break;

Test

After making those updates let’s program the whole shooting match again.  Then test by sending some messages to the PADDLE topic.

It’s good.  Things are working.

Mouser PSoC6-WiFi-BT L6 : Integrate GoBle Bluetooth into the Game

Designing low-power, cloud-connected IoT devices with PSoC® 6 MCU’s and WICED® Wi-Fi/Bluetooth

Summary

At this point we have a working BLE Remote Control and a working Game.  In this lesson I’ll merge the two projects together so that the GoBle remote control will be able to control the game paddle.  It will do this by creating messages (just like the CapSense messages) and sending them to the Game thread via the paddleQueue.  And it will send “Button0” messages when “Button A” is pressed.

To implement this lesson I will follow these steps:

  1. Copy everything into a new project
  2. Modify the Makefile
  3. Modify main.c
  4. Create a new make target
  5. Test it all to make sure things are still working
  6. Modify GoBleThread.c
  7. Program and Test

Copy L4Game into a new project L6GameBle

Use “copy” and “paste” to create a new project, when you paste give it the name “L6GameBle” (there is an error in the screenshot)

Copy the files GoBle_db.c/h, GoBleThread.c/h, and wiced_bt_cfg.c from the GoBle project into your new GameBle project.  After that is done your project should look like this:

Modify the Makefile

  1. Change the name of the App to “App_WStudio_L6GameBle”
  2. Add the new source files
  3. Add the BLE Library “libraries/drivers/bluetooth/low_energy”
NAME := App_WStudio_L6GameBle

$(NAME)_SOURCES := main.c \
    CapSenseThread.c \
	GameThread.c \
	cy_tft_display.c \
	GoBleThread.c \
	GoBle_db.c \
	wiced_bt_cfg.c

$(NAME)_COMPONENTS := graphics/ugui \
                      libraries/drivers/bluetooth/low_energy

Modify main.c

Add the include for the GeBleThread.

#include "GameThread.h"
#include "GoBleThread.h"
#include "CapSenseThread.h"
#include "wiced.h"

In the application_start function add a call GoBleThread_start() to get the GoBle thread going

void application_start( )
{
    wiced_init( );
    wiced_rtos_init_queue(&paddleQueue,"paddleQueue",sizeof(game_msg_t),10);
    wiced_rtos_create_thread(&blinkThreadHandle,7,"Blink Thread",pdlBlinkThread,500,0);
    wiced_rtos_create_thread(&capSenseThreadHandle,7,"CapSense Thread",capSenseThread,1024,0);
    wiced_rtos_create_thread(&gameThreadHandle,7,"game Thread",gameThread,4096,0);
    GoBleThread_start();
 }

Create a new make target

Test it all to make sure things are still working

Run the make target and make sure that the game still plays and that you can get remote control messages.

Modify GoBleThread.c

Add the includes for the GameThread (to get access to the game message structure) and SystemGlobal (to get access to the queue)

#include "GameThread.h"
#include "SystemGlobal.h"

I don’t need (or want) the button/slider printout information.  So I will delete the old button code from goble_event_handler. (delete this stuff)

            WPRINT_APP_INFO(("# Buttons = %d ButtonMask=%02X Slider x=%02X Slider Y=%02X Raw=",(int)numButtons,(unsigned int)buttonMask,(unsigned int)sliderX,(unsigned int)sliderY));

            for(int i=0;i<p_attr_req->data.write_req.val_len;i++)
            {
                WPRINT_APP_INFO(("%02X ",p_attr_req->data.write_req.p_val[i]));
            }
            WPRINT_APP_INFO(("\n"));

Now, update the code to send the slider and button information to the GameThread (via the paddleQueue).  You may have noticed that the slider on GoBle gives you 0->0xFF and the direction is the inverse from the game we setup.  So, I will scale the value to 100 and invert it so that the controller moves the paddle the right way on line 143.  The message format is exactly the same as what we setup for the CapSense.  This means the CapSense slider works at the same time as the GoBle controller.

   case GATT_ATTRIBUTE_REQUEST_EVT:

        p_attr_req = &p_event_data->attribute_request;
        if( p_attr_req->request_type == GATTS_REQ_TYPE_WRITE  && p_attr_req->data.handle == HDLC_GOBLE_SERIALPORTID_VALUE)
        {
            uint32_t numButtons = p_attr_req->data.write_req.p_val[3];
            uint32_t sliderY = p_attr_req->data.write_req.p_val[5+numButtons];
            uint32_t sliderX = p_attr_req->data.write_req.p_val[6+numButtons];
            uint32_t buttonMask = 0x00;
            for(int i=0;i<numButtons;i++)
            {
                buttonMask |= (1<<p_attr_req->data.write_req.p_val[5+i]);
            }

            game_msg_t msg;
            msg.evt = MSG_POSITION;
            msg.val = 100 - (100*sliderY/255);
            wiced_rtos_push_to_queue(&paddleQueue,&msg,0);
            if(buttonMask & 0x10)
            {
                msg.evt = MSG_BUTTON0;
                msg.val = 1;
                wiced_result_t result=wiced_rtos_push_to_queue(&paddleQueue,&msg,0);
            }
            status = WICED_BT_GATT_SUCCESS;
        }
        break;

Program and Test

Mouser PSoC6-WiFi-BT L4 : The Video Game

Designing low-power, cloud-connected IoT devices with PSoC® 6 MCU’s and WICED® Wi-Fi/Bluetooth

Summary

In this lesson I’ll finish the video game thread by adding the graphics etc. to play the game.  In addition I’ll fix up the CapSense thread so that it is connected to the game via an RTOS queue.

There are three main things going on in this game.

  1. A state machine for the game screen (Splash, Start, Running, Over)
  2. A 20ms timer that updates the screen while the game is running (moves the Paddle and the Ball)
  3. A GUI queue where the rest of the system – CapSense,  Bluetooth and WiFi – can send Button and Paddle messages.

To implement this project I will:

  1. Setup the project and makefile by copying L3CapSenseTft
  2. Update gamethread.h to define the GUI queue messages
  3. Fix main.c to create the queue
  4. Create SystemGlobal.h to give the rest of the files access to the gui queue
  5. Updating the CapSenseThread to send GUI messages
  6. Update the includes in GameThread.c
  7. Add some #define macros to define game parameters
  8. Add a State Machine for the game & define some paddle movement methods
  9. Make forward declarations for the thread functions
  10. Create some variables to maintain game state
  11. Add display functions for the score and the speed
  12. Add functions to start and end the game
  13. Add helper functions to calculate the top and bottom of the paddle
  14. Add a function to update and draw the paddle
  15. Add a function to update and draw the ball
  16. Add a function for the game timer to call
  17. Update the main game thread

Setup the project and makefile by copying L3CapSenseTft

Use copy/paste to copy the L3CapSenseTft project to a new folder name L4Game.   Change the name of the makefile to be L4Game.mk.

Edit the makefile and change the name of the application.

NAME := App_WStudio_L4Game

$(NAME)_SOURCES := main.c \
    CapSenseThread.c \
	GameThread.c \
	cy_tft_display.c

$(NAME)_COMPONENTS := graphics/ugui

Create a make target for this project

Update GameThread.h to Define the GUI Messages

All of the threads in the system (CapSense, Bluetooth, and WiFi) will control the paddle and the button by sending messages to an RTOS queue.  In gameThread.h we will add a definition of that message.  The message is just a structure with two values – which GUI element and what value to send.

#pragma once
#include "wiced.h"

typedef enum {
    MSG_POSITION,
    MSG_BUTTON0,
    MSG_BUTTON1,
} game_evt_t;

typedef struct {
    game_evt_t evt;
    uint32_t val;
} game_msg_t;

void gameThread(wiced_thread_arg_t arg);

Fix main.c to Create the Queue

I typically believe that the RTOS primitives should be owned by the main.c.  To do this edit main.c and fix the includes.

#include "GameThread.h"
#include "wiced.h"
#include "CapSenseThread.h"
#include "SystemGlobal.h"

Then define the queue variable “paddleQueue” which I should have names “guiQueue” but it is way to late to fix it now — oh well.

/******************************************************
 *               Variable Definitions
 ******************************************************/

wiced_thread_t blinkThreadHandle;
wiced_thread_t capSenseThreadHandle;
wiced_thread_t gameThreadHandle;
wiced_queue_t paddleQueue;

Create the queue

void application_start( )
{
    wiced_init( );
    wiced_rtos_init_queue(&paddleQueue,"paddleQueue",sizeof(game_msg_t),10);
    wiced_rtos_create_thread(&blinkThreadHandle,7,"Blink Thread",pdlBlinkThread,500,0);
    wiced_rtos_create_thread(&capSenseThreadHandle,7,"CapSense Thread",capSenseThread,1024,0);
    wiced_rtos_create_thread(&gameThreadHandle,7,"game Thread",gameThread,4096,0);
 }

Create SystemGlobal.h

Each of the threads in the system need to have access to the paddleQueue.  In order to do that create a file called SystemGlobal.h and extern the variable to give them that access.

#pragma once

extern wiced_queue_t paddleQueue;

Updating the CapSenseThread to send GUI messages

Remember that when we setup the CapSenseThread originally, it just printed out the values.  Let’s fix it so send messages.  So, edit CapSenseThread.c.

  1. Add a message variable (line 8)
  2. Fix the button0 and button 1 to make and send RTOS messages (lines 20/21 and 26/27)
  3. Fix the slider to send the position (lines 33-35)
#include "wiced.h"
#include "GameThread.h"
#include "SystemGlobal.h"

void capSenseThread(wiced_thread_arg_t arg)
{

    game_msg_t msg;

    CapSense_Start();
    CapSense_ScanAllWidgets();
    while(1)
    {
        if(!CapSense_IsBusy())
        {

            CapSense_ProcessAllWidgets();
            if(CapSense_IsWidgetActive(CapSense_BUTTON0_WDGT_ID))
            {
                msg.evt = MSG_BUTTON0;
                wiced_rtos_push_to_queue(&paddleQueue,&msg,0);
            }

            if(CapSense_IsWidgetActive(CapSense_BUTTON1_WDGT_ID))
            {
                msg.evt = MSG_BUTTON1;
                wiced_rtos_push_to_queue(&paddleQueue,&msg,0);
            }

            uint32_t val = CapSense_GetCentroidPos(CapSense_LINEARSLIDER0_WDGT_ID);
            if(val < 0xFFFF)
            {
                msg.evt = MSG_POSITION;
                msg.val = val;
                wiced_rtos_push_to_queue(&paddleQueue,&msg,0);
            }
            CapSense_ScanAllWidgets();
        }
        wiced_rtos_delay_milliseconds(25); // Poll every 25ms (actual scan time ~8ms)
    }
}

Update the includes in GameThread.c

Now let’s fix GameThread.c.  Start by editing the includes to add a new file called “SystemGlobal.h” which contains the global variable for the GUI queue.

#include "GameThread.h"
#include "cy_tft_display.h"
#include "SystemGlobal.h"
#include "ugui.h"

Add some #define macros in GameThread.c

There are a number of constants which I use in the game.  In this section I use #define macros to define them.

#define UPDATE_SCREEN_TIME (20) // Update the screen every 20ms
#define SPEED (2)
#define SCREEN_X (320)
#define SCREEN_Y (240)
#define TOP_FIELD (21)
#define PD_WIDTH (10)
#define PD_LEN (70)
#define DOTS (3)
#define PADDLE0_COLOR (C_BLUE)
#define BALL_COLOR (C_GREEN)
#define BALL_SIZE (10)

Add a State Machine for the Game & Define Paddle Movement

Open up GameThread.c – all of the game control functions will go there.

There will be four screens in the game.  A splash screen to display Cypress and Mouser, a Ready Player 1 Screen, the actual game screen and a game over screen.

In addition the paddle can move a little bit at a time (increment) or jump directly to the position (absolute)

// States of the game
typedef enum {
    GS_SPLASH,
    GS_START,
    GS_RUNNING,
    GS_OVER
} game_state_t;

// Methods to move the paddle
typedef enum {
    PADDLE_INCREMENT,
    PADDLE_ABSOLUTE
} paddle_update_t;

Fix the gameState statemachine

In the splash screen I need to set the state machine to GS_SPLASH

static void displaySplashScreen()
{
    gameState = GS_SPLASH;
    UG_FontSelect( &FONT_22X36 );
    UG_PutStringCenter(SCREEN_X/2,SCREEN_Y/5,22,36,"Cypress");
    UG_PutStringCenter(SCREEN_X/2,SCREEN_Y/5*2,22,36,"Mouser");
    UG_PutStringCenter(SCREEN_X/2,SCREEN_Y/5*3,22,36,"PSoC 6");
    UG_PutStringCenter(SCREEN_X/2,SCREEN_Y/5*4,22,36,"WICED 4343");

    wiced_rtos_delay_milliseconds(2000);
}

In the start screen I need to set the state machine to GS_START

// Display the Start Screen
static void  displayStartScreen()
{
    gameState = GS_START;
    UG_FillScreen( C_BLACK );
    UG_FontSelect( &FONT_22X36 );
    UG_PutStringCenter(SCREEN_X/2,SCREEN_Y/2 -2 - 18 ,22,36,"Ready");
    UG_PutStringCenter(SCREEN_X/2,SCREEN_Y/2 + 2 + 18 ,22,36,"Player 1");
    displayStartButton();
}

Make Forward Declarations for Functions

You should define the functions in advance of using them

/******************************************************
 *               Static Function Declarations
 ******************************************************/
static void UG_PutStringCenter(uint32_t x, uint32_t y, uint32_t fontx, uint32_t fonty,char *string);
static void displaySplashScreen();
static void displayStartButton();
static void  displayStartScreen();
static void displayScore();
static void displaySpeed();
static void endGame();
static inline uint32_t calcPaddleTop();
static inline uint32_t calcPaddleBottom();
static void updatePaddle(paddle_update_t type);
static void updateBall();
static void updateScreen(void *arg);

Create some variables to maintain game state

The updateScreenTimer is used while the game is running to call the updateScreen every 20ms.  The rest of the variables are self explanatory.

/******************************************************
 *               Variable Definitions
 ******************************************************/
static UG_GUI   gui;
static wiced_timer_t updateScreenTimer;

static uint32_t gameScore;
static game_state_t gameState;

// position of the paddle
static uint32_t paddle0_desire_pos=0;
static uint32_t paddle0_cur_pos=0;

// Position, direction and speed of the ball
static uint32_t ballx,bally;
static int32_t ballXdir, ballYdir;
static uint32_t ballSpeed;

Add Display Functions for the Score & Speed

These two functions print the speed and score at the top of the screen.

// This function displays the score
static void displayScore()
{
    char buff[10];
    sprintf(buff,"%2X",(unsigned int)gameScore);
    UG_FontSelect(&FONT_12X20);
    UG_PutString( 75, 0, buff);
}

// This function displays the speed
static void displaySpeed()
{
    char buff[10];
    sprintf(buff,"%2X",(unsigned int)ballSpeed-1);
    UG_FontSelect(&FONT_12X20);
    UG_PutString( 275, 0, buff);
}

Add Function to Start the Game

When the game needs to start you:

  1. Reset the score
  2. Set the paddle position
  3. Move the ball to the middle of the paddle
  4. Set the ball to move to the right and down
  5. Clear the screen, display score and speed
  6. Start the game running
// This function initializes everything and starts a new game
static void startGame()
{
    gameScore = 0;

    paddle0_desire_pos = 50; // start the game with the paddle moving
    paddle0_cur_pos = 0;

    ballx = PD_WIDTH ;                   // start the ball on the paddle on the right of the screen
    bally  = calcPaddleTop() + PD_LEN/2; // start the ball in the middle of the paddle

    ballSpeed = SPEED;

    ballXdir = ballSpeed;
    ballYdir = ballSpeed;

    UG_FillScreen( C_BLACK );  // clear screen
    UG_FontSelect(&FONT_12X20);
    UG_PutString( 0, 0,  "Score:");
    displayScore();
    UG_PutString(200,0,"Speed:");
    displaySpeed();
    UG_DrawLine(0,20,SCREEN_X,20,C_RED); // red line under text to represent top of play screen

    gameState = GS_RUNNING;
    wiced_rtos_start_timer(&updateScreenTimer); // Timer to update screen
}

Add Function to End the Game

When the game is over you should:

  1. Move the game state to over
  2. Stop the timer
  3. Display game over
  4. Display press button 0 to start
// Stop the game
static void endGame()
{
    gameState = GS_OVER;
    wiced_rtos_stop_timer(&updateScreenTimer);
    UG_FontSelect( &FONT_22X36 );
    UG_PutStringCenter(SCREEN_X/2,SCREEN_Y/2,22,36,"Game Over");
    displayStartButton();
}

Add Helper Functions to Calculate Paddle Top & Bottom

There are two places where you need to know the position of the Paddle.  Specifically:

  1. To draw the paddle
  2. To figure out if the ball hit the paddle or not.

These two functions calculate the pixel position of the top and bottom of the paddle based on it current position

// Figure out the y position of the top of the paddle
static inline uint32_t calcPaddleTop()
{
    return (paddle0_cur_pos)*DOTS+TOP_FIELD;
}

// Figure out the y position of the bottom of the paddle
static inline uint32_t calcPaddleBottom()
{
    return (paddle0_cur_pos)*DOTS+PD_LEN+TOP_FIELD;
}

Add a Function to Update & Draw the Paddle

While the game is running you need the paddle to move.  There are two methods:

  1. Absolute just moves the current position immediately to the desired position.
  2. Incremental, which moves the paddle a little bit towards the desired position.
// Move the paddle either to : PADDLE_INCREMENT the next location or PADDLE_ABSOLUTE - final location
static void updatePaddle(paddle_update_t type)
{
    // If the paddle is where it is supposed to be then just return
    if(paddle0_cur_pos == paddle0_desire_pos)
        return;

    // erase the current paddle
    UG_FillFrame(0,calcPaddleTop(),PD_WIDTH,calcPaddleBottom(),C_BLACK);

    switch (type)
    {

    case PADDLE_INCREMENT:

        if(paddle0_cur_pos < paddle0_desire_pos)
            paddle0_cur_pos += SPEED;
        else
            paddle0_cur_pos -= SPEED;

        // If the paddle is within one move of the final spot, put it there
        if(abs((int)paddle0_cur_pos-(int)paddle0_desire_pos) < SPEED)
            paddle0_cur_pos = paddle0_desire_pos;
        break;

    case PADDLE_ABSOLUTE:
        paddle0_cur_pos = paddle0_desire_pos;
        break;
    }
    // draw the paddle
    UG_FillFrame(0,calcPaddleTop(),PD_WIDTH,calcPaddleBottom(),PADDLE0_COLOR);
}

Add a function to update and draw the ball

You need a function to:

  1. Move the ball
  2. Figure out if it hit the right/left/top/bottom of the screen and do the right thing.

When the ball hits one of those surfaces it needs to change direction to either plus or minus.

Every time it hits the paddle the score should increase and possibly speed up.

If it misses the paddle the game is over.

// Move the ball to the next location
static void updateBall()
{
    static const uint32_t BallFudgeFactor=3;

    UG_DrawCircle(ballx,bally,BALL_SIZE,C_BLACK);

    ballx += ballXdir;
    bally += ballYdir;

    // Check to see if the ball hit the far right side
    if(ballx > SCREEN_X - BALL_SIZE)
    {

        ballx = SCREEN_X - BALL_SIZE;
        ballXdir = -ballSpeed;
    }

    // check to see if the ball hit the far left side... or the paddle
    if(ballx < (BALL_SIZE + PD_WIDTH + BallFudgeFactor))
    {
        // See if the ball missed the paddle
        if(bally + BALL_SIZE < calcPaddleTop() || bally - BALL_SIZE > calcPaddleBottom())
        {
            endGame();
            //WPRINT_APP_INFO(("Missed Paddle\r\n"));
        }

        gameScore = gameScore + 1;
        displayScore();
        if(gameScore % 3 == 0) // Speed up every three hits
        {
            ballSpeed +=1;
            displaySpeed();
        }

        ballx = BALL_SIZE + PD_WIDTH + BallFudgeFactor;
        ballXdir = +ballSpeed;
    }
    // Check to see if the ball hit the top or bottom
    if(bally > SCREEN_Y - BALL_SIZE) // bottom
    {
        bally = SCREEN_Y - BALL_SIZE;
        ballYdir = -ballSpeed;
    }

    if(bally < TOP_FIELD+BALL_SIZE) // top
    {
        bally = BALL_SIZE+TOP_FIELD;
        ballYdir = +ballSpeed;
    }
    UG_DrawCircle(ballx,bally,BALL_SIZE,BALL_COLOR);
}

Create a Function for the Game Timer

An RTOS timer runs every 20ms.  That timer needs a function to move the paddle and move the ball.

// This function is called every UPADTE_SCREEN_TIME milliseconds by the updateScreenTimer
static void updateScreen(void *arg)
{
    updatePaddle(PADDLE_INCREMENT);
    updateBall();
}

Update the Main Game Thread

The main game thread needs to get messages out of the queue and then do the right thing based on the game state.

// Main game thread
void gameThread(wiced_thread_arg_t arg)
{
    game_msg_t msg;

    Cy_TFT_Init();                                             // Init the TFT
    UG_Init( &gui, Cy_TFT_displayDriver, SCREEN_X, SCREEN_Y ); // Connect the driver

    UG_FillScreen( C_BLACK );   // Clear the screen
    UG_SetBackcolor( C_BLACK );
    UG_SetForecolor( C_WHITE );

    wiced_rtos_init_timer(&updateScreenTimer,UPDATE_SCREEN_TIME,updateScreen,0);
    displaySplashScreen();
    displayStartScreen();

    while(1)
    {
        wiced_rtos_pop_from_queue(&paddleQueue,&msg,WICED_WAIT_FOREVER);
        switch(msg.evt)
        {
        case MSG_POSITION:
            if(gameState == GS_RUNNING)
                paddle0_desire_pos = msg.val/2;
            if(gameState == GS_OVER)
            {
                paddle0_desire_pos = msg.val/2;
                updatePaddle(PADDLE_ABSOLUTE);
            }
            break;

        case MSG_BUTTON0:
            if(gameState == GS_OVER || gameState == GS_START)
                startGame();
            break;
        case MSG_BUTTON1:
            break;
        }
    }
}

Program and Test

Now that it is all done… program and test it.

GameThread.c

Here is the whole thread is here so you can copy/paste it into your file.

#include "GameThread.h"
#include "cy_tft_display.h"
#include "SystemGlobal.h"
#include "ugui.h"
/******************************************************
*                      Macros
******************************************************/
#define UPDATE_SCREEN_TIME (20) // Update the screen every 20ms
#define SPEED (2)
#define SCREEN_X (320)
#define SCREEN_Y (240)
#define TOP_FIELD (21)
#define PD_WIDTH (10)
#define PD_LEN (70)
#define DOTS (3)
#define PADDLE0_COLOR (C_BLUE)
#define BALL_COLOR (C_GREEN)
#define BALL_SIZE (10)
/******************************************************
*                    Constants
******************************************************/
/******************************************************
*                   Enumerations
******************************************************/
/******************************************************
*                 Type Definitions
******************************************************/
// States of the game
typedef enum {
GS_SPLASH,
GS_START,
GS_RUNNING,
GS_OVER
} game_state_t;
// Methods to move the paddle
typedef enum {
PADDLE_INCREMENT,
PADDLE_ABSOLUTE
} paddle_update_t;
/******************************************************
*                    Structures
******************************************************/
/******************************************************
*               Static Function Declarations
******************************************************/
static void UG_PutStringCenter(uint32_t x, uint32_t y, uint32_t fontx, uint32_t fonty,char *string);
static void displaySplashScreen();
static void displayStartButton();
static void  displayStartScreen();
static void displayScore();
static void displaySpeed();
static void endGame();
static inline uint32_t calcPaddleTop();
static inline uint32_t calcPaddleBottom();
static void updatePaddle(paddle_update_t type);
static void updateBall();
static void updateScreen(void *arg);
/******************************************************
*               Variable Definitions
******************************************************/
static UG_GUI   gui;
static wiced_timer_t updateScreenTimer;
static uint32_t gameScore;
static game_state_t gameState;
// position of the paddle
static uint32_t paddle0_desire_pos=0;
static uint32_t paddle0_cur_pos=0;
// Position, direction and speed of the ball
static uint32_t ballx,bally;
static int32_t ballXdir, ballYdir;
static uint32_t ballSpeed;
/******************************************************
*               Functions
******************************************************/
// ARH Function to put text in the center of a point (UG_PutString does upper left)
static void UG_PutStringCenter(uint32_t x, uint32_t y, uint32_t fontx, uint32_t fonty,char *string)
{
y = y - fonty/2;
x = x - (strlen(string)/2)*fontx;
if(strlen(string)%2)
x = x - fontx/2;
UG_PutString(x,y,string);
}
// Display the splash screen
static void displaySplashScreen()
{
gameState = GS_SPLASH;
UG_FontSelect( &FONT_22X36 );
UG_PutStringCenter(SCREEN_X/2,SCREEN_Y/5,22,36,"Cypress");
UG_PutStringCenter(SCREEN_X/2,SCREEN_Y/5*2,22,36,"Mouser");
UG_PutStringCenter(SCREEN_X/2,SCREEN_Y/5*3,22,36,"PSoC 6");
UG_PutStringCenter(SCREEN_X/2,SCREEN_Y/5*4,22,36,"WICED 4343");
wiced_rtos_delay_milliseconds(2000);
}
// This function displays the start button message
static void displayStartButton()
{
UG_FontSelect(&FONT_12X20);
UG_PutStringCenter(SCREEN_X/2 , SCREEN_Y - 30 ,12,22,  "Press B0 To Start");
}
// Display the Start Screen
static void  displayStartScreen()
{
gameState = GS_START;
UG_FillScreen( C_BLACK );
UG_FontSelect( &FONT_22X36 );
UG_PutStringCenter(SCREEN_X/2,SCREEN_Y/2 -2 - 18 ,22,36,"Ready");
UG_PutStringCenter(SCREEN_X/2,SCREEN_Y/2 + 2 + 18 ,22,36,"Player 1");
displayStartButton();
}
// This function displays the score
static void displayScore()
{
char buff[10];
sprintf(buff,"%2X",(unsigned int)gameScore);
UG_FontSelect(&FONT_12X20);
UG_PutString( 75, 0, buff);
}
// This function displays the speed
static void displaySpeed()
{
char buff[10];
sprintf(buff,"%2X",(unsigned int)ballSpeed-1);
UG_FontSelect(&FONT_12X20);
UG_PutString( 275, 0, buff);
}
// This function initializes everything and starts a new game
static void startGame()
{
gameScore = 0;
paddle0_desire_pos = 50; // start the game with the paddle moving
paddle0_cur_pos = 0;
ballx = PD_WIDTH ;                   // start the ball on the paddle on the right of the screen
bally  = calcPaddleTop() + PD_LEN/2; // start the ball in the middle of the paddle
ballSpeed = SPEED;
ballXdir = ballSpeed;
ballYdir = ballSpeed;
UG_FillScreen( C_BLACK );  // clear screen
UG_FontSelect(&FONT_12X20);
UG_PutString( 0, 0,  "Score:");
displayScore();
UG_PutString(200,0,"Speed:");
displaySpeed();
UG_DrawLine(0,20,SCREEN_X,20,C_RED); // red line under text to represent top of play screen
gameState = GS_RUNNING;
wiced_rtos_start_timer(&updateScreenTimer); // Timer to update screen
}
// Stop the game
static void endGame()
{
gameState = GS_OVER;
wiced_rtos_stop_timer(&updateScreenTimer);
UG_FontSelect( &FONT_22X36 );
UG_PutStringCenter(SCREEN_X/2,SCREEN_Y/2,22,36,"Game Over");
displayStartButton();
}
// Figure out the y position of the top of the paddle
static inline uint32_t calcPaddleTop()
{
return (paddle0_cur_pos)*DOTS+TOP_FIELD;
}
// Figure out the y position of the bottom of the paddle
static inline uint32_t calcPaddleBottom()
{
return (paddle0_cur_pos)*DOTS+PD_LEN+TOP_FIELD;
}
// Move the paddle either to : PADDLE_INCREMENT the next location or PADDLE_ABSOLUTE - final location
static void updatePaddle(paddle_update_t type)
{
// If the paddle is where it is supposed to be then just return
if(paddle0_cur_pos == paddle0_desire_pos)
return;
// erase the current paddle
UG_FillFrame(0,calcPaddleTop(),PD_WIDTH,calcPaddleBottom(),C_BLACK);
switch (type)
{
case PADDLE_INCREMENT:
if(paddle0_cur_pos < paddle0_desire_pos)
paddle0_cur_pos += SPEED;
else
paddle0_cur_pos -= SPEED;
// If the paddle is within one move of the final spot, put it there
if(abs((int)paddle0_cur_pos-(int)paddle0_desire_pos) < SPEED)
paddle0_cur_pos = paddle0_desire_pos;
break;
case PADDLE_ABSOLUTE:
paddle0_cur_pos = paddle0_desire_pos;
break;
}
// draw the paddle
UG_FillFrame(0,calcPaddleTop(),PD_WIDTH,calcPaddleBottom(),PADDLE0_COLOR);
}
// Move the ball to the next location
static void updateBall()
{
static const uint32_t BallFudgeFactor=3;
UG_DrawCircle(ballx,bally,BALL_SIZE,C_BLACK);
ballx += ballXdir;
bally += ballYdir;
// Check to see if the ball hit the far right side
if(ballx > SCREEN_X - BALL_SIZE)
{
ballx = SCREEN_X - BALL_SIZE;
ballXdir = -ballSpeed;
}
// check to see if the ball hit the far left side... or the paddle
if(ballx < (BALL_SIZE + PD_WIDTH + BallFudgeFactor))
{
// See if the ball missed the paddle
if(bally + BALL_SIZE < calcPaddleTop() || bally - BALL_SIZE > calcPaddleBottom())
{
endGame();
//WPRINT_APP_INFO(("Missed Paddle\r\n"));
}
gameScore = gameScore + 1;
displayScore();
if(gameScore % 3 == 0) // Speed up every three hits
{
ballSpeed +=1;
displaySpeed();
}
ballx = BALL_SIZE + PD_WIDTH + BallFudgeFactor;
ballXdir = +ballSpeed;
}
// Check to see if the ball hit the top or bottom
if(bally > SCREEN_Y - BALL_SIZE) // bottom
{
bally = SCREEN_Y - BALL_SIZE;
ballYdir = -ballSpeed;
}
if(bally < TOP_FIELD+BALL_SIZE) // top
{
bally = BALL_SIZE+TOP_FIELD;
ballYdir = +ballSpeed;
}
UG_DrawCircle(ballx,bally,BALL_SIZE,BALL_COLOR);
}
// This function is called every UPADTE_SCREEN_TIME milliseconds by the updateScreenTimer
static void updateScreen(void *arg)
{
updatePaddle(PADDLE_INCREMENT);
updateBall();
}
// Main game thread
void gameThread(wiced_thread_arg_t arg)
{
game_msg_t msg;
Cy_TFT_Init();                                             // Init the TFT
UG_Init( &gui, Cy_TFT_displayDriver, SCREEN_X, SCREEN_Y ); // Connect the driver
UG_FillScreen( C_BLACK );   // Clear the screen
UG_SetBackcolor( C_BLACK );
UG_SetForecolor( C_WHITE );
wiced_rtos_init_timer(&updateScreenTimer,UPDATE_SCREEN_TIME,updateScreen,0);
displaySplashScreen();
displayStartScreen();
while(1)
{
wiced_rtos_pop_from_queue(&paddleQueue,&msg,WICED_WAIT_FOREVER);
switch(msg.evt)
{
case MSG_POSITION:
if(gameState == GS_RUNNING)
paddle0_desire_pos = msg.val/2;
if(gameState == GS_OVER)
{
paddle0_desire_pos = msg.val/2;
updatePaddle(PADDLE_ABSOLUTE);
}
break;
case MSG_BUTTON0:
if(gameState == GS_OVER || gameState == GS_START)
startGame();
break;
case MSG_BUTTON1:
break;
}
}
}

 

Mouser PSoC 6-WiFi-BT L3: Using the CY8CKIT-028-TFT Shield

Designing low-power, cloud-connected IoT devices with PSoC® 6 MCU’s and WICED® Wi-Fi/Bluetooth

Summary

In this lesson we will start making the game.  The first thing that it will need is a display and we will use the CY8CKIT-028-TFT.  In order to talk to the display we will use a library built into WICED called ugui.  That library needs a driver configuration which we will copy of out the code example we provide.  Finally we will start building a thread called the “GameThread” which will actually make up the game.

  1. Download CE222494_PSoC6_WICED_WiFi
  2. Copy the L2CapSense into L3CapSenseTft
  3. Copy cy_tft_display.c/h into the project
  4. Make a file GameThread.h
  5. Make a file GameThread.c
  6. Rename L2CapSense.mk to be L3CapSenseTft.mk & Fix
  7. Update main.c
  8. Test

Download CE222494_PSoC6_WICED_WiFi

If you click on the CY8CKIT-062-WiFi-BT webpage you will find that there are a bunch of files which are associated with the development kit, including CY8CKIT-062-WiFi-BT PSoC® 6 WiFi-BT Pioneer Kit Code Examples.zip.

Download that folder, then copy the directory into your WICED Studio Apps/WStudio folder.

Once you do that it should look like this:

Copy L3CapSense into L3CapSenseTft

Now copy/paste the L2CapSense project into a new project called L3CapSenseTft

Copy cy_tft_display.c/h into the project

Open up the CE222494 code example directory and copy the two files cy_tft_display.c andcy_tft_display.c which are drivers for the ugui library and then paste them into your new project L3CapSenseTft.

Make a file GameThread.h

Create a new file called GamThread.h and a definition of the GameThread which will be used by the main.c to get the game thread going.

#pragma once
#include "wiced.h"
void gameThread(wiced_thread_arg_t arg);

Make a file GameThread.c

Now create a file called GameThread.c it will have 5 functions.  Here is the whole file to make it simpler to copy and paste, but Ill explain each function one by one

#include "GameThread.h"
#include "cy_tft_display.h"
#define SCREEN_X (320)
#define SCREEN_Y (240)
static UG_GUI   gui;
// ARH Function to put text in the center of a point (UG_PutString does upper left)
static void UG_PutStringCenter(uint32_t x, uint32_t y, uint32_t fontx, uint32_t fonty,char *string)
{
y = y - fonty/2;
x = x - (strlen(string)/2)*fontx;
if(strlen(string)%2)
x = x - fontx/2;
UG_PutString(x,y,string);
}
// Display the splash screen
static void displaySplashScreen()
{
UG_FontSelect( &FONT_22X36 );
UG_PutStringCenter(SCREEN_X/2,SCREEN_Y/5,22,36,"Cypress");
UG_PutStringCenter(SCREEN_X/2,SCREEN_Y/5*2,22,36,"Mouser");
UG_PutStringCenter(SCREEN_X/2,SCREEN_Y/5*3,22,36,"PSoC 6");
UG_PutStringCenter(SCREEN_X/2,SCREEN_Y/5*4,22,36,"WICED 4343");
wiced_rtos_delay_milliseconds(2000);
}
// This function displays the start button message
static void displayStartButton()
{
UG_FontSelect(&FONT_12X20);
UG_PutStringCenter(SCREEN_X/2 , SCREEN_Y - 30 ,12,22,  "Press B0 To Start");
}
// Display the Start Screen
static void  displayStartScreen()
{
UG_FillScreen( C_BLACK );
UG_FontSelect( &FONT_22X36 );
UG_PutStringCenter(SCREEN_X/2,SCREEN_Y/2 -2 - 18 ,22,36,"Ready");
UG_PutStringCenter(SCREEN_X/2,SCREEN_Y/2 + 2 + 18 ,22,36,"Player 1");
displayStartButton();
}
// Main game thread
void gameThread(wiced_thread_arg_t arg)
{
Cy_TFT_Init();                                             // Init the TFT
UG_Init( &gui, Cy_TFT_displayDriver, SCREEN_X, SCREEN_Y ); // Connect the driver
UG_FillScreen( C_BLACK );   // Clear the screen
UG_SetBackcolor( C_BLACK );
UG_SetForecolor( C_WHITE );
displaySplashScreen();
displayStartScreen();
while(1)
{
wiced_rtos_delay_milliseconds(1000);
}
}

The main game thread function is: void gameThread(wiced_thread_arg_t arg).  This function

  1. Initializes the TFT
  2. Initializes the UGUI library
  3. Clears the screen (by setting it all black)
  4. Sets the colors to draw white on black
  5. Displays the splash screen (which takes 2 seconds)
  6. Displays the start screen
  7. Waits until the end of time
// Main game thread
void gameThread(wiced_thread_arg_t arg)
{
Cy_TFT_Init();                                             // Init the TFT
UG_Init( &gui, Cy_TFT_displayDriver, SCREEN_X, SCREEN_Y ); // Connect the driver
UG_FillScreen( C_BLACK );   // Clear the screen
UG_SetBackcolor( C_BLACK );
UG_SetForecolor( C_WHITE );
displaySplashScreen();
displayStartScreen();
while(1)
{
wiced_rtos_delay_milliseconds(1000);
}
}

The function displaySplashScreen simply sets the font, then draws 4 text strings, then waits for a few seconds… then moves on

// Display the splash screen
static void displaySplashScreen()
{
UG_FontSelect( &FONT_22X36 );
UG_PutStringCenter(SCREEN_X/2,SCREEN_Y/5,22,36,"Cypress");
UG_PutStringCenter(SCREEN_X/2,SCREEN_Y/5*2,22,36,"Mouser");
UG_PutStringCenter(SCREEN_X/2,SCREEN_Y/5*3,22,36,"PSoC 6");
UG_PutStringCenter(SCREEN_X/2,SCREEN_Y/5*4,22,36,"WICED 4343");
wiced_rtos_delay_milliseconds(2000);
}

The displayStartScreen put the “Ready Player 1 on the screen” and then tells the user to press the B0 to start the game.

// This function displays the start button message
static void displayStartButton()
{
UG_FontSelect(&FONT_12X20);
UG_PutStringCenter(SCREEN_X/2 , SCREEN_Y - 30 ,12,22,  "Press B0 To Start");
}
// Display the Start Screen
static void  displayStartScreen()
{
UG_FillScreen( C_BLACK );
UG_FontSelect( &FONT_22X36 );
UG_PutStringCenter(SCREEN_X/2,SCREEN_Y/2 -2 - 18 ,22,36,"Ready");
UG_PutStringCenter(SCREEN_X/2,SCREEN_Y/2 + 2 + 18 ,22,36,"Player 1");
displayStartButton();
}

The U8G_PutString function uses coordinates x and y to set the upper left of the text.  For formatting purposes it is easier for me to think about the middle of the string.  This function just calculates the upper left (x,y) given the middle center (x,y).  To do this you need to also know the (x,y) size of the font.

static void UG_PutStringCenter(uint32_t x, uint32_t y, uint32_t fontx, uint32_t fonty,char *string)

// ARH Function to put text in the center of a point (UG_PutString does upper left)
static void UG_PutStringCenter(uint32_t x, uint32_t y, uint32_t fontx, uint32_t fonty,char *string)
{
y = y - fonty/2;
x = x - (strlen(string)/2)*fontx;
if(strlen(string)%2)
x = x - fontx/2;
UG_PutString(x,y,string);
}

Rename L2CapSense.mk to be L3CapSenseTft.mk & Fix

To make this build we need to modify the makefile to know about the new thread as well as the tft driver.  In addition we need to tell the linker to link with the graphics library.

NAME := App_WStudio_L3CapSenseTft
$(NAME)_SOURCES := 	main.c \
CapSenseThread.c \
GameThread.c \
cy_tft_display.c
$(NAME)_COMPONENTS := graphics/ugui

Update main.c

In main.c I will:

  1. Include the GameThread.h
  2. Add a variable to hold the gameThreadHandle
  3. Then start the gameThread
#include "wiced.h"
#include "CapSenseThread.h"
#include "GameThread.h"
/******************************************************
*                      Macros
******************************************************/
/******************************************************
*                    Constants
******************************************************/
/******************************************************
*                   Enumerations
******************************************************/
/******************************************************
*                 Type Definitions
******************************************************/
/******************************************************
*                    Structures
******************************************************/
/******************************************************
*               Static Function Declarations
******************************************************/
/******************************************************
*               Variable Definitions
******************************************************/
wiced_thread_t blinkThreadHandle;
wiced_thread_t capSenseThreadHandle;
wiced_thread_t gameThreadHandle;
/******************************************************
*               Function Definitions
******************************************************/
void pdlBlinkThread(wiced_thread_arg_t arg)
{
while(1)
{
Cy_GPIO_Inv(GPIO_PRT0,3);
wiced_rtos_delay_milliseconds(500);
}
}
void application_start( )
{
wiced_init();
WPRINT_APP_INFO(("Started Application\n"));
wiced_rtos_create_thread(&blinkThreadHandle,7,"Blink Thread",pdlBlinkThread,500,0);
wiced_rtos_create_thread(&capSenseThreadHandle,7,"CapSense Thread",capSenseThread,1024,0);
wiced_rtos_create_thread(&gameThreadHandle,7,"game Thread",gameThread,4096,0);
}

Test

Now it is ready to test.  So create a Make Target, then Build and Program.  Hopefully you are now Ready Player 1.

 

Mouser PSoC 6-WiFi-BT L2 : WICED Studio & CapSense

Designing low-power, cloud-connected IoT devices with PSoC® 6 MCU’s and WICED® Wi-Fi/Bluetooth

Summary

In this lesson we will build your first WICED Studio project (the blinking LED)  and make sure that you can program the development kit.  Then we will update the project to include a thread for managing the CapSense block.  This thread will be carried into the other projects.

To implement this lesson I will follow these steps:

  1. Start WICED Studio 6.2
  2. Select 43xxx
  3. Create a folder called L2CapSense
  4. Create main.c and build a blinking LED thread
  5. Create L2CapSense.mk
  6. Create a make target
  7. Build Program and test it
  8. Create CapSenseThread.c
  9. Create CapSenseThread.h
  10. Update main.c
  11. Update the makefile
  12. Build Program and Test

Create the L2CapSense Folder

Create main.c

Right click on the folder and create a new file.  Name it L2CapSense

Insert the blinking LED code into main.c

#include "wiced.h"
/******************************************************
*                      Macros
******************************************************/
/******************************************************
*                    Constants
******************************************************/
/******************************************************
*                   Enumerations
******************************************************/
/******************************************************
*                 Type Definitions
******************************************************/
/******************************************************
*                    Structures
******************************************************/
/******************************************************
*               Static Function Declarations
******************************************************/
/******************************************************
*               Variable Definitions
******************************************************/
wiced_thread_t blinkThreadHandle;
/******************************************************
*               Function Definitions
******************************************************/
void pdlBlinkThread(wiced_thread_arg_t arg)
{
while(1)
{
Cy_GPIO_Inv(GPIO_PRT0,3);
wiced_rtos_delay_milliseconds(500);
}
}
void application_start( )
{
WPRINT_APP_INFO(("Started Application\n"));
wiced_rtos_create_thread(&blinkThreadHandle,7,"Blink Thread",pdlBlinkThread,500,0);
}

Create L2CapSense.mk

Create a makefile called L2CapSense.mk

Put the build information into the L2CapSense.mk

NAME := App_WStudio_L2CapSense
$(NAME)_SOURCES := 	main.c

Create a Make Target to run the project

Build and Test the Blinking LED

Create/Edit a File called CapSenseThread.c

#include "wiced.h"
void capSenseThread(wiced_thread_arg_t arg)
{
CapSense_Start();
CapSense_ScanAllWidgets();
while(1)
{
if(!CapSense_IsBusy())
{
CapSense_ProcessAllWidgets();
if(CapSense_IsWidgetActive(CapSense_BUTTON0_WDGT_ID))
{
WPRINT_APP_INFO(("Button 0 Active\n"));
}
if(CapSense_IsWidgetActive(CapSense_BUTTON1_WDGT_ID))
{
WPRINT_APP_INFO(("Button 1 Active\n"));
}
uint32_t val = CapSense_GetCentroidPos(CapSense_LINEARSLIDER0_WDGT_ID);
if(val < 0xFFFF)
{
WPRINT_APP_INFO(("Slider = %d\n",(int)val));
}
CapSense_ScanAllWidgets();
}
wiced_rtos_delay_milliseconds(25); // Poll every 25ms (actual scan time ~8ms)
}
}

Create/Edit a File Called CapSenseThread.h

#pragma once
#include "wiced.h"
void capSenseThread(wiced_thread_arg_t arg);

Update main.c

#include "wiced.h"
#include "CapSenseThread.h"

Add a variable to hold the handle for the capSenseThread at the top of main.c

wiced_thread_t capSenseThreadHandle;

Update the main function to start the CapSenseThread

void application_start( )
{
WPRINT_APP_INFO(("Started Application\n"));
wiced_rtos_create_thread(&blinkThreadHandle,7,"Blink Thread",pdlBlinkThread,500,0);
wiced_rtos_create_thread(&capSenseThreadHandle,7,"CapSense Thread",capSenseThread,1024,0);
}

Update the L2CapsenseThread.mk

NAME := App_WStudio_L2CapSense
$(NAME)_SOURCES := 	main.c \
CapSenseThread.c

Build, Program and Test the CapSenseThread

 

 

Mouser PSoC 6-WiFi-BT L1 : Developer Resources

Designing low-power, cloud-connected IoT devices with PSoC® 6 MCU’s and WICED® Wi-Fi/Bluetooth

Summary

This is an index of links to all of the PSoC 6 & CYW4343W learning resources.  You can click the links to go the website or see screen captures of the resources.

  1. PSoC 6 Product Page
  2. WiFi + Bluetooth Combo Product Page
  3. PSoC 6 Documentation
  4. PSoC 6 Community
  5. Wireless Combo Community
  6. CY8CKIT-062-BT-WiFi Development Kit Product Page
  7. CY8CKIT-062-BT-WiFi Development Kit Guide
  8. PSoC 6 Datasheet
  9. CYW4343W Datasheet
  10. PSoC 6 Technical Reference Manuals
  11. PSoC 6 Application Notes
  12. WiFi + Bluetooth Combo Application Notes
  13. PSoC 6 Code Examples
  14. Video Tutorials
  15. PSoC 6 Knowledge Base
  16. Peripheral Driver Library Documentation (Doxygen)
  17. WICED Documentation

PSoC 6 Product Page

You can find the PSoC 6 Product landing page for PSoC 6 here

WiFi + Bluetooth Combo Page

PSoC 6 Documentation

On the PSoC 6 Product Landing page there is a documentation tab that has links to all of the current documentation.

PSoC 6 Community

Cypress has an active development community and forum.  It can be found here.

Wireless WiFi + Bluetooth Combo Community

CY8CKIT-062-WiFi-BT Development Kit Web Page

Every Cypress development kit has a web page that contains all of the information about it, including links to the documentation and store.  The CY8CKIT-062-WiFi-BT kit page is here.

CY8CKIT-062-WiFi-BT Development Kit Guide

You can find the development kit guide here.

 

PSoC 6 Datasheet

The PSoC 6 Datasheet is available on Cypress.com here.

 

CYW4343W Datasheet

The CYW4343W datasheet can be found here.

PSoC 6 Technical Reference Manual

Each of the PSoC 6 devices has a lengthy discussion of the Technical Resources.  These documents can be found here

PSoC 6 Application Notes

You can get them all on our website… here is a link to the filtered list of PSoC 6 Application Notes.

The best application note is always the “Getting Started”.  In this case it is AN210781 “Getting Started with PSoC 6 MCU with Bluetooth Low Energy (BLE) Connectivity”

WiFi + Bluetooth Combo Application Notes

Here is a link to all of the WiFI Bluetooth Combo Application Notes.

PSoC 6 Code Examples

You can find all of the PSoC 6 code examples on the web.  In addition they are built into PSoC Creator.

Or in PSoC Creator:

Videos

Cypress has made a bunch of videos that take you step by step through an introduction to PSoC 6.  You can find them on the Cypress training website.

PSoC 6 Knowledge Base

The Cypress technical support team writes “Knowledge Base” articles when there are repeated issues reported by customers.  You can find them here.

Peripheral Driver Library Documentation (Doxygen)

All of the APIs in the PDL are documented in a Doxygen generated HTML document.  You can get there from

  • Help -> Peripheral Driver Library (this link is live only when you have a PSoC 6 project open)
  • Right click on a component -> Open PDL Documentation

IoT Mailbox: An Introduction

Summary

On the days that my newspaper carrier decides to deliver the newspaper, you can be sure that it won’t be there before 4:00am or after 10:00am.  Why do I still get a newspaper?  Because my wife likes it.  The status of the delivery is a constant source of speculation in the morning.  Have you been out to the newspaper box?  Will you go?  etc.  So it seems only logical to make an IoT device that figures out if I have a newspaper.  Obviously this is a bit silly to put as much time and effort as I have into that simple task.  But, doing something cool has it own worth.  And it will give me a chance to use the new WICED XR (extended range) Bluetooth radio module CYBT-483039-02.

And here is a picture of the new module on the CYBT-483039-EVAL EZ-BT evaluation kit.

For this design I want

  1. A newspaper sensor (not sure what that will be)
  2. Waterproof case
  3. A temperature sensor
  4. A custom circuit board
  5. A solar battery charger
  6. The ability to program and debug the 20719 (which will be acting as a beacon)
  7. An OTA Bootloader
  8. A Real Time Clock
  9. A wifi/ble bridge (inside the house)
  10. Some cloud data capture and display
  11. An iOS App

So far, this project has been quite a rabbit hole as I don’t/didn’t know anything about battery power supplies.  Specifically energy harvesting solar powered lithium ion battery chargers and system power supply.  Unfortunately Cypress doesnt make an IC that performs this application … so if you send Mouser a few hundred dollars and they will send you a box of a bunch of different Solar PMIC Evaluation kits, ST, TI, Linear, Analog Devices, and Microchip. (which I will write about in detail later)

Architecture

Here is a picture of the architecture.

In the next Article I will start the process of learning about Solar Battery Charging PMICs.

Article Description
IoT Mailbox Introduction An introduction to the long series of articles about creating a new IoT System.
IoT Mailbox: Solar Battery Chargers & System Power Supplies

Lesson 1 – WICED Bluetooth: A Tour of the Resources

WICED Bluetooth Using the CYW20719

# Title Comment
0 A Two Hour WICED Bluetooth Class WICED Bluetooth Using the CYW20719 in all its glory
1 Resources Links to all of the Cypress WICED information including videos, application notes etc.
2 Your First Project Making Hello World & the Blinky LED
3 The Super Mux Tool Learning about platforms and the Super Mux Tool
4 Snips Using the example projects to learn Bluetooth
5 Bluetooth Designer Using the tool to customize a project and get going fast
6 The CCCD & Notification Writing back to the Central
7 Advertising  Beacon Building a beacon project to advertise your custom information 
8 Scanner Viewing the world around you
9 Bluetooth Classic SPP Using the Serial Port Profile to Transmit Lots of Data

Source code: 

  • git@github.com:iotexpert/wiced_bt_intro.git
  • https://github.com/iotexpert/wiced_bt_intro

 

A Tour of the Resources

Cypress is committed to the “Whole Product”.  What that means is that we believe that you should have great software, hardware, dev kits, community etc. experience while using our chip.  So, before we get started Id like to show you all of the learning and development resources available to you.

  1. BLE & Bluetooth Connectivity Solutions
  2. WICED CYW20719 Product Page
  3. CYW20719 Product Guide
  4. CYW20719 Datasheet
  5. CYW20719 Software Features
  6. WICED Module Selection Guide
  7. CYW920719Q40EVB-01 Development Kit
  8. CYW920719Q40EVB-01 Product Page
  9. CYW920719 Quick Start
  10. CYW920719Q40EVB-01 Evaluation Board User Guide
  11. Cypress Community
  12. WICED Studio Bluetooth Community
  13. WICED Studio Bluetooth Forums
  14. WICED Studio
  15. WICED Studio Bluetooth Example Projects
  16. WICED Studio Documentation
  17. WICED Bluetooth API Guide
  18. WICED README.txt
  19. WICED Studio Release Notes
  20. WICED Studio Technical Brief
  21. WICED Bluetooth 101

Bluetooth BR+EDR Connectivity Solutions Page

This pages gets to you all of the Cypress WICED BR+EDR+Bluetooth products

WICED CYW20719 Product Page

When you get the the BLE+Bluetooth products page, then click “BLE+BT” to see just the chips Im talking about here (CYW20719)

CYW20719 Product Guide

The Product Guide is a website that has all (most?) of the links you might need to learn about the CYW20719

CYW20719 Datasheet

The Datasheet always anchors you to the reality of what the chip can and cannot do

CYW20719 Software Features

This webpage has a list of all of the stuff that you have access to inside of the WICED Bluetooth SDK.

And it goes on and on and on from here.

WICED Module Selection Guide

If you feel like building a Bluetooth Product, you are almost certainly going to want to use a FCC certified module.  This guide is a discussion of all of the module vendors.

CYW920719Q40EVB-01 Development Kit

Here is the development kit.  You can see in the picture that this is an Arduino form factor board.  It has a button and and LED plus programmer and UART bridge.  Most importantly it has a daughter card with the 20719 and and antenna.

CYW920719Q40EVB-01 Product Page

The product landing pages for the development kit has lots of resources specific to this kit including the manual and quick start guide.

CYW920719 Quick Start

The Quickstart guide is included in the kit.  Just a single sheet of paper that points out all of the features of the development kit.

CYW920719Q40EVB-01 Evaluation Board User Guide

The Users Guide is the manual for the development kit.  It shows you how to use all of the resouces on the board and how to get going with WICED Studio.

Cypress Community

The community is your anchor for support.  It has all of the documentation etc… and most importantly a vibrant user forum.

WICED Studio Bluetooth Community

The Bluetooth Community website brings together all of the people and product collateral for WICED Bluetooth.

WICED Studio Bluetooth Forums

The actual forum is accessible to everyone to ask questions about the Cypress products.  It is staffed by our technical support team and you will get good answers.

WICED Studio

WICED Studio is the development tool which you can use to build projects.  This will be the central tool used for the rest of this class.

WICED Studio Bluetooth Example Projects

Cypress delivers a bunch of “apps” which range from small examples we call SNIPs to more fully featured projects (in the Demo) folder.  Ill be showing you how to use the in the next set of tutorials.

WICED Studio Documentation

In the “doc” folder resides all of the documentation for WICED bluetooth.

WICED Bluetooth API Guide

The API guide is doxygen generated API documentation for the WICED Bluetooth SDK.

WICED Studio README.txt

WICED Studio Release Notes

 

WICED Studio Technical Brief

WICED Bluetooth 101

I have been working with  some amazing people to build a class for learning WICED Bluetooth.  You can find all of the material at https://github.com/cypresssemiconductorco/CypressAcademy_WBT101_Files

Lesson 0 – A Two Hour WICED Bluetooth Class

Summary

This is the top level web page for a two hour class about getting you started building products with WICED Bluetooth using the CYW20719.  My friend Victor told me that I am totally insane and that I have enough material for a semester long class, but I have faith in you.  The whole point of WICED Bluetooth is to make it possible for you to build your own Bluetooth application using the best Bluetooth radios in the world.  Life is too short for flaky Bluetooth!

When I started working on this class the marketing guys asked if they could show a “few” powerpoint slides at the begining.  But I knew that is just a euphemism for power point carpet bombing you to sleep.  That sucks, so we aren’t doing that.

AFH, TDD, ∏/4 DQPSK, ISM, 8DPSK, Symbol Rate, binary FM modulation, dBi, LMP, AMP, Gaussian Frequency Shift Keying,  Modulation Index, ppm, eye diagram, FCC, Frequency Offset, Slot Length, Frequency Drift, Differential Phase Encoding, Pulse Shaping, Modulation Accuracy, Differential Error Vector Magnitude, BER, Sensitivity, Co-Chanel interference, Intermodulation Characteristics, Symbol rate, Timeslot, piconet clock, piconet channel timing,  blah blah blah blah….

Whew… now that is out of the way.  Forget that.  Rather than start at the bottom with the radio and Maxwells equations I going to start at the top.  Cypress has a huge team of radio designers to deal with all of that so you don’t have to.  To be clear, this stuff matter A LOT to how well your product works but it is only the second best reason to use Cypress WICED Bluetooth.  The best reason to use Cypress is that our software team lets you have access to the most robust Bluetooth stack and radio infrastructure without having to figure all that crap out.  You may, in time, dig into all of that.  But none of it matters for building your applications.

This workshop is hands on, as that is the only real way to learn.  This series of web pages have the exact steps that I am going to use, so you can follow along with me.

You will need a few things for the class:

  • WICED Studio 6.2.1 which you can download from the Cypress Community
  • Copies of the example projects which you can get from GitHub.
  • A CYW920719Q40EVB-01 which you can get from Mouser
  • A Terminal Program like Putty
  • CySmart, a Bluetooth GATT DB Browser for Android (Google Play Store) or iPhone (Apple App Store)
  • The courage to be WICED!

Todays virtual workshop is going to go like this:

WICED Bluetooth Using the CYW20719

# Title Comment
0 A Two Hour WICED Bluetooth Class WICED Bluetooth Using the CYW20719 in all its glory
1 Resources Links to all of the Cypress WICED information including videos, application notes etc.
2 Your First Project Making Hello World & the Blinky LED
3 The Super Mux Tool Learning about platforms and the Super Mux Tool
4 Snips Using the example projects to learn Bluetooth
5 Bluetooth Designer Using the tool to customize a project and get going fast
6 The CCCD & Notification Writing back to the Central
7 Advertising  Beacon Building a beacon project to advertise your custom information 
8 Scanner Viewing the world around you
9 Bluetooth Classic SPP Using the Serial Port Profile to Transmit Lots of Data

Source code: 

  • git@github.com:iotexpert/wiced_bt_intro.git
  • https://github.com/iotexpert/wiced_bt_intro

 

WICED Studio 6.2.1

This class is build around WICED Studio 6.2, the Cypress IDE built on top of Eclipse.  WICED Studio has all of the tools, examples and SDKs to build projects for the Cypress WICED Bluetooth and WiFi products.  We support Windows, Mac and Linux and you can download it from our community website: https://community.cypress.com/community/wireless (which I hope you have done by now)

CYW920719Q40EVB-01

I am going to build and program all of the projects in this class into our development kit, the CY920719Q40EVB-01.  This development kit (which you should buy from Mouser) uses the Cypress CYW20719 Bluetooth chip.  This is the worlds best Dual-mode Bluetooth 5.0 chip.  Dual mode means that it does Bluetooth Classic BR/EDR as well as Bluetooth Low Energy.  Even better it can do both standards at the same time.

PSoC 6 BLE – Find Me Profile (Target)

Summary

I have been working on making some new videos for Cypress about the PSoC 6 BLE.  Everyone always likes to start with an easy BLE example, and the go to example is the “Find Me”.  I started by building a Find Me Profile example, but when I looked my example I decided that I wanted to trace the actual application all the way back to the Bluetooth Special Interest Group (SIG) specification.  So, that is exactly what I do for this article.

  • Bluetooth SIG Find Me Profile
  • Bluetooth SIG Immediate Alert Service
  • Configure PSoC 6 BLE Schematic & Pins
  • Setup FreeRTOS and Retarget I/O
  • PSoC 6 BLE Firmware Architecture
  • Firmware for the alertTask
  • Firmware for the Immediate Alert Service Callback
  • Firmware for the BLE Callback
  • Firmware for the BLE Task
  • Firmware to start the system

Bluetooth SIG Find Me Profile

The Bluetooth SIG defines a bunch of standard profiles.  A standard Profile is just some combination of standardized Services and Characteristics.  One of those Profiles is the Find Me Profile.  The concept behind the Find Me Profile was that you could connect to a device with the Find Me Profile, and then send it an alert, at which point it would start “alerting” (presumably blinking or beeping).  You could imagine a tag attached to your car keys for instance.  You can get the Find Me Profile Specification from the Bluetooth SIG website.  The spec is a 10ish page pdf that says a Find Me Profile is just a device with an Immediate Alert Service server or client.  Here is a picture from the spec:

The other interesting part of the specification defines how the advertising is supposed to work

Immediate Alert Service

But, what is an Immediate Alert Service?  Well, you can get the Immediate Alert Service specification from the Bluetooth SIG website as well.  It basically says that there is one Service called Immediate Alert with a UUID of 0x1802 and that Service has one Characteristic called “Alert Level” with a UUID of 0x2A06. Here is a screen shot.

Unfortunately the spec doesnt have the UUIDs in it, and you have to follow the [1] to the website.  On the Bluetooth SIG GATT Services UUID definition webpage you can see the UUID of the Immediate Alert Service (0x1802)

And on the Bluetooth SIG GATT Characteristics definition webpage you can see the UUID of the Alert Level (0x2A06)

It also says the alert characteristic is writable with three values (No, Mild, High) and if you click on Alert Level you can see the definition of those:

Next you can see information about the Alert Characteristic.

Configure PSoC 6 BLE Schematic & Pins

Now, lets build the project.  Create a new PSoC 6 BLE project and edit the schematic to have a BLE, UART, and two digital output pins (red, led9).

Assign the pins to the correct place on the CY8CKIT-062-BLE development kit.  I am going to use led9 to show when the device is connected (or not)

Change the build settings to add FreeRTOS (Heap 4) and Retarget I/O

Configure the BLE to be dual core and a peripheral

Add the Find Me Target (GATT Server) profile

Once that is done you will see the Immediate Alert Service (with the correct UUID from above)

And the Alert Level with the correct UUID

Next give the device a name (“FindMe”)

The spec calls for the advertising setup to be as follows

But I have to admit that I dont like to wait… so I configured it like this:

Next, configure the advertising packet to have the name of the device and the fact that it has a IAS Service

Setup FreeRTOS and Retarget I/O

Run “Generate Application” to bring in all of the BLE and PDL firmware.  Then edit “stdio_user.h” to setup the stdio support.

#include "project.h"
/* Must remain uncommented to use this utility */
#define IO_STDOUT_ENABLE
#define IO_STDIN_ENABLE
#define IO_STDOUT_UART      UART_1_HW
#define IO_STDIN_UART       UART_1_HW

Next, modify FreeRTOSConfig.h to include support for semaphores.

#define configUSE_COUNTING_SEMAPHORES           1

and a much bigger stack

#define configTOTAL_HEAP_SIZE                   (48*1024)

And finally the interrupts to support BLE

/* Put KERNEL_INTERRUPT_PRIORITY in top __NVIC_PRIO_BITS bits of CM4 register */
#define configKERNEL_INTERRUPT_PRIORITY         0xFF
/* Put MAX_SYSCALL_INTERRUPT_PRIORITY in top __NVIC_PRIO_BITS bits of CM4 register */
//#define configMAX_SYSCALL_INTERRUPT_PRIORITY    0xBF
#ifdef __NVIC_PRIO_BITS
/* __BVIC_PRIO_BITS will be specified when CMSIS is being used. */
#define configPRIO_BITS                   __NVIC_PRIO_BITS
#else
#define configPRIO_BITS                   4        /* 15 priority levels */
#endif    
#define configMAX_SYSCALL_INTERRUPT_PRIORITY    ( 1 << (8 - configPRIO_BITS) )

Firmware Architecture

There will be two tasks

  1. The alertTask which will be responsible for setting the state of the red LED.  To set the state of the LED any other task can set the EventGroup bits.
  2. The bleTask which will handle running the BLE and the generic BLE callback and the IAS callback.

Firmware for the alertTask

The alert task will

  1. Set things up including eventBits which I call “alertState”
  2. Turn off the red led
  3. Go into an infinite loop
  4. The loop will either 1) wait until the end of time or 2) timeout at 500ms)
  5. Then it will determine the cause of the timeout and set the state of the red LED
// These BITs are used to set the state of the red LED
EventGroupHandle_t alertState;
#define ALERT_NO_MASK   1<<0
#define ALERT_MILD_MASK 1<<1
#define ALERT_HIGH_MASK 1<<2
/*****************************************************************************\
* Function:    alertTask
* Input:       FreeRTOS Template - unused argument
* Returns:     void
* Description: 
*     This funtion is the mainloop for the alertTask.  It manages the state of
*     the RED led.  Other tasks communitcate with this task using the alertState
*     event bits.
\*****************************************************************************/
void alertTask(void *arg)
{
(void)arg;
printf("Alert Task Started\r\n");
TickType_t delay=portMAX_DELAY;
EventBits_t currentBits;
alertState = xEventGroupCreate();
xEventGroupSetBits(alertState,ALERT_NO_MASK);
Cy_GPIO_Write(red_PORT,red_NUM,LED_OFF);
while(1)
{
currentBits = xEventGroupWaitBits(alertState,ALERT_HIGH_MASK|ALERT_MILD_MASK|ALERT_NO_MASK,
pdTRUE,pdFALSE,delay);
switch(currentBits)
{
case ALERT_NO_MASK:
delay = portMAX_DELAY;
Cy_GPIO_Write(red_PORT,red_NUM,LED_OFF);
break;
case ALERT_HIGH_MASK:
delay = portMAX_DELAY;
Cy_GPIO_Write(red_PORT,red_NUM,LED_ON);
break;
case 0: // case 0 means timer expired & no bits set.   
case ALERT_MILD_MASK:
delay = 500;
Cy_GPIO_Inv(red_PORT,red_NUM);
break;
}   
}
}

Firmware for the Immediate Alert Service Callback

Cypress setup a bunch of APIs that know how to handle a bunch of the Bluetooth SIG Profiles/Services.  First lets look at the PSoC 6 BLE Middleware PDL Documentation to find the IAS Service.  It is in the “BLE Service-Specific API” section.

We want to implement a “GATT Server”… meaning our device has the IAS Server running on it so that a GATT Client can write into our database.  When I click on “IAS Server Functions” it takes me to this section of the documentation.  Basically what you do in your firmware is

  1. Register a callback with Cy_BLE_IAS_RegisterAttrCallback
  2. Setup the function that will be called back when the Alert Level characteristic is written.

Here is the documentation for the callback.  You can see that you need to make a function that matches the prototype of “cy_ble_callback_t”)

When you are called back you will get a void * which you can then cast into a pointer of type “cy_stc_ble_ias_char_value_t *”.  This structure will contain the value of the alert in the “cy_stc_ble_gatt_value_t *value” member.

If you look at the “cy_stc_ble_Gatt_value_t” structure you will find that it contains a pointer to an array of uint8_t (actually one one)

Now to actually write the callback function.  It just:

  1. Looks and sees if it is a write callback
  2. finds the value using the “Cy_BLE_IASS_GetCharacteristicValue” function.
  3. Then sends a message to the alertTask
/*****************************************************************************\
* Function:    iasCallback
* Input:       BLE IAS Service Handler Function: 
*      - eventCode (which only can be CY_BLE_EVT_IASS_WRITE_CHAR_CMD
*      - eventParam which is a pointer to  (and unused)
* Returns:     void
* Description: 
*   This is called back by the BLE stack when there is a write to the IAS
*   service.  This only occurs when the GATT Client Writes a new value
*   for the alert.  The function figures out the state of the alert then
*   sends a message to the alertTask usign the EventGroup alterState
\*****************************************************************************/
void iasCallback(uint32_t eventCode, void *eventParam)
{
(void)eventParam;
uint8_t alertLevel;
if(eventCode == CY_BLE_EVT_IASS_WRITE_CHAR_CMD)
{
/* Read the updated Alert Level value from the GATT database */
Cy_BLE_IASS_GetCharacteristicValue(CY_BLE_IAS_ALERT_LEVEL, 
sizeof(alertLevel), &alertLevel);
// The value of the characteristic could also be gotten like this:
//switch(((cy_stc_ble_ias_char_value_t *)eventParam)->value->val[0])
switch(alertLevel)
{
case CY_BLE_NO_ALERT:
printf("No alert\n");
xEventGroupSetBits(alertState,ALERT_NO_MASK);               
break;
case CY_BLE_MILD_ALERT:
printf("Medium alert\n");
xEventGroupSetBits(alertState,ALERT_MILD_MASK);               
break;
case CY_BLE_HIGH_ALERT:        
printf("High alert\n");
xEventGroupSetBits(alertState,ALERT_HIGH_MASK);               
break;
}   
}   
}

Instead of calling the function to get the values, you could have also been done this:

switch(((cy_stc_ble_ias_char_value_t *)eventParam)->value->val[0])

Firmware for the Ble Event Handler

The BLE event handler is really simple,  it just prints out some debugging information depending on the event.  It also starts the advertising when the stack starts or when it has been disconnected.

/*****************************************************************************\
* Function:    customEventHandler
* Input:       Cy_BLE Event Handler event and eventParameter
* Returns:     void
* Description: 
*   This funtion is the BLE Event Handler function.  It is called by the BLE
*   stack when an event occurs 
\*****************************************************************************/
void customEventHandler(uint32_t event, void *eventParameter)
{
(void)eventParameter; // not used
switch (event)
{
case CY_BLE_EVT_STACK_ON:
printf("Stack Started\r\n");
Cy_BLE_GAPP_StartAdvertisement(CY_BLE_ADVERTISING_FAST, CY_BLE_PERIPHERAL_CONFIGURATION_0_INDEX);
break;
case CY_BLE_EVT_GAP_DEVICE_DISCONNECTED:
printf("Disconnected\r\n");
Cy_GPIO_Write(led9_PORT,led9_NUM,LED_OFF); // Turn the LED9 Off
Cy_BLE_GAPP_StartAdvertisement(CY_BLE_ADVERTISING_FAST, CY_BLE_PERIPHERAL_CONFIGURATION_0_INDEX);
break;
case CY_BLE_EVT_GATT_CONNECT_IND:
printf("Connected\r\n");
Cy_GPIO_Write(led9_PORT,led9_NUM,LED_ON); // Turn the LED9 On             
break;
default:
break;
}
}

Firmware for the bleTask

The bleTask has two functions

  1. an ISR that is called whenever an IPC event happens (so that it can unlock the semaphore to process events)
  2. a main function which starts the system, registers the callback and runs process events at the right time.
/*****************************************************************************\
* Function:    bleInterruptNotify
* Input:       void (it is called inside of the ISR)
* Returns:     void
* Description: 
*   This is called back in the BLE ISR when an event has occured and needs to
*   be processed.  It will then set/give the sempahore to tell the BLE task to
*   process events.
\*****************************************************************************/
void bleInterruptNotify()
{
BaseType_t xHigherPriorityTaskWoken;
xHigherPriorityTaskWoken = pdFALSE;
xSemaphoreGiveFromISR(bleSemaphore, &xHigherPriorityTaskWoken); 
portYIELD_FROM_ISR( xHigherPriorityTaskWoken );
}
/*****************************************************************************\
* Function:    bleTask
* Input:       A FreeRTOS Task - void * that is unused
* Returns:     void
* Description: 
*  This task starts the BLE stack... and processes events when the bleSempahore
*  is set by the ISR.
\*****************************************************************************/
void bleTask(void *arg)
{
(void)arg;
printf("BLE Task Started\n");
bleSemaphore = xSemaphoreCreateCounting(UINT_MAX,0);
Cy_BLE_Start(customEventHandler);
Cy_BLE_IPC_RegisterAppHostCallback(bleInterruptNotify);
while(Cy_BLE_GetState() != CY_BLE_STATE_ON) // Get the stack going
{
Cy_BLE_ProcessEvents();
}
Cy_BLE_IAS_RegisterAttrCallback (iasCallback);
for(;;)
{
xSemaphoreTake(bleSemaphore,portMAX_DELAY);
Cy_BLE_ProcessEvents();
}
}

Main Firmware

Finally the main firmware just starts all of the tasks.

// Starts the system
int main(void)
{
__enable_irq(); 
UART_1_Start();
setvbuf( stdin, NULL, _IONBF, 0 ); 
setvbuf( stdout, NULL, _IONBF, 0 ); 
printf("System Started\r\n");
xTaskCreate(bleTask,"bleTask",8*1024,0,2,&bleTaskHandle);
xTaskCreate(alertTask,"AlertTask",configMINIMAL_STACK_SIZE,0,1,0);
vTaskStartScheduler();
for(;;)
{
}
}

Test

After doing all of this, you can run CySmart to test the system:

 

Particle Photon Configuration with Tinker Firmware

Summary

In the last article I wrote about using a CY3280 MBR3 Shield on a WICED WiFI 943907AEVAL1F development kit at the GE MegaHackathon.  In the next article I will show you the WICED HTTP firmware that I wrote to connect to the Particle Photon which they were using (I have written about Particle Photon before).  Obviously, I like Particle Photon because it is a WICED 43362 WiFi Radio.  In this article, I am going to show you how to get a Particle Photon going with the Tinker firmware.  Then test it with the iOS app, the web console and finally using CURL.  The CURL is just a mechanism to send HTTP Posts to the Tinker firmware, exactly what I will do in the next article on the WICED development kit.

Configure the Particle Photon

When you get a fresh Particle Photon out of the box you need to get it introduced to your WiFi network, and attached to your console.  To do this, start by going to setup.particle.io and following the process.

Particle Photon Configuration

 

Particle Photon Configuration

Particle Photon Configuration

Particle Photon Configuration

Particle Photon Configuration

Particle Photon Configuration

 

 

Particle Photon Configuration

 

 

Particle Photon Configuration

Testing with iPhone App

One you have “claimed” your Particle Photon and it is connected to the network, then you can talk to it with the Particle iOS app.  Here is what it looked like when I started the App on my phone.  You can see the device called “iotexpert-example” (remember that is what I named it from above).  The “Elkhorn_creek” Photon is the one that I wrote about in this article.

Particle Photon iOS App

Although the console says that I have the Tinker Firmware on the app, I wanted to make sure, so I use the iOS App to re-flash the Tinker Firmware.  You can do this by clicking the new device, then press the little “…” at the top right of the screen.  Finally click “Reflash Tinker”. It is awesome how fast that happens.

Particle Photon iOS Flash Firmware

Once I am sure about the version of the Tinker firmware, I test to make sure that things are working correctly.  First I click on “D7”

Particle Photon iOS Tinker App

Which brings up this screen where you can select digitalRead and digitalWrite.

Particle Photon iOS Tinker App

Then when you press the button it will switch from high to low and vice versa.

Particle Photon iOS Tinker App Particle Photon iOS Tinker App

You can also call the API directly by pressing “inspect” then “Data”

Particle Photon iOS digitalwrite Data Function

Particle Photon Console

In addition to using the iOS App, you can also interact with the Photon using the console.

Particle Photon Console

When I click on the “iotexpert-example” it brings up this screen where I can run the Tinker firmware functions.  In this case I ran “digitialwrite” with “D7,HIGH” which turns on the LED on the Photon.

Particle Photon Console

Testing the Particle Photon with CURL

The Particle Cloud exposes an API which lets you send Restful API requests to specific devices.  The Unix tool CURL lets me type command line requests (which I will mimic in the next Article using WICED).  The first command that I send is and HTTP GET which will return a JSON document that describes the configuration on my Photon.

curl https://api.particle.io/v1/devices/iotexpert-example?access_token=1311f67269d6

You can see the device name etc in the JSON below.  It also shows that the Tinker firmware has four function built into it.

{  
"id":"2a001b000347353137323334",
"name":"iotexpert-example",
"last_app":null,
"last_ip_address":"69.23.226.142",
"last_heard":"2017-09-17T15:38:56.406Z",
"product_id":6,
"connected":true,
"platform_id":6,
"cellular":false,
"notes":null,
"status":"normal",
"current_build_target":"0.5.3",
"variables":{  
},
"functions":[  
"digitalread",
"digitalwrite",
"analogread",
"analogwrite"
]
}

Then I can send a HTTP POST using Curl to turn on the D7 LED.

 curl https://api.spark.io/v1/devices/2a001b000347354/digitalwrite -d access_token=1311f67269d66b -d params=D7,HIGH

You can see the Blue LED right next to D7 turns on.

Particle Photon

Particle Photon Tinker App

I wondered where the Tinker App API was documented?  It turns out that the App is available in Particle library.  You can search by typing “tinker”

The section of code that matters is here.  The function takes a “String” and parses it.  It demands that the 2nd character be a PIN number between 0 and 7.  The first character be a capital “D”.  Then you need to have a “HIGH” (in caps) or a “LOW” (in caps)

/*******************************************************************************
* Function Name  : tinkerDigitalWrite
* Description    : Sets the specified pin HIGH or LOW
* Input          : Pin and value
* Output         : None.
* Return         : 1 on success and a negative number on failure
*******************************************************************************/
int tinkerDigitalWrite(String command)
{
bool value = 0;
//convert ascii to integer
int pinNumber = command.charAt(1) - '0';
//Sanity check to see if the pin numbers are within limits
if (pinNumber< 0 || pinNumber >7) return -1;
if(command.substring(3,7) == "HIGH") value = 1;
else if(command.substring(3,6) == "LOW") value = 0;
else return -2;
if(command.startsWith("D"))
{
pinMode(pinNumber, OUTPUT);
digitalWrite(pinNumber, value);
return 1;
}
else if(command.startsWith("A"))
{
pinMode(pinNumber+10, OUTPUT);
digitalWrite(pinNumber+10, value);
return 1;
}
else return -3;
}

In order to access the digitialwrite function via the network, they publish it (and some others).

/***
* All the based tinker functions are registered. You can add you own code.
***/
// This #include statement was automatically added by the Spark IDE.
#include "Tinker.h"
void setup() {
//Register all the Tinker functions
Spark.function("digitalread", tinkerDigitalRead);
Spark.function("digitalwrite", tinkerDigitalWrite);
Spark.function("analogread", tinkerAnalogRead);
Spark.function("analogwrite", tinkerAnalogWrite);
}
void loop() {
}

When I first thought about the Tinker firmware I thought that it must be reasonably complex.  But it is not because it takes advantage of the Particle cloud functionality.  All in all what they did is very elegant and simple..

Now that it looks like my Particle Photon is working, in the next article I will show you WICED firmware to read the CapSense buttons and then Turn On/Off the LED on the Photon.

CY3280 MBR3 & WICED CYW943907

Summary

In the last Article I talked about the GE Megahackathon.  One of the groups at the event got interested in using a CY3280 MBR3 to send signals via a WICED CYW943907 to the Particle IO server which was connected to a Particle Photon.  I helped them with the implementation and thought that it would be useful to show here.

CYW3280 & WICED CYW943907

Cypress CY3280 MBR3

The CY3280 MBR development kit is a CapSense demonstration kit that shows the Mechanical Button Replacement 3 chip.  It features 4 CapSense buttons with LEDs, a proximity sensor, and a buzzer.  It is connected to another MCU via the Arduino pins. of the WICED CYW943907.  The device sits on the I2C bus and acts as an I2C Slave.  You configure it using EZ Click.

When you run EZ Click you can setup the configure the internal registers of the MBR3 to make the board act like a bunch of different things.  In this case I turned on

  • The MBR buttons 1-4 (you can see them in the picture above)
  • The Flanking Sensor rejection which makes it so that you can only press one button at a time.
  • All of the automatic tuning features.

EZ-Click CY3280

Once the main CapSense is configured, I moved to the other part of the configuration where I setup

  • The 4 LEDs to toggle and be full brightness when on
  • The buzzer to buzz for 100ms at 4kHz when something happens
  • The host interrupt pin as CS15/SH/HI.  This made the Arduino pin D2 be an interrupt when something happened so that WICED would poll the MBR3

EZ-Click CY3280

Once these settings were all done, I downloaded the firmware configuration via the KitProg USB connector.  Then I tested it using the bridge control panel which I have shown you a bunch of different times in the past.  The MBR3 acts as an I2C slave.  To find out what the state of the buttons are you need to read register 0xAA.  The only little trick is that the chip goes to sleep to save power.  In order to wake it up you need to send an I2C transaction, which ends up getting NAK’d.  But the next transaction you send will be ACKd.  In the screenshot below you can see that I tried two of the buttons (0x08 and 0x20)

Bridge Control Panel

One problem that I had is that the power system of this board is setup to take 5V from the Arduino base board but the WICED development kit gives only 3.3v.  Here is a picture of the power system from the MBR3 schematic.

CY3280 Power Supply

The MBR3 can run on 3.3Vs.  In fact it can run all the way down to 1.7v and up to 5.5v, but for some reason (which I can’t remember) we made the board only work with 5.0v.  To fix this problem I removed J12 and then wired a wire from the 3.3V Arduino pin.  The wire is soldered onto the 3.3v pin, but has a female connector on the other side so that it can be plugged into J12.  Here is a picture:

CY3280

The last thing that I needed to do was move the jumpers to position “A” which made the host interrupt pin be connected to D2, and move the I2C jumpers so that the MBR3 was connected to the Arduino instead of the P5LP kitprog.  You can see that in the J3_scl and J3_sda in the picture above.

WICED CYW943907

The CYW934907AEVAL1F is an development kit for the Cypress 43907 MCU+WiFi module.  The WICED CYW943907 board can do dual band (2.4 and 5Ghz), 802.11 a/b/g/n,  Ethernet and a whole bunch of other stuff.

WICED CYW943907

The first firmware that I wrote in WICED Studio:

  • Sets up an ISR to unlock a semaphore when the interrupt occurs
  • Initialized WICED_GPIO_9 to be an input and connected to an ISR … this is also known as Arduino D2
  • Setup the I2C Master Hardware in the 43907
  • Wait for a semaphore (from the ISR)
  • Read the I2C register 0xAA from the MBR

The only trick in the firmware is that I read the I2C with a “do” loop until I get a valid result, meaning that the MBR3 has woken up.

#include "wiced.h"
#define I2C_ADDRESS (0x37)
#define BUTTON_REG (0xAA)
wiced_semaphore_t buttonPress;
/* Interrupt service routine for the button */
void button_isr(void* arg)
{
wiced_rtos_set_semaphore(&buttonPress);
}
/* Main application */
void application_start( )
{
wiced_init();	/* Initialize the WICED device */
WPRINT_APP_INFO(("Started\n"));
wiced_result_t result;
wiced_rtos_init_semaphore(&buttonPress);
// WICED_GPIO_9 is the MBR3 Interrupt Pin
wiced_gpio_init(WICED_GPIO_9,INPUT_PULL_UP);
wiced_gpio_input_irq_enable(WICED_GPIO_9, IRQ_TRIGGER_FALLING_EDGE, button_isr, NULL); /* Setup interrupt */
/* Setup I2C master */
const wiced_i2c_device_t i2cDevice = {
.port = WICED_I2C_2,
.address = I2C_ADDRESS,
.address_width = I2C_ADDRESS_WIDTH_7BIT,
.speed_mode = I2C_STANDARD_SPEED_MODE
};
wiced_i2c_init(&i2cDevice);
/* Tx buffer is used to set the offset */
uint8_t tx_buffer[] = {BUTTON_REG};
uint8_t buttonStatus;
while ( 1 )
{
wiced_rtos_get_semaphore(&buttonPress,WICED_WAIT_FOREVER);
// Do this until the MBR3 is alive.  It goes into deep sleep and wakes when you
// send the first command.
do {
result = wiced_i2c_write(&i2cDevice, WICED_I2C_START_FLAG | WICED_I2C_STOP_FLAG, tx_buffer, sizeof(tx_buffer));
}
while(result != WICED_SUCCESS);
result=wiced_i2c_read(&i2cDevice, WICED_I2C_START_FLAG | WICED_I2C_STOP_FLAG, &buttonStatus, sizeof(buttonStatus));
WPRINT_APP_INFO(("Button State = %X\n", buttonStatus)); /* Print data to terminal */
}
}

Once that is programmed I program and test the firmware.

Testing WICED CYW943907

In the next article I will modify all of this firmware and make the WICED CYW943907 send data via HTTP to the Cloud.

Leviton HomeKit D15S Light Switch – WICED WiFi

Summary

A couple of week ago I was in San Jose teaching a WICED WiFi programming class.  One of the bad-ass WICED Applications Engineers told me that Leviton was shipping a new Leviton HomeKit Light Switch that used WICED WiFi and Bluetooth called the Leviton DS15.  What was even cooler was the he had done a decent amount of the design with them.  So, I ordered a few of them from amazon.com to try out.  This started my normal spectacle of house-wiring that is a spiral of me shocking myself, bleeding, cussing and worst of all spilling my beer.  You would think that a guy with a degree from Georgia Tech and 25 years as a practicing engineer would know better… but props to those electricians out there as they have mad wiring skills.

The Panel

I know that it should be obvious.  Really, I do know.  But every time I start working on wiring in my house I think… “Oh I just have 1 or 2 things to do I don’t need to turn off the breaker.”  And literally every damn time, I shock the piss out of myself.  So, today I decided to turn over a new leaf and turn off the breaker.  What was interesting this time was I also decided to use my meter to make sure that the breaker was off… and today I discovered that the label on the panel in my barn is wrong.  Damn that electrician… I take back everything nice I said about electricians.  (That ugly handwriting is mine fixing the label)

electric panel

Installing the Leviton HomeKit Light Switch

I bought a bunch of the the Leviton HomeKit Decora switches from Amazon.com.  They are a normal looking paddle light switches, but they come with WiFi and Bluetooth and are compatible with Apple HomeKit.  This means you don’t need a special app on your phone to use them, and hopefully that means that the wife-factor is low enough.  (A hint for all you guys out there… if the lights don’t come on when your wife clicks them… you had better have a good couch).   Originally I had installed ZWave Light Switches in the barn as my son always leaves the stupid lights on, but I wanted to try a product that had chips in it that I work on.  Here we go:

Leviton HomeKit Light Switch

When you open the box you get the normal book worth of instructions, in 5 languages (or something).  You also get a cream colored faceplate.  The paper on the upper left of the picture shows the serial number that you scan in with the camera in the Leviton app to attach to your device to HomeKit.

Leviton HomeKit inside the box

Here is a zoom in on just the light switch.

Leviton HomeKit Light Switch

Here is a picture of the light switches in the box.  One of the things that I always struggle with is getting all of the wires to fit neatly in the box.  This box was originally done old school (only switching the hot)… which doesn’t work with these light switches.

Leviton HomeKit Light Switch Installed

Once you have the switches installed, you turn the breaker back on and run the Leviton App. The App will let you add the switch to HomeKit by scanning the numbers with your camera.

Leviton HomeKit Decora App

Once paired you can control them with the Leviton App.

Leviton HomeKit Decora App

Or with the HomeKit App.

Apple Homekit App

I did notice that there is now two generations of light switches 6 inches from where they were originally installed.   I suppose I should have cleaned that up.

Installation Aftermath

Finally… dont cry over spilled beer (actually do.. as it is a really good West 6th IPA)

West 6th IPA