Application Structure
This chapter describes the organization of the Bluetooth Low Energy demo applications that can be found in the SDK. By familiarizing with the application structure, the user is able to quickly adapt its design to an existing demo or create a new application.
The Temperature Sensor application is used as a reference to showcase the architecture.
Folder structure
The Figure 1 shows the application folder structure.
The app folder follows a specific structure which is recommended for any application developed using the Bluetooth Low Energy Host Stack:
The common group contains the application framework shared by all profiles and demo applications:
Bluetooth Low Energy Connection Manager
Bluetooth Service Discovery Manager
Bluetooth Low Energy Stack and Task Initialization and Configuration
GATT Database
The source group contains code specific to the Temperature Sensor application
In the following examples, app.c is used as a placeholder for the main application source file. In the case of Temperature Sensor, it is temperature_sensor.c.
The source group also contains files which aid with the handling of Host events and framework related functionality. These are: app_con.c/app_con.h, app_advertiser.c, app_connection.c, app_scanner.c, app_nvm.c, and app_lowpower.c. These files do not allow the application to implement its own state machines, unless application-specific functionality is required. For example, the application is restricted from setting advertising parameters and starting advertising, unless certain application-specific functionalities, such as UI related updates are required.
The bluetooth folder/group contains:
The controller/interface, host/interface, and host/config. These are public interfaces and configuration files for the Controller and the Host. For the Host, functionality is included in the library located in the host/lib subfolder. The folder is not shown in the IAR project structure, but added into the toolchain linker settings under the library category.
profiles contains profile-specific code; it is used by each demo application of standard profiles.
The framework and component folders/groups contain framework components used by the demo applications. For additional information, see the Connectivity Framework Reference Manual.
The freertos folder contains sources for the supported operating system.
Parent topic:Application Structure
Application main framework
The Application Main module contains common code used by all the applications, such as:
The Main Task
Messaging framework between the Bluetooth LE Host Stack Task and the Application Task
Start task
The Start Task (start_task) is the first task created by the operating system and is also the one that initializes the rest of the system. It initializes framework components (Memory Manager, Timers Manager etc.) and it calls BluetoothLEHost_AppInit from app.c, which is used to initialize the Bluetooth LE Host Stack as well as peripheral drivers specific to the implemented application.
The function calls BluetoothLEHost_HandleMessages, which represents the Application Task and is used to process events and messages from the Host Stack.
The stack size and priority of the main task are defined in fsl_os_abstraction_config.h:
**\#ifndef** gMainThreadStackSize_c
**\#define** gMainThreadStackSize_c 1024
**\#endif**
**\#ifndef** gMainThreadPriority_c
**\#define** gMainThreadPriority_c 7
**\#endif**
Parent topic:Application main framework
Application messaging
The module contains a wrapper that is used to create messages for events generated by the Bluetooth LE Host Stack in the Host Task context. The wrapper also sends them to be processed by the application in the context of the Application Task.
For example, connection events generated by the Host are received by App_ConnectionCallback. The function creates a message, places it in the Host to Application queue and signals the Application with gAppEvtMsgFromHostStack_c. The Application Task de-queues the message and calls App_HandleHostMessageInput, which calls the corresponding callback implemented the application-specific code (app.c), in this example: BleApp_ConnectionCallback.
It is strongly recommended that the application developer uses the app.c module to add custom code on this type of callbacks.
Parent topic:Application main framework
Parent topic:Application Structure
Bluetooth LE Connection Manager
The connection manager is a helper module that contains common application configurations and interactions with the Bluetooth LE Host Stack. It implements the following events and methods:
Host Stack GAP Generic Event
Host Stack Connection Event on both GAP Peripheral and GAP Central configuration
Host Stack configuration for GAP Peripheral or GAP Central
GAP generic event
The GAP Generic Event is triggered by the Bluetooth LE Host Stack and sent to the application via the generic callback. Before any application-specific interactions, the Connection Manager callback is called to handle common application events, such as device address storage.
**void** **BleApp\_GenericCallback** ( gapGenericEvent_t * pGenericEvent)
{
/* Call Bluetooth Low Energy Conn Manager */
BleConnManager_GenericEvent(pGenericEvent);
**switch** (pGenericEvent-> eventType )
{
...
}
}
In the BleConnManager_GenericEvent function, local keys are generated.
The local LTK, IRK, and CSRK as well as the EDIV and RAND are obtained hashing over the board’s UID and stored in RAM as plain-text every time the gInitializationComplete_c event is received.
In Advanced Secure mode, local IRK and CSRK are generated using the EdgeLock Secure Enclave and stored into a dedicated NVM data set as ELKE blobs (40 bytes blob encrypted using unique die key) on the first
gInitializationComplete_c
event received.
The NBU Decryption key for IRK is generated and distributed to the NBU over the private key bus. The EIRK blob (16 bytes blob which can be decrypted only by NBU hardware using NBU Decryption key for IRK) is generated from the IRK ELKE blob and stored in the RAM to be used for controller privacy on every gInitializationComplete_c
event received. For the host privacy, the ELKE IRK blob is used instead. For details, refer to the section “Advanced security capabilities”.
Parent topic:Bluetooth LE Connection Manager
GAP configuration
The GAP Central or Peripheral Configuration is used to create common configurations (such as setting the public address, registering the security requirements, adding the addresses of bonded devices in the Controller Filter Accept List), which can be customized by the application afterwards. It is called inside the BluetoothLEHost_Initialized callback function, before any application-specific configuration, as shown in the example code below.
**static** **void** **BluetoothLEHost\_Initialized**()
{
/* Set common GAP configuration */
BleConnManager_GapCommonConfig();
...
}
Parent topic:Bluetooth LE Connection Manager
GAP connection event
The GAP Connection Event is triggered by the Host Stack and sent to the application via the connection callback. Before any application-specific interactions, the Connection Manager callback is called to handle common application events, such as device connect, disconnect or pairing-related requests. It is called inside the registered connection such as shown below:
**static** **void** **BleApp\_ConnectionCallback** ( deviceId_t peerDeviceId, gapConnectionEvent_t * pConnectionEvent)
{
/* Connection Manager to handle Host Stack interactions */
BleConnManager_GapPeripheralEvent(peerDeviceId, pConnectionEvent);
**switch** (pConnectionEvent-> eventType )
{
...
}
}
It is strongly recommended that the application developer uses the app.c
module to add custom code.
Parent topic:Bluetooth LE Connection Manager
Privacy
To enable or disable Privacy, the following APIs may be used:
bleResult_t
**BleConnManager\_EnablePrivacy**(void);
bleResult_t
**BleConnManager\_DisablePrivacy**(void);
The function BleConnManager_EnablePrivacy calls BleConnManager_ManagePrivacyInternal after checking if the privacy is enabled.
static bleResult_t
**BleConnManager\_ManagePrivacyInternal**
(bool_t bCheckNewBond);
If the privacy feature is supported (gAppUsePrivacy_d = 1), the Connection Manager activates Controller Privacy or Host Privacy depending on the board capabilities.
The bCheckNewBond is a boolean that tells the Manager whether it should check or not if a bond between the devices already exists.
In order to update the identity information after a bond is added or removed privacy should be disabled and enabled. For pairing with bonding this is done automatically in ble_conn_manager. In case the application adds or removes a bond through the GAP API, it should also disable and enable privacy.
Parent topic:Bluetooth LE Connection Manager
Parent topic:Application Structure
GATT database
The gatt_db contains a set of header files grouped in the macros subfolder. These macros are used for static code generation for the GATT Database by expanding the contents of the gatt_db.h file in different ways. Creating GATT database explains how to write the gatt_db.h file using user-friendly macros that define the GATT database.
At application compile time, the gatt_database.c file is populated with enumerations, structures, and initialization code used to allocate and properly populate the GATT database. In this way, the gattDatabasearray and the gGattDbAttributeCount_c variable (see GATT database) are created and properly initialized.
Note: Do not modify any of the files contained in the gatt_db folder and its subfolder.
To complete the GATT database initialization, this demo application includes the required gatt_db.h
and gatt_uuid128.h
files in its specific application folder, along with other profile-specific configuration and code files.
Parent topic:Application Structure
RTOS specifics
Operating system selection
The SDK offers different projects for each supported operating system (FreeRTOS OS) and for BareMetal configuration. To switch between systems, the user needs to switch the workspace.
The RTOS source code is found in the SDK package and is linked in the workspace in the freertos virtual folder, as shown in Figure 1:
Parent topic:RTOS specifics
Bluetooth LE Host task configuration
Application developers are provided with two files for RTOS task initialization:
ble_host_task_config.h, and ble_host_tasks.c for the Host.
Reusing these files is recommended because they perform all the necessary RTOS-related work. The application developer must only modify the macros from *_config.h files whenever tasks need a bigger stack size or different priority settings. The new values should be overridden in the app_preinclude.h file.
Parent topic:RTOS specifics
Parent topic:Application Structure
Board configuration
The configuration files for the supported boards can be found in the board folder, as shown in Figure 1. The files contain clock and pin configurations that are used by the drivers. The user can customize the board files by modifying the configuration of the pins and clock source according to his design.
Parent topic:Application Structure
Bluetooth Low Energy initialization
The ble_init.h and ble_init.c files contain the declaration and the implementation of the following function:
bleResult_t **Ble\_Initialize**
(
gapGenericCallback_t gapGenericCallback
)
{
#if defined(gUseHciTransportDownward_d) && gUseHciTransportDownward_d
/* HCI Transport Init */
if (gHciSuccess_c != Hcit_Init(Ble_HciRecvFromIsr))
{
return gHciTransportError_c;
}
#if defined(KW45B41Z83_SERIES) || \
defined(KW45B41Z82_SERIES) || \
defined(K32W1480_SERIES)
/*
* Set BD Address in Controller. Must be done after HCI init
* and before Host init.
*/
Ble_SetBDAddr();
#endif /* KW45B41Z83_SERIES */
/* Check for available memory storage */
if (!Ble_CheckMemoryStorage())
{
return gBleOutOfMemory_c;
}
/* Bluetooth Low Energy Host Tasks Init */
if (KOSA_StatusSuccess != Ble_HostTaskInit())
{
return gBleOsError_c;
}
/* Bluetooth Low Energy Host Stack Init */
return Ble_HostInitialize(gapGenericCallback, Hcit_SendPacket);
#elif defined(gUseHciTransportUpward_d) && gUseHciTransportUpward_d
#else /* gUseHciTransportUpward_d */
#endif /* gUseHciTransportUpward_d */
}
Note: This function should be used by your application because it correctly performs all the necessary Bluetooth Low Energy initialization.
Step-by-step analysis is provided below:
First, the HCI interface is initialized by calling Hcit_Init. This initializes communication between the Host and the Controller.
After setting the BD address into the Controller (Ble_SetBDAddr) and performing memory validation checks (Ble_CheckMemoryStorage), the Host task is initialized by calling Ble_HostTaskInit.
Finally the Ble_HostInitialize function initializes the Host with the transport packet transmit function used as the hciHostToControllerInterface_t parameter.
Parent topic:Application Structure
Bluetooth Low Energy Host Stack configuration
The Bluetooth LE Host Stack libraries are found in the middleware/wireless/bluetooth/host/lib
folder. The user should add the best matching library for its use case to the linker options of its project.
For example, the temperature sensor uses the Peripheral Host Stack library, as shown in Figure 1:
Parent topic:Application Structure
Profile configuration
The implemented profiles and services are located in middleware/wireless/bluetooth/profiles folder. The application links every service source file and interface it needs to implement the profile. For example, for the Temperature Sensor the tree looks as shown Figure 1:
The Temperature Profile implements the custom Temperature service, the Battery, and Device Information services.
Application code
The application folder contains the following modules:
app.c and app.h. This module stores the application-specific functionality (APIs for specific triggers, handling of peripherals, callbacks from the stack, handling of low power, and so on).
Before initializing the Bluetooth LE Host Stack, the start task calls BluetoothLEHost_AppInit. This function initializes application specific functionality before initializing the Bluetooth LE Host Stack by calling BluetoothLEHost_Init.
After the stack is initialized, the BluetoothLEHost_Initialized callback is called. The function contains configurations made to the Bluetooth LE Host Stack after the initialization. This includes registering callbacks, setting security for services, starting services, allocating timers, adding devices to the Filter Accept List, and so on. For example, the Temperature Sensor configures the following:
static void **BluetoothLEHost\_Initialized**(void)
{
/* Common GAP configuration */
BleConnManager_GapCommonConfig();
/* Register for callbacks*/
(void)App_RegisterGattServerCallback(BleApp_GattServerCallback);
mAdvState.advOn = FALSE;
/* Start services */
SENSORS_TriggerTemperatureMeasurement();
(void)SENSORS_RefreshTemperatureValue();
/* Multiply temperature value by 10. SENSORS_GetTemperature() reports temperature
value in tenths of degrees Celsius. Temperature characteristic value is degrees
Celsius with a resolution of 0.01 degrees Celsius (GATT Specification
Supplement v6). */
tmsServiceConfig.initialTemperature = (int16_t)(10 * SENSORS_GetTemperature());
(void)Tms_Start(&tmsServiceConfig);
basServiceConfig.batteryLevel = SENSORS_GetBatteryLevel();
(void)Bas_Start(&basServiceConfig);
(void)Dis_Start(&disServiceConfig);
/* Allocate application timer */
(void)TM_Open(appTimerId);
AppPrintString("\r\nTemperature sensor -> Press switch to start advertising.\r\n");
}
To start the application functionality, BleApp_Start()
function is called. This function usually contains code to start advertising for sensor nodes or scanning for central devices. In the example of the Temperature Sensor, the function is the following:
static void **BleApp\_Start**(void)
{
Led1On();
if (mPeerDeviceId == gInvalidDeviceId_c)
{
/* Device is not connected and not advertising */
if (!mAdvState.advOn)
{
/* Set advertising parameters, advertising to start on gAdvertisingParametersSetupComplete_c */
BleApp_Advertise();
}
}
else
{
/* Device is connected, send temperature value */
BleApp_SendTemperature();
}
}
app_config.c. This file contains data structures that are used to configure the stack.
This includes advertising data, scanning data, connection parameters, advertising parameters, SMP keys, security requirements, and so on.
app_preinclude.h.
This header file contains macros to override the default configuration of any module in the application. It is added as a preinclude file in the preprocessor command line in IAR, as shown in Figure 1:
gatt_db.h and gatt_uuid128.h. The two header files contain the definition of the GATT database and the custom UUIDs used by the application. See Creating GATT database for more information.
Parent topic:Profile configuration
Parent topic:Application Structure
Multiple connections
Applications can be configured to support multiple connections. To allow multiple connections, the gAppMaxConnections_c must be set to a value up to the maximum number of connections (this value is chip-specific). Refer to the chip documentation for the supported number of connections.
The application can save information about the peer devices it connects to according to the value of gAppMaxConnections_c. The Bluetooth Low Energy profile associated with the application use case must be instantiated to support the use of its functionality for each peer device. When handling multiple connections, the applications can behave as either the GAP central, GAP peripheral, or both at the same time. It is up to the application code to decide whether to start the advertising or scanning before creating the next connection. The supported combinations enable a device to connect as a peripheral to multiple centrals, as a central to multiple peripherals, or for it to be a central for some peers and a peripheral to others. The demo applications provide this functionality as an example of exercising multiple connection support. In such applications, the GAP role can be changed from central to peripheral and the information is saved for each peer device.
Parent topic:Application Structure
Bluetooth address generation
BD_ADDR is represented by 48 bits that uniquely identify a device and consist of a 24-bit OUI (Organizationally Unique Identifier) and a 24-bit random part that varies between devices. There are multiple options of storing and using the BD_ADDR. Depending on the chip, it may be read from a device specific register (if supported), from the global hardware parameters stored in the flash, or generated randomly based on the processor-unique identifier. The demo applications provide a combination of the last two options. The Ble_SetBDAddr function is called during the initialization process, after initializing HCI and before initializing the Bluetooth LE Host Stack. The global hardware parameters are read from the flash. If a useful value is found, it is used as the BD address. If the found value is all 0xFFs, an address is generated by concatenating the OUI configured at compile time with three randomly generated bytes. The result is stored in the hardware parameters for future use and then set into the Controller. The Bluetooth LE Host Stack uses little-endian format to represent all addresses, in compliance with the Bluetooth Core Specification.
Parent topic:Application Structure
Repeated attempts
Applications can be configured to enable protection against repeated Pairing Requests/Peripheral Security Requests coming from the same device. This is to prevent an intruder from repeating the pairing process with a large number of different keys in order to extract information about the local device’s private key. If this feature is enabled, after a pairing procedure fails, another attempt to pair coming from the same device is allowed only after a specific time period has passed. For each failure, the waiting period doubles up until a maximum period.
The following app_preinclude.h macros support this feature:
gRepeatedAttempts_d
Set to 1 to enable the feature. By default, it is disabled (0).
gRepeatedAttemptsNoOfDevices_c
Number of remote devices to keep track of. By default, the value is 4.
If a new device needs to be added and the list is full, one of the oldest entries will be replaced.
gRepeatedAttemptsTimeoutMin_c
Minimum waiting period in seconds – default 10.
gRepeatedAttemptsTimeoutMax_c
Maximum waiting period in seconds – default 640. The waiting period doubles after each failed pairing with the same device.
Parent topic:Application Structure
Advanced Secure Mode (kw45_k32w)
This section describes the advanced security capabilities of the Bluetooth LE Host Stack which are available on the KW45/K32W1 platform via the EdgeLock Secure Enclave (ELKE).
The security capabilities are enabled at application, Host and Controller level by setting Advanced Secure Mode to active. To do this, the user must set the gAppSecureMode_d macro to 1 in the project’s app_preinclude.h file. This macro is defined by default as 0 in app_preinclude_common.h:
#if (gAppSecureMode_d == 1U)
#define gSecLibSssUseEncryptedKeys_d (1U)
#define gHostSecureMode_d (1U)
#else
#define gHostSecureMode_d (0U)
#endif
At application level, when Advanced Secure Mode is enabled, the security mode and level for pairing is automatically enforced as Mode1 Level 4, ensuring LE Secure Connections pairing. Legacy pairing is not supported in this mode.
When enabled, the main benefit of Advanced Secure Mode is the secured storage and handling of Bluetooth LE security keys. The EdgeLock Secure Enclave is capable of generating, importing, and exporting security keys as plain text or as encrypted blobs. All encrypted blobs are created by the EdgeLock Secure Enclave using a die unique key, which makes them impossible to decrypt by devices other than the one that created them. The Bluetooth LE security keys are managed in Advanced Secure Mode as follows:
IRK
The peer IRKs received after pairing and bonding are no longer stored into NVM as plaintext, but as encrypted blobs 40 bytes in length (or ELKE blobs). They can still be retrieved and converted to plaintext.
The local IRK is no longer generated using the default method of hashing over the board’s UID at every startup. It is instead generated once using the EdgeLock Secure Enclave and stored into a new NVM dataset as an ELKE blob.
Local and peer IRKs are no longer transmitted through HCI in plaintext but as EIRK (Encrypted IRK) blobs, 16 bytes in length, which can be decrypted by the Controller.
LTK
The LTK is no longer stored into NVM as plaintext, but as an ELKE blob. Furthermore, the plaintext of the LTK is never available to the Host/application. Generating the LTK via the ECDH process and generating the Session Key for individual connections is done via the EdgeLock Secure Enclave and custom vendor HCI messages which are transparent to the application.
CSRK
The local CSRK is no longer generated using the default method of hashing over the board’s UID at every startup. It is instead generated once using the EdgeLock Secure Enclave and stored into a new NVM dataset as an ELKE blob.
At startup, Advanced Secure Mode for the Controller is enabled dynamically by calling:
#if (defined(gAppSecureMode_d) && (gAppSecureMode_d > 0U))
(void) PLATFORM_EnableBleSecureKeyManagement();
#endif
This call can be found in BluetoothLEHost_Init, as part of the initialization sequence.
Parent topic:Application Structure