esp-idf: Wrong default LE Event Mask in HCI for ESP32-C3 (IDFGH-4905)

Environment

  • Development Kit: ESP32-C3-DevKitM-1
  • Module or chip used: ESP32-C3-MINI-1
  • IDF version: v4.4-dev-4-g73db14240
  • Build System: idf.py
  • Compiler version: 1.24.0.123_64eb9ff-8.4.0
  • Operating System: Linux
  • Using an IDE?: No
  • Power Supply: USB

Problem Description

See Bluetooth Core specification, 5.0, Vol 2 Part E (HCI Functional Specification), section 7.8.1 LE Set Event Mask Command.

This command can be used to change the LE Event Mask. If the host does not send this event, the default mask is used by the controller.

The spec says the following:

The default is for bits 0 to 4 inclusive (the value 0x0000 0000 0000 001F) to be set.

This means the bit for “LE Connection Complete Event” is set, while “LE Enhanced Connection Complete Event” and “LE Channel Selection Algorithm Event” are not set.

For LE Connection Complete Event (section 7.8.12), we can read

Note: This event is not sent if the LE Enhanced Connection Complete event (see Section 7.7.65.10) is unmasked.

But by default, “LE Enhanced Connection Complete event” is masked away, and hence “LE Connection Complete Event” should be sent.

Steps to reproduce

Over vhci, send the following commands:

  1. Reset Command.
  2. Set Event Mask Command, with the “LE Meta Event” bit set to 1. (Note that “Set Event Mask” is a different command than “LE Set Event Mask”.)
  3. LE Create Connection Command

Then let a BLE peripheral advertise with the address specified in the LE Create Connection Command so that it connects.

Expected Behavior

A “LE Connection Complete Event” should be emitted when the device connects. “LE Enhanced Connection Complete Event” and “LE Channel Selection Algorithm Event” should not be emitted when the device connects.

Actual Behavior

The HCI layer in ESP32-C3 will emit a “LE Enhanced Connection Complete event” and no “LE Connection Complete Event”. This will cause Bluetooth Host stacks not supporting the LE Enhanced Connection Complete event to fail, since the expected LE Connection Complete Event is never emitted.

The “LE Channel Selection Algorithm Event” is also sent.

This worked fine in the older ESP32 chip, but not with the new ESP32-C3 chip. A workaround is to explicitly send an LE Set Event Mask command with the default mask 0x1f before creating a connection, to get the expected behaviour.

Code to reproduce this issue

Start from the examples/bluetooth/hci/controller_vhci_ble_adv example and change app_bt.c to the following (fill in the bd addr to an advertising target device in hci_cmd_send_create_conn):

/*
   This example code is in the Public Domain (or CC0 licensed, at your option.)

   Unless required by applicable law or agreed to in writing, this
   software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
   CONDITIONS OF ANY KIND, either express or implied.
*/

#include <stdio.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_bt.h"
#include "esp_log.h"
#include "nvs_flash.h"
#include "bt_hci_common.h"

#define HCI_BLE_CREATE_CONNECTION          (0x000D | HCI_GRP_BLE_CMDS)

#define UINT32_TO_STREAM(buf, val) do { UINT16_TO_STREAM((buf), (uint16_t)(val)); UINT16_TO_STREAM((buf), (uint16_t)((val) >> 16)); } while(0)
#define UINT64_TO_STREAM(buf, val) do { UINT32_TO_STREAM((buf), (uint32_t)(val)); UINT32_TO_STREAM((buf), (uint32_t)((val) >> 32)); } while(0)

static const char *tag = "BLE_TEST";

static uint8_t hci_cmd_buf[128];

/*
 * @brief: BT controller callback function, used to notify the upper layer that
 *         controller is ready to receive command
 */
static void controller_rcv_pkt_ready(void)
{
    printf("controller rcv pkt ready\n");
}

/*
 * @brief: BT controller callback function, to transfer data packet to upper
 *         controller is ready to receive command
 */
static int host_rcv_pkt(uint8_t *data, uint16_t len)
{
    printf("host rcv pkt: ");
    for (uint16_t i = 0; i < len; i++) {
        printf("%02x", data[i]);
    }
    printf("\n");
    return 0;
}

static esp_vhci_host_callback_t vhci_host_cb = {
    controller_rcv_pkt_ready,
    host_rcv_pkt
};
static void hci_cmd_send_reset(void)
{
    uint16_t sz = make_cmd_reset (hci_cmd_buf);
    esp_vhci_host_send_packet(hci_cmd_buf, sz);
}

static void hci_cmd_send_set_event_mask(void) {
    uint8_t *buf = hci_cmd_buf;
    UINT8_TO_STREAM(buf, H4_TYPE_COMMAND);
    UINT16_TO_STREAM(buf, HCI_SET_EVT_MASK);
    UINT8_TO_STREAM(buf, 8);
    uint64_t mask = 0x2000000000000000ULL; // LE Meta Event
    UINT64_TO_STREAM(buf, mask);
    esp_vhci_host_send_packet(hci_cmd_buf, HCI_H4_CMD_PREAMBLE_SIZE + 8);
}

static void hci_cmd_send_create_conn(void) {
    uint8_t *buf = hci_cmd_buf;
    UINT8_TO_STREAM(buf, H4_TYPE_COMMAND);
    UINT16_TO_STREAM(buf, HCI_BLE_CREATE_CONNECTION);
    UINT8_TO_STREAM(buf, 25); // len
    UINT16_TO_STREAM(buf, 160); // scan interval
    UINT16_TO_STREAM(buf, 160); // scan win
    UINT8_TO_STREAM(buf, 0); // no white list
    UINT8_TO_STREAM(buf, ...); // public addr type=0, random addr type=1
    uint8_t addr[6] = { ... }; // <-- fill in BD ADDR
    BDADDR_TO_STREAM(buf, addr);
    UINT8_TO_STREAM(buf, 0); // public own addr type
    UINT16_TO_STREAM(buf, 80); // con min
    UINT16_TO_STREAM(buf, 90); // con max
    UINT16_TO_STREAM(buf, 0); // conn latency
    UINT16_TO_STREAM(buf, 500); // supervision timeout
    UINT16_TO_STREAM(buf, 0); // min ce len
    UINT16_TO_STREAM(buf, 0); // max ce len
    
    
    esp_vhci_host_send_packet(hci_cmd_buf, HCI_H4_CMD_PREAMBLE_SIZE + 25);
}

/*
 * @brief: send HCI commands to perform BLE advertising;
 */
void bleAdvtTask(void *pvParameters)
{
    int cmd_cnt = 0;
    bool send_avail = false;
    esp_vhci_host_register_callback(&vhci_host_cb);
    printf("BLE advt task start\n");
    while (1) {
        vTaskDelay(1000 / portTICK_PERIOD_MS);
        send_avail = esp_vhci_host_check_send_available();
        if (send_avail) {
            switch (cmd_cnt) {
            case 0: hci_cmd_send_reset(); ++cmd_cnt; break;
            case 1: hci_cmd_send_set_event_mask(); ++cmd_cnt; break;
            case 2: hci_cmd_send_create_conn(); ++cmd_cnt; break;
            }
        }
        printf("BLE Advertise, flag_send_avail: %d, cmd_sent: %d\n", send_avail, cmd_cnt);
    }
}

void app_main(void)
{
    /* Initialize NVS — it is used to store PHY calibration data */
    esp_err_t ret = nvs_flash_init();
    if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
        ESP_ERROR_CHECK(nvs_flash_erase());
        ret = nvs_flash_init();
    }
    ESP_ERROR_CHECK( ret );
    esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT();

    ret = esp_bt_controller_mem_release(ESP_BT_MODE_CLASSIC_BT);
    if (ret) {
        ESP_LOGI(tag, "Bluetooth controller release classic bt memory failed: %s", esp_err_to_name(ret));
        return;
    }

    if ((ret = esp_bt_controller_init(&bt_cfg)) != ESP_OK) {
        ESP_LOGI(tag, "Bluetooth controller initialize failed: %s", esp_err_to_name(ret));
        return;
    }

    if ((ret = esp_bt_controller_enable(ESP_BT_MODE_BLE)) != ESP_OK) {
        ESP_LOGI(tag, "Bluetooth controller enable failed: %s", esp_err_to_name(ret));
        return;
    }

    /*
     * If call mem release here, also work. Input ESP_BT_MODE_CLASSIC_BT, the function will
     * release the memory of classic bt mode.
     * esp_bt_controller_mem_release(ESP_BT_MODE_CLASSIC_BT);
     *
     */

    /*
     * If call mem release here, also work. Input ESP_BT_MODE_BTDM, the function will calculate
     * that the BLE mode is already used, so it will release of only classic bt mode.
     * esp_bt_controller_mem_release(ESP_BT_MODE_BTDM);
     */

    xTaskCreatePinnedToCore(&bleAdvtTask, "bleAdvtTask", 2048, NULL, 5, NULL, 0);
}

Debug Logs

BLE advt task start
controller rcv pkt ready
host rcv pkt: 040e0405030c00
BLE Advertise, flag_send_avail: 1, cmd_sent: 1
controller rcv pkt ready
host rcv pkt: 040e0405010c00
BLE Advertise, flag_send_avail: 1, cmd_sent: 2
controller rcv pkt ready
host rcv pkt: 040f0400050d20
BLE Advertise, flag_send_avail: 1, cmd_sent: 3
BLE Advertise, flag_send_avail: 1, cmd_sent: 3
BLE Advertise, flag_send_avail: 1, cmd_sent: 3
BLE Advertise, flag_send_avail: 1, cmd_sent: 3
host rcv pkt: 043e1f0a00000000[bd addr here]0000000000000000000000005a000000f40100
host rcv pkt: 043e0414000001

Decoding of the received packet 043e1f0a…: 04: Type is Event packet 3e: LE Meta Event 1f: Length of packet 0a: Subevent Code for the LE Enhanced Connection Complete Event

Decoding of the received packet 043e0414…: 04: Type is Event packet 3e: LE Meta Event 04: Length of packet 14: Subevent code for the LE Channel Selection Algorithm event

About this issue

  • Original URL
  • State: closed
  • Created 3 years ago
  • Comments: 25 (10 by maintainers)

Most upvoted comments

@AxelLin @Emill

We will fix it ASAP.

The default values are not allowed to be customized per implementation by a vendor, since these are standardised in the Bluetooth Core specification.

As I wrote in my initial post:

The spec says the following:

The default is for bits 0 to 4 inclusive (the value 0x0000 0000 0000 001F) to be set.

So what Espressif must do, to be Bluetooth standard compliant, is to change their default value to 0x1f.

@AxelLin

Code:

/*
   This example code is in the Public Domain (or CC0 licensed, at your option.)

   Unless required by applicable law or agreed to in writing, this
   software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
   CONDITIONS OF ANY KIND, either express or implied.
*/

#include <stdio.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_bt.h"
#include "esp_log.h"
#include "nvs_flash.h"
#include "bt_hci_common.h"

#define HCI_BLE_CREATE_CONNECTION           (0x000D | HCI_GRP_BLE_CMDS)
#define HCI_SET_BLE_EVT_MASK                (0x0001 | HCI_GRP_BLE_CMDS)

#define UINT32_TO_STREAM(buf, val) do { UINT16_TO_STREAM((buf), (uint16_t)(val)); UINT16_TO_STREAM((buf), (uint16_t)((val) >> 16)); } while(0)
#define UINT64_TO_STREAM(buf, val) do { UINT32_TO_STREAM((buf), (uint32_t)(val)); UINT32_TO_STREAM((buf), (uint32_t)((val) >> 32)); } while(0)

static const char *tag = "BLE_TEST";

static uint8_t hci_cmd_buf[128];

/*
 * @brief: BT controller callback function, used to notify the upper layer that
 *         controller is ready to receive command
 */
static void controller_rcv_pkt_ready(void)
{
    printf("controller rcv pkt ready\n");
}

/*
 * @brief: BT controller callback function, to transfer data packet to upper
 *         controller is ready to receive command
 */
static int host_rcv_pkt(uint8_t *data, uint16_t len)
{
    printf("host rcv pkt: ");
    for (uint16_t i = 0; i < len; i++) {
        printf("%02x", data[i]);
    }
    printf("\n");
    return 0;
}

uint16_t make_cmd_reset(uint8_t *buf)
{
    UINT8_TO_STREAM (buf, H4_TYPE_COMMAND);
    UINT16_TO_STREAM (buf, HCI_RESET);
    UINT8_TO_STREAM (buf, 0);
    return HCI_H4_CMD_PREAMBLE_SIZE;
}


static esp_vhci_host_callback_t vhci_host_cb = {
    controller_rcv_pkt_ready,
    host_rcv_pkt
};
static void hci_cmd_send_reset(void)
{
    uint16_t sz = make_cmd_reset (hci_cmd_buf);
    esp_vhci_host_send_packet(hci_cmd_buf, sz);
}

static void hci_cmd_send_set_event_mask(void) {
    uint8_t *buf = hci_cmd_buf;
    UINT8_TO_STREAM(buf, H4_TYPE_COMMAND);
    UINT16_TO_STREAM(buf, HCI_SET_EVT_MASK);
    UINT8_TO_STREAM(buf, 8);
    uint64_t mask = 0x2000000000000000ULL; // LE Meta Event
    UINT64_TO_STREAM(buf, mask);
    esp_vhci_host_send_packet(hci_cmd_buf, HCI_H4_CMD_PREAMBLE_SIZE + 8);
}

static void hci_cmd_send_set_ble_event_mask(void) {
    uint8_t *buf = hci_cmd_buf;
    UINT8_TO_STREAM(buf, H4_TYPE_COMMAND);
    UINT16_TO_STREAM(buf, HCI_SET_BLE_EVT_MASK);
    UINT8_TO_STREAM(buf, 8);
    uint64_t mask = 0x000000000000001fULL;
    UINT64_TO_STREAM(buf, mask);
    esp_vhci_host_send_packet(hci_cmd_buf, HCI_H4_CMD_PREAMBLE_SIZE + 8);
}

static void hci_cmd_send_create_conn(void) {
    uint8_t *buf = hci_cmd_buf;
    UINT8_TO_STREAM(buf, H4_TYPE_COMMAND);
    UINT16_TO_STREAM(buf, HCI_BLE_CREATE_CONNECTION);
    UINT8_TO_STREAM(buf, 25); // len
    UINT16_TO_STREAM(buf, 160); // scan interval
    UINT16_TO_STREAM(buf, 160); // scan win
    UINT8_TO_STREAM(buf, 0); // no white list
    UINT8_TO_STREAM(buf, 0); // public addr type=0, random addr type=1
    uint8_t addr[6] = { 0x7c,0xdf,0xa1,0x86,0xdc,0xa5 }; // <-- fill in BD ADDR
    BDADDR_TO_STREAM(buf, addr);
    UINT8_TO_STREAM(buf, 0); // public own addr type
    UINT16_TO_STREAM(buf, 80); // con min
    UINT16_TO_STREAM(buf, 90); // con max
    UINT16_TO_STREAM(buf, 0); // conn latency
    UINT16_TO_STREAM(buf, 500); // supervision timeout
    UINT16_TO_STREAM(buf, 0); // min ce len
    UINT16_TO_STREAM(buf, 0); // max ce len
    
    
    esp_vhci_host_send_packet(hci_cmd_buf, HCI_H4_CMD_PREAMBLE_SIZE + 25);
}

/*
 * @brief: send HCI commands to perform BLE advertising;
 */
void bleAdvtTask(void *pvParameters)
{
    int cmd_cnt = 0;
    bool send_avail = false;
    esp_vhci_host_register_callback(&vhci_host_cb);
    printf("BLE advt task start\n");
    while (1) {
        vTaskDelay(1000 / portTICK_PERIOD_MS);
        send_avail = esp_vhci_host_check_send_available();
        if (send_avail) {
            switch (cmd_cnt) {
            case 0: hci_cmd_send_reset(); ++cmd_cnt; break;
            case 1: hci_cmd_send_set_event_mask(); ++cmd_cnt; break;
            case 2: hci_cmd_send_set_ble_event_mask(); ++cmd_cnt; break;
            case 3: hci_cmd_send_create_conn(); ++cmd_cnt; break;
            }
        }
        printf("BLE Advertise, flag_send_avail: %d, cmd_sent: %d\n", send_avail, cmd_cnt);
    }
}

void app_main(void)
{
    /* Initialize NVS — it is used to store PHY calibration data */
    esp_err_t ret = nvs_flash_init();
    if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
        ESP_ERROR_CHECK(nvs_flash_erase());
        ret = nvs_flash_init();
    }
    ESP_ERROR_CHECK( ret );
    esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT();

    ret = esp_bt_controller_mem_release(ESP_BT_MODE_CLASSIC_BT);
    if (ret) {
        ESP_LOGI(tag, "Bluetooth controller release classic bt memory failed: %s", esp_err_to_name(ret));
        return;
    }

    if ((ret = esp_bt_controller_init(&bt_cfg)) != ESP_OK) {
        ESP_LOGI(tag, "Bluetooth controller initialize failed: %s", esp_err_to_name(ret));
        return;
    }

    if ((ret = esp_bt_controller_enable(ESP_BT_MODE_BLE)) != ESP_OK) {
        ESP_LOGI(tag, "Bluetooth controller enable failed: %s", esp_err_to_name(ret));
        return;
    }

    /*
     * If call mem release here, also work. Input ESP_BT_MODE_CLASSIC_BT, the function will
     * release the memory of classic bt mode.
     * esp_bt_controller_mem_release(ESP_BT_MODE_CLASSIC_BT);
     *
     */

    /*
     * If call mem release here, also work. Input ESP_BT_MODE_BTDM, the function will calculate
     * that the BLE mode is already used, so it will release of only classic bt mode.
     * esp_bt_controller_mem_release(ESP_BT_MODE_BTDM);
     */

    xTaskCreatePinnedToCore(&bleAdvtTask, "bleAdvtTask", 2048, NULL, 5, NULL, 0);
}

Log:

I (202) cpu_start: Project name:     gatt_client_demo
I (207) cpu_start: App version:      qa-test-v4.3.1-20210705-dirty
I (214) cpu_start: Compile time:     Jul 16 2021 15:31:36
I (220) cpu_start: ELF file SHA256:  986b9818023d0021...
I (226) cpu_start: ESP-IDF:          qa-test-v4.3.1-20210705-dirty
I (233) heap_init: Initializing. RAM available for dynamic allocation:
I (240) heap_init: At 3FC911A0 len 0002EE60 (187 KiB): DRAM
I (247) heap_init: At 3FCC0000 len 0001F060 (124 KiB): STACK/DRAM
I (253) heap_init: At 50000010 len 00001FF0 (7 KiB): RTCRAM
I (260) spi_flash: detected chip: generic
I (264) spi_flash: flash io: dio
W (268) spi_flash: Detected size(4096k) larger than the size in the binary image header(2048k). Using the size in the binary image header.
I (287) sleep: Configure to isolate all GPIO pins in sleep state
I (288) sleep: Enable automatic switching of GPIO sleep configuration
I (295) cpu_start: Starting scheduler.
W (304) BTDM_INIT: esp_bt_controller_mem_release not implemented, return OK
I (304) BTDM_INIT: BT controller compile version [9c99115]
I (314) coexist: coexist rom version 9387209
I (314) phy_init: phy_version 500,985899c,Apr 19 2021,16:05:08
W (324) phy_init: failed to load RF calibration data (0x1102), falling back to full calibration
I (534) system_api: Base MAC address is not set
I (534) system_api: read default base MAC address from EFUSE
I (534) BTDM_INIT: Bluetooth MAC: 7c:df:a1:86:d9:b5

BLE advt task start
controller rcv pkt ready
host rcv pkt: 040e0405030c00
BLE Advertise, flag_send_avail: 1, cmd_sent: 1
controller rcv pkt ready
host rcv pkt: 040e0405010c00
BLE Advertise, flag_send_avail: 1, cmd_sent: 2
controller rcv pkt ready
host rcv pkt: 040e0405012000
BLE Advertise, flag_send_avail: 1, cmd_sent: 3
controller rcv pkt ready
host rcv pkt: 040f0400050d20
BLE Advertise, flag_send_avail: 1, cmd_sent: 4
host rcv pkt: 043e13010000000000a5dc86a1df7c5a000000f40100
host rcv pkt: 02002010000c000500120208001000200000009001
BLE Advertise, flag_send_avail: 1, cmd_sent: 4
BLE Advertise, flag_send_avail: 1, cmd_sent: 4
BLE Advertise, flag_send_avail: 1, cmd_sent: 4
BLE Advertise, flag_send_avail: 1, cmd_sent: 4

Done
➜  gatt_client git:(release/v4.3) ✗