NimBLE-based host APIs

Overview

Apache MyNewt NimBLE is a highly configurable and BT SIG qualifiable BLE stack providing both host and controller functionalities. ESP-IDF supports NimBLE host stack which is specifically ported for ESP32 platform and FreeRTOS. The underlying controller is still the same (as in case of Bluedroid) providing VHCI interface. Refer to NimBLE user guide for a complete list of features and additional information on NimBLE stack. Most features of NimBLE including BLE Mesh are supported by ESP-IDF. The porting layer is kept cleaner by maintaining all the existing APIs of NimBLE along with a single ESP-NimBLE API for initialization, making it simpler for the application developers.

Architecture

Currently, NimBLE host and controller support different transports such as UART and RAM between them. However, RAM transport cannot be used as is in case of ESP as ESP controller supports VHCI interface and buffering schemes used by NimBLE host is incompatible with that used by ESP controller. Therefore, a new transport between NimBLE host and ESP controller has been added. This is depicted in the figure below. This layer is responsible for maintaining pool of transport buffers and formatting buffers exchanges between host and controller as per the requirements.

ESP NimBLE Stack.

ESP NimBLE Stack

Threading Model

The NimBLE host can run inside the application thread or can have its own independent thread. This flexibility is inherently provided by NimBLE design. By default, a thread is spawned by the porting function nimble_port_freertos_init. This behavior can be changed by overriding the same function. For BLE Mesh, additional thread (advertising thread) is used which keeps on feeding advertisement events to the main thread.

Programming Sequence

To begin with, make sure that the NimBLE stack is enabled from menuconfig choose NimBLE for the Bluetooth host.

Typical programming sequence with NimBLE stack consists of the following steps:
  • Initialize NVS flash using nvs_flash_init() API. This is because ESP controller uses NVS during initialization.
  • Call esp_nimble_hci_and_controller_init() to initialize ESP controller as well as transport layer. This will also link the host and controller modules together. Alternatively, if ESP controller is already initialized, then esp_nimble_hci_init() can be called for the remaining initialization.
  • Initialize the host stack using nimble_port_init.
  • Initialize the required NimBLE host configuration parameters and callbacks
  • Perform application specific tasks/initialization
  • Run the thread for host stack using nimble_port_freertos_init

This documentation does not cover NimBLE APIs. Refer to NimBLE tutorial for more details on the programming sequence/NimBLE APIs for different scenarios.

API Reference

Functions

esp_err_t esp_nimble_hci_init(void)

Initialize VHCI transport layer between NimBLE Host and ESP Bluetooth controller.

This function initializes the transport buffers to be exchanged between NimBLE host and ESP controller. It also registers required host callbacks with the controller.

Return
  • ESP_OK if the initialization is successful
  • Appropriate error code from esp_err_t in case of an error

esp_err_t esp_nimble_hci_and_controller_init(void)

Initialize ESP Bluetooth controller(link layer) and VHCI transport layer between NimBLE Host and ESP Bluetooth controller.

This function initializes ESP controller in BLE only mode and the transport buffers to be exchanged between NimBLE host and ESP controller. It also registers required host callbacks with the controller.

Below is the sequence of APIs to be called to init/enable NimBLE host and ESP controller:

void ble_host_task(void *param)
{
     nimble_port_run(); //This function will return only when nimble_port_stop() is executed.
     nimble_port_freertos_deinit();
}

int ret = esp_nimble_hci_and_controller_init();
if (ret != ESP_OK) {
     ESP_LOGE(TAG, "esp_nimble_hci_and_controller_init() failed with error: %d", ret);
     return;
}

nimble_port_init();

//Initialize the NimBLE Host configuration

nimble_port_freertos_init(ble_host_task);

nimble_port_freertos_init() is an optional call that creates a new task in which the NimBLE host will run. The task function should have a call to nimble_port_run(). If a separate task is not required, calling nimble_port_run() will run the NimBLE host in the current task.

Return
  • ESP_OK if the initialization is successful
  • Appropriate error code from esp_err_t in case of an error

esp_err_t esp_nimble_hci_deinit(void)

Deinitialize VHCI transport layer between NimBLE Host and ESP Bluetooth controller.

Note
This function should be called after the NimBLE host is deinitialized.
Return
  • ESP_OK if the deinitialization is successful
  • Appropriate error codes from esp_err_t in case of an error

esp_err_t esp_nimble_hci_and_controller_deinit(void)

Deinitialize VHCI transport layer between NimBLE Host and ESP Bluetooth controller and disable and deinitialize the controller.

Below is the sequence of APIs to be called to disable/deinit NimBLE host and ESP controller:

Note
This function should not be executed in the context of Bluetooth host task.
Note
This function should be called after the NimBLE host is deinitialized.

int ret = nimble_port_stop();
if (ret == 0) {
     nimble_port_deinit();

     ret = esp_nimble_hci_and_controller_deinit();
     if (ret != ESP_OK) {
         ESP_LOGE(TAG, "esp_nimble_hci_and_controller_deinit() failed with error: %d", ret);
     }
}

If nimble_port_freertos_init() is used during initialization, then nimble_port_freertos_deinit() should be called in the host task after nimble_port_run().

Return
  • ESP_OK if the deinitialization is successful
  • Appropriate error codes from esp_err_t in case of an error

Macros

BLE_HCI_UART_H4_NONE
BLE_HCI_UART_H4_CMD
BLE_HCI_UART_H4_ACL
BLE_HCI_UART_H4_SCO
BLE_HCI_UART_H4_EVT