ESP32 (34) – BLE, raw advertising

In the previous post, you learned how to send BLE advertising packets with the esp32 chip.

To define the content of the packet, you used a struct, of the esp_ble_adv_data_t type:

raw-adv-001

The struct’s definition is included in the esp_gap_ble_api.h file:

raw-adv-002

Although there are many fields available, sometimes it is necessary to be able to define the content of the advertising packet arbitrarily. For this reason, the esp-idf framework provides a raw mode.

Instead of defining a struct, you create a byte array and fill it with the entire contents of the packet’s payload:

static uint8_t adv_raw_data[10] = 
  {0x09,0x09,0x4c,0x75,0x6b,0x45,0x53,0x50,0x33,0x32};

then you can use the esp_ble_gap_config_scan_rsp_data_raw() function to pass the array to the driver. You have to specify both the array and its size as parameters:

esp_ble_gap_config_scan_rsp_data_raw(scan_rsp_raw_data, 8);

When using this new function, it also changes the event that the driver passes to your callback function when the configuration is complete. The new event is ESP_GAP_BLE_ADV_DATA_RAW_SET_COMPLETE_EVT. As in the previous example, when this event is triggered you can start the advertising process:

case ESP_GAP_BLE_ADV_DATA_RAW_SET_COMPLETE_EVT: 
  esp_ble_gap_start_advertising(&ble_adv_params);
  break;

Raw data

For the advertising process to work, the data contained in the array must correspond to a valid payload.

In the blog post about the iBeacons, I’ve already shown you its structure. Let’s briefly review it:

ibeacon-002

The payload contains one or more AD (advertising data) structures. Each structure is made by 3 fields:

  • an initial byte that represents the length (in bytes) of the structure, excluding itself
  • a byte that represents the type of the data contained in the structure
  • a variable number of bytes which are the actual data

The codes that can be used to define the type of data can be found in the Bluetooth specifications. Depending on the type of data, it is then necessary to apply a particular format to the data that follows. The necessary information is found in the Core Specification Supplement document (available on the Bluetooth.com website).

Let’s see a simple example: the ADType 0x09 represents the complete local name, which is the name of the device. This name must be specified in AD data with simply a sequence of the ASCII codes that correspond to the different letters.

You can use a website to do the conversion:

raw-adv-003

The payload to transmit this name is therefore:

adv_raw_data[7] = {0x06,0x09,0x4d,0x79,0x42,0x4c,0x45};

The first byte has value 0x06 that is the sum of the name length (5 bytes) and 1 byte for the data type (0x09).

Demo

In the following video you can see how I use the raw advertising feature to simulate the advertising packet of my iBeacon and therefore I’m able to activate the relay as in the previous example.

The source code of the program is available in my Github repository.

ESP32 (33) – BLE, advertising

In the previous posts you learned how to use the esp32 chip to receive and parse the advertising packets transmitted by BLE peripherals. As a practical example, I developed a program to detect the presence of a particular iBeacon and activate an output accordingly.

In today’s tutorial, you’ll learn how to transmit advertising packets instead.

Advertising process

You’ve already discovered that the Bluetooth driver included in the esp-idf stack is executed in a dedicated thread. Whenever the driver needs to send a notification to your program, it calls a callback function indicating which event has triggered.

The advertising process is very simple:

  • the program configures the data to be transmitted with esp_ble_gap_config_adv_data()
  • the driver reports that it has finished the configuration with the event ESP_GAP_BLE_ADV_DATA_SET_COMPLETE_EVT
  • the program can now start the advertising process with esp_ble_gap_start_advertising()
  • the driver reports that the process has started with the event ESP_GAP_BLE_ADV_START_COMPLETE_EVT

esp32-adv-001

Advertising DATA

It’s possible to tell the driver which data to include in the advertising packet with the following command:

esp_err_t esp_ble_gap_config_adv_data(esp_ble_adv_data_t *adv_data);

The command accepts as parameter a pointer to an esp_ble_adv_data_t struct:

esp32-adv-002

The meaning of the different fields is explained in the Supplement to the Bluetooth Core Specification document.

First let’s find out how to transmit the device name. You have to use the esp_ble_gap_set_device_name() function to pass the name to the driver and set the field include_name to true in the struct:

static esp_ble_adv_data_t adv_data = {
  .include_name = true,
};
[...]
ESP_ERROR_CHECK(esp_ble_gap_set_device_name("ESP32_BLE"));
ESP_ERROR_CHECK(esp_ble_gap_config_adv_data(&adv_data));

Using the flags, you can publish some features of your device. The available constants are:

esp32-adv-003

you can combine them with the OR operator. If, for example, you want to tell the world that your device is limited discoverable (i.e. it sends the advertising packets only for a limited time, usually 30 seconds) and that it doesn’t support classic Bluetooth (BR/EDR, Basic Rate/Enhanced Data Rate) you’ll write:

static esp_ble_adv_data_t adv_data = {
  .flag = ESP_BLE_ADV_FLAG_LIMIT_DISC | ESP_BLE_ADV_FLAG_BREDR_NOT_SPT,
};

Advertising PARAMETERS

After configuring the content of the advertising packet, you have also to tell the driver how to send the packet.

The command:

esp_err_t esp_ble_gap_start_advertising(esp_ble_adv_params_t *adv_params);

accepts as parameter an esp_ble_adv_params_t struct:

esp32-adv-004

You can configure the minimum and maximum transmission interval of the packet. The two parameters can assume a value from 0x20 to 0x4000. To calculate the interval in milliseconds, the value specified must be multiplied for 0.625. This means that the minimum value (0x20) corresponds to an interval of 12.5ms.

The esp_gap_ble_api.h file lists the constants that can be used for the other parameters (esp_ble_adv_type_t, esp_ble_addr_type_t …).

For example, let’s configure the advertising process as it follows:

  • minimum transmission interval: 0x20, maximum: 0x40
  • non connectable device (it doesn’t accept incoming connections and only sends data in broadcast)
  • public MAC address
  • transmission on all the 3 channels dedicated to advertising packets
  • no filter on devices who can perform a scan or connect
static esp_ble_adv_params_t ble_adv_params = {
  .adv_int_min = 0x20,
  .adv_int_max = 0x40,
  .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,
};
[...]
esp_ble_gap_start_advertising(&ble_adv_params);

Demo

I prepared a program that includes what explained above. The source code is available in my Github repository.

Here’s how it works: