PSOC 4 BLE Simple GAP Peripheral/GATT Server Example

Created by Matthew Bon on Nov 14, 2016
image

Introduction:

This article will demonstrate how to setup the PSOC 4 BLE in the typical use case of GAP Peripheral/GATT Server as well as demonstrate how to transfer data from the PSOC 4 BLE to another device.

I would highly recommend reading through the CY8CKIT_042_BLE_BLUETOOTH_LOW_ENERGY_BLE_PIONEER_KIT_GUIDE , which is a great introduction to the PSOC 4 BLE Pioneer kit, PSOC Creator and Cysmart.

Useful Links:

PSOC 4 BLE Cypress Page

PSOC 4 BLE Digikey Page

PSOC Creator IDE

Cypress’s Cysmart BLE Evaluation Software

Required Hardware:

Either CY8CKIT-042-BLE or CY8CKIT-042-BLE-A . The difference between the two is that the former is compliant with the BLE 4.1 spec and the latter to the BLE 4.2 spec. This difference is irrelevant for the example shown in this article.

GAP and GATT:

The key to understanding how BLE works is to understand the function of the GAP and GATT layers.

GAP (Generic Access Protocol) -The GAP layer determines how BLE devices find and connect to each other. The GAP roles are the GAP Central which typically initiates connections and the GAP Peripheral which accepts connection requests from the GAP Central. There is also the GAP Broadcaster and GAP Observer roles which only deal with advertising events. The article will only deal with the GAP Central and GAP Peripheral roles.

GATT (Generic Attribute Protocol) -The GATT layer determines how two connected BLE devices exchange data.The two roles are the GATT Client and GATT Server. The GATT Client is the device that wants data and will send commands and requests to the GATT Server. The GATT Server is the device that contains data and it will send this data to the GATT Client via either responses, notifications and indications. These methods of transferring data are described in detail in the ‘Method of Transferring Data’ section later in this article.

How the GATT Server Organizes Data:

Characteristics- A characteristic is a data entity that is compromised of two parts, a characteristic value and characteristic descriptor. A characteristic value is simply a buffer containing data that the GATT Server wants to exchange such as a temperature or heart rate measurement. A characteristic descriptor is a data field which contains a short description detailing what the characteristic value represents. In addition, a characteristic can also contain an optional field called the Client Characteristic Configuration Descriptor which is a buffer that the GATT client can write to in order to enable or disable notifications and indications for a particular characteristic.

Services - A service is simply a collection of characteristics. Typically, the characteristics will share some sort functionality. The purpose of services is to make it easier for the user to retrieve and understand the data on the device. For example, if your BLE device has several characteristics containing temperature sensor measurements, then it might make sense to group them in a single service called “Temp Measurements”.

Profiles- A profile is an over-arching concept that describes the application and functionality of a device. A profile will typically contain several services. The Bluetooth spec defines several supported profiles and also allows for the creation of custom profiles. The example in this article uses a custom profile.

Methods of Transferring Data:

Writes - In this method, the GATT Client will send a command to the GATT Server, instructing it to update the value or descriptor of a characteristic.

Reads - In this method, the GATT Client will ask the GATT Server to send the value or descriptor of a characteristic. The GATT Server then sends a response with the requested data.

Notifications - In this method, the GATT Client will enable notifications on the GATT Server . Once notifications are enabled, the GATT Server will send data as it’s programming dictates, without any prompting from the GATT Client. The GATT Client will not give any confirmation whether it received the data or not.

Indications - These are basically the same as notifications. However with indications, the GATT Client will send confirmation to the GATT Server that it received the indication.

Example Program Overview:

The goal of this example program is demonstrate how to setup the PSOC 4 BLE as a GAP Peripheral and GATT Central and well as implement the methods of transferring data listed in the section above. To do this, the program uses a custom profile with one custom service called “Number”. The “Number” service contains the following characteristics.

Number_Read -This characteristic contains a value that is incremented every time the GATT Client writes to the 'Number_Write" characteristic. It’s value can be retrieved by the GATT Client sending a read request to the characteristic value.

Number_Write -Every time a value is written to this characteristic, the value of “Number_Read” increases by one.

Number_Notify -Once the GATT Client enables notifications on this characteristic, the GATT Server will increase the value of this characteristic by one and send the value as a notification. It will continue to increment the value and send notifications until the GATT Client disables notifications for this characteristic.

Number_Indicate -This characteristic functions the same as the “Number_Notify” characteristic, but sends it’s values as indications instead of notifications.

Setting Up the BLE Component in PSOC Creator:

To make things easy, I’ve created a PSOC Creator project with the BLE component already configured. Simply download and unzip the following file, open the workspace, navigate to Main.C and you’re ready to go.

Simple Peripheral Example_Setup_Only.zip
55574643.zip (2.4 MB)

Writing the Firmware Walk-through:

  • Once you have Main.c open, the first thing we need to do is to initialize the variables that we’ll use to demonstrate functionality of the BLE component. Modify the main.c file so that it matches the code below.

Initialize variables

#include "project.h"
  
uint8 number_read = 0;
uint8 number_write = 0;
uint8 number_notify = 0;
uint8 number_indicate = 0;
  
uint8 notification_enabled = 0;
uint8 indication_enabled = 0;
  
int main(void)
{
    CyGlobalIntEnable; /* Enable global interrupts. */
 
    for(;;)
    {
        
    }
}
  • The next step is to create a callback function to receive and process events from the BLE stack. This function is initialized by the “CyBLE_Start()” api and can be named whatever you like provided the name isn’t already reserved. The code to initialize the callback function is shown below.

Application with callback function added

#include "project.h"
  
uint8 number_read = 0;
uint8 number_write = 0;
uint8 number_notify = 0;
uint8 number_indicate = 0;
  
uint8 notification_enabled = 0;
uint8 indication_enabled = 0;
 
void BLECallBack(uint32 event, void *eventParam)
{
     
}
 
int main(void)
{
    CyGlobalIntEnable; /* Enable global interrupts. */
    CyBle_Start(BLECallBack);
    for(;;)
    {
        /* Place your application code here. */
    }
}
  • Next we’ll add some code to handle connection events. Modify the callback function as follows:

Handle connection events

void BLECallBack(uint32 event, void *eventParam)
{
 
    switch (event)
    {
        /*Handle Connection Events*/
          case CYBLE_EVT_STACK_ON:
        {
            CyBle_GappStartAdvertisement(CYBLE_ADVERTISING_FAST); // Start advertising
            break;
        }
 
        case CYBLE_EVT_GAP_DEVICE_DISCONNECTED:
        {
           CyBle_GappStartAdvertisement(CYBLE_ADVERTISING_FAST);  // Restart advertising
           break;
        }
 
        case CYBLE_EVT_GAP_DEVICE_CONNECTED:
        {
          CyBle_GappStopAdvertisement();
           break;
 
        }       
 
    }
}
  • Next, we need to add a parameter to hold commands sent by the GATT client. At the start of the BLE callback function, before the switch statement, add the following line: “CYBLE_GATTS_WRITE_REQ_PARAM_T *wrReqParam;”

Write parameter

void BLECallBack(uint32 event, void *eventParam)
{
    CYBLE_GATTS_WRITE_REQ_PARAM_T *wrReqParam;
 
    switch (event)
......
  • Next, we’ll add some code to handle write and write without response requests from a GATT client. Modify the callback function as follows:

Handle write requests

void BLECallBack(uint32 event, void *eventParam)
{
    CYBLE_GATTS_WRITE_REQ_PARAM_T *wrReqParam;
 
    switch (event)
    {
        /*Handle Connection Events*/
          case CYBLE_EVT_STACK_ON:
        {
            CyBle_GappStartAdvertisement(CYBLE_ADVERTISING_FAST); // Start advertising
            break;
        }
 
        case CYBLE_EVT_GAP_DEVICE_DISCONNECTED:
        {
           CyBle_GappStartAdvertisement(CYBLE_ADVERTISING_FAST);  // Restart advertising
           break;
        }
 
        case CYBLE_EVT_GAP_DEVICE_CONNECTED:
        {
          CyBle_GappStopAdvertisement();
           break;
 
        }
 
        case CYBLE_EVT_GATTS_WRITE_REQ: //Write with response
        case CYBLE_EVT_GATTS_WRITE_CMD_REQ: //Write without response
        {
            wrReqParam = (CYBLE_GATTS_WRITE_REQ_PARAM_T *) eventParam; //read in event command
 
            if(wrReqParam->handleValPair.attrHandle == CYBLE_NUMBER_NUMBER_WRITE_CHAR_HANDLE) //If client writes to the number_write characteristic
                {
                    number_write = wrReqParam->handleValPair.value.val[0]; //Pull out the number_write value
                    if (number_write != 0) //If Client wrote anything other than 0, increment number_read++
                    {
                        number_read++;
                        number_write =0;
 
                    }
 
                }   
 
            if (event == CYBLE_EVT_GATTS_WRITE_REQ) //If write was a write with response request.
                {
                    CyBle_GattsWriteRsp(cyBle_connHandle); //Send response back to Client
                }
 
            break;
        }
 
    }
 
 
}
  • Next, within the “if (number !=0)” if statement we just added, we’ll add some code to update the number_read attribute. This is all we need to do so add the read functionality to our application because the BLE stack takes care of pretty much all the work for us. Note, it’s not necessary to initiate the structure each time, however I did it this way in order to make this example program easier to follow.

Update number_read attribute

.... 
  if (number_write != 0) //If Client wrote anything other than 0, increment number_read++
                    {
                        number_read++;
                        number_write =0;
 
                        /*update read characteristic*/
                        CYBLE_GATT_HANDLE_VALUE_PAIR_T Number_R; 
                        Number_R.attrHandle= CYBLE_NUMBER_NUMBER_READ_CHAR_HANDLE; 
                        Number_R.value.val = &number_read;
                        Number_R.value.len = 1;
                        CyBle_GattsWriteAttributeValue(&Number_R, 0, &cyBle_connHandle, CYBLE_GATT_DB_LOCALLY_INITIATED);
 
                    }
  
....
  • The BLE callback function should now look like the following:

Callback write/read

void BLECallBack(uint32 event, void *eventParam)
{
    CYBLE_GATTS_WRITE_REQ_PARAM_T *wrReqParam;
 
    switch (event)
    {
        /*Handle Connection Events*/
          case CYBLE_EVT_STACK_ON:
        {
            CyBle_GappStartAdvertisement(CYBLE_ADVERTISING_FAST); // Start advertising
            break;
        }
 
        case CYBLE_EVT_GAP_DEVICE_DISCONNECTED:
        {
           CyBle_GappStartAdvertisement(CYBLE_ADVERTISING_FAST);  // Restart advertising
           break;
        }
 
        case CYBLE_EVT_GAP_DEVICE_CONNECTED:
        {
          CyBle_GappStopAdvertisement();
           break;
 
        }
 
        case CYBLE_EVT_GATTS_WRITE_REQ: //Write with response
        case CYBLE_EVT_GATTS_WRITE_CMD_REQ: //Write without response
        {
            wrReqParam = (CYBLE_GATTS_WRITE_REQ_PARAM_T *) eventParam; //read in event command
 
            if(wrReqParam->handleValPair.attrHandle == CYBLE_NUMBER_NUMBER_WRITE_CHAR_HANDLE)  //If client writes to the number_write characteristic
                {
                    number_write = wrReqParam->handleValPair.value.val[0]; //Pull out the number_write value
                    if (number_write != 0) //If Client wrote anything other than 0, increment number_read++
                    {
                        number_read++;
                        number_write =0;
 
                        /*update read characteristic*/
                        CYBLE_GATT_HANDLE_VALUE_PAIR_T Number_R; 
                        Number_R.attrHandle= CYBLE_NUMBER_NUMBER_READ_CHAR_HANDLE; 
                        Number_R.value.val = &number_read;
                        Number_R.value.len = 1;
                        CyBle_GattsWriteAttributeValue(&Number_R, 0, &cyBle_connHandle, CYBLE_GATT_DB_LOCALLY_INITIATED);
 
                    }
 
                }   
 
 
            if (event == CYBLE_EVT_GATTS_WRITE_REQ) //If write was a write with response request.
                {
                    CyBle_GattsWriteRsp(cyBle_connHandle); //Send response back to Client
                }
 
            break;
        }
 
    }
 
}
  • Now it’s time to enable notifications. Notifications are enabled and disabled by writing to the Client Characteristic Configuration Descriptor of the characteristic that you want to send notifications. Basically, the Client Characteristic Configuration Descriptor (CCCD) is a buffer that is associated with a specific characteristic, that allows the GATT Client to instruct the GATT Server to start or stop sending notifications and indications for that particular characteristic. To add this feature to our example, we’ll start by modifying the call back function. The code below will check if the CCCD has been written to and if it has, it will check its value. If the CCCD is equal to 1, then notifications are enabled as per the Bluetooth spec and if the CCCD is greater than 3, an error response is sent.

Adding notifications_ble_callback

void BLECallBack(uint32 event, void *eventParam)
{
    CYBLE_GATTS_WRITE_REQ_PARAM_T *wrReqParam;
     
    switch (event)
    {
        /*Handle Connection Events*/
        case CYBLE_EVT_STACK_ON:
        {
            CyBle_GappStartAdvertisement(CYBLE_ADVERTISING_FAST); // Start advertising
            break;
        }
         
        case CYBLE_EVT_GAP_DEVICE_DISCONNECTED:
        {
           CyBle_GappStartAdvertisement(CYBLE_ADVERTISING_FAST);  // Restart advertising
           break;
        }
         
        case CYBLE_EVT_GAP_DEVICE_CONNECTED:
        {
          CyBle_GappStopAdvertisement();
           break;
             
        }
         
        case CYBLE_EVT_GATTS_WRITE_REQ: //Write with response
        case CYBLE_EVT_GATTS_WRITE_CMD_REQ: //Write without response
        {
            wrReqParam = (CYBLE_GATTS_WRITE_REQ_PARAM_T *) eventParam; //read in event command
                
            if(wrReqParam->handleValPair.attrHandle == CYBLE_NUMBER_NUMBER_WRITE_CHAR_HANDLE)  //If client writes to the number_write characteristic
                {
                    number_write = wrReqParam->handleValPair.value.val[0]; //Pull out the number_write value
                    if (number_write != 0) //If Client wrote anything other than 0, increment number_read++
                    {
                        number_read++;
                        number_write =0;
                         
                        /*update read characteristic*/
                        CYBLE_GATT_HANDLE_VALUE_PAIR_T Number_R; 
                        Number_R.attrHandle= CYBLE_NUMBER_NUMBER_READ_CHAR_HANDLE; 
                        Number_R.value.val = &number_read;
                        Number_R.value.len = 1;
                        CyBle_GattsWriteAttributeValue(&Number_R, 0, &cyBle_connHandle, CYBLE_GATT_DB_LOCALLY_INITIATED);
                         
                    }
                      
                     
                }
            if(wrReqParam->handleValPair.attrHandle == CYBLE_NUMBER_NUMBER_NOTIFY_CLIENT_CHARACTERISTIC_CONFIGURATION_DESC_HANDLE)
                {
                     if (0 == (wrReqParam->handleValPair.value.val [CYBLE_NUMBER_NUMBER_NOTIFY_CLIENT_CHARACTERISTIC_CONFIGURATION_DESC_INDEX]))
                        {
                           notification_enabled = 0; 
                        }
                         
                     else if (1 == (wrReqParam->handleValPair.value.val [CYBLE_NUMBER_NUMBER_NOTIFY_CLIENT_CHARACTERISTIC_CONFIGURATION_DESC_INDEX]))
                        {
                          notification_enabled = 1;
                        }
                         
                     else if (4 <= (wrReqParam->handleValPair.value.val [CYBLE_NUMBER_NUMBER_NOTIFY_CLIENT_CHARACTERISTIC_CONFIGURATION_DESC_INDEX]))
                        {
                          //Invalid PDU, stop notifications
                          notification_enabled = 0;
                           
                          //Inform the user that they are being stupid
                          CYBLE_GATTS_ERR_PARAM_T error_param;
                          error_param.opcode = CYBLE_GATT_WRITE_REQ;
                          error_param.attrHandle = wrReqParam->handleValPair.attrHandle;
                          error_param.errorCode = 0x04;
                             
                          //Send Error Response
                          (void)CyBle_GattsErrorRsp(cyBle_connHandle, &error_param);
                         
                        }
                }       
             
             
            if (event == CYBLE_EVT_GATTS_WRITE_REQ) //If write was a write with response request.
                {
                    CyBle_GattsWriteRsp(cyBle_connHandle); //Send response back to Client
                }            
            
            break;
        }    
         
    }
       
}
  • From there, we need to modify our main function. Outside the main loop, we’ll declare a structure “notificationHandle” which will hold all the information we need to send a notification. Within the main loop, we’ll include an if statement which will check to see if notifications are enabled and if so, increment the number_notify counter and send its value as a notification.

Adding notifications_main

int main(void)
{
    CyGlobalIntEnable; /* Enable global interrupts. */
    CyBle_Start(BLECallBack);
    CYBLE_GATTS_HANDLE_VALUE_NTF_T notificationHandle;
     
    for(;;)
    {
       CyBle_ProcessEvents();
     
        if (notification_enabled == 1) //If notifications enabled, increment the number and send its value as a notification
        {
            number_notify++;
            /* Update Notification handle with new data*/
            notificationHandle.attrHandle = CYBLE_NUMBER_NUMBER_NOTIFY_CHAR_HANDLE;
            notificationHandle.value.val = &number_notify;
            notificationHandle.value.len = 1;
            /* Report data to BLE component for sending data by notifications*/
            CyBle_GattsNotification(cyBle_connHandle, &notificationHandle);
            CyDelay(10); //Short delay to make sure the user doesn't get flooded with notifications between when they send the request to disable the notification and when GATT Server actually disables the notification
             
        }      
    }
}
  • Indication functionality is added in pretty much the same way as notification functionality, expect we expect a value of 2 for the CCCD as per the Bluetooth spec.

Adding indications_callback

.....
 if(wrReqParam->handleValPair.attrHandle == CYBLE_NUMBER_NUMBER_INDICATE_CLIENT_CHARACTERISTIC_CONFIGURATION_DESC_HANDLE)
                {
                     if (0 == (wrReqParam->handleValPair.value.val [CYBLE_NUMBER_NUMBER_INDICATE_CLIENT_CHARACTERISTIC_CONFIGURATION_DESC_INDEX]))
                        {
                           indication_enabled = 0; 
                        }
                         
                     else if (2 == (wrReqParam->handleValPair.value.val [CYBLE_NUMBER_NUMBER_INDICATE_CLIENT_CHARACTERISTIC_CONFIGURATION_DESC_INDEX]))
                        {
                          indication_enabled = 1;
                        }
                         
                     else if (4 <= (wrReqParam->handleValPair.value.val [CYBLE_NUMBER_NUMBER_INDICATE_CLIENT_CHARACTERISTIC_CONFIGURATION_DESC_INDEX]))
                        {
                          //Invalid PDU, stop notifications
                          indication_enabled = 0;
                           
                          //Inform the user that they are being stupid
                          CYBLE_GATTS_ERR_PARAM_T error_param;
                          error_param.opcode = CYBLE_GATT_WRITE_REQ;
                          error_param.attrHandle = wrReqParam->handleValPair.attrHandle;
                          error_param.errorCode = 0x04;
                             
                          //Send Error Response
                          (void)CyBle_GattsErrorRsp(cyBle_connHandle, &error_param);
                         
                        }
                }
....
  • Likewise the modifications to the main function are also similar to the changes made for notifications. The only difference here is that after the indication is sent, we’ll disable further indications until a response is received from the GATT Client.

Adding indications_main

 ......
     if (indication_enabled == 1) //If notifications enabled, increment the number and send its value as a indication
        {
            number_indicate++;
            // Update Indication handle with new data
            indicationHandle.attrHandle = CYBLE_NUMBER_NUMBER_INDICATE_CHAR_HANDLE;
            indicationHandle.value.val = &number_indicate;
            indicationHandle.value.len = 1;
            // Send Indication
            CyBle_GattsIndication(cyBle_connHandle, &indicationHandle);
            indication_enabled = 0; //Disable indications, if the client confirms that indications is received, indications will be re-enable in the callback function.
            CyDelay(100); //Short delay to make sure the user doesn't get flooded with indications between when they send the request to disable the notification and when GATT Server actually disables the notification
             
        }
....
  • In the callback function, we’ll add case to handle the response from the GATT Client. If a response is received, indications are re-enabled.

Indication confirmation

...
    case CYBLE_EVT_GATTS_HANDLE_VALUE_CNF:
        {
            indication_enabled = 1; //If confirmation from GATT Client is received, re-enable indications.
            break;
        }
....
  • The final main.c file should look like the following.

Final main.c

/*
   PSOC BLE Peripheral Example
   Written by Matthew Bon for Digikey Electronics
   November 2016
*/
#include "project.h"
  
uint8 number_read = 0;
uint8 number_write = 0;
uint8 number_notify = 0;
uint8 number_indicate = 0;
uint8 notification_enabled = 0;
uint8 indication_enabled = 0;
 
void BLECallBack(uint32 event, void *eventParam)
{
    CYBLE_GATTS_WRITE_REQ_PARAM_T *wrReqParam;
     
    switch (event)
    {
        /*Handle Connection Events*/
        case CYBLE_EVT_STACK_ON:
        {
            CyBle_GappStartAdvertisement(CYBLE_ADVERTISING_FAST); // Start advertising
            break;
        }
         
        case CYBLE_EVT_GAP_DEVICE_DISCONNECTED:
        {
           CyBle_GappStartAdvertisement(CYBLE_ADVERTISING_FAST);  // Restart advertising
           break;
        }
         
        case CYBLE_EVT_GAP_DEVICE_CONNECTED:
        {
          CyBle_GappStopAdvertisement();
           break;
             
        }
         
         
        case CYBLE_EVT_GATTS_WRITE_REQ: //Write with response
        case CYBLE_EVT_GATTS_WRITE_CMD_REQ: //Write without response
        {
            wrReqParam = (CYBLE_GATTS_WRITE_REQ_PARAM_T *) eventParam; //read in event command
                
            if(wrReqParam->handleValPair.attrHandle == CYBLE_NUMBER_NUMBER_WRITE_CHAR_HANDLE)  //If client writes to the number_write characteristic
                {
                    number_write = wrReqParam->handleValPair.value.val[0]; //Pull out the number_write value
                    if (number_write != 0) //If Client wrote anything other than 0, increment number_read++
                    {
                        number_read++;
                        number_write =0;
                         
                        /*update read characteristic*/
                        CYBLE_GATT_HANDLE_VALUE_PAIR_T Number_R; 
                        Number_R.attrHandle= CYBLE_NUMBER_NUMBER_READ_CHAR_HANDLE; 
                        Number_R.value.val = &number_read;
                        Number_R.value.len = 1;
                        CyBle_GattsWriteAttributeValue(&Number_R, 0, &cyBle_connHandle, CYBLE_GATT_DB_LOCALLY_INITIATED);
                         
                    }
                      
                     
                }
                 
            if(wrReqParam->handleValPair.attrHandle == CYBLE_NUMBER_NUMBER_NOTIFY_CLIENT_CHARACTERISTIC_CONFIGURATION_DESC_HANDLE)
                {
                     if (0 == (wrReqParam->handleValPair.value.val [CYBLE_NUMBER_NUMBER_NOTIFY_CLIENT_CHARACTERISTIC_CONFIGURATION_DESC_INDEX]))
                        {
                           notification_enabled = 0; 
                        }
                         
                     else if (1 == (wrReqParam->handleValPair.value.val [CYBLE_NUMBER_NUMBER_NOTIFY_CLIENT_CHARACTERISTIC_CONFIGURATION_DESC_INDEX]))
                        {
                          notification_enabled = 1;
                        }
                         
                     else if (4 <= (wrReqParam->handleValPair.value.val [CYBLE_NUMBER_NUMBER_NOTIFY_CLIENT_CHARACTERISTIC_CONFIGURATION_DESC_INDEX]))
                        {
                          //Invalid PDU, stop notifications
                          notification_enabled = 0;
                           
                          //Inform the user that they are being stupid
                          CYBLE_GATTS_ERR_PARAM_T error_param;
                          error_param.opcode = CYBLE_GATT_WRITE_REQ;
                          error_param.attrHandle = wrReqParam->handleValPair.attrHandle;
                          error_param.errorCode = 0x04;
                             
                          //Send Error Response
                          (void)CyBle_GattsErrorRsp(cyBle_connHandle, &error_param);
                         
                        }
                }  
                 
             if(wrReqParam->handleValPair.attrHandle == CYBLE_NUMBER_NUMBER_INDICATE_CLIENT_CHARACTERISTIC_CONFIGURATION_DESC_HANDLE)
                {
                     if (0 == (wrReqParam->handleValPair.value.val [CYBLE_NUMBER_NUMBER_INDICATE_CLIENT_CHARACTERISTIC_CONFIGURATION_DESC_INDEX]))
                        {
                           indication_enabled = 0; 
                        }
                         
                     else if (2 == (wrReqParam->handleValPair.value.val [CYBLE_NUMBER_NUMBER_INDICATE_CLIENT_CHARACTERISTIC_CONFIGURATION_DESC_INDEX]))
                        {
                          indication_enabled = 1;
                        }
                         
                     else if (4 <= (wrReqParam->handleValPair.value.val [CYBLE_NUMBER_NUMBER_INDICATE_CLIENT_CHARACTERISTIC_CONFIGURATION_DESC_INDEX]))
                        {
                          //Invalid PDU, stop notifications
                          indication_enabled = 0;
                           
                          //Inform the user that they are being stupid
                          CYBLE_GATTS_ERR_PARAM_T error_param;
                          error_param.opcode = CYBLE_GATT_WRITE_REQ;
                          error_param.attrHandle = wrReqParam->handleValPair.attrHandle;
                          error_param.errorCode = 0x04;
                             
                          //Send Error Response
                          (void)CyBle_GattsErrorRsp(cyBle_connHandle, &error_param);
                         
                        }
                }  
             
             
            if (event == CYBLE_EVT_GATTS_WRITE_REQ) //If write was a write with response request.
                {
                    CyBle_GattsWriteRsp(cyBle_connHandle); //Send response back to Client
                }            
            
            break;
        }
         
        case CYBLE_EVT_GATTS_HANDLE_VALUE_CNF:
        {
            indication_enabled = 1; //If confirmation from GATT Client is recieved, re-enable indications.
            break;
        }
        
         
    }
       
}
 
int main(void)
{
    CyGlobalIntEnable; /* Enable global interrupts. */
    CyBle_Start(BLECallBack);
    CYBLE_GATTS_HANDLE_VALUE_NTF_T notificationHandle;
    CYBLE_GATTS_HANDLE_VALUE_IND_T indicationHandle;
    for(;;)
    {
       CyBle_ProcessEvents();
     
        if (notification_enabled == 1) //If notifications enabled, increment the number and send its value as a notification
        {
            number_notify++;
            //Update Notification handle with new data
            notificationHandle.attrHandle = CYBLE_NUMBER_NUMBER_NOTIFY_CHAR_HANDLE;
            notificationHandle.value.val = &number_notify;
            notificationHandle.value.len = 1;
            // Send Notification
            CyBle_GattsNotification(cyBle_connHandle, &notificationHandle);
            CyDelay(100); //Short delay to make sure the user doesn't get flooded with notifications between when they send the request to disable the notification and when GATT Server actually disables the notification
             
        }
         
        if (indication_enabled == 1) //If notifications enabled, increment the number and send its value as a indication
        {
            number_indicate++;
            // Update Indication handle with new data
            indicationHandle.attrHandle = CYBLE_NUMBER_NUMBER_INDICATE_CHAR_HANDLE;
            indicationHandle.value.val = &number_indicate;
            indicationHandle.value.len = 1;
            // Send Indication
            CyBle_GattsIndication(cyBle_connHandle, &indicationHandle);
            indication_enabled = 0; //Disable indications, if the client confirms that indications is received, indications will be re-enable in the callback function.
            CyDelay(100); //Short delay to make sure the user doesn't get flooded with indications between when they send the request to disable the notification and when GATT Server actually disables the notification
             
        }
         
    }
}
/* [] END OF FILE */

Complete Main.C File and PSOC Creator Project:

If you’d rather not follow the walk-through shown above, the main.C file and full PSOC creator project for this example are included below:

main.c
55574641.c (8.0 KB)

Simple Peripheral Example.zip
55574642.zip (2.7 MB)

Testing the Example:

This example can easily be tested using Cypress’s Cysmart software. The steps below detail how to verify this example.

  1. Start by building the PSOC creator project by pressing “shift +F6” or by selecting it from the ‘Build’ drop down menu.

  2. If everything builds correctly, ensure that the PSOC BLE pioneer board is plugged into your PC. Then, program the demo kit by either pressing “Ctrl + F5” or by selecting it from the “Debug” drop down menu.

  3. Once the board is programmed, insert the BLE Dongle that came provided with the PSOC BLE kit.

  4. Open Cysmart 1.2 and connect to the dongle as shown below. If you have difficulties connecting to the dongle, I would suggest you take a look at this article: Fixing Connection Issues between the Cypress CySmart Software and the CY5670 USB Dongle
    image

  5. Once connected, click on the start scan button in the top left of the Cysmart window. After a few seconds, a device called “Peripheral_Example” should appear. You might have to press the reset button on the PSOC 4 BLE pioneer board for the device to appear. Select that device and then hit the connect button.

  6. Once the device connects, click on the “Discover All Attributes” button. You should then see a screen similar to the following.

  7. From there, scroll down to handle 0x0013. Select it. Once it is selected, click on the “Read Value” button toward the right of the Cysmart window. This will instruct Cysmart to read the value of the “Number_Right” characteristic which should be 00

  8. As stated earlier, we need to write to the “Number_Write” service in order to increment the “Number_Read” service’s value. To do this in Cysmart, click on handle 0x0010, enter a value in the value window and click the “Write Without Responding” button.


    image

  9. Now, read the “Number_Read” characteristic as shown in step 7. The value should have incremented by 1. You can repeated steps 8 and 9 to continue incrementing the value.

  10. Enabling notifications in Cysmart is fairly straight forward, simply write a value of ‘1’ to the Client Characteristic Configuration Descriptor of the “Number_Notify” characteristic which has a handle of 0x0018. Cysmart is a little picky about how the value if formatted, so enter it as shown below.
    image

  11. Once the value is written, Cysmart should start receiving notifications. You can disable notifications by writing a ‘0’ to the Client Characteristic Configuration Descriptor

  12. Likewise, indications can be enabled by writing a value of ‘2’ to the Client Characteristic Configuration Descriptor of “Number_Indicate” whose handle is 0x001C.
    image

  13. Once it the value is written, Cysmart should start receiving indications. As before, you can disable them by writing a ‘0’ to the Client Characteristic Configuration Descriptor.

Hi, Im checking the article and trying to make the example, but I have error trying to download the sources files, it sais is blocked. Is it an error or how can I download it.
thease are the links:

https://www.digikey.com/eewiki/download/attachments/55574662/Simple%20Peripheral%20Example_Setup_Only.zip?version=1&modificationDate=1479157293350&api=v2&_ga=2.166742078.1263207360.1652812587-83451777.1652812587

https://www.digikey.com/eewiki/download/attachments/55574662/Simple%20Peripheral%20Example.zip?version=3&modificationDate=1479157293290&api=v2&_ga=2.91393818.1263207360.1652812587-83451777.1652812587

This is the eror:
P2627166660510572360

Hello @ramonhlara,
I am checking into the download issue and will reply once I have more info.
Best Regards,
Scott Raeker

Hello @ramonhlara,
This article was originally composed on a different platform back in 2016 and, unfortunately, when the article was moved over to this platform the downloadable zip files were not included and are no longer available. The article will be edited to remove the links. We apologize for the inconvenience.

Best Regards,
Scott Raeker

Hi @ramonhlara from backup…

55574641.c (8.0 KB)

55574642.zip (2.7 MB)

55574643.zip (2.4 MB)

Regards,

Robert, I really appreciate your help send me the links to donwload thease files, thank you for your help .