Bluetooth Low Energy offers the ability to broadcast data in format of non-connectable advertising packets while not being in a connection. This GAP Advertisement is widely known as a beacon and is used in today’s IoT applications in different forms. This article will present the current beacon format in our demo application from the KW40Z software package and how to create the most popular beacon formats on the market.
The advertising packet format and payload are declared in the gAppAdvertisingData structure from app_config.c. This structure points to an array of AD elements, advScanStruct:
static const gapAdStructure_t advScanStruct[] = {
{
.length = NumberOfElements(adData0) + 1,
.adType = gAdFlags_c,
.aData = (void *)adData0
},
{
.length = NumberOfElements(adData1) + 1,
.adType = gAdManufacturerSpecificData_c,
.aData = (void *)adData1
}
};
Due to the fact that all beacons use the advertising flags structure and that the advertising PDU is 31 bytes in length (Bluetooth Low Energy v4.1), the maximum payload length is 28 bytes, including length and type for the AD elements.
The AD Flags element is declared as it follows:
static const uint8_t adData0[1] = { (gapAdTypeFlags_t)(gLeGeneralDiscoverableMode_c | gBrEdrNotSupported_c) };
The demo application uses a hash function to generate a random UUID for the KW40Z default beacon. This is done in BleApp_Init:
void BleApp_Init(void)
{
sha1Context_t ctx;
/* Initialize sha buffer with values from SIM_UID */
FLib_MemCopy32Unaligned(&ctx.buffer[0], SIM_UIDL);
FLib_MemCopy32Unaligned(&ctx.buffer[4], SIM_UIDML);
FLib_MemCopy32Unaligned(&ctx.buffer[8], SIM_UIDMH);
FLib_MemCopy32Unaligned(&ctx.buffer[12], 0);
SHA1_Hash(&ctx, ctx.buffer, 16);
/* Updated UUID value from advertising data with the hashed value */
FLib_MemCpy(&gAppAdvertisingData.aAdStructures[1].aData[3], ctx.hash, 16);
}
When implementing a constant beacon payload, please bear in mind to disable this code section.
The KW40Z software implements a proprietary beacon with the maximum ADV payload and uses the following Manufacturer Specific Advertising Data structure of 26 bytes.
This is the default implementation of the beacon demo example from the KW40Z Connectivity Software package.
static uint8_t adData1[26] = {
/* Company Identifier*/ 0xFF, 0x01
/* Beacon Identifier */ 0xBC,
/* UUID */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
/* A */ 0x00, 0x00,
/* B */ 0x00, 0x00,
/* C */ 0x00, 0x00,
/* RSSI at 1m */ 0x1E};
iBeacon is a protocol designed by Apple. It uses a 20 byte payload that consists of the following identifying information [1] :
To advertise an iBeacon packet, the user needs to change the second AD element, adData1, like below:
static uint8_t adData1[25] = {
0x4C, 0x00,
0x02, 0x15,
/* UUID */ 0xD9, 0xB9, 0xEC, 0x1F, 0x39, 0x25, 0x43, 0xD0, 0x80, 0xA9, 0x1E, 0x39, 0xD4, 0xCE, 0xA9, 0x5C,
/* Major Version */ 0x00, 0x01
/* Minor Version */ 0x00, 0x0A,
0xC5};
AltBeacon
AltBeacon is an open specification designed for proximity beacon advertisements [2]. It also uses a Manufacturer Specific Advertising Data structure:
To advertise an AltBeacon packet, the user needs to change the second AD element, like below:
static uint8_t adData1[26] = {
/* MFG ID*/ 0xFF, 0x01,
/* Beacon Code */ 0xBE, 0xAC,
/* Beacon ID */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04,
/* Ref RSSI*/ 0xC5,
/* MFG RSVD*/ 0x00};
Eddystone™ is an open Bluetooth® Smart beacon format from Google [3]. It offers three data type packets:
Eddystone™ uses two advertising structures:
Thus, advScanStruct will now have 3 elements:
static const gapAdStructure_t advScanStruct[] = {
{
.length = NumberOfElements(adData0) + 1,
.adType = gAdFlags_c,
.aData = (void *)adData0
},
{
.length = NumberOfElements(adData1) + 1,
.adType = gAdComplete16bitServiceList_c,
.aData = (void *)adData1
},
{
.length = NumberOfElements(adData2) + 1,
.adType = gAdServiceData16bit_c,
.aData = (void *)adData2
}
};
The complete List of 16-bit Service UUIDs element will look like:
static const uint8_t adData1[2] = { 0xAA, 0xFE };
Eddystone™-UID broadcasts a unique 16-bit Beacon ID to identify a particular device in a group. The Service Data block has the following structure:
To implement this, the user needs to add a third AD element, as follows:
static uint8_t adData2[22] = {
/* ID */ 0xAA, 0xFE,
/* Frame Type */ 0x00,
/* Ranging Data */ 0xEE,
/* Namespace */ 0x8B, 0x0C, 0xA7, 0x50, 0x09, 0x54, 0x77, 0xCB, 0x3E, 0x77,
/* Instance */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
/* RFU */ 0x00, 0x00};
Eddystone™-URL broadcasts a compressed URL. The Service Data block has the following structure:
In this example, we will implement a beacon which will advertise NXP’s webpage, http://www.nxp.com.
To implement this, the user needs to add a third AD element, as follows:
static const uint8_t adData2[9] = {
/* ID */ 0xAA, 0xFE,
/* Frame Type */ 0x10,
/* TX Power */ 0xEE,
/* URL scheme */ 0x00,
/* Encode URL */ 'n', 'x, 'p', 0x07};
Eddystone™-TLM broadcasts telemetry data about the beacon device operation. The Service Data block has the following structure:
To implement this, the user needs to add a third AD element, as follows:
static uint8_t adData2[16] = {
/* ID */ 0xAA, 0xFE,
/* Frame Type */ 0x20,
/* TLM Version */ 0x00,
/* VBATT */ 0x00, 0x00,
/* TEMP */ 0x00, 0x00,
/* ADV_CNT */ 0x00, 0x00, 0x00, 0x00,
/* SEC_CNT */ 0x00, 0x00, 0x00, 0x00};
Thanks alexandruandreescu, Great tutorial.
in case of iBeacon, We need to have different major/minor for different KW40 module. In this example, we have to compile every time for different beacons/KW40 by changing the adData1 parameters.
Is there anyway to read the iBeacon parameters (uint8_t adData1[25]) from a config file? In that case, we can use the same binary with different config files for different KW40.
Thanks.
The eddystone beacon is not being detected by Chrome (v51) or the Physical Web Android app. I commented out BleApp_Init(), and also tried using a https url in addition to the nxp example provided.
static const uint8_t adData2[19] = {
/* ID */ 0xAA, 0xFE,
/* Frame Type */ 0x10,
/* TX Power */ 0xEE,
/* URL scheme */ 0x03, /* https:// */
/* Encode URL */ 'c','o','m','m','u','n','i','t','y','.','n','x','p', 0x07};
Can confirm that the default "beacon" example in the KW40Z_1.0.1 ConnSw is detected by the Kinetis BLE toolbox.
This was resolved on Android side.
Dear sir,
I tried to implement ibeacon feature on KW41z.
I used the beacon example in MKW41Z_ConnSw_1.0.2.
The default packet is NXP beacon format.
I tried to change it to ibeacon packet format like you mentioned below.
I used the beacon app "Locate" from App store. But I can't get my KW41 device.
Could you help me how I could implement the ibeacon feature on KW41 successfully.
Thanks.
BR,
Sean Wu
Weikeng Inc.
Dear Sir,
In the actual application can not only run a mode (ibeacon / EddyStone ...).
How to modify the software can achieve two modes (iBeacon and EddyStone) can run at the same time?
Thanks,
Daniel Tseng
@Alexandru Andreescu Thanks for the article; it was really helpful.
I am a student and was trying to convert KW41 to Alt Beacon. It was successful the only thing I would like to mention is "When implementing a constant beacon payload, please bear in mind to disable this code section." this line is really important.