Edit: It turns out that I wrote this article in February of 2018 and never published it.  But it has good stuff… so here it is.

Summary

Over the last several weeks I have been spending time working with the PSoC 6 BLE.  Specifically the BLE part of the system.  Last weekend I found myself having problems with my design and I was really struggling to figure them out.  I realized that I “knew” what was happening with the BLE callbacks… but that I didn’t really “KNOW!”.  So I decided to build a simple project to show what events were happening and why.

In this article I will show/tell you how (to):

  1. The BLE is supposed to work
  2. The PSoC 6 BLE example project
  3. Write the firmware
  4. Test the system

You can “git” this workspace at git@github.com:iotexpert/PSoC-6-BLE-Events.git or GitHub.

How the PSoC 6 BLE is supposed to work

Honestly, the first time I started looking at the BLE (in my case it was the PSoC 4 BLE), it felt overwhelming.  But, it is actually pretty simple.  You, the firmware developer, send commands to the BLE stack using the Cypress PDL BLE middleware API.  Then, when “something” happens, the BLE stack sends you back an “event” in the callback.  That “something” can either be an acknowledgement that your API call has done something, or, it can be that something has happened on the radio side (e.g. you have been connected to by a client).  When you write a PSoC 6 BLE program you need to provide an event handler function (so that the BLE stack can send you events).  The function prototype for that handler is:

void CyBle_AppCallback( uint32 eventCode, void *eventParam )

This function has two arguments.

  1. An integer that is an eventCode (which you can put into the switch).  All of the event codes will look like “CYBLE_EVT_”
  2. A void pointer, that you will cast into a specific pointer based on the event code.

Your event handler code will then be a big switch statement, where you will look at the events, and do something (or not).

void CyBle_AppCallback( uint32 eventCode, void *eventParam )
{
    switch( eventCode )
    {
        /* Generic events */

        case CYBLE_EVT_STACK_ON:
            /* CyBle_GappStartAdvertisement( CYBLE_ADVERTISING_FAST ); */
        break;

When you look in the PDL BLE Middleware documentation you can see the APIs and what events happen based on your API calls.  For example Cy_BLE_GAPP_StartAdvertisement tell the PSoC BLE Stack to start advertising.  You can see that it will generate 4 possible events i.e. CY_BLE_EVT_GAPP_START_STOP

When you click on the event in the documentation it will tell you the meaning of the event, and what the eventParameter means (i.e. what you should cast it to in order to figure out the data passed to you)

Build the project

To build the project, first make a new PSoC 63 BLE project.  Then edit the schematic to have a BLE and a UART.

PSoC 6 BLE Events Schematic

Assign the UART pins to the KitProg UART bridge pins (aka P50/P51)

PSoC 6 BLE Events Pin Assignment

Configure the BLE to be a GAP Peripheral.

PSoC 6 BLE Events Configuration

Add a custom service to the project by loading the LED Service.  It doesn’t really matter what service you add for this project.  I just picked this one because I am using it for another project.  You could have just as easily picked one of the pre-existing definitions or made your own.

PSoC 6 BLE Events Configuration

This is what the LED Service looks like.

PSoC 6 BLE Events - LED Service

Configure the GAP settings.  Specifically name your device – in this case I named mine “mytest”

PSoC 6 BLE Events GAP Configuration

Edit the advertising packet to include the name and the service.

PSoC 6 BLE Events Advertising Configuration

Write the Firmware

Remember the main event in this example project is the BLE Event Handler.  I created this event handler with the events that I normally used (i.e. CY_BLE_EVT_STACK_ON) and then kept adding events until I had them all defined.  The way that I knew an event was missing from the “switch” was by the default case printing out the event number.

void customEventHandler(uint32_t event, void *eventParameter)
{
    
    /* Take an action based on the current event */
    switch (event)
    {
        /* This event is received when the BLE stack is Started */
        case CY_BLE_EVT_STACK_ON:
            printf("CY_BLE_EVT_STACK_ON\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("CY_BLE_EVT_GAP_DEVICE_DISCONNECTED: bdHandle=%x, reason=%x, status=%x\r\n-------------\r\n",
            (unsigned int)(*(cy_stc_ble_gap_disconnect_param_t *)eventParameter).bdHandle,
            (unsigned int)(*(cy_stc_ble_gap_disconnect_param_t *)eventParameter).reason,
            (unsigned int)(*(cy_stc_ble_gap_disconnect_param_t *)eventParameter).status);
        
            Cy_BLE_GAPP_StartAdvertisement(CY_BLE_ADVERTISING_FAST, CY_BLE_PERIPHERAL_CONFIGURATION_0_INDEX);
    
        break;
            
        case CY_BLE_EVT_GATT_CONNECT_IND:
            printf("CY_BLE_EVT_GATT_CONNECT_IND bdHandle=%x\r\n",((cy_stc_ble_conn_handle_t *)eventParameter)->bdHandle);
           
        break;
            
        case CY_BLE_EVT_GAP_ENHANCE_CONN_COMPLETE:
             printf("CY_BLE_EVT_GAP_ENHANCE_CONN_COMPLETE\r\n");
        break;
            
        case CY_BLE_EVT_TIMEOUT:
            printf("CY_BLE_EVT_TIMEOUT\r\n");
        break;
                
        case CY_BLE_EVT_GATTS_READ_CHAR_VAL_ACCESS_REQ:
            printf("CY_BLE_EVT_GATTS_READ_CHAR_VAL_ACCESS_REQ\r\n");
        break;
                
        case CY_BLE_EVT_GATTS_XCNHG_MTU_REQ:
            printf("CY_BLE_EVT_GATTS_XCNHG_MTU_REQ\r\n");
        break;
            
        case CY_BLE_EVT_SET_DEVICE_ADDR_COMPLETE:
            printf("CY_BLE_EVT_SET_DEVICE_ADDR_COMPLETE\r\n");
        break;
            
        case CY_BLE_EVT_LE_SET_EVENT_MASK_COMPLETE:
            printf("CY_BLE_EVT_LE_SET_EVENT_MASK_COMPLETE\r\n");
        break;
            
        case CY_BLE_EVT_SET_TX_PWR_COMPLETE:
            printf("CY_BLE_EVT_SET_TX_PWR_COMPLETE\r\n");
        break;
            
        case CY_BLE_EVT_GATT_DISCONNECT_IND:
            printf("CY_BLE_EVT_GATT_DISCONNECT_IND\r\n");
        break;
            
        case CY_BLE_EVT_GAPP_ADVERTISEMENT_START_STOP:
            printf("CY_BLE_EVT_GAPP_ADVERTISEMENT_START_STOP = ");
            if(Cy_BLE_GetAdvertisementState() == CY_BLE_ADV_STATE_STOPPED)
                printf("CY_BLE_ADV_STATE_STOPPED");
            if(Cy_BLE_GetAdvertisementState() == CY_BLE_ADV_STATE_ADV_INITIATED)
                printf("CY_BLE_ADV_STATE_ADV_INITIATED");
            if(Cy_BLE_GetAdvertisementState() == CY_BLE_ADV_STATE_ADVERTISING)
                printf("CY_BLE_ADV_STATE_ADVERTISING");
            if(Cy_BLE_GetAdvertisementState() == CY_BLE_ADV_STATE_STOP_INITIATED)
                printf("CY_BLE_ADV_STATE_STOP_INITIATED");
             printf("\r\n");
        break;
            
        case CY_BLE_EVT_GATTS_INDICATION_ENABLED:
            printf("CY_BLE_EVT_GATTS_INDICATION_ENABLED\r\n");
        break;
   
        case CY_BLE_EVT_GAP_DEVICE_CONNECTED:
            printf("CY_BLE_EVT_GAP_DEVICE_CONNECTED\r\n");
        break;
                
        default:
            printf("Unknown Event = %X\n",(unsigned int)event);
        break;
    }
}

Now you need a task to run the BLE.  It just runs the Cy_BLE_ProcessEvents each time an event needs to be handled.

void bleTask(void *arg)
{
    (void)arg;
    printf("3[2J3[H"); // Clear Screen
    printf("Started BLE Task\r\n");
    #ifdef USE_RTOS
    bleSemaphore = xSemaphoreCreateCounting(2^32-1,0);
    printf("Using RTOS\r\n");
    #else
        printf("Bare Metal\r\n");
    #endif
 
    printf("Cy_SysLib_GetDeviceRevision() %X \r\n", Cy_SysLib_GetDeviceRevision());
    
    Cy_BLE_Start(customEventHandler);
    #ifdef USE_RTOS
        
    Cy_BLE_IPC_RegisterAppHostCallback(bleInterruptNotify);
    //Cy_BLE_RegisterInterruptCallback(2^32-1,bleInterruptNotify);
    while(Cy_BLE_GetState() != CY_BLE_STATE_ON)
    {
        Cy_BLE_ProcessEvents();
    }
    #endif
   
    for(;;)
    {
        #ifdef USE_RTOS
            xSemaphoreTake(bleSemaphore,portMAX_DELAY);
        #endif
        Cy_BLE_ProcessEvents();         
    }
}

int main(void)
{
    __enable_irq(); /* Enable global interrupts. */
    UART_1_Start();
    printf("Started Project\r\n");
    #ifndef USE_RTOS
    bleTask(0);
    #endif
    
    xTaskCreate(bleTask,"bleTask",4*1024,0,1,0);
    vTaskStartScheduler();
 
}

Test the System

Finally program the CY8CKIT-062-BLE and attach with it to CySmart or LightBlue.  There are three phases

  1. The stack turns on and starts advertising (CY_BLE_EVT_STACK_ON –> CY_BLE_EVT_GAPP_ADVERTISMENT)
  2. A connection is made and eventually disconnected (CY_BLE_EVT_CONNECT_IND –> CY_BLE_EVT_GAP_DEVICE_DISCONNECTED
  3. You start advertising again CY_BLE_EVT_GAPP_ADVERTISEMENT_START_STOP

PSoC 6 BLE Events

 

Recommended Posts

No comment yet, add your voice below!


Add a Comment

Your email address will not be published. Required fields are marked *