Merge branch 'doc/update_document_of_demo_bt_discovery' into 'master'

Doc/update document of demo bt discovery

Closes BT-2133

See merge request espressif/esp-idf!16265
This commit is contained in:
Wang Meng Yang 2021-12-29 06:07:19 +00:00
commit bc1a679052
5 changed files with 280 additions and 57 deletions

View File

@ -228,7 +228,7 @@ typedef enum {
#define ESP_BT_GAP_MIN_INQ_LEN (0x01) /*!< Minimum inquiry duration, unit is 1.28s */
#define ESP_BT_GAP_MAX_INQ_LEN (0x30) /*!< Maximum inquiry duration, unit is 1.28s */
/// A2DP state callback parameters
/// GAP state callback parameters
typedef union {
/**
* @brief ESP_BT_GAP_DISC_RES_EVT

View File

@ -1,10 +1,11 @@
| Supported Targets | ESP32 |
| ----------------- | ----- |
# ESP-IDF BT-INQUIRY demo
# ESP-IDF BT-DISCOVERY Example
Demo of Classic Bluetooth Device and Service Discovery
This is the demo for user to use ESP_APIs to perform inquiry to search for a target device and then performs service search via SDP.
Example of Classic Bluetooth Device and Service Discovery.
This is the example of using APIs to perform inquiry to search for a target device with a Major device type "Phone" or "Audio/Video" in the CoD (Class of Device) field and then performing a search via SDP (Service Discovery Protocol).
## How to use example
@ -23,21 +24,246 @@ idf.py menuconfig
Build the project and flash it to the board, then run monitor tool to view serial output:
```
idf.py -p PORT flash monitor
idf.py -p PORT build flash monitor
```
(Replace PORT with the name of the serial port to use.)
(To exit the serial monitor, type ``Ctrl-]``.)
To exit the serial monitor, type `Ctrl-]`.
See the [Getting Started Guide](https://docs.espressif.com/projects/esp-idf/en/latest/get-started/index.html) for full steps to configure and use ESP-IDF to build projects.
## Example Description
# TUTORIAL
After the program started, the device will start inquiry to search for a device with Major device type "PHONE" in the Class of Device Field. Then it will cancel the inquiry and started to perform service discovering on this remote device.
## Includes
## Trouble shooting
```c
#include <stdint.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "nvs.h"
#include "nvs_flash.h"
#include "esp_system.h"
#include "esp_log.h"
#include "esp_bt.h"
#include "esp_bt_main.h"
#include "esp_bt_device.h"
#include "esp_gap_bt_api.h"
```
- Some old BT device doesn't place their device name in EIR, but new devices place their device name in EIR. Users can obtain the peer device name in `bt_app_gap_cb` `ESP_BT_GAP_DISC_RES_EVT` event handler or resolve it in EIR just like the function `get_name_from_eir` does.
These `includes` are required for the FreeRTOS and underlaying system components to run, including the logging functionality and a library to store data in non-volatile flash memory. We are interested in `bt.h`, `esp_bt_main.h`, `esp_bt_device.h` and `esp_gap_bt_api.h`, which expose the Classic Bluetooth APIs required to implement this example.
- ESP32 places its device name in EIR by default.
* `bt.h`: configures the Bluetooth controller and VHCI from the host side.
* `esp_bt_main.h`: initializes and enables the Bluedroid stack.
* `esp_gap_bt_api.h`: implements the GAP configuration, such as Classic Bluetooth device and service discovery.
* `esp_bt_device.h`: implements the device configuration, such as device address and device name.
## Main Entry Point
The programs entry point is the `app_main()` function.
### Non-volatile Storage Library Initialization
The main function starts by initializing the non-volatile storage library. This library allows to store PHY calibration data and save key-value pairs in flash memory.
```c
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 );
```
### Release The Controller Memory
Then the main function releases the controller memory for unused modes. It releases the BLE (Bluetooth Low Energy) memory in the controller via:
```c
ESP_ERROR_CHECK(esp_bt_controller_mem_release(ESP_BT_MODE_BLE));
```
The controller memory should be released only before initializing Bluetooth controller or after deinitializing Bluetooth controller.
Note that once Bluetooth controller memory is released, the process cannot be reversed. It means you cannot use the Bluetooth mode which you have released by this function.
## Bluetooth Controller and Stack Initialization
The main function also initializes the Bluetooth controller by first creating the controller configuration structure named `esp_bt_controller_config_t` with default settings generated by the `BT_CONTROLLER_INIT_CONFIG_DEFAULT()` macro. The Bluetooth controller implements the Host Controller Interface (HCI) on the controller side, the Link Layer (LL), and the Physical Layer (PHY). The Bluetooth Controller is invisible to the user applications and deals with the lower layers of the Bluetooth stack. The controller configuration includes setting the Bluetooth controller stack size, priority, and HCI baud rate. With the settings created, the Bluetooth controller is initialized and enabled with the `esp_bt_controller_init()` function:
```c
esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT();
if ((ret = esp_bt_controller_init(&bt_cfg)) != ESP_OK) {
ESP_LOGE(GAP_TAG, "%s initialize controller failed: %s\n", __func__, esp_err_to_name(ret));
return;
}
```
Next, the controller is enabled in Classic Bluetooth Mode.
```c
if ((ret = esp_bt_controller_enable(ESP_BT_MODE_CLASSIC_BT)) != ESP_OK) {
ESP_LOGE(GAP_TAG, "%s enable controller failed: %s\n", __func__, esp_err_to_name(ret));
return;
}
```
> If you want to use the dual mode (BLE + Bluetooth Classic), the controller should be enabled in `ESP_BT_MODE_BTDM`, and should not releases the BLE memory in the controller.
There are four Bluetooth modes supported:
1. `ESP_BT_MODE_IDLE`: Bluetooth not running
2. `ESP_BT_MODE_BLE`: BLE mode
3. `ESP_BT_MODE_CLASSIC_BT`: Bluetooth Classic mode
4. `ESP_BT_MODE_BTDM`: Dual mode (BLE + Bluetooth Classic)
After the initialization of the Bluetooth controller, the Bluedroid stack, which includes the common definitions and APIs for both Bluetooth Classic and BLE, is initialized and enabled by using:
```c
if ((ret = esp_bluedroid_init()) != ESP_OK) {
ESP_LOGE(GAP_TAG, "%s initialize bluedroid failed: %s\n", __func__, esp_err_to_name(ret));
return;
}
if ((ret = esp_bluedroid_enable()) != ESP_OK) {
ESP_LOGE(GAP_TAG, "%s enable bluedroid failed: %s\n", __func__, esp_err_to_name(ret));
return;
}
```
The main function ends by starting up application function.
```c
bt_app_gap_start_up();
```
## Application Start Up
The application function starts by registering the GAP (Generic Access Profile) event handlers,
```c
/* register GAP callback function */
esp_bt_gap_register_callback(bt_app_gap_cb);
```
The GAP event handlers are the functions used to catch the events generated by the Bluetooth stack and execute functions to configure parameters of the application.
The functions `bt_app_gap_cb` handle all the events generated by the Bluetooth stack.
The application function then sets the device name and sets it as discoverable and connectable. After this, this device can be discovered and connected.
```c
char *dev_name = "ESP_GAP_INQRUIY";
esp_bt_dev_set_device_name(dev_name);
/* set discoverable and connectable mode, wait to be connected */
esp_bt_gap_set_scan_mode(ESP_BT_CONNECTABLE, ESP_BT_GENERAL_DISCOVERABLE);
```
The application function then initialises the information and status of application layer and starts to discover nearby Bluetooth devices.
```c
/* inititialize device information and status */
bt_app_gap_init();
/* start to discover nearby Bluetooth devices */
app_gap_cb_t *p_dev = &m_dev_info;
p_dev->state = APP_GAP_STATE_DEVICE_DISCOVERING;
esp_bt_gap_start_discovery(ESP_BT_INQ_MODE_GENERAL_INQUIRY, 10, 0);
```
## GAP Callback
### Get Device Discovery Result
After device discovery gets started the state will change to `ESP_BT_GAP_DISCOVERY_STARTED`, and `bt_app_gap_cb` will be called with `ESP_BT_GAP_DISC_STATE_CHANGED_EVT`.
If nearby Bluetooth devices are discovered `bt_app_gap_cb` will be called with `ESP_BT_GAP_DISC_RES_EVT`, to post the result to users.
```c
case ESP_BT_GAP_DISC_RES_EVT: {
update_device_info(param);
break;
}
```
### Search For A Target Device
Function `update_device_info` gets the CoD (Class of Device), RSSI (Received Signal Strength Indication), name, EIR (Extended Inquiry Result) from the discovery result.
```c
for (int i = 0; i < param->disc_res.num_prop; i++) {
p = param->disc_res.prop + i;
switch (p->type) {
case ESP_BT_GAP_DEV_PROP_COD:
cod = *(uint32_t *)(p->val);
ESP_LOGI(GAP_TAG, "--Class of Device: 0x%x", cod);
break;
case ESP_BT_GAP_DEV_PROP_RSSI:
rssi = *(int8_t *)(p->val);
ESP_LOGI(GAP_TAG, "--RSSI: %d", rssi);
break;
case ESP_BT_GAP_DEV_PROP_BDNAME:
bdname_len = (p->len > ESP_BT_GAP_MAX_BDNAME_LEN) ? ESP_BT_GAP_MAX_BDNAME_LEN :
(uint8_t)p->len;
bdname = (uint8_t *)(p->val);
break;
case ESP_BT_GAP_DEV_PROP_EIR: {
eir_len = p->len;
eir = (uint8_t *)(p->val);
break;
}
default:
break;
}
}
```
Pay attention that some Bluetooth devices may put their name in EIR data. We can get the device name from EIR data.
```c
if (p_dev->eir && p_dev->bdname_len == 0) {
get_name_from_eir(p_dev->eir, p_dev->bdname, &p_dev->bdname_len);
}
```
Then, function `update_device_info` searches for a device with Major device type "Phone" or "Audio/Video" according to COD. For more information on COD, check [Bluetooth specifications](https://www.bluetooth.com/specifications/assigned-numbers/).
```c
if (!esp_bt_gap_is_valid_cod(cod) ||
(!(esp_bt_gap_get_cod_major_dev(cod) == ESP_BT_COD_MAJOR_DEV_PHONE) &&
!(esp_bt_gap_get_cod_major_dev(cod) == ESP_BT_COD_MAJOR_DEV_AV))) {
return;
}
```
If the target device is found, the device discovery procedure is stopped.
```c
ESP_LOGI(GAP_TAG, "Found a target device, address %s, name %s", bda_str, p_dev->bdname);
p_dev->state = APP_GAP_STATE_DEVICE_DISCOVER_COMPLETE;
ESP_LOGI(GAP_TAG, "Cancel device discovery ...");
esp_bt_gap_cancel_discovery();
```
### Service Discovery
After device discovery stops the state will change to `ESP_BT_GAP_DISCOVERY_STOPPED` and `bt_app_gap_cb` will be called with `ESP_BT_GAP_DISC_STATE_CHANGED_EVT`.
Then it performs service discovery targeting at this device.
```c
p_dev->state = APP_GAP_STATE_SERVICE_DISCOVERING;
ESP_LOGI(GAP_TAG, "Discover services ...");
esp_bt_gap_get_remote_services(p_dev->bda);
```
Then `bt_app_gap_cb` will be called with `ESP_BT_GAP_RMT_SRVCS_EVT` to post the service discovery result to users including the service UUID. For more information on the service UUID, you can check [Bluetooth specifications](https://www.bluetooth.com/specifications/assigned-numbers/).
```c
for (int i = 0; i < param->rmt_srvcs.num_uuids; i++) {
esp_bt_uuid_t *u = param->rmt_srvcs.uuid_list + i;
ESP_LOGI(GAP_TAG, "--%s", uuid2str(u, uuid_str, 37));
}
```
## Conclusion
We have reviewed the Bluetooth discovery example code. This example searches for nearby devices. When the device of interest is found, the example searches for the services of this device. We can get the device discovery result and service discovery result from the GAP callback, which is registered in advance.

View File

@ -1,2 +1,2 @@
idf_component_register(SRCS "bt_discovery.c"
idf_component_register(SRCS "main.c"
INCLUDE_DIRS ".")

View File

@ -120,6 +120,10 @@ static void update_device_info(esp_bt_gap_cb_param_t *param)
char bda_str[18];
uint32_t cod = 0;
int32_t rssi = -129; /* invalid value */
uint8_t *bdname = NULL;
uint8_t bdname_len = 0;
uint8_t *eir = NULL;
uint8_t eir_len = 0;
esp_bt_gap_dev_prop_t *p;
ESP_LOGI(GAP_TAG, "Device found: %s", bda2str(param->disc_res.bda, bda_str, 18));
@ -135,14 +139,23 @@ static void update_device_info(esp_bt_gap_cb_param_t *param)
ESP_LOGI(GAP_TAG, "--RSSI: %d", rssi);
break;
case ESP_BT_GAP_DEV_PROP_BDNAME:
bdname_len = (p->len > ESP_BT_GAP_MAX_BDNAME_LEN) ? ESP_BT_GAP_MAX_BDNAME_LEN :
(uint8_t)p->len;
bdname = (uint8_t *)(p->val);
break;
case ESP_BT_GAP_DEV_PROP_EIR: {
eir_len = p->len;
eir = (uint8_t *)(p->val);
break;
}
default:
break;
}
}
/* search for device with MAJOR service class as "rendering" in COD */
/* search for device with Major device type "PHONE" or "Audio/Video" in COD */
app_gap_cb_t *p_dev = &m_dev_info;
if (p_dev->dev_found && 0 != memcmp(param->disc_res.bda, p_dev->bda, ESP_BD_ADDR_LEN)) {
if (p_dev->dev_found) {
return;
}
@ -154,49 +167,35 @@ static void update_device_info(esp_bt_gap_cb_param_t *param)
memcpy(p_dev->bda, param->disc_res.bda, ESP_BD_ADDR_LEN);
p_dev->dev_found = true;
for (int i = 0; i < param->disc_res.num_prop; i++) {
p = param->disc_res.prop + i;
switch (p->type) {
case ESP_BT_GAP_DEV_PROP_COD:
p_dev->cod = *(uint32_t *)(p->val);
break;
case ESP_BT_GAP_DEV_PROP_RSSI:
p_dev->rssi = *(int8_t *)(p->val);
break;
case ESP_BT_GAP_DEV_PROP_BDNAME: {
uint8_t len = (p->len > ESP_BT_GAP_MAX_BDNAME_LEN) ? ESP_BT_GAP_MAX_BDNAME_LEN :
(uint8_t)p->len;
memcpy(p_dev->bdname, (uint8_t *)(p->val), len);
p_dev->bdname[len] = '\0';
p_dev->bdname_len = len;
break;
}
case ESP_BT_GAP_DEV_PROP_EIR: {
memcpy(p_dev->eir, (uint8_t *)(p->val), p->len);
p_dev->eir_len = p->len;
break;
}
default:
break;
p_dev->cod = cod;
p_dev->rssi = rssi;
if (bdname_len > 0) {
memcpy(p_dev->bdname, bdname, bdname_len);
p_dev->bdname[bdname_len] = '\0';
p_dev->bdname_len = bdname_len;
}
if (eir_len > 0) {
memcpy(p_dev->eir, eir, eir_len);
p_dev->eir_len = eir_len;
}
if (p_dev->eir && p_dev->bdname_len == 0) {
get_name_from_eir(p_dev->eir, p_dev->bdname, &p_dev->bdname_len);
}
ESP_LOGI(GAP_TAG, "Found a target device, address %s, name %s", bda_str, p_dev->bdname);
p_dev->state = APP_GAP_STATE_DEVICE_DISCOVER_COMPLETE;
ESP_LOGI(GAP_TAG, "Cancel device discovery ...");
esp_bt_gap_cancel_discovery();
}
}
static void bt_app_gap_init(void)
{
app_gap_cb_t *p_dev = &m_dev_info;
memset(p_dev, 0, sizeof(app_gap_cb_t));
/* start to discover nearby Bluetooth devices */
p_dev->state = APP_GAP_STATE_DEVICE_DISCOVERING;
p_dev->state = APP_GAP_STATE_IDLE;
}
static void bt_app_gap_cb(esp_bt_gap_cb_event_t event, esp_bt_gap_cb_param_t *param)
@ -234,7 +233,6 @@ static void bt_app_gap_cb(esp_bt_gap_cb_event_t event, esp_bt_gap_cb_param_t *pa
for (int i = 0; i < param->rmt_srvcs.num_uuids; i++) {
esp_bt_uuid_t *u = param->rmt_srvcs.uuid_list + i;
ESP_LOGI(GAP_TAG, "--%s", uuid2str(u, uuid_str, 37));
// ESP_LOGI(GAP_TAG, "--%d", u->len);
}
} else {
ESP_LOGI(GAP_TAG, "Services for device %s not found", bda2str(p_dev->bda, bda_str, 18));
@ -253,24 +251,27 @@ static void bt_app_gap_cb(esp_bt_gap_cb_event_t event, esp_bt_gap_cb_param_t *pa
static void bt_app_gap_start_up(void)
{
char *dev_name = "ESP_GAP_INQRUIY";
esp_bt_dev_set_device_name(dev_name);
/* register GAP callback function */
esp_bt_gap_register_callback(bt_app_gap_cb);
char *dev_name = "ESP_GAP_INQRUIY";
esp_bt_dev_set_device_name(dev_name);
/* set discoverable and connectable mode, wait to be connected */
esp_bt_gap_set_scan_mode(ESP_BT_CONNECTABLE, ESP_BT_GENERAL_DISCOVERABLE);
/* inititialize device information and status */
bt_app_gap_init();
/* start to discover nearby Bluetooth devices */
app_gap_cb_t *p_dev = &m_dev_info;
p_dev->state = APP_GAP_STATE_DEVICE_DISCOVERING;
esp_bt_gap_start_discovery(ESP_BT_INQ_MODE_GENERAL_INQUIRY, 10, 0);
}
void app_main(void)
{
/* Initialize NVS — it is used to store PHY calibration data */
/* Initialize NVS — it is used to store PHY calibration data and save key-value pairs in flash memory*/
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());

View File

@ -1,9 +1,5 @@
# Override some defaults so BT stack is enabled and
# Classic BT is enabled and BT_DRAM_RELEASE is disabled
# Enable Classic BT and disable BLE
CONFIG_BT_ENABLED=y
CONFIG_BTDM_CTRL_MODE_BLE_ONLY=n
CONFIG_BTDM_CTRL_MODE_BR_EDR_ONLY=y
CONFIG_BTDM_CTRL_MODE_BTDM=n
CONFIG_BT_CLASSIC_ENABLED=y
CONFIG_BT_A2DP_ENABLE=n
CONFIG_BT_BLE_ENABLED=n