3.1.2.7 BLE Custom Service
Getting Started with Peripheral Building Blocks
BLE Connection –> BLE Custom Service
Introduction
This document will help users create a peripheral device with custom profile and control. The RGB LEDs and user button on the WBZ451 Curiosity board shall be used to enable or disable the LED status. The peripheral device will be the WBZ451 Curiosity board and the central device can either be a smartphone with a Light Blue app or another WBZ451 Curiosity board. The instructions mentioned below are applicable for a BLE peripheral device.
Users can choose to just run the precompiled Application Example hex file on the WBZ451 Curiosity board and experience the demo or can go through the steps involved in developing this application from scratch.
It is recommended to follow the examples in order, by learning the basic concepts first and then progressing to the more advanced topics.
Recommended Reading
Hardware Requirement
Tool |
Qty |
---|---|
WBZ451 Curiosity Board | 1 |
Micro USB Cable | 1 |
Android/iOS Smart phone |
1 |
SDK Setup
Software
Smartphone App
-
Light Blue iOS/Android app available in stores
Programming the Precompiled Hex File or Application Example
.hex
file using MPLAB X IPE- 1. Import and program the
Precompiled Hex file:
<Harmony Content Path>\wireless_apps_pic32cxbz2_wbz45\apps\ble\building_blocks\peripheral\profiles_services\custom_service\hex\custom_service.X.production.signed.hex
- For more details on the
steps, go to Programming a DeviceNote: Users must choose the correct device and tool information
Programming the Application using MPLAB X IDE
- Follow steps mentioned in the Running a Precompiled Example
- Open and program the Application:
<Harmony Content Path>\wireless_apps_pic32cxbz2_wbz45\apps\ble\building_blocks\peripheral\profiles_services\custom_service\firmware\custom_service.X
For more details on finding the Harmony content path, refer to Installing the MCC Plugin
Demo Description
This application enables users to control a RGB LEDs on the WBZ451 Curiosity board and read its status using a central device. On reset, demo will print "[BLE] Device Initialized. Ready for connection" which denotes the start of advertisements and then "[BLE] Connected to Peer Device:” when connection is established. Application data to be sent to the connected central device (smartphone or another WBZ451 Curiosity board) is entered in a terminal emulator like Tera Term.
Testing
- Using a micro USB cable, connect the Debug USB on the Curiosity board to a PC
- Program the precompiled hex file or application example as mentioned
- Open Tera Term and set the
“Serial Port” to USB Serial Device and “Speed” to 115200.
For more details on how to set the “Serial Port” and “Speed”, refer to COM Port Setup
- Press the Reset Switch on the Curiosity board. This must be displayed in Tera Term
- Launch the Light Blue mobile app and search for the device name “pic32cx-bz” and press Connect
-
Once connected, basic information like Advertisement Data and Device Information will be available
- Find the custom service with the UUID: 4d434850-5255-42d0-aef8-881facf4ceea. Two characteristics will be available with the custom service. The button characteristics with the Readable/Notify property and GREEN LED characteristics with Readable/Writable property.
- Click on the button characteristics and press subscribe. Press the User button on the curiosity board. The Read Value in the app will give its current status. Pressing the button will also toggle the GREEN LED.
- Go back and click on the RGB LEDs characteristics which has a read/write option. The write option changes the RGB LEDs On/Off status by writing a hex value in the form of XXXXXX. A value of 000000 will turn OFF the LED and value of FFFFFF turns it ON. The read option will give the current value stored for the RGB LEDs.
- Press back two times to disconnect the BLE connection. A disconnected device will advertise again.
Developing the Application from Scratch using MCC
- Create a new MCC Harmony Project.
- To setup the basic components and
configuration required to develop this application, import component
configuration:
<Harmony Content Path>\wireless_apps_pic32cxbz2_wbz45\apps\ble\building_blocks\peripheral\profiles_services\custom_service\firmware\custom_service.X\custom_service.mc3
For more details, refer to Importing Existing App Example Configuration
Note: Import and Export functionality of the Harmony component configuration will help users to start from a known working setup of the MCC configuration. - To accept dependencies or satisfiers, select "Yes"
- Verify if the project graph window has all the expected configuration as illustrated in the following figure:
Verify Custom Service Configuration
-
Select BLE Stack component the Project Graph and configure the following in the Configuration Options panel
Note: If users cannot see the Configuration Options panel in the right-hand side of the MPLAB X IDE, it might be minimized. Hover the cursor towards the Configuration Options side tab and click the “dot” on the top right-hand corner to pin it (see the following figure). - Select the FreeRTOS component in the Project Graph and configure the following
- Select the SERCOM0 component in the Project Graph and configure the following
- Select the System component in the Project Graph and configure the following
- Select the EIC component in the Project Graph and configure the following
- Select the Customized Service component in the Project Graph and configure the following
Verify the Clock Configuration as per the Low Power BLE Application Design.
Generating a Code
For more details on code generation, refer to MPLAB Code Configurator (MCC) Code Generation
Files and Routines Automatically generated by the MCC
The OSAL, RF System and BLE System initialization routine executed during program initialization can be found in the project file. This initialization routine is automatically generated by the MCC.
The BLE stack initialization routine executed during Application Initialization can be found in project files. This initialization routine is automatically generated by the MCC. This call initializes and configures the GAP, GATT, SMP, L2CAP and BLE middleware layers.
Source Files | Usage |
---|---|
app.c | Application State machine, includes calls for Initialization of all BLE stack (GAP,GATT, SMP, L2CAP) related component configurations |
app_ble\app_ble.c | Source Code for the BLE stack related component
configurations, code related to function calls from
app.c |
app_ble\app_ble_handler.c | All GAP, GATT, SMP and L2CAP Event handlers |
ble_button_led_svc.c | All Custom Service button RGB LEDs configurations and functions |
app.c
is autogenerated and has a state
machine-based application code. Users can use this template to develop their own
application.User Application Development
Add the code to enable advertisement, call the button external interrupt callback
functions and initialize custom service in APP_STATE_INIT
of the
function APP_Tasks() in app.c
/* Application's initial state. */
case APP_STATE_INIT:
{
bool appInitialized = true;
//appData.appQueue = xQueueCreate( 10, sizeof(APP_Msg_T) );
APP_BleStackInit();
RTC_Timer32Start();
BLE_GAP_SetAdvEnable(0x01, 0); //Enable BLE Advertisement
BLE_BUTTON_LED_Add();
APP_Button_Init();
SYS_CONSOLE_PRINT("[BLE] Device Initialized. Ready for connection. \r\n");
APP_Tasks()
in app.c
case APP_STATE_SERVICE_TASKS:
{
if (OSAL_QUEUE_Receive(&appData.appQueue, &appMsg, OSAL_WAIT_FOREVER))
{
// if(p_appMsg->msgId==APP_MSG_BLE_STACK_EVT)
// {
// // Pass BLE Stack Event Message to User Application for handling
// APP_BleStackEvtHandler((STACK_Event_T *)p_appMsg->msgData);
// }
switch (p_appMsg->msgId)
{
case APP_MSG_BLE_STACK_EVT:
{
APP_BleStackEvtHandler((STACK_Event_T *)p_appMsg->msgData);
}
break;
case APP_MSG_BLE_CS_LED_EVT:
{
APP_CustomService_RGB_Handler((uint8_t *)((STACK_Event_T *)p_appMsg->msgData));
}
break;
case APP_MSG_BLE_CS_BUTTON_EVT:
{
APP_CustomService_Button_Handler();
}
break;
}
}
break;
}
Add the message IDs for the RGB LEDs and button events in
app.h
typedef enum APP_MsgId_T
{
APP_MSG_BLE_STACK_EVT,
APP_MSG_ZB_STACK_EVT,
APP_MSG_ZB_STACK_CB,
APP_MSG_BLE_CS_LED_EVT,
APP_MSG_BLE_CS_BUTTON_EVT,
APP_MSG_STACK_END
} APP_MsgId_T;
Add the custom service source and header files “
which has the supporting
functions for the button LED custom service file<Harmony Content Path>\wireless_apps_pic32cxbz2_wbz45
and
\apps\ble\building_blocks\peripheral\profiles_services\custom_service\firmware\src\app_ble_custom_service
.c
and app_ble_custom_service
.h
app_ble_custom_service.h
app_ble_handler.c
along with the connect and
disconnect message informationvoid APP_BleGapEvtHandler(BLE_GAP_Event_T *p_event)
{
switch(p_event->eventId)
{
case BLE_GAP_EVT_CONNECTED:
{
/* TODO: implement your application code.*/
SYS_CONSOLE_PRINT("[BLE] Connected to Peer Device: 0x");
for(int8_t idx=(GAP_MAX_BD_ADDRESS_LEN-1); idx>=0; idx--)
{
SYS_CONSOLE_PRINT("%02x", p_event->eventField.evtConnect.remoteAddr.addr[idx]);
}
SYS_CONSOLE_PRINT("\n\r[BLE] Connection Handle: %d\n\r",p_event->eventField.evtConnect.connHandle);
G_ConnHandle = p_event->eventField.evtConnect.connHandle;
}
break;
case BLE_GAP_EVT_DISCONNECTED:
{
/* TODO: implement your application code.*/
SYS_CONSOLE_PRINT("[BLE] Disconnected Handle: %d, Reason: 0x%X\n\r",p_event->eventField.evtDisconnect.connHandle, p_event->eventField.evtDisconnect.reason);
G_ConnHandle = 0;
BLE_GAP_SetAdvEnable(0x01, 0); //Enable BLE Advertisement
}
break;
Add the functions for the GATT read/write handlers which will act on the received
GATT request in app_ble_handler.c
void APP_GattSEvtReadHandler(GATT_EvtRead_T p_event)
{
uint8_t error = 0;
uint16_t status;
SYS_CONSOLE_PRINT("[BLE] GATT Read ATTR Handle 0x%X \r\n",p_event.attrHandle);
if ((p_event.attrHandle <= BUTTON_LED_START_HDL) ||
(p_event.attrHandle > BUTTON_LED_END_HDL))
{
/* Not BLE Custom Service characteristic. */
return;
}
switch(p_event.attrHandle)
{
case BUTTON_LED_HDL_CHAR_0: /**< Handle of characteristic 0. */
case BUTTON_LED_HDL_CCCD_0: /**< Handle of characteristic 0 CCCD . */
case BUTTON_LED_HDL_CHAR_1: /**< Handle of characteristic 1. */
case BUTTON_LED_HDL_CHARVAL_1: /**< Handle of characteristic 1 value. */
error = ATT_ERRCODE_APPLICATION_ERROR;
break;
case BUTTON_LED_HDL_CHARVAL_0: /**< Handle of characteristic 0 value. */
// SYS_CONSOLE_PRINT(" ATTR Handle Read 0x%X \r\n",p_event.attrHandle);
break;
}
if ((p_event.readType == ATT_READ_REQ)
|| (p_event.readType == ATT_READ_BLOB_REQ))
{
if (!error)
{
sp_trsReadRespParams = (GATTS_SendReadRespParams_T *)OSAL_Malloc(sizeof(GATTS_SendReadRespParams_T));
if (sp_trsReadRespParams == NULL)
{
return;
}
trsRespErrConnHandle = p_event.connHandle;
sp_trsReadRespParams->responseType = ATT_READ_RSP;
sp_trsReadRespParams->attrLength = 0x01;
sp_trsReadRespParams->attrValue[0]= bleCSdata.rgbOnOffStatus;
// sp_trsReadRespParams->attrLength = 0x03;
// sp_trsReadRespParams->attrValue[0]= bleCSdata.RGB_LED.Red;
// sp_trsReadRespParams->attrValue[1]= bleCSdata.RGB_LED.Green;
// sp_trsReadRespParams->attrValue[2]= bleCSdata.RGB_LED.Blue;
status = GATTS_SendReadResponse(p_event.connHandle, sp_trsReadRespParams);
if (status == MBA_RES_SUCCESS)
{
OSAL_Free(sp_trsReadRespParams);
sp_trsReadRespParams = NULL;
}
}
else
{
sp_trsErrParams = (GATTS_SendErrRespParams_T *)OSAL_Malloc(sizeof(GATTS_SendErrRespParams_T));
if (sp_trsErrParams == NULL)
{
return;
}
trsRespErrConnHandle = p_event.connHandle;
sp_trsErrParams->reqOpcode = p_event.readType;
sp_trsErrParams->attrHandle = p_event.attrHandle;
sp_trsErrParams->errorCode = error;
status = GATTS_SendErrorResponse(p_event.connHandle, sp_trsErrParams);
if (status == MBA_RES_SUCCESS)
{
OSAL_Free(sp_trsErrParams);
sp_trsErrParams = NULL;
}
}
}
}
void APP_GattSEvtWriteHandler(GATT_EvtWrite_T p_event)
{
uint8_t error = 0;
uint16_t status;
SYS_CONSOLE_PRINT("[BLE] GATT Write ATTR Handle 0x%X \r\n",p_event.attrHandle);
if ((p_event.attrHandle <= BUTTON_LED_START_HDL) ||
(p_event.attrHandle > BUTTON_LED_END_HDL))
{
/* Not BLE Custom Service characteristic. */
error = ATT_ERRCODE_INVALID_HANDLE;
return;
}
switch(p_event.attrHandle)
{
case BUTTON_LED_HDL_CHAR_0: /**< Handle of characteristic 0. */
case BUTTON_LED_HDL_CHARVAL_0: /**< Handle of characteristic 0 value. */
case BUTTON_LED_HDL_CCCD_0: /**< Handle of characteristic 0 CCCD . */
case BUTTON_LED_HDL_CHAR_1: /**< Handle of characteristic 1. */
error = ATT_ERRCODE_APPLICATION_ERROR;
break;
case BUTTON_LED_HDL_CHARVAL_1: /**< Handle of characteristic 1 value. */
// SYS_CONSOLE_PRINT(" ATTR Handle %d \r\n",p_event.attrHandle);
APP_CustomService_RGB_Callback(p_event.writeValue);
break;
}
if ((p_event.writeType == ATT_WRITE_REQ)
|| (p_event.writeType == ATT_PREPARE_WRITE_REQ))
{
if (!error)
{
sp_trsRespParams = (GATTS_SendWriteRespParams_T *)OSAL_Malloc(sizeof(GATTS_SendWriteRespParams_T));
if (sp_trsRespParams == NULL)
{
return;
}
trsRespErrConnHandle = p_event.connHandle;
sp_trsRespParams->responseType = ATT_WRITE_RSP;
status = GATTS_SendWriteResponse(p_event.connHandle, sp_trsRespParams);
if (status == MBA_RES_SUCCESS)
{
OSAL_Free(sp_trsRespParams);
sp_trsRespParams = NULL;
}
}
else
{
sp_trsErrParams = (GATTS_SendErrRespParams_T *)OSAL_Malloc(sizeof(GATTS_SendErrRespParams_T));
if (sp_trsErrParams == NULL)
{
return;
}
trsRespErrConnHandle = p_event.connHandle;
sp_trsErrParams->reqOpcode = p_event.writeType;
sp_trsErrParams->attrHandle = p_event.attrHandle;
sp_trsErrParams->errorCode = error;
status = GATTS_SendErrorResponse(p_event.connHandle, sp_trsErrParams);
if (status == MBA_RES_SUCCESS)
{
OSAL_Free(sp_trsErrParams);
sp_trsErrParams = NULL;
}
}
}
}
app_ble_handler.c