ESP32 (35) – BLE, scan response

by luca
6 comments

In the previous posts I explained how to receive and send advertising packets based on the Bluetooth LE standard.

The payload (that is the amount of “useful” data) of those packets is at most 31 bytes. It isn’t much: if – for example – you want to include the device name, little place remains for other data.

The BLE standard allows peripherals to send additional data using the scan request – scan response process.

When a device receives an advertising packet, it can contact the transmitter by sending a scan request packet to request further information. When receiving a scan request package, the peripheral can respond with a scan response packet:

scan-response-001

Advertising and scan request packets have the same format; it’s therefore possible to transfer, using scan response, additional 31 bytes of data.

esp32

The esp framework offers two modes for configuring the content of a scan response packet: using the esp_ble_adv_data_t struct or creating a byte array (raw mode). These modes are similar to the ones used to configure advertising packets you learned in previous articles (struct and raw mode).

In the first case, you have to declare a second struct, in addition to the one related to the advertising packet, to define the content of the scan response packet:

static uint8_t manufacturer_data[6] = {0xE5,0x02,0x01,0x01,0x01,0x01};
static esp_ble_adv_data_t scan_rsp_data = {
  .set_scan_rsp = true,
  .manufacturer_len = 6,
  .p_manufacturer_data = manufacturer_data,
};

Very important is set to true the set_scan_rsp parameter. It’s indeed this parameter what tells the driver that this struct is related to the scan response packet.

You can then pass the new struct to the driver, with the same function used previously:

esp_ble_gap_config_adv_data(&scan_rsp_data);

The driver will call the callback function twice: one to indicate the successful configuration of the advertising packet and one for the configuration of the scan response one. The two events are different:

case ESP_GAP_BLE_ADV_DATA_SET_COMPLETE_EVT:
  [...]
case ESP_GAP_BLE_SCAN_RSP_DATA_SET_COMPLETE_EVT:
  [...]

You have to wait until both the events have triggered before starting the advertising process. In my example program (you can download the source code from my Github repository) I use two boolean variables:

bool adv_data_set = false;
bool scan_rsp_data_set = false;
[...]
case ESP_GAP_BLE_ADV_DATA_SET_COMPLETE_EVT:
  adv_data_set = true;
  if(scan_rsp_data_set) esp_ble_gap_start_advertising(&ble_adv_params); break;
 
case ESP_GAP_BLE_SCAN_RSP_DATA_SET_COMPLETE_EVT:
  scan_rsp_data_set = true;
  if(adv_data_set) esp_ble_gap_start_advertising(&ble_adv_params); break;

If you want to use the raw mode instead, you have to declare a byte array and fill it with the content of the payload of the packet. Then you can use a specific function of the framework to pass the array to the driver:

static uint8_t scan_rsp_raw_data[8] = {0x07,0xFF,0xE5,0x02,0x01,0x01,0x01,0x01};
[...]
esp_ble_gap_config_scan_rsp_data_raw(scan_rsp_raw_data, 8);

did you notice that the content of the scan response packet is the same in the two examples?

The driver will confirm the configuration of the packet with a dedicated event. Also in this case you have to wait for the end of both configurations (advertising and scan response):

case ESP_GAP_BLE_SCAN_RSP_DATA_RAW_SET_COMPLETE_EVT:
  scan_rsp_data_set = true;
  if(adv_data_set) esp_ble_gap_start_advertising(&ble_adv_params); break;
You can also mix the two modes in your program. For example you can configure the advertising packet using the struct and configure the scan response one using the raw mode.

Now with the nRF Connect app you can verify that your scan response packet is correctly received by your smartphone:

scan-response-002

In the following video I explain how I built the payload of the packet and how the program works:

[youtube id=”6E4eW-lNKBE” width=”600″ height=”350″]

Related Posts

6 comments

Federico Thursday April 5th, 2018 - 04:37 PM

Ciao Luca, complimenti per gli articoli, li apprezzo molto, sono molto chiari. Continua così!
Sto indagando sulla possibilità di usare l’ESP32 per far comunicare via BLE (4.2) due dispositivi, un ESP32 e uno smartphone, SENZA INTERVENTO DELL’UTENTE.
Speravo si potesse fare senza connessione (pairing) ma da quanto ho capito in quel caso gli unici payload sono quelli dei pacchetti di advertisement e di scan response, quindi poche decine di byte.
C’è un modo per ovviare al problema o, eventualmente, per fare un pairing automatico per poi scambiare dati via GATT?

Reply
luca Saturday April 7th, 2018 - 02:56 PM

ciao Federico! Se sviluppi una app sullo smartphone, questa può tranquillamente effettuare lo scan, collegarsi alla periferica BLE e scambiare dati via GATT senza alcun altro intervento… funziona così ad esempio l’app di Fitbit…

Reply
Yannik Wednesday July 4th, 2018 - 11:00 AM

You are one of the only people out there explaining the native BLE api for the esp32. Thank you! I really appreciate it.
But i have a question or rather a comment regarding this example. You’ve set the adv_type to ADV_TYPE_NONCONN_IND but the bluetooth specs say that you need ADV_SCAN_IND to enable scan responses. After changing that attribute of my ble_adv_params I finally got the scan response to work.
Maybe you should edit your tutorial for future visitors.

here are the 4 PDU Types explained:
http://blog.bluetooth.com/bluetooth-low-energy-it-starts-with-advertising

Cheers

Reply
luca Sunday July 8th, 2018 - 01:47 PM

Thanks Yannik! I’ll update my tutorial following your suggestion!

Reply
RM Tuesday April 2nd, 2019 - 10:08 AM

Complimenti per la guida: una domanda è per caso riuscito a misurare i consumi in modalità BLE dell’ESP32. Se si a quanto ammontano?

Reply
luca Thursday April 4th, 2019 - 08:07 AM

non con precisione ma la maggiore lamentela è che il chip non è in grado di supportare applicazioni BLE che richiedano bassissimi consumi

Reply

Leave a Reply to luca Cancel Reply

twenty − 12 =