esp32c3 - BLE advertisements, extracting manufacturer specific data
Posted: Sat Apr 17, 2021 3:17 pm
I am running two esp32c3, one sends advertisements (call A) and the other is simply scanning passively (call B). The goal is that A broadcasts some data via BLE, B receives it and print it to console. The data should be stored in the manufacturer specified data section of an advertisement.
Problem:
Although B receives packets from A, I don't know how to parse them to be able to extract the manufacturer specified data!
What I did so far:
I set up A to advertise and B to scan continuously.
I have an Ubertooth plugged into my PC and can confirm that the packets sent by A are well-formed and that they indeed contain the correct payload.
A advertisement setup:
A advertisement data setup:
--------------
B has a whitelist only containing A's public address.
B's scan parameters:
B's logic:
The only real logic besides the boilerplate code of setting up the bluedroid stack is the callback function that gets called whenever a GAP-Event happens:
Sample output:
Serial output from B:
Packet sniffed by Wireshark using the Ubertooth dongle:
My questions:
Problem:
Although B receives packets from A, I don't know how to parse them to be able to extract the manufacturer specified data!
What I did so far:
I set up A to advertise and B to scan continuously.
I have an Ubertooth plugged into my PC and can confirm that the packets sent by A are well-formed and that they indeed contain the correct payload.
A advertisement setup:
Code: Select all
static esp_ble_adv_params_t ble_adv_params = {
.adv_int_min = 0x0800,
.adv_int_max = 0x0900,
.adv_type = ADV_TYPE_NONCONN_IND,
.own_addr_type = BLE_ADDR_TYPE_PUBLIC,
.channel_map = ADV_CHNL_ALL,
.adv_filter_policy = ADV_FILTER_ALLOW_SCAN_ANY_CON_ANY, // NO IDEA ABOUT THAT ONE, ESPECIALLY GIVEN THAT WE SEND NONCONNECTABLE AND NONSCANNABLE ADVERTISEMENTS!
};
Code: Select all
esp_ble_adv_data_t ble_adv_data =
{
.set_scan_rsp = false,
.include_name = true, // "Name" refers to the name passed as an argument within "esp_ble_gap_set_device_name()"
.include_txpower = false,
.min_interval = 0xffff, // Not sure what those are for, as the chosen advertisement packets are non-connectable...
.max_interval = 0xFFFF,
.appearance = 64, // 64 is appearance ID of phone. Only to maybe be able to find it on my smartphone
.manufacturer_len = ble_adv_payload_len,
.p_manufacturer_data = (uint8_t *) ble_adv_payload,
.service_data_len = 0,
.p_service_data = NULL,
.service_uuid_len = 0,
.p_service_uuid = NULL,
.flag = 0
};
B has a whitelist only containing A's public address.
B's scan parameters:
Code: Select all
static esp_ble_scan_params_t ble_scan_params = {
.scan_type = BLE_SCAN_TYPE_PASSIVE, // Don't send scan requests upon receiving an advertisement
.own_addr_type = BLE_ADDR_TYPE_PUBLIC, // Use (static) public address, makes debugging easier
.scan_filter_policy = BLE_SCAN_FILTER_ALLOW_ONLY_WLST, // Consider all advertisements
.scan_interval = 0x50, // Time between each scan window begin
.scan_window = 0x30, // Length of scan window
.scan_duplicate = BLE_SCAN_DUPLICATE_DISABLE // Filters out duplicate advertisements, e.g. if an advertisement received k times, it is only reported once
};
The only real logic besides the boilerplate code of setting up the bluedroid stack is the callback function that gets called whenever a GAP-Event happens:
Code: Select all
void esp_ble_callback_fun(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param)
{
switch (event) {
case ESP_GAP_BLE_SCAN_RESULT_EVT:
if ((param->scan_rst).search_evt != ESP_GAP_SEARCH_INQ_RES_EVT) {
printf(
"B: Callback function received a non-\"ESP_GAP_SEARCH_INQ_RES_EVT\" event!\n");
return;
}
// Copy the parameter UNION
esp_ble_gap_cb_param_t scan_result = *param;
// Create a POINTER to the "bda" entry, which is accessed by interpreting the scan_result UNION as a scan_rst STRUCT
// Note that "esp_bd_addr_t" is a typedef for a uint8_t array!
esp_bd_addr_t *ble_adv_addr = &scan_result.scan_rst.bda;
printf("\n-------------------------\nMessage!: \n");
uint8_t adv_data_len = scan_result.scan_rst.adv_data_len;
uint8_t *adv_data = scan_result.scan_rst.ble_adv;
esp_ble_adv_data_t* received_payload = (esp_ble_adv_data_t*) adv_data;
printf("Message length: %i\n", adv_data_len);
printf("Message body:\n"); // NOT SO SURE ABOUT THIS!
for(int i = 0; i < adv_data_len; ++i)
{
printf("%X", adv_data[i]);
}
printf("\n-------------------------\n");
break; // @suppress("No break at end of case")
default:
// WONT BE SUPPORTED! JUST IGNORE THEM!
break;
}
}
Serial output from B:
Code: Select all
Message!:
Message length: 22
Message body:
31940069414C4943454FF486579512FFFFFFFF
Code: Select all
Frame 78135: 61 bytes on wire (488 bits), 61 bytes captured (488 bits) on interface /tmp/pipe, id 0
PPI version 0, 24 bytes
Version: 0
Flags: 0x00
.... ...0 = Alignment: Not aligned
0000 000. = Reserved: 0x00
Header length: 24
DLT: 251
Reserved: 36750c0000620900e05b56b811051000
Bluetooth
Bluetooth Low Energy Link Layer
Access Address: 0x8e89bed6
Packet Header: 0x1c22 (PDU Type: ADV_NONCONN_IND, ChSel: #2, TxAdd: Public)
.... 0010 = PDU Type: ADV_NONCONN_IND (0x2)
...0 .... = RFU: 0
..1. .... = Channel Selection Algorithm: #2
.0.. .... = Tx Address: Public
0... .... = Reserved: False
Length: 28
Advertising Address: Espressi_43:3e:d6 (7c:df:a1:43:3e:d6)
Advertising Data
Appearance: Generic Phone
Device Name: ALICE
Manufacturer Specific
Slave Connection Interval Range: 81918.8 - 81918.8 msec
Connection Interval Min: 65535 (81918.8 msec)
Connection Interval Max: 65535 (81918.8 msec)
CRC: 0x905934
My questions:
- What is stored in "ble_adv" (esp_gap_ble_api.h, line 936)?
Note: I reckon it contains the packet, but if I convert the content of the ble_adv to binary and compare it with the binary of the message received via the Ubertooth, only a very short substring of the binary matches (For an empty manufacturer payload, only 46 bits matched).
- How can B extract the manufacturer specified data from the advertisement sent by A?