KW38 Custom Profile

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

KW38 Custom Profile

KW38 Custom Profile

This example of custom profile uses the Temperature Sensor and Temperature Collector examples as a base, so it can be easily modified. Both examples are in the SDK, so this document explains how to add the Humidity profile, and how to modify the code to get the Humidity Sensor and Collector working.

Introduction

Generic Attribute Profile (GATT) establishes in detail how to exchange all profile and user data over a BLE connection. GATT deals only with actual data transfer procedures and formats. All standard BLE profiles are based on GATT and must comply with it to operate correctly. This makes GATT a key section of the BLE specification, because every single item of data relevant to applications and users must be formatted, packed, and sent according to the rules.

GATT defines two roles: Server and Client.

  • The GATT server stores the data transported over the Attribute Protocol (ATT) and accepts Attribute Protocol requests, commands and confirmations from the GATT client.
  • The GATT client accesses data on the remote GATT server via read, write, notify, or indicate operations. Notify and indicate operations are enabled by the client but initiated by the server, providing a way to push data to the client. Notifications are unacknowledged, while indications are acknowledged. Notifications are therefore faster, but less reliable.

Snagit Custom Profile Client Server.png

 GATT Database establishes a hierarchy to organize attributes. These are the Profile, Service, Characteristic and Descriptor. Profiles are high level definitions that define how services can be used to enable an application and Services are collections of characteristics. Descriptors defined attributes that describe a characteristic value.

Snagit Custom Profile Levels.png

 

 To define a GATT Database several macros are provided by the GATT_DB API in the Freescale BLE Stack, which is part KW38 SDK.

Server (Sensor) 

First, we need to use the Temperature Sensor project as a base, to create our Humidity Custom Profile Server (Sensor).

BLE SIG profiles

To know if the Profile or service is already defined in the specification, you have to look for in Bluetooth SIG profiles and check in the ble_sig_defines.h file (${workspace_loc:/${ProjName}/bluetooth/host/interface) if this is already declared in the code. In our case, the service is not declared, but the characteristic of the humidity is declared in the specification. Then, we need to check if the characteristic is already included in ble_sig_defines.h. Since, the characteristic is not included, we need to define it as shown next:

 

/*! Humidity Charactristic UUID */
#define gBleSig_Humidity_d                      0x2A6F

 

GATT Database

The Humidity Sensor is going to have the GATT Server, because is going to be the device that has all the information for the GATT Client.

On the Temperature Sensor demo have the Battery Service and Device Information, so you only have to change the Temperature Service to Humidity Service

 Snagit Custom Profile Humidity.png

 In order to create the demo we need to define or develop a service that has to be the same as in the GATT Client, this is declared in the gatt_uuid128.h.If the new service is not the same, they will never be able to communicate each other. All macros, function or structure in SDK have a common template which helps the application to act accordingly. Hence, we need to define this service in the gatt_uuid128.h as shown next: 

 

/* Humidity */ 
UUID128(uuid_service_humidity, 0xfe ,0x34 ,0x9b ,0x5f ,0x80 ,0x00 ,0x00 ,0x80 ,0x00 ,0x10 ,0x00 ,0x02 ,0x00 ,0xfa ,0x10 ,0x10)

 

All the Service and Characteristics is declared in gattdb.h. Descriptors are declared after the Characteristic Value declaration but before the next Characteristic declaration. In this case the permission is the CharPresFormatDescriptor that have specific description by the standard. The Units of the Humidity Characteristic is on Percentage that is 0x27AD.

Client Characteristic Configuration Descriptor (CCCD) is a descriptor where clients write some of the bits to activate Server notifications and/or indications.

 

PRIMARY_SERVICE_UUID128(service_humidity, uuid_service_humidity)
CHARACTERISTIC(char_humidity, gBleSig_Humidity_d, (gGattCharPropNotify_c))
 VALUE(value_humidity, gBleSig_Humidity_d, (gPermissionNone_c), 2, 0x00, 0x25)
DESCRIPTOR(desc_humidity, gBleSig_CharPresFormatDescriptor_d, (gPermissionFlagReadable_c), 7, 0x0E, 0x00, 0xAD, 0x27, 0x00, 0x00, 0x00)
CCCD(cccd_humidity)

 

After that, create a folder humidity in the next path ${workspace_loc:/${ProjName}/bluetooth/profiles. Found the temperature folder, copy the temperature_service.c and paste inside of the humidity folder with another name (humidity_service.c). Then go back and look for the interface folder, copy temperature_interface.h and change the name (humidity_interface.h) in the same path.

You need to include the path of the created folder. Project properties>C/C+ Build>Settings>Tool Settings>MCU C Compiler>Includes:

Ricardo_Zamora_0-1619646619427.png

Humidity Interface

The humidity_interface.h file should have the following code.

The Service structure has the service handle, and the initialization value.

 

/*! Humidity Service - Configuration */
typedef struct humsConfig_tag {
	uint16_t serviceHandle;
	int16_t initialHumidity;
} humsConfig_t;

/*! Humidity Client - Configuration */
typedef struct humcConfig_tag {
	uint16_t hService;
	uint16_t hHumidity;
	uint16_t hHumCccd;
	uint16_t hHumDesc;
	gattDbCharPresFormat_t humFormat;
} humcConfig_t;

 

Humidity Service

At minimum on humidity_service.c file, should have the following code.

The service stores the device identification for the connected client. This value is changed on subscription and non-subscription events.

 

/*! Humidity Service - Subscribed Client*/
static deviceId_t mHums_SubscribedClientId;

 

The initialization of the service is made by calling the start procedure. This function is usually called when the application is initialized. In this case is on the BleApp_Config().

 

bleResult_t Hums_Start(humsConfig_t *pServiceConfig)
{
	mHums_SubscribedClientId = gInvalidDeviceId_c;

    /* Set the initial value of the humidity characteristic */
    return Hums_RecordHumidityMeasurement(pServiceConfig->serviceHandle,
                                            pServiceConfig->initialHumidity);
}

 

On stop function, the unsubscribe function is called.

 

bleResult_t Hums_Stop(humsConfig_t *pServiceConfig)
{
    /* Stop functionality by unsubscribing */
    return Hums_Unsubscribe();
}

bleResult_t Hums_Unsubscribe(void)
{
    /* Unsubscribe by invalidating the client ID */
	mHums_SubscribedClientId = gInvalidDeviceId_c;
    return gBleSuccess_c;
}

 

The subscribe function will be used in the main file, to subscribe the GATT client to the Humidity service.

 

bleResult_t Hums_Subscribe(deviceId_t clientDeviceId)
{
    /* Subscribe by saving the client ID */
	mHums_SubscribedClientId = clientDeviceId;
    return gBleSuccess_c;
}

 

Depending on the complexity of the service, the API will implement additional functions. For the Humidity Sensor only have a one characteristic.

The measurement will be saving on the GATT database and send the notification to the client. This function will need the service handle and the new value as input parameters.

 

bleResult_t Hums_RecordHumidityMeasurement(uint16_t serviceHandle, int16_t humidity)
{
	uint16_t handle;
	bleResult_t result;
	bleUuid_t uuid = Uuid16(gBleSig_Humidity_d);

	/* Get handle of Humidity characteristic */
	result = GattDb_FindCharValueHandleInService(serviceHandle,
	gBleUuidType16_c, &uuid, &handle);

	if (result != gBleSuccess_c)
		return result;

	/* Update characteristic value */
	result = GattDb_WriteAttribute(handle, sizeof(uint16_t), (uint8_t*) &humidity);

	if (result != gBleSuccess_c)
		return result;

	Hts_SendHumidityMeasurementNotification(handle);
	return gBleSuccess_c;
}

 

After save the measurement on the GATT database with GattDb_WriteAttribute function we send the notification.

To send the notification, first have to get the CCCD and after check if the notification is active, if is active send the notification.

 

static void Hts_SendHumidityMeasurementNotification
(
		uint16_t handle
)
{
	uint16_t hCccd;
	bool_t isNotificationActive;

	/* Get handle of CCCD */
	if (GattDb_FindCccdHandleForCharValueHandle(handle, &hCccd)
			!= gBleSuccess_c)
		return;

	if (gBleSuccess_c == Gap_CheckNotificationStatus
			(mHums_SubscribedClientId, hCccd, &isNotificationActive) &&
			TRUE == isNotificationActive)
	{
		GattServer_SendNotification(mHums_SubscribedClientId, handle);
	}
}

 

Humidity Sensor Main file

There are some modifications that have to be done, to use the new Humidity profile in our sensor example.

First, we need to declare the humidity service:

Ricardo_Zamora_1-1619647031739.png

 

static humsConfig_t humsServiceConfig = {(uint16_t)service_humidity, 0};

 

Then, we need to add or modify the following functions:

BleApp_Start

You need to modify this line:

Ricardo_Zamora_2-1619647129208.png

 

/* Device is connected, send humidity value */
BleApp_SendHumidity();

 

BleApp_Config

You need to start the Humidity Service, and to modify the PrintString line:

Ricardo_Zamora_3-1619647184184.png

 

humsServiceConfig.initialHumidity = 0;
(void)Hums_Start(&humsServiceConfig);

 

 

AppPrintString("\r\nHumidity sensor -> Press switch to start advertising.\r\n");

 

BleApp_ConnectionCallback

There are some modifications required in two Connection Events.

gConnEvtConnected_c

Ricardo_Zamora_4-1619647250926.png

 

(void)Hums_Subscribe(peerDeviceId);
gConnEvtDisconnected_c

 

gConnEvtDisconnected_c

Ricardo_Zamora_0-1619647637262.png

 

(void)Hums_Unsubscribe();

 

BleApp_GattServerCallback

Ricardo_Zamora_1-1619647701427.png

 

/* Notify the humidity value when CCCD is written */
BleApp_SendHumidity()

 

BleApp_SendHumidity

And, we need to add this function:

Ricardo_Zamora_2-1619647746232.png

 

static void BleApp_SendHumidity(void)
{
    (void)TMR_StopTimer(appTimerId);

    /* Update with initial humidity */
    (void)Hums_RecordHumidityMeasurement((uint16_t)service_humidity,
                                           (int16_t)(BOARD_GetTemperature()));

#if defined(cPWR_UsePowerDownMode) && (cPWR_UsePowerDownMode)
    /* Start Sleep After Data timer */
    (void)TMR_StartLowPowerTimer(appTimerId,
                           gTmrLowPowerSecondTimer_c,
                           TmrSeconds(gGoToSleepAfterDataTime_c),
                           DisconnectTimerCallback, NULL);
#endif
}

 

In this example, the Record Humidity uses the BOARD_GetTemperature, to use the example without any external sensor and to be able to see a change in the collector, but, in this section would be a GetHumidity function.

Client (Collector) 

First, we need to use the Temperature Collector project as a base, to create our Humidity Custom Profile Client (Collector).

BLE SIG profiles

The same applies for the Client. To know if the Profile or service is already defined in the specification, you have to look for in Bluetooth SIG profiles and check in the ble_sig_defines.h file (${workspace_loc:/${ProjName}/bluetooth/host/interface) if this is already declared in the code. In our case, the service is not declared, but the characteristic of the humidity is declared in the specification. Then, we need to check if the characteristic is already included in ble_sig_defines.h. Since, the characteristic is not included, we need to define it as shown next:

 

/*! Humidity Charactristic UUID */
#define gBleSig_Humidity_d                      0x2A6F

 

GATT Database

The Humidity Collector is going to have the GATT client; this is the device that will receive all information from  the GATT server.

Demo provided in this post works like the Temperature Collector. When the Collector enables the notifications from the sensor, received notifications will be printed in the serial terminal.

In order to create the demo we need to define or develop a service that has to be the same as in the GATT Server, this is declared in the gatt_uuid128.h.If the new service is not the same, they will never be able to communicate each other. All macros, function or structure in SDK have a common template which helps the application to act accordingly. Hence, we need to define this service in the gatt_uuid128.h as shown next:

 

/* Humidity */ 
UUID128(uuid_service_humidity, 0xfe ,0x34 ,0x9b ,0x5f ,0x80 ,0x00 ,0x00 ,0x80 ,0x00 ,0x10 ,0x00 ,0x02 ,0x00 ,0xfa ,0x10 ,0x10)

 

After that, copy the humidity profile folder from the Sensor project, to the Collector project ${workspace_loc:/${ProjName}/bluetooth/profiles. And also for this project, include the path of the new folder. Project properties>C/C+ Build>Settings>Tool Settings>MCU C Compiler>Includes:

Ricardo_Zamora_3-1619647932968.png

Humidity Collector Main file

In the Collector source file, we need to do also some modifications, to use the Humidity Profile.

First, we need to modify the Custom Information of the Peer device:

Ricardo_Zamora_0-1619720069736.png

 

humcConfig_t     humsClientConfig;

 

BleApp_StoreServiceHandles

Ricardo_Zamora_1-1619720107622.png

 

static void BleApp_StoreServiceHandles
(
    gattService_t   *pService
)
{
    uint8_t i,j;

    if ((pService->uuidType == gBleUuidType128_c) &&
        FLib_MemCmp(pService->uuid.uuid128, uuid_service_humidity, 16))
    {
        /* Found Humidity Service */
        mPeerInformation.customInfo.humsClientConfig.hService = pService->startHandle;

        for (i = 0; i < pService->cNumCharacteristics; i++)
        {
            if ((pService->aCharacteristics[i].value.uuidType == gBleUuidType16_c) &&
                (pService->aCharacteristics[i].value.uuid.uuid16 == gBleSig_Humidity_d))
            {
                /* Found Humudity Char */
            	mPeerInformation.customInfo.humsClientConfig.hHumidity = pService->aCharacteristics[i].value.handle;

                for (j = 0; j < pService->aCharacteristics[i].cNumDescriptors; j++)
                {
                    if (pService->aCharacteristics[i].aDescriptors[j].uuidType == gBleUuidType16_c)
                    {
                        switch (pService->aCharacteristics[i].aDescriptors[j].uuid.uuid16)
                        {
                            /* Found Humidity Char Presentation Format Descriptor */
                            case gBleSig_CharPresFormatDescriptor_d:
                            {
                                mPeerInformation.customInfo.humsClientConfig.hHumDesc = pService->aCharacteristics[i].aDescriptors[j].handle;
                                break;
                            }
                            /* Found Humidity Char CCCD */
                            case gBleSig_CCCD_d:
                            {
                                mPeerInformation.customInfo.humsClientConfig.hHumCccd = pService->aCharacteristics[i].aDescriptors[j].handle;
                                break;
                            }
                            default:
                                ; /* No action required */
                                break;
                        }
                    }
                }
            }
        }
    }
}

 

BleApp_StoreDescValues

Ricardo_Zamora_3-1619720232121.png

 

    if (pDesc->handle == mPeerInformation.customInfo.humsClientConfig.hHumDesc)
    {
        /* Store Humidity format*/
        FLib_MemCpy(&mPeerInformation.customInfo.humsClientConfig.humFormat,
                    pDesc->paValue,
                    pDesc->valueLength);
    }

 

BleApp_PrintHumidity

Ricardo_Zamora_4-1619720253851.png

 

/*www.bluetooth.com/specifications/assigned-numbers/units */
if (mPeerInformation.customInfo.humsClientConfig.humFormat.unitUuid16 == 0x27ADU)
{
    AppPrintString(" %\r\n");
}
else
{
    AppPrintString("\r\n");
}

 

BleApp_GattNotificationCallback

Ricardo_Zamora_5-1619720342076.png

 

if (characteristicValueHandle == mPeerInformation.customInfo.humsClientConfig.hHumidity)
{
    BleApp_PrintHumidity(Utils_ExtractTwoByteValue(aValue));
}

 

 CheckScanEvent

Ricardo_Zamora_6-1619720374078.png

 

foundMatch = MatchDataInAdvElementList(&adElement, &uuid_service_humidity, 16);

 

BleApp_StateMachineHandler

mAppIdle_c

Ricardo_Zamora_7-1619720411241.png

 

if (mPeerInformation.customInfo.humsClientConfig.hHumidity != gGattDbInvalidHandle_d)

 

mAppServiceDisc_c

Ricardo_Zamora_8-1619720466660.png

 

if (mPeerInformation.customInfo.humsClientConfig.hHumDesc != 0U)
mpCharProcBuffer->handle = mPeerInformation.customInfo.humsClientConfig.hHumDesc;

 

mAppReadDescriptor_c

Ricardo_Zamora_9-1619720517344.png

 

if (mPeerInformation.customInfo.humsClientConfig.hHumCccd != 0U)

 

BleApp_ConfigureNotifications

Ricardo_Zamora_10-1619720543249.png

 

mpCharProcBuffer->handle = mPeerInformation.customInfo.humsClientConfig.hHumCccd;

 

Demonstration

Now, after connection, every time that you press the SW3 on KW38 Humidity Sensor is going to send the value to KW38 Humidity Collector.

 

Ricardo_Zamora_14-1619720689734.pngRicardo_Zamora_13-1619720681169.png

Labels (2)
No ratings
Version history
Last update:
‎04-29-2021 02:36 PM
Updated by: