TL;DR: One ESP32 broadcasts via BLE (already working), another ESP32 listens. I am unable to parse the received advertisements correctly, i.e. can't extract the manufacturer specific data!
Goal: One ESP32 (call A) broadcasts an advertisement containing manufacturer specific data (MSD), which is received by another ESP32 (call B) who prints that data to the console.
I am using the new RISC-V based ESP32C3 which supports Bluetooth 5.0, though everything I do is based on Bluetooth 4.2.
Where I am:
Code:
Structs used to set up A:
// Struct defining advertising parameters
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!
};
Struct used to define the payload of the advertisement:
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 Galaxy phone
.manufacturer_len = ble_adv_payload_len,
.p_manufacturer_data = (uint8_t *) ble_adv_payload, // Currently just some human-readable string used for debugging
.service_data_len = 0,
.p_service_data = NULL,
.service_uuid_len = 0,
.p_service_uuid = NULL,
.flag = 0
};
As the Ubertooth receives correct packets sent by A, I reckon A has been set up correctly.
Struct used to define scanning behavior of B:
// Struct defining scanning parameters
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 majority of the remaining code is just boilerplate, the only really relevant part is the callback function of B, which gets called whenever a GAP-Event occurs (GAP-Events can be found in esp_gap_ble_api.h, beginning on line 138).
B's callback function:
void esp_ble_callback_fun(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param)
{
// Do a case split on the different events. For now, only "ESP_GAP_BLE_SCAN_RESULT_EVT" is of interest
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;
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:
// NOT SUPPORTED! JUST IGNORE THEM!
break;
}
}
Sample output:
Serial output by B:
Message:
Message length: 22
Message body:
31940069414C4943454FF486579512FFFFFFFF
Packet as received in Wireshark:
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
0000 00 00 18 00 fb 00 00 00 36 75 0c 00 00 62 09 00 ........6u...b..
0010 e0 5b 56 b8 11 05 10 00 d6 be 89 8e 22 1c d6 3e .[V........."..>
0020 43 a1 df 7c 03 19 40 00 06 09 41 4c 49 43 45 04 C..|[email protected].
0030 ff 48 65 79 05 12 ff ff ff ff 09 9a 2c .Hey........,
For a packet without MSD, I computed the longest common subsequence between the binary representation of a packet received by B (i.e. content of adv_data
) and the packet received by the Ubertooth. They only had 46 bits in common, a weird number for sure!
My questions:
adv_data
, i.e. scan_result.scan_rst.ble_adv
holds the raw BLE packet? The definition of ble_adv
(esp_gap_ble_api.h, line 936) is incredibly confusing IMO, as it is called "Received EIR" despite EIRs only being introduced in Bluetooth 5.0...EIR was introduced a long time ago and was present in Bluetooth 4.0.
You should use %02X
when printing hex strings since that will include leading zeros.
ble_adv
contains only the EIR content, not the whole packet.
EIR uses length, type, value encoding. Your manufacturing data is encoded like this:
4 (length) 0xff (manufacturer data) Hey (content)
Note that the two bytes of the manufacturer data content should be a Bluetooth SIG registered company id.