iBeacon Example

iBeacon Example

1. Understanding the Script

This example will scan for for all visible iBeacon beacons, publishes and prints found data to RS232.


1.1. Defines

Initially script generates numerous definitions, primarily intended to define the protocol structure. The first eight protocol definitions pertain to the packet header used by XG devices, which includes the MAC address, advertisement message type, and RSSI. For further details regarding the packet header, please refer to the function documentation: bt_adv_packet_read().

Subsequently, the following nine bytes are reserved for the iBeacon protocol header. This section comprises flags, a company identifier, and a sub-device type.

The remaining portion of the data represents the payload, which will be copied and published accordingly.

image-20250515-131502.png

 


1.2. Scan Filter

Next, we define the fixed length of the iBeacon manufacturer data, which will be used as a safeguard within the code to ensure data integrity. Additionally, we specify an iBeacon identifier to serve as a filter for manufacturer data. In particular, the values 0x00 0x4C represent Apple's company identifier (but it’s passed as little-endian 0x4C 0x00), and 0x02 denotes the iBeacon type.

/** @brief iBeacon setup */ #define IBEACON_MANUFACTURER_DATA_LEN 0x1A new ibeacon_identifier[3] = [0x4C, 0x00, 0x02];

1.3. Initial Setup

The main function primarily performs the initial setup, including variable initializations and scan configuration.

/** * @brief Main function that initializes the Bluetooth scan, debug peripheral and MQTT callback */ main() { Init(MQTT_PUBLISH_PENDING); Init(RS232, RS232_SPEED, WORD_LENGTH, STOP_BITS, PARITY); debug_init(RS232); bt_init(); bt_scan_init(scan_parameters); bt_scan_filter_add(BT_SCAN_FILTER_TYPE_MANUFACTURER_DATA , ibeacon_identifier, sizeof(ibeacon_identifier)); bt_scan_filter_enable(BT_SCAN_FILTER_MANUFACTURER_DATA, false); bt_scan_callback_enable(BT_FILTER_MATCH); bt_scan_start(); for(;;) { Delay(10000); } }

1.4. Scan Data Callback

During the execution of the main function, the system initiates a scan for iBeacon devices. When an iBeacon device is detected, control is transferred to a designated callback function. Within this function, the advertisement packet broadcast by the iBeacon device is accessed and parsed accordingly.

/** * @brief Device event callback function * * @param event Device event * @return 0 on success */ public callback (event) { switch(event) { case BT_FILTER_MATCH: { memset(scanned_adv_data, 0x00, sizeof(scanned_adv_data)); memset(str_buffer, 0x00, sizeof(str_buffer)); bt_adv_packet_read(scanned_adv_data, BT_FILTER_MATCH); if (scanned_adv_data[IBEACON_HEADER_MANUF_DATA_LEN] == IBEACON_MANUFACTURER_DATA_LEN) { ibeacon_parse(); } } case MQTT_PUBLISH_PENDING: { ibeacon_sensors_publish(); } } }

1.5. Beacon Data Parsing

The iBeacon parsing function retrieves and formats the Universally Unique Identifier (UUID), major, minor, and transmission (TX) power values. This function employs the debug_print(); function for outputting information. To suppress these print statements, the user must comment out or remove the debug include.

/** * @brief iBeacon parse function that parses incoming iBeacon messages */ ibeacon_parse() { new temp_val = 0; snprintf(str_buffer, sizeof(str_buffer), "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x", scanned_adv_data[IBEACON_UUID_1], scanned_adv_data[IBEACON_UUID_2], scanned_adv_data[IBEACON_UUID_3], scanned_adv_data[IBEACON_UUID_4], scanned_adv_data[IBEACON_UUID_5], scanned_adv_data[IBEACON_UUID_6], scanned_adv_data[IBEACON_UUID_7], scanned_adv_data[IBEACON_UUID_8], scanned_adv_data[IBEACON_UUID_9], scanned_adv_data[IBEACON_UUID_10], scanned_adv_data[IBEACON_UUID_11], scanned_adv_data[IBEACON_UUID_12], scanned_adv_data[IBEACON_UUID_13], scanned_adv_data[IBEACON_UUID_14], scanned_adv_data[IBEACON_UUID_15], scanned_adv_data[IBEACON_UUID_16]); debug_print("iBeacon UUID: "); debug_print(str_buffer); debug_print("\r\n"); write_buf(SENSOR_UUID, strlen(str_buffer), str_buffer); temp_val = scanned_adv_data[IBEACON_MAJOR_ID_1] << 8 | scanned_adv_data[IBEACON_MAJOR_ID_2]; debug_print("iBeacon Major: %04X\r\n", temp_val); set_val(SENSOR_MAJOR, temp_val); temp_val = 0; temp_val = scanned_adv_data[IBEACON_MINOR_ID_1] << 8 | scanned_adv_data[IBEACON_MINOR_ID_2]; debug_print("iBeacon Minor: %04X\r\n", temp_val); set_val(SENSOR_MINOR, temp_val); set_val(SENSOR_TX_POWER, scanned_adv_data[IBEACON_TX_POWER]); ibeacon_received_flag = true; }

1.6. Sensors For Publishing

Once the SENSOR_GNSS_COORDINATES are ready for publishing, the code will include the newly configured sensors with iBeacon values for publishing as well. These sensors can be configured using the following defines:

/** @brief Data publish setup */ #define SENSOR_UUID SENSOR_STRING_1 #define SENSOR_MAJOR SENSOR_U16_USER_DEFINED_0 #define SENSOR_MINOR SENSOR_U16_USER_DEFINED_1 #define SENSOR_TX_POWER SENSOR_U8_USER_DEFINED_0

1.7. Publishing Data

Publishing itself is done in ibeacon_sensors_publish function:

/** * @brief Sensor publish function that adds data for publishing if ibeacon was detected */ ibeacon_sensors_publish() { if (ibeacon_received_flag == true) { MQTT_PublishSensor(SENSOR_UUID); MQTT_PublishSensor(SENSOR_MAJOR); MQTT_PublishSensor(SENSOR_MINOR); MQTT_PublishSensor(SENSOR_TX_POWER); ibeacon_received_flag = false; } }

1.8. Printed Data

The data output to the RS232 interface using the debug_print() function will appear as follows:

iBeacon UUID: fda50693-a4e2-4fb1-afcf-c6eb07647825
iBeacon Major: 2711
iBeacon Minor: 4CB9
iBeacon UUID: e2c56db5-dffb-48d2-b060-d0f5a71096e0
iBeacon Major: 0000
iBeacon Minor: 0000
iBeacon UUID: e2c56db5-dffb-48d2-b060-d0f5a71096e0
iBeacon Major: 0000
iBeacon Minor: 0000
iBeacon UUID: fda50693-a4e2-4fb1-afcf-c6eb07647825
iBeacon Major: 2711
iBeacon Minor: 4CB9
iBeacon UUID: e2c56db5-dffb-48d2-b060-d0f5a7109600
iBeacon Major: 0000
iBeacon Minor: 0000
iBeacon UUID: fda50693-a4e2-4fb1-afcf-c6eb07647825
iBeacon Major: 2711
iBeacon Minor: 4CB9

iBeacon UUID: fda50693-a4e2-4fb1-afcf-c6eb07647825
iBeacon Major: 2711
iBeacon Minor: 4CB9
iBeacon UUID: e2c56db5-dffb-48d2-b060-d0f5a71096e0
iBeacon Major: 0000
iBeacon Minor: 0000
iBeacon UUID: e2c56db5-dffb-48d2-b060-d0f5a71096e0
iBeacon Major: 0000
iBeacon Minor: 0000
iBeacon UUID: fda50693-a4e2-4fb1-afcf-c6eb07647825
iBeacon Major: 2711
iBeacon Minor: 4CB9
iBeacon UUID: e2c56db5-dffb-48d2-b060-d0f5a7109600
iBeacon Major: 0000
iBeacon Minor: 0000
iBeacon UUID: fda50693-a4e2-4fb1-afcf-c6eb07647825
iBeacon Major: 2711
iBeacon Minor: 4CB9


2. Full Example Code

/* * Script Name: iBeacon example * Version: 1.0.0 * Created: 2025-05-12 * Description: This script demonstrates how to configure scanner for iBeacon scanning, parse iBeacon * beacons and publish parsed data. This script detects all possible ibeacons in range. * Usage: * - Script automatically parses iBeacon messages inside 'ibeacon_parse' function. * - To configure data publish sensors user should edit defines under 'Data publish setup' * - To remove debug messages user should comment out or remove '#include <debug>' * Prerequisites: Device firmware 01_131 or up. Auxiliary MCU firmware 2.1.7 or up. * Changelog: * 2025-05-12 (1.0.0): */ #include <io> #include <read> #include <float> #include <string> #include <core> #include <write> #include <define> #include <socket> #include <bluetooth> #include <debug> /** @brief Debug peripheral setup */ #define RS232_SPEED 115200 #define WORD_LENGTH WORDLENGTH_8 #define STOP_BITS STOPBITS_1 #define PARITY PARITY_NONE #define UUID_STRING_BUFFER_SIZE 37 /** @brief Data publish setup */ #define SENSOR_UUID SENSOR_STRING_1 #define SENSOR_MAJOR SENSOR_U16_USER_DEFINED_0 #define SENSOR_MINOR SENSOR_U16_USER_DEFINED_1 #define SENSOR_TX_POWER SENSOR_U8_USER_DEFINED_0 /** @brief Scan data */ #define ADV_HEADER_MAC_BYTE_1 0 #define ADV_HEADER_MAC_BYTE_2 1 #define ADV_HEADER_MAC_BYTE_3 2 #define ADV_HEADER_MAC_BYTE_4 3 #define ADV_HEADER_MAC_BYTE_5 4 #define ADV_HEADER_MAC_BYTE_6 5 #define ADV_HEADER_ADV_TYPE 6 #define ADV_HEADER_RSSI 7 #define IBEACON_HEADER_FLAGS_DATA_TYPE_LEN 8 #define IBEACON_HEADER_FLAGS_DATA_TYPE 9 #define IBEACON_HEADER_FLAGS 10 #define IBEACON_HEADER_MANUF_DATA_LEN 11 #define IBEACON_HEADER_MANUF_DATA 12 #define IBEACON_HEADER_COMPANY_ID_1 13 #define IBEACON_HEADER_COMPANY_ID_2 14 #define IBEACON_HEADER_SUB_TYPE 15 #define IBEACON_HEADER_SUB_LEN 16 #define IBEACON_UUID_1 17 #define IBEACON_UUID_2 18 #define IBEACON_UUID_3 19 #define IBEACON_UUID_4 20 #define IBEACON_UUID_5 21 #define IBEACON_UUID_6 22 #define IBEACON_UUID_7 23 #define IBEACON_UUID_8 24 #define IBEACON_UUID_9 25 #define IBEACON_UUID_10 26 #define IBEACON_UUID_11 27 #define IBEACON_UUID_12 28 #define IBEACON_UUID_13 29 #define IBEACON_UUID_14 30 #define IBEACON_UUID_15 31 #define IBEACON_UUID_16 32 #define IBEACON_MAJOR_ID_1 33 #define IBEACON_MAJOR_ID_2 34 #define IBEACON_MINOR_ID_1 35 #define IBEACON_MINOR_ID_2 36 #define IBEACON_TX_POWER 37 /** @brief Scan setup */ #define SCANNED_DATA_BUFFER_SIZE 40 /** @brief iBeacon setup */ #define IBEACON_MANUFACTURER_DATA_LEN 0x1A new ibeacon_identifier[3] = [0x4C, 0x00, 0x02]; new scan_parameters[.type, .options, .interval, .window, .timeout, .interval_coded, .window_coded] = [ BT_SCAN_TYPE_PASSIVE, BT_SCAN_OPT_NONE, BT_SCAN_INTERVAL_FAST, BT_SCAN_WINDOW_FAST, 0, 0, 0 ]; new scanned_adv_data[SCANNED_DATA_BUFFER_SIZE]; new str_buffer[UUID_STRING_BUFFER_SIZE]; new bool:ibeacon_received_flag = false; forward public callback (event); forward ibeacon_parse(); /** * @brief Device event callback function * * @param event Device event * @return 0 on success */ public callback (event) { switch(event) { case BT_FILTER_MATCH: { memset(scanned_adv_data, 0x00, sizeof(scanned_adv_data)); memset(str_buffer, 0x00, sizeof(str_buffer)); bt_adv_packet_read(scanned_adv_data, BT_FILTER_MATCH); if (scanned_adv_data[IBEACON_HEADER_MANUF_DATA_LEN] == IBEACON_MANUFACTURER_DATA_LEN) { ibeacon_parse(); } } case MQTT_PUBLISH_PENDING: { ibeacon_sensors_publish(); } } } /** * @brief Sensor publish function that adds data for publishing if ibeacon was detected */ ibeacon_sensors_publish() { if (ibeacon_received_flag == true) { MQTT_PublishSensor(SENSOR_UUID); MQTT_PublishSensor(SENSOR_MAJOR); MQTT_PublishSensor(SENSOR_MINOR); MQTT_PublishSensor(SENSOR_TX_POWER); ibeacon_received_flag = false; } } /** * @brief iBeacon parse function that parses incoming iBeacon messages */ ibeacon_parse() { new temp_val = 0; snprintf(str_buffer, sizeof(str_buffer), "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x", scanned_adv_data[IBEACON_UUID_1], scanned_adv_data[IBEACON_UUID_2], scanned_adv_data[IBEACON_UUID_3], scanned_adv_data[IBEACON_UUID_4], scanned_adv_data[IBEACON_UUID_5], scanned_adv_data[IBEACON_UUID_6], scanned_adv_data[IBEACON_UUID_7], scanned_adv_data[IBEACON_UUID_8], scanned_adv_data[IBEACON_UUID_9], scanned_adv_data[IBEACON_UUID_10], scanned_adv_data[IBEACON_UUID_11], scanned_adv_data[IBEACON_UUID_12], scanned_adv_data[IBEACON_UUID_13], scanned_adv_data[IBEACON_UUID_14], scanned_adv_data[IBEACON_UUID_15], scanned_adv_data[IBEACON_UUID_16]); debug_print("iBeacon UUID: "); debug_print(str_buffer); debug_print("\r\n"); write_buf(SENSOR_UUID, strlen(str_buffer), str_buffer); temp_val = scanned_adv_data[IBEACON_MAJOR_ID_1] << 8 | scanned_adv_data[IBEACON_MAJOR_ID_2]; debug_print("iBeacon Major: %04X\r\n", temp_val); set_val(SENSOR_MAJOR, temp_val); temp_val = 0; temp_val = scanned_adv_data[IBEACON_MINOR_ID_1] << 8 | scanned_adv_data[IBEACON_MINOR_ID_2]; debug_print("iBeacon Minor: %04X\r\n", temp_val); set_val(SENSOR_MINOR, temp_val); set_val(SENSOR_TX_POWER, scanned_adv_data[IBEACON_TX_POWER]); ibeacon_received_flag = true; } /** * @brief Main function that initializes the Bluetooth scan, debug peripheral and MQTT callback */ main() { Init(MQTT_PUBLISH_PENDING); Init(RS232, RS232_SPEED, WORD_LENGTH, STOP_BITS, PARITY); debug_init(RS232); bt_init(); bt_scan_init(scan_parameters); bt_scan_filter_add(BT_SCAN_FILTER_TYPE_MANUFACTURER_DATA , ibeacon_identifier, sizeof(ibeacon_identifier)); bt_scan_filter_enable(BT_SCAN_FILTER_MANUFACTURER_DATA, false); bt_scan_callback_enable(BT_FILTER_MATCH); bt_scan_start(); for(;;) { Delay(10000); } }