Bluetooth Low Energy (BLE)

Note: The code snippets in this article are intended for XDK-Workbench versions 3.0.0 and later. If you use a 2.x version, please refer to this article.

The XDK provides several BLE APIs to manage the BLE functionality on the device. The interfaces can be used by applications in order to communicate via the ALPWISE BLE stack with surrounding BLE devices. With the BLE API, the XDK can communicate with other BLE devices easily via a bidirectional communication service and BLE clients can access the XDK’s sensor data. Currently, the XDK can only act as a peripheral device (server).

  1. General Information
    1. Understanding the Protocols
      1. GAP General Access Profile
      2. GATT Generic Attribute Profile
      3. L2CAP Logical Link Control and Adaptation Protocol
      4. HCI Host Controller Interface
    2. About BLE Communication Roles
    3. Understanding BLE Data Profiles and Services
      1. BLE Data Hierarchy
      2. XDK Services
  2. API Overview
  3. Implementation
    1. Simple Implementation
      1. Initializing the simplified BLE Implementation
      2. Enabling the simplified BLE Implementation
      3. Receiving Data over the simplified BLE Implementation
      4. Sending Data over the simplified BLE Implementation
      5. Full Code Example
    2. Advanced Implementation
      1. Preparation for the advanced BLE Implementation
      2. Set up the Service for the advanced BLE Implementation
      3. Handling BLE Events for the advanced BLE Implementation
      4. Initialize and starting up BLE in the advanced BLE Implementation
      5. Sending Data to Remote Client in the advanced BLE Implementation
      6. Full Code Example 2

General Information

Understanding the Protocols

BLE is made up of several protocols that are being used on different layers to transfer information between physical parts of the chip and the application level. The protocols supported by the ALPWISE BLE stack are depicted in the following picture and explained beneath.

Image

GAP General Access Profile

  • Initializing and configuring the internal BLE module in regards to BLE role and parameters.
  • Advertising data visible to other BLE devices with the goal of a connection establishment.
  • Discovering other devices that are in an advertising state.
  • Connecting to and disconnecting from devices.

GATT Generic Attribute Profile

  • Configuring the data to be provided to other BLE devices.
  • Querying available data and how the data can be read and/or written.
  • Providing data exchange services between two BLE devices which interact as server and client (e.g. reading and writing data).
  • Multiplexing data between higher layer protocols and lower physical layer protocols.
  • Segmenting and reassembling incoming and outgoing packets.

HCI Host Controller Interface

  • Provides a command interface between a BLE application on a host controller (e.g. XDK application, laptop debugging console) and the BLE controller inside of the XDK.
  • Enables direct interaction possibilities for the host, like device configuration, discovery, connection, sending / receiving data, etc.

About BLE Communication Roles

The BLE controller (EM9301) supports Bluetooth version 4.0 and can be run as a master or slave device. Depending on state and configuration, the BLE controller can act in the communication roles as seen in the following picture.

Image

One of the most important differences between a peripheral and central device is that:

  • BLE peripheral mode will support advertising data and accepting incoming connection requests.
  • BLE central mode will take over the role of discovering other devices and initiating connections.

Once the connection is established the BLE devices exchange data in a client-server-communication consisting of two roles:

  • A server hosting the BLE data services. Typically a data sensor offering values like heart rate or accelerometer.
  • A client requesting information from the server. Typically a data collector computing information (e.g. displaying it to a user).

Currently, the XDK can only be employed as a peripheral device, to advertise data for interested central devices. After accepting the connection initiation, the XDK communicates as a slave device with the master device by providing data as a server to the client. So the usual setup of BLE use cases with the XDK looks as shown in the following pictures.

Image

Understanding BLE Data Profiles and Services

BLE Data Hierarchy

BLE data is organized in hierarchical elements which makes it easy to understand what the BLE device is offering as a data provider on different detail levels. It also provides the possibility to reuse single components of the structure in other use cases. Typical data structure is shown at an example profile in the following picture.

Image

BLE services that fit together for a specific use case (e.g. battery service and heart rate service into heart rate profile) are combined into profiles. Services consist of at least one or more characteristics. Characteristics can be seen as data containers with a value and descriptors describing the access rules and allowed data operations. The following operations to move data between the client and the server are supported in BLE:

  • read requests can be executed by the client and the server responds with the requested data.
  • write commands are issued by the client to write values to the characteristics of the server.
  • notify needs to be actively enabled by the client at the server so the server sends notifications including the values periodically or whenever an update of data is available.
  • indicate works the same way notify does, but it also requests acknowledgement by the client whenever the values have been received which makes the notify faster but less reliable.

XDK Services

By using the highest level API, two major service groups can be initialized on the XDK. The first group is a single service, which is a bidirectional communication service. This service consists of two characteristics. The first is responsible for writing data to the XDK, the second one is responsible for reading data from the XDK. The XDK can send data to the remote client, which is a reading operation from the remote client’s point of view. Additionally, the remote client can send data to the XDK, which is a write operation from the remote clients point of view.

The second group consists of services for every sensor. The remote client can address different sensors via their respective services. Each service offers a characteristic for every dimension of the respective sensors. For example, the accelerometer service has three characteristics, one for every axis (x, y and z). On the other hand, the light sensor only has one characteristic for the illuminance.

Currently, the services are not customizable, i.e. it is not possible to change how many services there are and which characteristics they have. It is also not possible to create completely new custom services, but for most applications, the given services are sufficient enough.

API Overview

The BLE library consists of three parts, which are the general API for BLE initialization and handling, one for the bidirectional communication service and one for the sensor services. While it is recommended to use the highest level API, deeper levels can be accessed via the ALPWISE BLE stack library.

Image

The BLE API is built up on the following parts:

  • BCDS_BlePeripheral.h, which contains the BLE initialization, service registration, connection handling API.
  • BCDS_BidirectionalService.h, which contains the BLE bidirectional service API.
  • BCDS_SensorServices.h, BLE sensor services API.

All of them are combined for simple usage in the interface XDK_BLE.h.

For the following sections, the bidirectional service will be presented and implemented with the XDK_BLE.h and also with the underlying interfaces for more configurability.

Implementation

This chapter demonstrates how to implement the XDK as a BLE server/client and how to send and receive data. The ongoing chapter will describe how to initialize BLE in general with the XDK_BLE.h interface in only a short number of configurations. Secondly an explanation will follow about how to initialize BLE in greater detail with the interface BCDS_BlePeripheral.h, if more configuration is required.

Simple Implementation

Initializing the simplified BLE Implementation

Note: The code snippets in this subchapter are intended for XDK-Workbench versions 3.4.0 and higher.

The interface XDK_BLE.h utilizes a new kind of initialization, by using a configuration struct of the type BLE_Setup_T, which includes the following configuration elements:

Structure elementDescription
DeviceNameA pointer holding the device name.
IsMacAddrConfiguredA boolean representing if the MAC address should be configured.
MacAddrThe MAC address itself. Unused if IsMacAddrConfigured is set to false.
ServiceThe BLE peripheral service type.
IsDeviceCharacteristicEnabledA boolean representing if a device characteristic should be added to the service information such as model number, manufacturer and software revision.
DataRxCBA pointer to the data receive callback function used for the bidirectional send receive service.
CustomServiceRegistryCBA pointer to the custom receive callback function for the custom service.

An example about how to configure and initialize the simplified BLE functionality is shown in the following code below.

Please note that the example code for the XDK_BLE.h interface is following the conventions of the new XdkApplicationtemplate example from the XDK-Workbench 3.4.0.

#include "XDK_BLE.h"
// other interfaces here

static void BleDataRxCB(uint8_t *rxBuffer, uint8_t rxDataLength, void * param);

static BLE_Setup_T BLESetupInfo =
        {
                .DeviceName = "XDK_BLE_APP",
                .IsMacAddrConfigured = false,
                .MacAddr = 0UL,
                .Service = BLE_BCDS_BIDIRECTIONAL_SERVICE,
                .IsDeviceCharacteristicEnabled = false,
                .CharacteristicValue =
                        {
                                .ModelNumber = NULL,
                                .Manufacturer = NULL,
                                .SoftwareRevision = NULL
                        },
                .DataRxCB = BleDataRxCB,
                .CustomServiceRegistryCB = NULL,
        };/**< BLE setup parameters */

static void AppControllerSetup(void * param1, uint32_t param2) {
    // Other XdkApplicationTemplate code here

    vTaskDelay(UINT32_C(5000));
    BLE_Setup(&BLESetupInfo);

    // Other XdkApplicationTemplate code here
}

The example code shows first the inclusion of the interface XDK_BLE.h. Afterwards, a struct called BLESetupInfo of the type BLE_Setup_T is declared and filled with the parameters described in the previously listed table. The configuration is made for the bidirectional send receive service. As such, a prototype called BleDataRxCB for the callback function parameter DataRxCB is defined before the declaration of the setup struct BleSetupInfo. Please note that only the necessary parameters for the bidirectional send receive service such as DeviceName, IsMacAddrConfigured, Service and DataRxCB are configured.

Afterwards, the function BLE_Setup() with the as-reference-passed BLESetupInfo struct is called within the context of the function AppControllerSetup to initialize the BLE module. Please note that a small delay of 5 seconds is necessary before calling the function BLE_Setup() to ensure that the underlying semaphore implementation does not collide with the startup of the FreeRTOS scheduler.

Enabling the simplified BLE Implementation

Now that all setup configurations are made, it is time to enable the BLE module. This means in detail that the BLE chip EM9301 on the XDK needs to be started and sent into the fully operational state.

For that, the function BLE_Enable() needs to be called in the context of the function AppControllerEnable() as shown in the code below.

static void AppControllerEnable(void * param1, uint32_t param2) {
  // Other XdkApplicationTemplate code here

    BLE_Enable();
}

Receiving Data over the simplified BLE Implementation

Now that all configuration and enabling is done, the receiving callback, which is passed to the struct element DataRxCB needs to be implemented. As already a prototype definition was made for that, the actual function body for the receive data callback is BleDataRxCB().

static void BleDataRxCB(uint8_t *rxBuffer, uint8_t rxDataLength, void * param) {

    BCDS_UNUSED(param);

    uint8_t bleReceiveBuff[UINT8_C(21)];

    memset(bleReceiveBuff, 0, sizeof(bleReceiveBuff));
    memcpy(bleReceiveBuff, rxBuffer, rxDataLength < UINT8_C(21) ? rxDataLength : UINT8_C(21) - 1);
    printf("Received data: %s \n", bleReceiveBuff);
}

The function callback takes in its signature three parameters, the receive buffer rxBuffer holding the received data, the length rxDataLength of the received data buffer and an additional parameter param. The parameter param will not be used for further processing. Furthermore, the function will copy the data that the XDK received via BLE into a buffer. If the message is too long, it will be cut off. Finally, the received data will be printed as a string.

Sending Data over the simplified BLE Implementation

Now that everything is set up and enabled, the XDK is not only able to receive messages but also able to send messages. For that, the function BLE_SendData() can be used as shown in the code below.

BLE_SendData((uint8_t*) "Hello Client!\0",(uint8_t) sizeof("Hello Client!\0") - 1,NULL,UINT32_C(1000));

The function BLE_SendData() takes four parameters, the actual payload containing the message to be sent, the length of the payload and additional parameter, which is in case of the bidirectional send receive service NULL and therefore not relevant and a timeout in which the message should be sent out.

Full Code Example

Note: The full code example is intended for XDK-Workbench versions 3.4.0 and later.

/*----------------------------------------------------------------------------*/

/* --------------------------------------------------------------------------- |
 * INCLUDES & DEFINES ******************************************************** |
 * -------------------------------------------------------------------------- */

/* own header files */
#include "XdkAppInfo.h"
#undef BCDS_MODULE_ID  /* Module ID define before including Basics package*/
#define BCDS_MODULE_ID XDK_APP_MODULE_ID_APP_CONTROLLER

/* system header files */
#include <stdio.h>

/* additional interface header files */
#include "BCDS_CmdProcessor.h"
#include "FreeRTOS.h"

#include "XDK_BLE.h"

/* --------------------------------------------------------------------------- |
 * HANDLES ******************************************************************* |
 * -------------------------------------------------------------------------- */

static CmdProcessor_T * AppCmdProcessor;/**< Handle to store the main Command processor handle to be used by run-time event driven threads */

/* --------------------------------------------------------------------------- |
 * VARIABLES ***************************************************************** |
 * -------------------------------------------------------------------------- */

 /* --------------------------------------------------------------------------- |
 * EXECUTING FUNCTIONS ******************************************************* |
 * -------------------------------------------------------------------------- */

static void BleDataRxCB(uint8_t *rxBuffer, uint8_t rxDataLength, void * param);

static BLE_Setup_T BLESetupInfo =
        {
                .DeviceName = "XDK_FS_BLE_APP",
                .IsMacAddrConfigured = false,
                .MacAddr = 0UL,
                .Service = BLE_BCDS_BIDIRECTIONAL_SERVICE,
                .IsDeviceCharacteristicEnabled = false,
                .CharacteristicValue =
                        {
                                .ModelNumber = NULL,
                                .Manufacturer = NULL,
                                .SoftwareRevision = NULL
                        },
                .DataRxCB = BleDataRxCB,
                .CustomServiceRegistryCB = NULL,
        };/**< BLE setup parameters */

static void BleDataRxCB(uint8_t *rxBuffer, uint8_t rxDataLength, void * param) {

    BCDS_UNUSED(param);

    uint8_t bleReceiveBuff[UINT8_C(21)];

    memset(bleReceiveBuff, 0, sizeof(bleReceiveBuff));
    memcpy(bleReceiveBuff, rxBuffer, rxDataLength < UINT8_C(21) ? rxDataLength : UINT8_C(21) - 1);
    printf("Received data: %s \n", bleReceiveBuff);

    BLE_SendData((uint8_t*) "Hello Client!\0",(uint8_t) sizeof("Hello Client!\0") - 1,NULL,UINT32_C(1000));
}

/* --------------------------------------------------------------------------- |
 * BOOTING- AND SETUP FUNCTIONS ********************************************** |
 * -------------------------------------------------------------------------- */

static void AppControllerEnable(void * param1, uint32_t param2)
{
    BCDS_UNUSED(param1);
    BCDS_UNUSED(param2);

    /* Enable necessary modules for the application and check their return values */
    BLE_Enable();
}

static void AppControllerSetup(void * param1, uint32_t param2)
{
    BCDS_UNUSED(param1);
    BCDS_UNUSED(param2);
    Retcode_T retcode = RETCODE_OK;

    /* Setup the necessary modules required for the application */
    vTaskDelay(UINT32_C(5000));
    BLE_Setup(&BLESetupInfo);

    retcode = CmdProcessor_Enqueue(AppCmdProcessor, AppControllerEnable, NULL, UINT32_C(0));
    if (RETCODE_OK != retcode)
    {
        printf("AppControllerSetup : Failed \r\n");
        Retcode_RaiseError(retcode);
        assert(0);
    }
}

void AppController_Init(void * cmdProcessorHandle, uint32_t param2)
{
    BCDS_UNUSED(param2);

    Retcode_T retcode = RETCODE_OK;

    if (cmdProcessorHandle == NULL)
    {
        printf("AppController_Init : Command processor handle is NULL \r\n");
        retcode = RETCODE(RETCODE_SEVERITY_ERROR, RETCODE_NULL_POINTER);
    }
    else
    {
        AppCmdProcessor = (CmdProcessor_T *) cmdProcessorHandle;
        retcode = CmdProcessor_Enqueue(AppCmdProcessor, AppControllerSetup, NULL, UINT32_C(0));
    }

    if (RETCODE_OK != retcode)
    {
        Retcode_RaiseError(retcode);
        assert(0);
    }
}

/** ************************************************************************* */

Advanced Implementation

Preparation for the advanced BLE Implementation

To use the advanced BLE API, the necessary header files have to be included, as seen in the code below. Additionally, semphr.h will be included, as they are necessary to ensure that BLE will function correctly. For more information on semaphores, please refer to the FreeRTOS section.

#include "BCDS_BlePeripheral.h"
#include "BCDS_BidirectionalService.h"
#include "semphr.h"

Additionally, this subchapter will use semaphores, and these have to be globally accessible. Additionally, functions that access semaphores usually have a timeout. For this, constant defines will be used. The following code can be placed near the top of the implementation file, below the includes.

#define BLE_START_SYNC_TIMEOUT    UINT32_C(5000)
#define BLE_WAKEUP_SYNC_TIMEOUT   UINT32_C(5000)
#define BLE_SEND_TIMEOUT    UINT32_C(1000)

static SemaphoreHandle_t BleStartSyncSemphr = NULL;
static SemaphoreHandle_t BleWakeUpSyncSemphr = NULL;
static SemaphoreHandle_t SendCompleteSyncSemphr = NULL;

Set up the Service for the advanced BLE Implementation

Before BLE can be initialized, some functions have to be defined to set up the service. A service is set up by defining two callbacks. The first will handle incoming data and read requests. The second one will monitor the sending process of every message sent to the remote client. These functions are defined in the code below and will be used in another function, that will set up the service.

static void dataReceived(uint8_t *rxBuffer, uint8_t rxDataLength) {
  uint8_t receiveBuffer[UINT8_C(24)];
  memset(receiveBuffer, 0, sizeof(receiveBuffer));
  memcpy(receiveBuffer, rxBuffer,
          rxDataLength < UINT8_C(24) ? rxDataLength : UINT8_C(23));
  printf("Received data: %s \n\r", receiveBuffer);
}

static void dataSent(Retcode_T sendStatus) {
  BCDS_UNUSED(sendStatus);
  xSemaphoreGive(SendCompleteSyncSemphr);
}

The function dataReceived() will copy the data that the XDK received via BLE into a buffer. If the message is too long, it will be cut off. Finally, the received data will be printed as a string. The function dataSent() only releases a semaphore that handles synchronizing multiple sending processes. The function dataSent() is called everytime a message is done being sent (either successfully or not successfully). The signatures should stay the same as they are presented here. Otherwise the functions will not work. These two functions are going to be used in a third function that handles initializing and registering the service. The code below shows how to implement the function.

static Retcode_T initializeAndRegisterService(void) {
  BidirectionalService_Init(dataReceived, dataSent);
  BidirectionalService_Register();
  return RETCODE_OK;
}

Again, the function’s signature should not be changed, otherwise it might lead to unexpected behaviour later on. The function initializeAndRegisterService() will be used during initialization of the BLE peripherial, later on in this guide.

Handling BLE Events for the advanced BLE Implementation

The BLE library will eventually throw peripheral events, for example when a device connects, or disconnects, but also others, that are needed during initialization and startup. For this, a callback function will be defined. This function is presented in the code below. It has two inputs, the event itself, and data that belongs to the event. The events and the corresponding data types are listed in the header file BCDS_BlePeripheral.h.

static void handleEvent(BlePeripheral_Event_T event, void *data) {
  switch(event) {
    case BLE_PERIPHERAL_STARTED:
        xSemaphoreGive( BleStartSyncSemphr );
        break;
    case BLE_PERIPHERAL_WAKEUP_SUCCEEDED:
        xSemaphoreGive( BleWakeUpSyncSemphr );
        break;
    case BLE_PERIPHERAL_CONNECTED:
        {
          Ble_RemoteDeviceAddress_T *remoteAddress;
          remoteAddress = (Ble_RemoteDeviceAddress_T*) data;
          printf("Device connected: %02x:%02x:%02x:%02x:%02x:%02x\n\r",
          remoteAddress->Addr[0], remoteAddress->Addr[1],
          remoteAddress->Addr[2], remoteAddress->Addr[3],
          remoteAddress->Addr[4], remoteAddress->Addr[5]);
        }
        break;
    case BLE_PERIPHERAL_DISCONNECTED:
        printf("Device Disconnected\n\r");
        break;
    default:
        break;
  }
}

The first two events occur during the starting up process. In these cases the semaphores, that were previously taken, will be given back. In the case of connection events, the remote client’s address will be printed. In disconnection events, a simple message will be printed.

Initialize and starting up BLE in the advanced BLE Implementation

Now, everything is prepared and it is possible to initialize and startup the BLE peripheral. The code below shows which functions have to be called to get the BLE peripheral up and running, using all the functions that have been previously defined in one sequence. This code can be used in any function, for example AppControllerSetup().

BleStartSyncSemphr = xSemaphoreCreateBinary();
BleWakeUpSyncSemphr = xSemaphoreCreateBinary();
SendCompleteSyncSemphr = xSemaphoreCreateBinary();
BlePeripheral_Initialize(handleEvent, initializeAndRegisterService);
BlePeripheral_SetDeviceName((uint8_t *) "XDK BLE Guide");
if(RETCODE_OK == BlePeripheral_Start()) {
  xSemaphoreTake(BleStartSyncSemphr, BLE_START_SYNC_TIMEOUT);
}
if(RETCODE_OK == BlePeripheral_Wakeup()) {
  xSemaphoreTake(BleWakeUpSyncSemphr, BLE_WAKEUP_SYNC_TIMEOUT);
}

Sending Data to Remote Client in the advanced BLE Implementation

Now that the BLE peripheral is set up, and clients can connect, the XDK is able to send messages. For this, a function can be defined, that can be called from anywhere. The function is defined in the following code snippet. It will send a message, and take the corresponding semaphore to synchronize with other messages.

void transmitData(void) {
  Retcode_T sendingReturnValue = BidirectionalService_SendData(
  (uint8_t *) "Hello Client",
  (uint8_t) sizeof("Hello Client"));
  if (sendingReturnValue == RETCODE_OK) {
    xSemaphoreTake(SendCompleteSyncSemphr, BLE_SEND_TIMEOUT);
  }
}

A message can even be sent inside the dataReceived() function, as an immediate reaction to incoming messages.

Full Code Example 2

Note: The full code example is intended for XDK-Workbench versions 3.4.0 and later.

/*----------------------------------------------------------------------------*/

/* --------------------------------------------------------------------------- |
 * INCLUDES & DEFINES ******************************************************** |
 * -------------------------------------------------------------------------- */

/* own header files */
#include "XdkAppInfo.h"
#undef BCDS_MODULE_ID  /* Module ID define before including Basics package*/
#define BCDS_MODULE_ID XDK_APP_MODULE_ID_APP_CONTROLLER

/* system header files */
#include <stdio.h>

/* additional interface header files */
#include "BCDS_CmdProcessor.h"
#include "FreeRTOS.h"
#include "task.h"

#include "BCDS_BlePeripheral.h"
#include "BCDS_BidirectionalService.h"
#include "semphr.h"

/* --------------------------------------------------------------------------- |
 * HANDLES ******************************************************************* |
 * -------------------------------------------------------------------------- */

static CmdProcessor_T * AppCmdProcessor;/**< Handle to store the main Command processor handle to be used by run-time event driven threads */

/* --------------------------------------------------------------------------- |
 * VARIABLES ***************************************************************** |
 * -------------------------------------------------------------------------- */

#define BLE_START_SYNC_TIMEOUT        UINT32_C(5000)
#define BLE_WAKEUP_SYNC_TIMEOUT       UINT32_C(5000)
#define BLE_SEND_TIMEOUT              UINT32_C(1000)

static SemaphoreHandle_t BleStartSyncSemphr = NULL;
static SemaphoreHandle_t BleWakeUpSyncSemphr = NULL;
static SemaphoreHandle_t SendCompleteSync = NULL;

/* --------------------------------------------------------------------------- |
 * EXECUTING FUNCTIONS ******************************************************* |
 * -------------------------------------------------------------------------- */

static void TransmitBleData(void)
{
    Retcode_T sendingReturnValue = BidirectionalService_SendData(
            (uint8_t*) "Hello Client!\0",
            (uint8_t) sizeof("Hello Client!\0") - 1);
    if (sendingReturnValue == RETCODE_OK) {
        xSemaphoreTake(SendCompleteSync, BLE_SEND_TIMEOUT);
    }
}

static void BleDataReceivedCallBack(uint8_t *rxBuffer, uint8_t rxDataLength)
{
    uint8_t bleReceiveBuff[UINT8_C(21)];

    memset(bleReceiveBuff, 0, sizeof(bleReceiveBuff));
    memcpy(bleReceiveBuff, rxBuffer, rxDataLength < UINT8_C(21) ? rxDataLength : UINT8_C(21) - 1);
    printf("Received data: %s \n", bleReceiveBuff);
    TransmitBleData();

}

static void BleDataSentCallback(Retcode_T sendStatus) {
    BCDS_UNUSED(sendStatus);
    xSemaphoreGive(SendCompleteSync);
}

static void BleEventCallBack(BlePeripheral_Event_T event, void * data) {
    switch (event) {

        case BLE_PERIPHERAL_STARTED:
            xSemaphoreGive( BleStartSyncSemphr );
            printf("EM9301 started \n\r");
            break;

        case BLE_PERIPHERAL_WAKEUP_SUCCEEDED:
            xSemaphoreGive( BleWakeUpSyncSemphr );
            printf("EM9301 woked up \n\r");
            break;

        case BLE_PERIPHERAL_CONNECTED: {
            Ble_RemoteDeviceAddress_T *remoteAddress;
            remoteAddress = (Ble_RemoteDeviceAddress_T*) data;
            printf("Device connected: %02x:%02x:%02x:%02x:%02x:%02x \r\n", remoteAddress->Addr[0], remoteAddress->Addr[1], remoteAddress->Addr[2],
                        remoteAddress->Addr[3], remoteAddress->Addr[4], remoteAddress->Addr[5]);
        }
        break;

    case BLE_PERIPHERAL_DISCONNECTED:
        printf("Device Disconnected: \r\n");
        break;

    default:
        break;
    }
}

static Retcode_T CreateServiceCallback(void)
{
    BidirectionalService_Init(BleDataReceivedCallBack, BleDataSentCallback);
    BidirectionalService_Register();
    return RETCODE_OK;
}

void BleInit(void){
    BleStartSyncSemphr = xSemaphoreCreateBinary();
    BleWakeUpSyncSemphr = xSemaphoreCreateBinary();
    SendCompleteSync = xSemaphoreCreateBinary();

    BlePeripheral_Initialize(BleEventCallBack, CreateServiceCallback);
    printf("BLE Peripheral initialized \n\r");
    BlePeripheral_SetDeviceName((uint8_t*) "XDK BLE Guide");
    if(RETCODE_OK == BlePeripheral_Start()) {
        xSemaphoreTake(BleStartSyncSemphr, BLE_START_SYNC_TIMEOUT);
    }
    if(RETCODE_OK == BlePeripheral_Wakeup()) {
        xSemaphoreTake(BleWakeUpSyncSemphr, BLE_WAKEUP_SYNC_TIMEOUT);
    }
}

/* --------------------------------------------------------------------------- |
 * BOOTING- AND SETUP FUNCTIONS ********************************************** |
 * -------------------------------------------------------------------------- */

static void AppControllerSetup(void * param1, uint32_t param2)
{
    BCDS_UNUSED(param1);
    BCDS_UNUSED(param2);

    /* @todo - Setup the necessary modules required for the application */
    vTaskDelay(5000);
    printf("Initializing BLE \n\r");
    BleInit();
}

void AppController_Init(void * cmdProcessorHandle, uint32_t param2)
{
    BCDS_UNUSED(param2);

    Retcode_T retcode = RETCODE_OK;

    if (cmdProcessorHandle == NULL)
    {
        printf("AppController_Init : Command processor handle is NULL \r\n");
        retcode = RETCODE(RETCODE_SEVERITY_ERROR, RETCODE_NULL_POINTER);
    }
    else
    {
        AppCmdProcessor = (CmdProcessor_T *) cmdProcessorHandle;
        retcode = CmdProcessor_Enqueue(AppCmdProcessor, AppControllerSetup, NULL, UINT32_C(0));
    }

    if (RETCODE_OK != retcode)
    {
        Retcode_RaiseError(retcode);
        assert(0); /* To provide LED indication for the user */
    }
}

/** ************************************************************************* */