Skip to content

BLE Advertising: Redundancy betweeen AdvData and ScanResponseData #4158

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
esikora opened this issue Jul 11, 2020 · 9 comments
Closed

BLE Advertising: Redundancy betweeen AdvData and ScanResponseData #4158

esikora opened this issue Jul 11, 2020 · 9 comments
Labels
Status: Stale Issue is stale stage (outdated/stuck)

Comments

@esikora
Copy link
Contributor

esikora commented Jul 11, 2020

Hardware:

Board: M5StickC ESP32-PICO Mini IoT Development Kit
Core Installation version: v3.2.3-14-gd3e562907
IDE name: Platform.io (Espressif32 v1.12.4, Arduino)
Flash Frequency: 40 Mhz
PSRAM enabled: No
Upload Speed: 1500000 Baud
Computer OS: Windows 10

Description:

During the development of a custom BLE gamepad, I experienced crashes including a backtrace after calling BLEAdvertising::start().

A deeper analysis using the 'nRF Connect for Mobile' app revealed that BLEAdvertising::start() configures the advertisment data and the scan response data in such a way that the Bluetooth stack includes specific information, e.g. the appearance attribute, into both, the advertisment message and the scan response message.
A crash occurs when the total payload size of the scan response message exceeds 31 bytes. The problem can be avoided by improving the configuration of the scan response data in BLEAdvertising::start().

I have included a more detailed description and solution outline below the debug messages.

Sketch:

#include <Arduino.h>
#include <BLEDevice.h>
#include <BLEHIDDevice.h>

void setup() {
    BLEDevice::init("ESP32 Wireless Joystick");
    BLEAdvertising *pAdvertising = BLEDevice::getAdvertising();
    pAdvertising->setAppearance(HID_GAMEPAD);
    pAdvertising->start();
}

void loop() {
}

Debug Messages:

[I][BLEDevice.cpp:554] getAdvertising(): create advertising
[D][BLEDevice.cpp:556] getAdvertising(): get advertising
[V][BLEAdvertising.cpp:177] start(): >> start: customAdvData: 0, customScanResponseData: 0
[D][BLEAdvertising.cpp:196] start(): - no services advertised
[V][BLEAdvertising.cpp:237] sta
Sta: << start

smashing protect failure!

abort() was called at PC 0x400da634 on core 0

Backtrace: 0x40090f78:0x3ffd13f0 0x400911a9:0x3ffd1410 0x400da634:0x3ffd1430 0x4010c52d:0x3ffd1450 0x40132171:0x3ffd14b0 0x401327b1:0x3ffd14d0 0x4013d4ee:0x3ffd14f0 0x40112c4e:0x3ffd1510 0x4008dbb9:0x3ffd1540

  #0  0x40090f78:0x3ffd13f0 in invoke_abort at /home/runner/work/esp32-arduino-lib-builder/esp32-arduino-lib-builder/esp-idf/components/esp32/panic.c:707
  #1  0x400911a9:0x3ffd1410 in abort at /home/runner/work/esp32-arduino-lib-builder/esp32-arduino-lib-builder/esp-idf/components/esp32/panic.c:707
  #2  0x400da634:0x3ffd1430 in __stack_chk_fail at /home/runner/work/esp32-arduino-lib-builder/esp32-arduino-lib-builder/esp-idf/components/esp32/stack_check.c:36
  #3  0x4010c52d:0x3ffd1450 in BTM_BleWriteScanRsp at /home/runner/work/esp32-arduino-lib-builder/esp32-arduino-lib-builder/esp-idf/components/bt/bluedroid/stack/btm/btm_ble_gap.c:1869
  #4  0x40132171:0x3ffd14b0 in bta_dm_ble_set_scan_rsp at /home/runner/work/esp32-arduino-lib-builder/esp32-arduino-lib-builder/esp-idf/components/bt/bluedroid/bta/dm/bta_dm_act.c:4965
  #5  0x401327b1:0x3ffd14d0 in bta_dm_sm_execute at /home/runner/work/esp32-arduino-lib-builder/esp32-arduino-lib-builder/esp-idf/components/bt/bluedroid/bta/dm/bta_dm_main.c:394
  #6  0x4013d4ee:0x3ffd14f0 in bta_sys_event at /home/runner/work/esp32-arduino-lib-builder/esp32-arduino-lib-builder/esp-idf/components/bt/bluedroid/bta/sys/bta_sys_main.c:496
  #7  0x40112c4e:0x3ffd1510 in btu_task_thread_handler at /home/runner/work/esp32-arduino-lib-builder/esp32-arduino-lib-builder/esp-idf/components/bt/bluedroid/stack/btu/btu_task.c:233
  #8  0x4008dbb9:0x3ffd1540 in vPortTaskWrapper at /home/runner/work/esp32-arduino-lib-builder/esp32-arduino-lib-builder/esp-idf/components/freertos/port.c:355 (discriminator 1)

Rebooting...

Analysis of the Problem:

The scan response is configured by the following lines of code in BLEAdvertising.cpp:

m_advData.set_scan_rsp = true;
m_advData.include_name = m_scanResp;
m_advData.include_txpower = m_scanResp;

Therein, m_advData is the same struct that is used for the configuration of the advertisement data.
Therefore, the attributes 'Flags (type: 0x01)' and 'Apperance (type: 0x19)' are contained in both messages. This can be seen in the following screenshot from the 'nRF Connect for Mobile' app:

Note: For taking the screenshot, I used a device name with 22 characters, so the payload of the scan response message is 31 bytes.

Solution Outline:

I suggest to configure the scan response message using a separate struct (not a copy of 'm_advData'), so that all fields are zero-initialized, and only the fields 'set_scan_rsp', 'include_name', and 'include_txpower' are set to 'true'. Thus the redundant attributes in the scan response message are eliminated, and a device name with up to 29 characters is possible without causing a crash. This can be seen in the following screenshot:

(Note: The Bluetooth LE stack includes the Tx power attribute in the scan response only if the device name has at most 26 characters.)

With the proposed solution, there is no loss of information because the other attributes such as 'Appearance' are still transmitted in the advertisement message.

Please let me know if you would like me to provide my solution proposal in a source code branch.

@chegewara
Copy link
Contributor

@esikora
Copy link
Contributor Author

esikora commented Jul 12, 2020

Hi @chegewara ,

thank you for pointing me to that possibility. Now, I tried disabling the scan response. It indeed avoids the crash but also has some disadvantages:

  • Only 31 bytes are available for advertising data instead of 31 + 31 bytes.
  • The Bluetooth stack truncates the device name, i.e. uses 'shortened local name' (type 0x08) instead of 'complete local name' (0x09), see screenshot below.

Furthermore, I have just done some more research into the advertisement topic:
In the document Supplement to the Bluetooth Core Specification (v9), I have found the following requirements in the table on Page 9:

  • 'Appearance' has got a "C2" in the table for 'AD' and 'SRD', which means it shall not be included in both
  • 'Flags' has got an "X" in the table for 'SRD' which means "reserved for future use"

Mainly because of the name truncation issue, I would keep the scan response enabled, and just avoid that some data types - specifically 'flags' and 'appearance' - appear in both, the advertisement, and the scan response.

What do you think about this?

@chegewara
Copy link
Contributor

There is few options, including changing code in library, to do this.
Mainly function to enable scan response has been added to allow use long names together with 128 bit UUID (there is example how to use API).

I dont remember if its possible to use raw data, but you can for sure use advertisement data builder:
https://github.com/espressif/arduino-esp32/blob/master/libraries/BLE/src/BLEAdvertising.h#L58
https://github.com/espressif/arduino-esp32/blob/master/libraries/BLE/src/BLEAdvertising.h#L60

@esikora
Copy link
Contributor Author

esikora commented Jul 13, 2020

For comparison, I have implemented three variants, all of which produce identical advertisement and scan response payload data:

  • setupAdvertisementA() uses a modified version of BLEAdvertising::start()
  • setupAdvertisementB() makes use of the advertisement data builder as you suggested
  • setupAdvertisementC() uses ESP-IDF functions and structs

From an application programmer's view I would prefer the first solution. But I also understand that changing the library might have some undesirable effects on existing programs. I think that as long as library users are aware of the problem and know which solutions there are, they can save a lot of time that it takes to analyse crashes.

#include <Arduino.h>
#include <BLEDevice.h>
#include <BLEHIDDevice.h>

const char kDeviceName[] = "ESP32 Wireless Joystick";

/* Let (a modified version of) the BLE library define advertisement and scan response data. */
void setupAdvertisementA()
{
    BLEAdvertising *pAdvertising = BLEDevice::getAdvertising();
    pAdvertising->setAppearance(HID_GAMEPAD);

    pAdvertising->start();
    // Note: BLEAdvertising::start() has been modified so that there is no redundant
    // data for 'Flags' and 'Appearance' in the scan response
}

/* Configure (unmodified) BLE library with custom advertisement and scan response data. */
void setupAdvertisementB()
{
    BLEAdvertising *pAdvertising = BLEDevice::getAdvertising();

    BLEAdvertisementData advData;
    advData.setFlags(ESP_BLE_ADV_FLAG_GEN_DISC | ESP_BLE_ADV_FLAG_BREDR_NOT_SPT);
    advData.setAppearance(ESP_BLE_APPEARANCE_HID_GAMEPAD);
    
    // Note: BLEAdvertisementData has no dedicated function for 'Slave Connection Interval Range' (data type 0x12)
    advData.addData(std::string({0x05, 0x12, 0x20, 0x00, 0x40, 0x00}));
    pAdvertising->setAdvertisementData(advData);

    BLEAdvertisementData scanResData;
    scanResData.setName(kDeviceName);

    // Note: BLEAdvertisementData has no dedicated function for 'Tx Power Level' (data type 0x0A)
    scanResData.addData(std::string({0x02, 0x0A, 0x03}));
    pAdvertising->setScanResponseData(scanResData);
    
    pAdvertising->start();
}

/* Configure and start advertisement using the ESP-IDF library directly. */
void setupAdvertisementC()
{
    esp_ble_adv_data_t   advData{};
    advData.min_interval = 0x20;
    advData.max_interval = 0x40;
    advData.appearance   = ESP_BLE_APPEARANCE_HID_GAMEPAD;
    advData.flag         = (ESP_BLE_ADV_FLAG_GEN_DISC | ESP_BLE_ADV_FLAG_BREDR_NOT_SPT);

    esp_ble_gap_config_adv_data(&advData);

    esp_ble_adv_data_t          scanResData{};
    scanResData.set_scan_rsp    = true;
    scanResData.include_name    = true;
    scanResData.include_txpower = true;

    esp_ble_gap_config_adv_data(&scanResData);

    esp_ble_adv_params_t        advParams{};
    advParams.adv_int_min       = 0x20;
    advParams.adv_int_max       = 0x40;
    advParams.adv_type          = ADV_TYPE_IND;
    advParams.own_addr_type     = BLE_ADDR_TYPE_PUBLIC;
    advParams.channel_map       = ADV_CHNL_ALL;
    advParams.adv_filter_policy = ADV_FILTER_ALLOW_SCAN_ANY_CON_ANY;
    advParams.peer_addr_type    = BLE_ADDR_TYPE_PUBLIC;

    esp_ble_gap_start_advertising(&advParams);
}

@esikora
Copy link
Contributor Author

esikora commented Jul 18, 2020

I would like to submit a branch with changes in BLEAdvertising and a pull request. Could you tell me please, what I need to do to get write permissions?

@chegewara
Copy link
Contributor

Nothing, just fork repo, make changes and create PR then i will eventually merge it.

esikora added a commit to esikora/arduino-esp32 that referenced this issue Jul 18, 2020
…luedroid

Improved configuration of scan response data in 'BLEAdvertising' avoids the crash:
- Added member variable 'm_scanRespData' to configure scan response differently from advertising data
- Initialization of 'm_scanRespData' in BLEAdvertising constructor
- Use of 'm_scanRespData' within BLEAdvertising::start() to configure the scan response
- 'Flags' and 'Appearance' are cleared in the scan response data
- With this fix, device names of up to 29 characters can be used without causing a crash.
@esikora
Copy link
Contributor Author

esikora commented Jul 18, 2020

Thanks a lot for your advice. I have followed the process that you described above.

@stale
Copy link

stale bot commented Sep 17, 2020

[STALE_SET] This issue has been automatically marked as stale because it has not had recent activity. It will be closed in 14 days if no further activity occurs. Thank you for your contributions.

@stale stale bot added the Status: Stale Issue is stale stage (outdated/stuck) label Sep 17, 2020
@stale
Copy link

stale bot commented Oct 1, 2020

[STALE_DEL] This stale issue has been automatically closed. Thank you for your contributions.

@stale stale bot closed this as completed Oct 1, 2020
me-no-dev pushed a commit that referenced this issue Nov 2, 2020
…ing in Bluedroid (#4182)

* Fix for issue #4158: Crash with stack trace originating in Bluedroid
Improved configuration of scan response data in 'BLEAdvertising' avoids the crash:
- Added member variable 'm_scanRespData' to configure scan response differently from advertising data
- Initialization of 'm_scanRespData' in BLEAdvertising constructor
- Use of 'm_scanRespData' within BLEAdvertising::start() to configure the scan response
- 'Flags' and 'Appearance' are cleared in the scan response data
- With this fix, device names of up to 29 characters can be used without causing a crash.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Status: Stale Issue is stale stage (outdated/stuck)
Projects
None yet
Development

No branches or pull requests

2 participants