component/bt: Update BLE examples tutorials and ReadMe files

- Make docs compatible with GitHub Markdown
- Add tutorials links in programming doc
- Modify ReadMe files
This commit is contained in:
island 2018-01-11 20:24:18 +08:00
parent ffd4187883
commit e22f8ad1aa
25 changed files with 519 additions and 481 deletions

View file

@ -11,9 +11,17 @@ Overview
Application Example
-------------------
Check :example:`bluetooth` folder in ESP-IDF examples, which contains the following applications:
Check :example:`bluetooth` folder in ESP-IDF examples, which contains the following demos and their tutorials:
* The two demos use different GAP APIs, such like advertising, scan, set device name and others - :example:`bluetooth/gatt_server`, :example:`bluetooth/gatt_client`
* This is a SMP security client demo and its tutorial. This demo initiates its security parameters and acts as a GATT client, which can send a security request to the peer device and then complete the encryption procedure.
- :example:`bluetooth/gatt_security_client`
- :example_file:`GATT Security Client Example Walkthrough <bluetooth/gatt_security_client/tutorial/Gatt_Security_Client_Example_Walkthrough.md>`
* This is a SMP security server demo and its tutorial. This demo initiates its security parameters and acts as a GATT server, which can send a pair request to the peer device and then complete the encryption procedure.
- :example:`bluetooth/gatt_security_server`
- :example_file:`GATT Security Server Example Walkthrough <bluetooth/gatt_security_server/tutorial/Gatt_Security_Server_Example_Walkthrough.md>`
API Reference
-------------

View file

@ -11,9 +11,21 @@ Overview
Application Example
-------------------
Check :example:`bluetooth` folder in ESP-IDF examples, which contains the following application:
Check :example:`bluetooth` folder in ESP-IDF examples, which contains the following demos and their tutorials:
* This is a GATT client demo. This demo can scan devices, connect to the GATT server and discover the service :example:`bluetooth/gatt_client`
* This is a GATT client demo and its tutorial. This demo can scan for devices, connect to the GATT server and discover its services.
- :example:`bluetooth/gatt_client`
- :example_file:`GATT Client Example Walkthrough <bluetooth/gatt_client/tutorial/Gatt_Client_Example_Walkthrough.md>`
* This is a multiple connection demo and its tutorial. This demo can connect to multiple GATT server devices and discover their services.
- :example:`bluetooth/gattc_multi_connect`
- :example_file:`GATT Client Multi-connection Example Walkthrough <bluetooth/gattc_multi_connect/tutorial/Gatt_Client_Multi_Connection_Example_Walkthrough.md>`
* This is a BLE SPP-Like demo. This demo, which acts as a GATT client, can receive data from UART and then send the data to the peer device automatically.
- :example:`bluetooth/ble_spp_client`
API Reference
-------------

View file

@ -11,9 +11,21 @@ Overview
Application Example
-------------------
Check :example:`bluetooth` folder in ESP-IDF examples, which contains the following application:
Check :example:`bluetooth` folder in ESP-IDF examples, which contains the following demos and their tutorials:
* This is a GATT server demo. Use GATT API to create a GATT server with send advertising. This GATT server can be connected and the service can be discovery - :example:`bluetooth/gatt_server`
* This is a GATT sever demo and its tutorial. This demo creates a GATT service with an attribute table, which releases the user from adding attributes one by one. This is the recommended method of adding attributes.
- :example:`bluetooth/gatt_server_service_table`
- :example_file:`GATT Server Service Table Example Walkthrough <bluetooth/gatt_server_service_table/tutorial/Gatt_Server_Service_Table_Example_Walkthrough.md>`
* This is a GATT server demo and its tutorial. This demo creates a GATT service by adding attributes one by one as defined by Bluedroid. The recommended method of adding attributes is presented in example above.
- :example:`bluetooth/gatt_server`
- :example_file:`GATT Server Example Walkthrough <bluetooth/gatt_server/tutorial/Gatt_Server_Example_Walkthrough.md>`
* This is a BLE SPP-Like demo. This demo, which acts as a GATT server, can receive data from UART and then send the data to the peer device automatically.
- :example:`bluetooth/ble_spp_server`
API Reference
-------------

View file

@ -10,4 +10,18 @@ Bluetooth API
Bluetooth Classic <classic_bt>
To see the overview of the ESP32 Bluetooth stack architecture, follow links below:
* `ESP32 Bluetooth Architecture (PDF) [English] <http://espressif.com/sites/default/files/documentation/esp32_bluetooth_architecture_en.pdf>`_
* `ESP32 Bluetooth Architecture (PDF) [中文] <http://espressif.com/sites/default/files/documentation/esp32_bluetooth_architecture_cn.pdf>`_
Example code for this API section is provided in :example:`bluetooth` directory of ESP-IDF examples.
Several examples contain detailed description. To see them please follow links below:
* :example_file:`GATT Client Example Walkthrough <bluetooth/gatt_client/tutorial/Gatt_Client_Example_Walkthrough.md>`
* :example_file:`GATT Server Service Table Example Walkthrough <bluetooth/gatt_server_service_table/tutorial/Gatt_Server_Service_Table_Example_Walkthrough.md>`
* :example_file:`GATT Server Example Walkthrough <bluetooth/gatt_server/tutorial/Gatt_Server_Example_Walkthrough.md>`
* :example_file:`GATT Security Client Example Walkthrough <bluetooth/gatt_security_client/tutorial/Gatt_Security_Client_Example_Walkthrough.md>`
* :example_file:`GATT Security Server Example Walkthrough <bluetooth/gatt_security_server/tutorial/Gatt_Security_Server_Example_Walkthrough.md>`
* :example_file:`GATT Client Multi-connection Example Walkthrough <bluetooth/gattc_multi_connect/tutorial/Gatt_Client_Multi_Connection_Example_Walkthrough.md>`

View file

@ -1,9 +1,10 @@
ESP-IDF GATT CLIENT demo
ESP-IDF Gatt Client Demo
========================
This is the demo for user to use ESP_APIs to create a GATT Client.
Run the gatt_server demo, the client demo will automatically connect to the gatt_server demo.
Client demo will enable gatt_server's notify after connection. Then the two devices will exchange
data.
This is the demo for users to use ESP_APIs to create a GATT Client.
Please check the [tutorial](tutorial/gatt_client_example_walkthrough.md) for more information about this example.
To test this demo, you can run the [gatt_server_demo](../gatt_server), which creates services and starts advertising. `Gatt_client_demo` will start scanning and connect to the `gatt_server_demo` automatically.
This demo will enable gatt server's notification function once the connection is established and then the devices start exchanging data.
Please check the [tutorial](tutorial/Gatt_Client_Example_Walkthrough.md) for more information about this example.

View file

@ -2,11 +2,11 @@
## Introduction
In this example, the GATT Client example code for the ESP32 is reviewed. This example implements a Bluetooth Low Energy (BLE) Generic Attribute (GATT) client which scans for nearby peripheral servers and connects to a predefined service. The client then searches for available characteristics and subscribes to a known characteristic in order to receive notifications or indications. The example can register an Application Profile and initializes a sequence of events, which can be used to configure Generic Access Profile (GAP) parameters and to handle events such as scanning, connecting to peripherals and reading and writing characteristics.
In this tutorial, the GATT client example code for the ESP32 is reviewed. The code implements a Bluetooth Low Energy (BLE) Generic Attribute (GATT) client, which scans for nearby peripheral servers and connects to a predefined service. The client then searches for available characteristics and subscribes to a known characteristic in order to receive notifications or indications. The example can register an Application Profile and initializes a sequence of events, which can be used to configure Generic Access Profile (GAP) parameters and to handle events such as scanning, connecting to peripherals and reading and writing characteristics.
# Includes
This example is located in the examples folder of the ESP-IDF under the `bluetooth/gatt_client/main` directory. The `gattc_demo.c` file located in the main folder contains all the functionality that we are going to review. The header files contained in `gattc_demo.c` are:
This example is located in the examples folder of the ESP-IDF under the [bluetooth/gatt_client/main](../main). The [gattc_demo.c](../main/gattc_demo.c) file located in the main folder contains all the functionality that we are going to review. The header files contained in [gattc_demo.c](../main/gattc_demo.c) are:
```c
#include <stdint.h>
@ -54,7 +54,7 @@ void app_main()
return;
}
ret = esp_bt_controller_enable(ESP_BT_MODE_BTDM);
ret = esp_bt_controller_enable(ESP_BT_MODE_BLE);
if (ret) {
ESP_LOGE(GATTC_TAG, "%s enable controller failed, error code = %x\n", __func__, ret);
return;
@ -102,12 +102,12 @@ void app_main()
The main function starts by initializing the non-volatile storage library. This library allows to save key-value pairs in flash memory and is used by some components such as the Wi-Fi library to save the SSID and password:
```c
esp_err_t ret = nvs_flash_init();
if (ret == ESP_ERR_NVS_NO_FREE_PAGES) {
ESP_ERROR_CHECK(nvs_flash_erase());
ret = nvs_flash_init();
}
ESP_ERROR_CHECK( ret );
esp_err_t ret = nvs_flash_init();
if (ret == ESP_ERR_NVS_NO_FREE_PAGES) {
ESP_ERROR_CHECK(nvs_flash_erase());
ret = nvs_flash_init();
}
ESP_ERROR_CHECK( ret );
```
## BT Controller and Stack Initialization
@ -116,15 +116,16 @@ The main function also initializes the BT controller by first creating a BT cont
```c
esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT();
ret = esp_bt_controller_init(&bt_cfg);
ret = esp_bt_controller_init(&bt_cfg);
```
Next, the controller is enabled in Dual Mode (BLE + BT Classic).
Next, the controller is enabled in BLE Mode.
```c
ret = esp_bt_controller_enable(ESP_BT_MODE_BTDM);
ret = esp_bt_controller_enable(ESP_BT_MODE_BLE);
```
>The controller should be enabled in `ESP_BT_MODE_BTDM`, if you want to use the dual mode (BLE + BT).
There are four Bluetooth modes supported:
1. `ESP_BT_MODE_IDLE`: Bluetooth not running
@ -202,9 +203,8 @@ The Application Profile are stored in the `gl_profile_tab` array, which is initi
```c
/* One gatt-based profile one app_id and one gattc_if, this array will store the gattc_if returned by ESP_GATTS_REG_EVT */
static struct gattc_profile_inst gl_profile_tab[PROFILE_NUM] = {
[PROFILE_A_APP_ID] = {
.gattc_cb = gattc_profile_event_handler,
.gattc_if = ESP_GATT_IF_NONE, /* Not get the gatt_if, so initial is ESP_GATT_IF_NONE */
[PROFILE_A_APP_ID] = {.gattc_cb = gattc_profile_event_handler,
.gattc_if = ESP_GATT_IF_NONE, /* Not get the gatt_if, so initial is ESP_GATT_IF_NONE */
},
};
```
@ -261,25 +261,15 @@ typedef struct {
esp_ble_scan_type_t scan_type; /*!< Scan type */
esp_ble_addr_type_t own_addr_type; /*!< Owner address type */
esp_ble_scan_filter_t scan_filter_policy; /*!< Scan filter policy */
uint16_t scan_interval; /*!< Scan interval. This is defined
as the time interval from
when the Controller started its
last LE scan until it begins the
subsequent LE scan.
Range: 0x0004 to 0x4000
Default: 0x0010 (10 ms)
Time = N * 0.625 msec
Time Range: 2.5 msec to 10.24
seconds*/
uint16_t scan_window; /*!< Scan window. The duration of
the LE scan. LE_Scan_Window
shall be less than or equal to
LE_Scan_Interval
Range: 0x0004 to 0x4000
Default: 0x0010 (10 ms)
Time = N * 0.625 msec
Time Range: 2.5 msec to 10240
msec */
uint16_t scan_interval; /*!< Scan interval. This is defined as the time interval from when the Controller started its last LE scan until it begins the subsequent LE scan.*/
//Range: 0x0004 to 0x4000
//Default: 0x0010 (10 ms)
//Time = N * 0.625 msec
//Time Range: 2.5 msec to 10.24 seconds
uint16_t scan_window; /*!< Scan window. The duration of the LE scan. LE_Scan_Window shall be less than or equal to LE_Scan_Interval*/
//Range: 0x0004 to 0x4000 //Default: 0x0010 (10 ms)
//Time = N * 0.625 msec
//Time Range: 2.5 msec to 10240 msec
} esp_ble_scan_params_t;
```
An it is initialized as:
@ -318,7 +308,7 @@ Once the scanning parameters are set, an `ESP_GAP_BLE_SCAN_PARAM_SET_COMPLETE_EV
uint32_t duration = 30;
esp_ble_gap_start_scanning(duration);
break;
}
}
```
The scanning is started using the `esp_ble_gap_start_scanning()` function which takes a parameter representing the duration of the continuous scanning (in seconds). Once the scanning period is ended, an `ESP_GAP_SEARCH_INQ_CMPL_EVT` event is triggered.
@ -333,19 +323,17 @@ The results of the scanning are displayed as soon as they arrive with the `ESP_G
*/
struct ble_scan_result_evt_param {
esp_gap_search_evt_t search_evt; /*!< Search event type */
esp_bd_addr_t bda; /*!< Bluetooth device address which
has been searched */
esp_bd_addr_t bda; /*!< Bluetooth device address which has been searched */
esp_bt_dev_type_t dev_type; /*!< Device type */
esp_ble_addr_type_t ble_addr_type; /*!< Ble device address type */
esp_ble_evt_type_t ble_evt_type; /*!< Ble scan result event type */
int rssi; /*!< Searched device's RSSI */
uint8_t ble_adv[ESP_BLE_ADV_DATA_LEN_MAX + ESP_BLE_SCAN_RSP_DATA_LEN_MAX]; /*!< Received EIR */
uint8_t ble_adv[ESP_BLE_ADV_DATA_LEN_MAX + ESP_BLE_SCAN_RSP_DATA_LEN_MAX]; /*!< Received EIR */
int flag; /*!< Advertising data flag bit */
int num_resps; /*!< Scan result number */
uint8_t adv_data_len; /*!< Adv data length */
uint8_t scan_rsp_len; /*!< Scan response length */
} scan_rst; /*!< Event parameter of
ESP_GAP_BLE_SCAN_RESULT_EVT */
} scan_rst; /*!< Event parameter of ESP_GAP_BLE_SCAN_RESULT_EVT */
```
This event also includes a list of sub events as shown below:
@ -353,34 +341,30 @@ This event also includes a list of sub events as shown below:
```c
/// Sub Event of ESP_GAP_BLE_SCAN_RESULT_EVT
typedef enum {
ESP_GAP_SEARCH_INQ_RES_EVT = 0, /*!< Inquiry result for a peer
device. */
ESP_GAP_SEARCH_INQ_RES_EVT = 0, /*!< Inquiry result for a peer device. */
ESP_GAP_SEARCH_INQ_CMPL_EVT = 1, /*!< Inquiry complete. */
ESP_GAP_SEARCH_DISC_RES_EVT = 2, /*!< Discovery result for a peer
device. */
ESP_GAP_SEARCH_DISC_BLE_RES_EVT = 3, /*!< Discovery result for BLE GATT
based service on a peer device.
*/
ESP_GAP_SEARCH_DISC_RES_EVT = 2, /*!< Discovery result for a peer device. */
ESP_GAP_SEARCH_DISC_BLE_RES_EVT = 3, /*!< Discovery result for BLE GATT based service on a peer device. */
ESP_GAP_SEARCH_DISC_CMPL_EVT = 4, /*!< Discovery complete. */
ESP_GAP_SEARCH_DI_DISC_CMPL_EVT = 5, /*!< Discovery complete. */
ESP_GAP_SEARCH_SEARCH_CANCEL_CMPL_EVT = 6, /*!< Search cancelled */
} esp_gap_search_evt_t;
```
We are interested in the `ESP_GAP_SEARCH_INQ_RES_EVT` event, which is called every time a new device is found. We are also interested in the `ESP_GAP_SEARCH_INQ_CMPL_EVT`, which is triggered when the duration of the scanning is completed and can be used to restart the scanning procedure:
```c
case ESP_GAP_BLE_SCAN_RESULT_EVT: {
esp_ble_gap_cb_param_t *scan_result = (esp_ble_gap_cb_param_t *)param;
switch (scan_result->scan_rst.search_evt) {
case ESP_GAP_SEARCH_INQ_RES_EVT:
esp_log_buffer_hex(GATTC_TAG, scan_result->scan_rst.bda, 6);
ESP_LOGI(GATTC_TAG, "searched Adv Data Len %d, Scan Response Len %d", scan_result->scan_rst.adv_data_len, scan_result->scan_rst.scan_rsp_len);
adv_name = esp_ble_resolve_adv_data(scan_result->scan_rst.ble_adv,
ESP_BLE_AD_TYPE_NAME_CMPL, &adv_name_len);
ESP_LOGI(GATTC_TAG, "searched Device Name Len %d", adv_name_len);
esp_log_buffer_char(GATTC_TAG, adv_name, adv_name_len);
ESP_LOGI(GATTC_TAG, "\n");
if (adv_name != NULL) {
if (strlen(remote_device_name) == adv_name_len && strncmp((char *)adv_name, remote_device_name, adv_name_len) == 0) {
case ESP_GAP_SEARCH_INQ_RES_EVT:
esp_log_buffer_hex(GATTC_TAG, scan_result->scan_rst.bda, 6);
ESP_LOGI(GATTC_TAG, "searched Adv Data Len %d, Scan Response Len %d", scan_result->scan_rst.adv_data_len, scan_result->scan_rst.scan_rsp_len);
adv_name = esp_ble_resolve_adv_data(scan_result->scan_rst.ble_adv, ESP_BLE_AD_TYPE_NAME_CMPL, &adv_name_len);
ESP_LOGI(GATTC_TAG, "searched Device Name Len %d", adv_name_len);
esp_log_buffer_char(GATTC_TAG, adv_name, adv_name_len);
ESP_LOGI(GATTC_TAG, "\n");
if (adv_name != NULL) {
if (strlen(remote_device_name) == adv_name_len && strncmp((char *)adv_name, remote_device_name, adv_name_len) == 0) {
ESP_LOGI(GATTC_TAG, "searched device %s\n", remote_device_name);
if (connect == false) {
connect = true;
@ -402,7 +386,7 @@ Every time we receive a result from the `ESP_GAP_SEARCH_INQ_RES_EVT` event, the
```c
case ESP_GAP_SEARCH_INQ_RES_EVT:
esp_log_buffer_hex(GATTC_TAG, scan_result->scan_rst.bda, 6);
esp_log_buffer_hex(GATTC_TAG, scan_result->scan_rst.bda, 6);
```
The client then prints the advertised data length and the scan response length:
@ -414,9 +398,7 @@ ESP_LOGI(GATTC_TAG, "searched Adv Data Len %d, Scan Response Len %d", scan_resul
In order to get the device name, we use the `esp_ble_resolve_adv_data()` function, which takes the advertised data stored in `scan_result->scan_rst.ble_adv`, the type of advertising data and the length, in order to extract the value from the advertising packet frame. Then the device name is printed.
```c
adv_name = esp_ble_resolve_adv_data(scan_result->scan_rst.ble_adv,
ESP_BLE_AD_TYPE_NAME_CMPL,
&adv_name_len);
adv_name = esp_ble_resolve_adv_data(scan_result->scan_rst.ble_adv, ESP_BLE_AD_TYPE_NAME_CMPL, &adv_name_len);
ESP_LOGI(GATTC_TAG, "searched Device Name Len %d", adv_name_len);
esp_log_buffer_char(GATTC_TAG, adv_name, adv_name_len);
```
@ -564,55 +546,55 @@ static esp_gatt_srvc_id_t remote_service_id = {
Once defined, we can get the characteristics from that service using the `esp_ble_gattc_get_characteristic()` function, which is called in the `ESP_GATTC_SEARCH_CMPL_EVT` event after the search for services is completed and the client has found the service that it was looking for.
```c
case ESP_GATTC_SEARCH_CMPL_EVT:
if (p_data->search_cmpl.status != ESP_GATT_OK){
ESP_LOGE(GATTC_TAG, "search service failed, error status = %x", p_data->search_cmpl.status);
break;
case ESP_GATTC_SEARCH_CMPL_EVT:
if (p_data->search_cmpl.status != ESP_GATT_OK){
ESP_LOGE(GATTC_TAG, "search service failed, error status = %x", p_data->search_cmpl.status);
break;
}
conn_id = p_data->search_cmpl.conn_id;h
if (get_server){
uint16_t count = 0;
esp_gatt_status_t status = esp_ble_gattc_get_attr_count( gattc_if,
p_data->search_cmpl.conn_id,ESP_GATT_DB_CHARACTERISTIC, gl_profile_tab[PROFILE_A_APP_ID].service_start_handle, gl_profile_tab[PROFILE_A_APP_ID].service_end_handle,
INVALID_HANDLE,
&count);
if (status != ESP_GATT_OK){
ESP_LOGE(GATTC_TAG, "esp_ble_gattc_get_attr_count error");
}
conn_id = p_data->search_cmpl.conn_id;h
if (get_server){
uint16_t count = 0;
esp_gatt_status_t status = esp_ble_gattc_get_attr_count( gattc_if,
p_data->search_cmpl.conn_id,ESP_GATT_DB_CHARACTERISTIC, gl_profile_tab[PROFILE_A_APP_ID].service_start_handle, gl_profile_tab[PROFILE_A_APP_ID].service_end_handle,
INVALID_HANDLE,
&count);
if (status != ESP_GATT_OK){
ESP_LOGE(GATTC_TAG, "esp_ble_gattc_get_attr_count error");
}
if (count > 0){
char_elem_result = (esp_gattc_char_elem_t*)malloc
(sizeof(esp_gattc_char_elem_t) * count);
if (!char_elem_result){
ESP_LOGE(GATTC_TAG, "gattc no mem");
}else{
status = esp_ble_gattc_get_char_by_uuid( gattc_if,
p_data->search_cmpl.conn_id,
gl_profile_tab[PROFILE_A_APP_ID].service_start_handle,
gl_profile_tab[PROFILE_A_APP_ID].service_end_handle,
remote_filter_char_uuid,
char_elem_result,
&count);
if (status != ESP_GATT_OK){
ESP_LOGE(GATTC_TAG, "esp_ble_gattc_get_char_by_uuid error");
}
/* Every service have only one char in our 'ESP_GATTS_DEMO' demo,
so we used first 'char_elem_result' */
if (count > 0 && (char_elem_result[0].properties
&ESP_GATT_CHAR_PROP_BIT_NOTIFY)){
gl_profile_tab[PROFILE_A_APP_ID].char_handle =
char_elem_result[0].char_handle;
esp_ble_gattc_register_for_notify (gattc_if,
gl_profile_tab[PROFILE_A_APP_ID].remote_bda,
char_elem_result[0].char_handle);
}
}
/* free char_elem_result */
free(char_elem_result);
if (count > 0){
char_elem_result = (esp_gattc_char_elem_t*)malloc
(sizeof(esp_gattc_char_elem_t) * count);
if (!char_elem_result){
ESP_LOGE(GATTC_TAG, "gattc no mem");
}else{
ESP_LOGE(GATTC_TAG, "no char found");
} }
status = esp_ble_gattc_get_char_by_uuid( gattc_if,
p_data->search_cmpl.conn_id,
gl_profile_tab[PROFILE_A_APP_ID].service_start_handle,
gl_profile_tab[PROFILE_A_APP_ID].service_end_handle,
remote_filter_char_uuid,
char_elem_result,
&count);
if (status != ESP_GATT_OK){
ESP_LOGE(GATTC_TAG, "esp_ble_gattc_get_char_by_uuid error");
}
/* Every service have only one char in our 'ESP_GATTS_DEMO' demo,
so we used first 'char_elem_result' */
if (count > 0 && (char_elem_result[0].properties
&ESP_GATT_CHAR_PROP_BIT_NOTIFY)){
gl_profile_tab[PROFILE_A_APP_ID].char_handle =
char_elem_result[0].char_handle;
esp_ble_gattc_register_for_notify (gattc_if,
gl_profile_tab[PROFILE_A_APP_ID].remote_bda,
char_elem_result[0].char_handle);
}
}
/* free char_elem_result */
free(char_elem_result);
}else{
ESP_LOGE(GATTC_TAG, "no char found");
} }
break;
```
@ -625,14 +607,11 @@ The client can register to receive notifications from the server every time the
```c
/* Every service have only one char in our 'ESP_GATTS_DEMO' demo, so we used first 'char_elem_result' */
if(count > 0 && (char_elem_result[0].properties &
ESP_GATT_CHAR_PROP_BIT_NOTIFY)){
gl_profile_tab[PROFILE_A_APP_ID].char_handle =
char_elem_result[0].char_handle;
esp_ble_gattc_register_for_notify (gattc_if,
gl_profile_tab[PROFILE_A_APP_ID].remote_bda,
char_elem_result[0].char_handle);
}
if(count > 0 && (char_elem_result[0].properties & ESP_GATT_CHAR_PROP_BIT_NOTIFY)){
gl_profile_tab[PROFILE_A_APP_ID].char_handle = char_elem_result[0].char_handle;
esp_ble_gattc_register_for_notify (gattc_if, gl_profile_tab[PROFILE_A_APP_ID].remote_bda,
char_elem_result[0].char_handle);
}
```
@ -642,18 +621,15 @@ This procedure registers notifications to the BLE stack, and triggers an `ESP_GA
case ESP_GATTC_REG_FOR_NOTIFY_EVT: {
ESP_LOGI(GATTC_TAG, "ESP_GATTC_REG_FOR_NOTIFY_EVT");
if (p_data->reg_for_notify.status != ESP_GATT_OK){
ESP_LOGE(GATTC_TAG, "REG FOR NOTIFY failed: error status = %d",
p_data->reg_for_notify.status);
ESP_LOGE(GATTC_TAG, "REG FOR NOTIFY failed: error status = %d", p_data->reg_for_notify.status);
}else{
uint16_t count = 0;
uint16_t notify_en = 1;
esp_gatt_status_t ret_status = esp_ble_gattc_get_attr_count( gattc_if,
gl_profile_tab[PROFILE_A_APP_ID].conn_id,
ESP_GATT_DB_DESCRIPTOR,
gl_profile_tab[PROFILE_A_APP_ID].service_start_handle,
gl_profile_tab[PROFILE_A_APP_ID].service_end_handle,
gl_profile_tab[PROFILE_A_APP_ID].char_handle,
&count);
esp_gatt_status_t ret_status = esp_ble_gattc_get_attr_count( gattc_if, gl_profile_tab[PROFILE_A_APP_ID].conn_id,
ESP_GATT_DB_DESCRIPTOR,
gl_profile_tab[PROFILE_A_APP_ID].service_start_handle,
gl_profile_tab[PROFILE_A_APP_ID].service_end_handle,
gl_profile_tab[PROFILE_A_APP_ID].char_handle, &count);
if (ret_status != ESP_GATT_OK){
ESP_LOGE(GATTC_TAG, "esp_ble_gattc_get_attr_count error");
}
@ -662,27 +638,27 @@ This procedure registers notifications to the BLE stack, and triggers an `ESP_GA
if (!descr_elem_result){
ESP_LOGE(GATTC_TAG, "malloc error, gattc no mem");
}else{
ret_status = esp_ble_gattc_get_descr_by_char_handle( gattc_if,
gl_profile_tab[PROFILE_A_APP_ID].conn_id,
p_data->reg_for_notify.handle,
notify_descr_uuid,
descr_elem_result,
&count);
ret_status = esp_ble_gattc_get_descr_by_char_handle(
gattc_if,
gl_profile_tab[PROFILE_A_APP_ID].conn_id,
p_data->reg_for_notify.handle,
notify_descr_uuid,
descr_elem_result,&count);
if (ret_status != ESP_GATT_OK){
ESP_LOGE(GATTC_TAG, "esp_ble_gattc_get_descr_by_char_handle
error");
}
/* Erery char have only one descriptor in our 'ESP_GATTS_DEMO' demo, so we used first 'descr_elem_result' */
if (count > 0 && descr_elem_result[0].uuid.len == ESP_UUID_LEN_16 &&
descr_elem_result[0].uuid.uuid.uuid16 == ESP_GATT_UUID_CHAR_CLIENT_CONFIG){
ret_status = esp_ble_gattc_write_char_descr( gattc_if,
gl_profile_tab[PROFILE_A_APP_ID].conn_id,
descr_elem_result[0].handle,
sizeof(notify_en),
(Uint8 *)&notify_en,
ESP_GATT_WRITE_TYPE_RSP,
ESP_GATT_AUTH_REQ_NONE);
if (count > 0 && descr_elem_result[0].uuid.len == ESP_UUID_LEN_16 && descr_elem_result[0].uuid.uuid.uuid16 == ESP_GATT_UUID_CHAR_CLIENT_CONFIG){
ret_status = esp_ble_gattc_write_char_descr( gattc_if,
gl_profile_tab[PROFILE_A_APP_ID].conn_id,
descr_elem_result[0].handle,
sizeof(notify_en),
(Uint8 *)&notify_en,
ESP_GATT_WRITE_TYPE_RSP,
ESP_GATT_AUTH_REQ_NONE);
}
if (ret_status != ESP_GATT_OK){

View file

@ -1,9 +1,13 @@
ESP-IDF GATT SECURITY CLIENT DEMO
ESP-IDF Gatt Security Client Demo
========================
This is the demo for user to use ESP BLE security API to connection with peer device & communication.
1.Should used the esp_ble_gap_set_security_param API to set the security parameter to the BLE stack in the init stage;
2.Used the esp_ble_set_encryption API to start encryption with peer device, if the peer device take the initiative encryption, should used the esp_ble_gap_security_rsp API to sent response to peer device when receive the ESP_GAP_BLE_SEC_REQ_EVT.
3.It will receive the ESP_GAP_BLE_AUTH_CMPL_EVT event when encryption finish will peer device.
This is the demo for users to use ESP BLE security APIs to connect to and encrypt with peer devices.
Please check the [tutorial](tutorial/GATT_Security_Client_Example_Walkthrough.md) for more information about this example.
To test this demo, you can run the [gatt_security_server_demo](../gatt_security_server), which starts advertising and can be connected to this demo automatically.
There are some important points for this demo:
1.`esp_ble_gap_set_security_param` should be used to set the security parameters in the initial stage;
2.`esp_ble_set_encryption` should be used to start encryption with peer device. If the peer device initiates the encryption, `esp_ble_gap_security_rsp` should be used to send security response to the peer device when `ESP_GAP_BLE_SEC_REQ_EVT` is received.
3.The `gatt_security_client_demo` will receive a `ESP_GAP_BLE_AUTH_CMPL_EVT` once the encryption procedure has completed.
Please check the [tutorial](tutorial/Gatt_Security_Client_Example_Walkthrough.md) for more information about this example.

View file

@ -4,42 +4,42 @@
## Introduction
This documents presents a review of the GATT Security Client code example for the ESP32. The GATT Client is capable of scanning for nearby devices and once it has found a device of interest, it requests a secure connection. The GATT client behaves as a master device that initiates a connection to a slave by sending a *Pairing Request* as specified by the [Bluetooth Core Specification Version 4.2](https://www.bluetooth.com/specifications/bluetooth-core-specification). The remote slave device is normally a GATT Server that exposes Services and Characteristics. The slave replies with a *Pairing Response* followed by authentication and exchange of keys. If the bonding process is also executed, the Long Term Keys are stored for subsequent connections. Finally an encrypted channel is established which can support protection against Man-In-The-Middle (MITM) attacks depending on the security configuration. The code is implemented using an Application Profile that upon registration, allows to set the local privacy configuration as events are triggered during the lifetime of the program.
This document presents a review of the GATT Security Client code example for the ESP32. The GATT Client is capable of scanning for nearby devices and once it has found a device of interest, it requests a secure connection. The GATT client behaves as a master device that initiates a connection to a slave by sending a *Pairing Request* as specified by the [Bluetooth Core Specification Version 4.2](https://www.bluetooth.com/specifications/bluetooth-core-specification). The remote slave device is normally a GATT Server that exposes Services and Characteristics. The slave replies with a *Pairing Response* followed by authentication and exchange of keys. If the bonding process is also executed, the Long Term Keys are stored for subsequent connections. Finally an encrypted channel is established which can support protection against Man-In-The-Middle (MITM) attacks depending on the security configuration. The code is implemented using an Application Profile that upon registration, allows to set the local privacy configuration as events are triggered during the lifetime of the program.
This document only includes a description of the security aspects of the GATT Client implementation, for a review of how to define the functionality of the GATT client such as scanning parameters and opening connections please refer to **GATT Client Example Walkthrough**.
This document only includes a description of the security aspects of the GATT Client implementation, for a review of how to define the functionality of the GATT client such as scanning parameters and opening connections please refer to [GATT Client Example Walkthrough](../../gatt_client/tutorial/Gatt_Client_Example_Walkthrough.md).
## Configuring Local Privacy of the Security Client
The example registers one Application Profile defined as:
```
```c
#define PROFILE_NUM 1
#define PROFILE_A_APP_ID 0
```
The registration takes place in the ``app_main()`` function by using the ``esp_ble_gattc_app_register()`` function:
```
```c
ret = esp_ble_gattc_app_register(PROFILE_A_APP_ID);
if (ret){
ESP_LOGE(GATTC_TAG, "%s gattc app register error, error code = %x\n", __func__, ret);
}
if (ret){
ESP_LOGE(GATTC_TAG, "%s gattc app register error, error code = %x\n", __func__, ret);
}
```
The Application Profile registration triggers an ``ESP_GATTC_REG_EVT`` event which is managed by the ``esp_gattc_cb()`` callback function and forwarded to the Profile A event handler ``gattc_profile_event_handler()``. Here, the event is used to configure the local privacy of the slave device by using the ``esp_ble_gap_config_local_privacy()`` function.
```
```c
case ESP_GATTC_REG_EVT:
ESP_LOGI(GATTC_TAG, "REG_EVT");
esp_ble_gap_config_local_privacy(true);
break;
ESP_LOGI(GATTC_TAG, "REG_EVT");
esp_ble_gap_config_local_privacy(true);
break;
```
This function is a Bluedroid API call for configuring default privacy settings on the local device. Once the privacy is set, an ``ESP_GAP_BLE_SET_LOCAL_PRIVACY_COMPLETE_EVT`` is triggered which is used to set scan parameters and start scanning for nearby peripherals:
```
```c
case ESP_GAP_BLE_SET_LOCAL_PRIVACY_COMPLETE_EVT:
if (param->local_privacy_cmpl.status != ESP_BT_STATUS_SUCCESS){
ESP_LOGE(GATTC_TAG, "config local privacy failed, error code =%x", param->local_privacy_cmpl.status);
@ -70,22 +70,23 @@ This function is a Bluedroid API call for configuring default privacy settings o
The rest of the configuration for the GATT Client is performed normally in the same way as the regular GATT Client example. That is, the client finds a device of interest and opens a connection. At this point the GATT client, which is usually the master, initiates the pairing process by sending a Pairing Request to the slave device. This request should be acknowledged with a Pairing Response. The Pairing process is implemented automatically by the stack and no extra user configuration is needed. However, depending on the I/O capabilities of both devices, a passkey might be generated on the ESP32 which is presented to the user with the ``ESP_GAP_BLE_PASSKEY_NOTIF_EVT``:
```
case ESP_GAP_BLE_PASSKEY_NOTIF_EVT: ///the app will receive this evt when the IO has Output capability and the peer device IO has Input capability.
///show the passkey number to the user to input it in the peer deivce.
ESP_LOGE(GATTS_TABLE_TAG, "The passkey Notify number:%d", param->ble_security.key_notif.passkey);
break;
```c
case ESP_GAP_BLE_PASSKEY_NOTIF_EVT:
///the app will receive this evt when the IO has Output capability and the peer device IO has Input capability.
///show the passkey number to the user to input it in the peer deivce.
ESP_LOGE(GATTS_TABLE_TAG, "The passkey Notify number:%d", param->ble_security.key_notif.passkey);
break;
```
The combination of input and output capabilities that determine which algorithm is used are:
| Display Only | Display Yes/No|Keyboard Only|No Input No Output|Keyboard Display|
--------------------|------------------|-----------------------|--------------------|------------------|-----------------------|
Display Only | Just Works| Just Works|Passkey Entry|Just Works|Passkey Entry|
Display Yes/No |Just Works|Just Works|Passkey Entry|Just Works|Passkey Entry|
Keyboard Only |Passkey Entry |Passkey Entry|Passkey Entry|Just Works|Passkey Entry|
No Input No Output| Just Works|Just Works|Just Works|Just Works|Just Works|
Keyboard Display|Passkey Entry|Passkey Entry|Passkey Entry|Just Works|Passkey Entry|
| | Display Only | Display Yes/No | Keyboard Only | No Input No Output | Keyboard Display|
| :-- | :------------- | :------------- | :------------- | :----------------- | :-------------- |
| **Display Only** | Just Works | Just Works | Passkey Entry | Just Works | Passkey Entry |
| **Display Yes/No** | Just Works | Just Works | Passkey Entry | Just Works | Passkey Entry |
| **Keyboard Only** | Passkey Entry | Passkey Entry | Passkey Entry | Just Works | Passkey Entry |
| **No Input No Output** | Just Works | Just Works | Just Works | Just Works | Just Works |
| **Keyboard Display** | Passkey Entry | Passkey Entry | Passkey Entry | Just Works | Passkey Entry |
In the Just Works method, the Temporary Key is set to 0. This is a practical way to authenticate devices when no display or keyboards are attached to them, so that there is no way to show or enter a passkey. However, if the ESP32 GATT Client has an LCD, it can present the passkey generated locally so that the user can input it on the other peer device, or if the GATT Client has a keyboard, it can input the passkey generated by the other peer device. Additionally, a numeric comparison can be performed if both devices have a display and yes/no confirm buttons and LE Secure Connections are used, that way an independently generated passkey is displayed on both devices and the user manually checks that both 6-digit confirmation values match.
@ -93,27 +94,26 @@ In the Just Works method, the Temporary Key is set to 0. This is a practical way
When the client connects to a remote device and the pairing is done successfully, the initiator and responder keys are exchanged. For each key exchange message, an ``ESP_GAP_BLE_KEY_EVT`` event is triggered which can be used to print the type of key received:
```
```c
case ESP_GAP_BLE_KEY_EVT:
//shows the ble key info share with peer device to the user.
ESP_LOGI(GATTS_TABLE_TAG, "key type = %s", esp_key_type_to_str(param->ble_security.ble_key.key_type));
break;
//shows the ble key info share with peer device to the user.
ESP_LOGI(GATTS_TABLE_TAG, "key type = %s", esp_key_type_to_str(param->ble_security.ble_key.key_type));
break;
```
When the keys are exchanged successfully, the pairing process is completed and encryption of payload data can be started using the AES-128 engine. This triggers an ``ESP_GAP_BLE_AUTH_CMPL_EVT`` event which is used to print information:
```
```c
case ESP_GAP_BLE_AUTH_CMPL_EVT: {
esp_bd_addr_t bd_addr;
memcpy(bd_addr, param->ble_security.auth_cmpl.bd_addr, sizeof(esp_bd_addr_t));
ESP_LOGI(GATTS_TABLE_TAG, "remote BD_ADDR: %08x%04x",\
(bd_addr[0] << 24) + (bd_addr[1] << 16) + (bd_addr[2] << 8) + bd_addr[3],
(bd_addr[4] << 8) + bd_addr[5]);
ESP_LOGI(GATTS_TABLE_TAG, "address type = %d", param->ble_security.auth_cmpl.addr_type);
ESP_LOGI(GATTS_TABLE_TAG, "pair status = %s",param->ble_security.auth_cmpl.success ? "success" : "fail");
break;
esp_bd_addr_t bd_addr;
memcpy(bd_addr, param->ble_security.auth_cmpl.bd_addr, sizeof(esp_bd_addr_t));
ESP_LOGI(GATTS_TABLE_TAG, "remote BD_ADDR: %08x%04x",\
(bd_addr[0] << 24) + (bd_addr[1] << 16) + (bd_addr[2] << 8) + bd_addr[3], (bd_addr[4] << 8) + bd_addr[5]);
ESP_LOGI(GATTS_TABLE_TAG, "address type = %d", param->ble_security.auth_cmpl.addr_type);
ESP_LOGI(GATTS_TABLE_TAG, "pair status = %s",param->ble_security.auth_cmpl.success ? "success" : "fail");
break;
```
## Conclusion
In this document, a review of the security aspects of the GATT Client has been realized. BLE security encompasses Pairing, Bonding and Encryption. In order to establish a secure link between a master and a slave device, the local privacy of the GATT client is set, which allows the BLE stack to set necessary security parameters automatically without the need of additional user configuration. The combination of features and capabilities of the peer devices results in the selection of the appropriate pairing method which the BLE stack then executes. Immediately, the required keys are generated and exchanged and the encryption of subsequent messages is started using the AES-128 engine. These steps trigger different events that are managed by the GATT and GAP event handlers which can be used to print useful information such as the types of keys exchanged and the pairing status. The rest of the security GATT client functionality such as registering for notifications of characteristics is implemented in the same way as in **GATT Client Example Walkthrough**.
In this document, a review of the security aspects of the GATT Client has been realized. BLE security encompasses Pairing, Bonding and Encryption. In order to establish a secure link between a master and a slave device, the local privacy of the GATT client is set, which allows the BLE stack to set necessary security parameters automatically without the need of additional user configuration. The combination of features and capabilities of the peer devices results in the selection of the appropriate pairing method which the BLE stack then executes. Immediately, the required keys are generated and exchanged and the encryption of subsequent messages is started using the AES-128 engine. These steps trigger different events that are managed by the GATT and GAP event handlers which can be used to print useful information such as the types of keys exchanged and the pairing status. The rest of the security GATT client functionality such as registering for notifications of characteristics is implemented in the same way as in [GATT Client Example Walkthrough](../../gatt_client/tutorial/Gatt_Client_Example_Walkthrough.md).

View file

@ -1,10 +1,14 @@
ESP-IDF GATT SECURITY SERVICE demo
ESP-IDF Gatt Security Server Demo
========================
This is the demo for user to use ESP BLE security API to connection with peer device & communication.
1.Should used the esp_ble_gap_set_security_param API to set the security parameter to the BLE stack in the init stage;
2.Used the esp_ble_set_encryption API to start encryption with peer device, if the peer device take the initiative encryption, should used the esp_ble_gap_security_rsp API to sent response to peer device when receive the ESP_GAP_BLE_SEC_REQ_EVT.
3.It will receive the ESP_GAP_BLE_AUTH_CMPL_EVT event when encryption finish will peer device.
This is the demo of APIs to connect to and encrypt with peer devices.
Please check the [tutorial](tutorial/GATT_SECURITY_SERVER_EXAMPLE_WALKTHROUGH.md) for more information about this example.
To test this demo, you can run [gatt_security_client_demo](../gatt_security_client), which starts scanning, connects to and starts encryption with `gatt_security_server_demo` automatically.
There are some important points for this demo:
1.`esp_ble_gap_set_security_param` should be used to set the security parameters in the initial stage;
2.`esp_ble_set_encryption` should be used to start encryption with peer device. If the peer device initiates the encryption, `esp_ble_gap_security_rsp` should be used to send security response to the peer device when `ESP_GAP_BLE_SEC_REQ_EVT` is received.
3.The `gatt_security_client_demo` will receive a `ESP_GAP_BLE_AUTH_CMPL_EVT` once the encryption procedure has completed.
Please check the [tutorial](tutorial/Gatt_Security_Server_Example_Walkthrough.md) for more information about this example.

View file

@ -2,7 +2,7 @@
## Introduction
In this document, a description of the security GATT Server BLE example for the ESP32 is presented. The security configuration enables a GATT Server acting as a slave device to bond with a master and establish an encrypted link between them. This functionality is defined by the [Bluetooth Specification version 4.2](https://www.bluetooth.com/specifications/bluetooth-core-specification) and implemented on the ESP-IDF BLE stack, specifically on the Security Manager Protocol (SMP) API.
In this document, a description of the security GATT Server BLE example for the ESP32 is presented. The security configuration enables a GATT Server acting as a slave device to bond with a master and establish an encrypted link between them. This functionality is defined by the [Bluetooth Specification version 4.2](https://www.bluetooth.com/specifications/bluetooth-core-specification) and implemented on the ESP-IDF BLE stack, specifically on the Security Manager Protocol (SMP) API.
BLE security involves three interrelated concepts: pairing, bonding and encryption. Pairing concerns with the exchange of security features and types of keys needed. In addition, the pairing procedure takes care of the generation and exchange of shared keys. The core specification defines the legacy pairing and Secure Connections pairing (introduced in Bluetooth 4.2), which are both supported by ESP32. Once the exchange of shared keys is completed, a temporary encrypted link is established to exchange short term and long term keys. Bonding refers to storing the exchanged keys for subsequent connections so that they do not have to be transmitted again. Finally, encryption pertains to the ciphering of plain text data using the AES-128 engine and the shared keys. Server attributes may also be defined to allow only encrypted write and read messages. At any point of the communication, a slave device can always ask to start encryption by issuing a security request to the other peer device, which returns a security response by calling an API.
@ -30,18 +30,17 @@ In code, these parameters are defined as follows:
The possible values for *IO Capabilities* are:
```c
ESP_IO_CAP_OUT 0 /*!< DisplayOnly */
ESP_IO_CAP_IO 1 /*!< DisplayYesNo */
ESP_IO_CAP_IN 2 /*!< KeyboardOnly */
ESP_IO_CAP_NONE 3 /*!< NoInputNoOutput */
ESP_IO_CAP_KBDISP 4 /*!< Keyboard display */
ESP_IO_CAP_OUT 0 /*!< DisplayOnly */
ESP_IO_CAP_IO 1 /*!< DisplayYesNo */
ESP_IO_CAP_IN 2 /*!< KeyboardOnly */
ESP_IO_CAP_NONE 3 /*!< NoInputNoOutput */
ESP_IO_CAP_KBDISP 4 /*!< Keyboard display */
```
* *Authorization Request*:
```c
esp_ble_auth_req_t auth_req = ESP_LE_AUTH_BOND;
//bonding with peer device after authentication
esp_ble_auth_req_t auth_req = ESP_LE_AUTH_BOND; //bonding with peer device after authentication
```
The possible values for *Authorization Request* are a combination of Bonding, MITM protection and Secure Connections requests:
@ -74,7 +73,7 @@ In code, these parameters are defined as follows:
```c
uint8_t rsp_key = ESP_BLE_ENC_KEY_MASK | ESP_BLE_ID_KEY_MASK;
```
```
The responder distributes the LTK and IRK keys by the setting the *EncKey* and *IdKey* masks.
@ -88,7 +87,7 @@ In code, these parameters are defined as follows:
esp_ble_gap_set_security_param(ESP_BLE_SM_SET_RSP_KEY, &rsp_key, sizeof(uint8_t));
```
This information is enough for the BLE stack to perform the pairing process, including pairing confirmation and key generation. The procedure is invisible to the user and executed automatically by the stack.
This information is enough for the BLE stack to perform the pairing process, including pairing confirmation and key generation. The procedure is invisible to the user and executed automatically by the stack.
## Connecting and Bonding to a Peer Device
@ -109,7 +108,7 @@ The types of encryptions available are:
The difference between `ESP_BLE_SEC_ENCRYPT` and `ESP_BLE_SEC_ENCRYPT_NO_MITM` lies in the fact that a previous connection might have a security level that needs to be upgraded, therefore requires to exchange keys again.
In this example, the I/O capabilities are set to *No Input No Output*, therefore the *Just Works* pairing method, which doesn't not require the generation of a random 6-digit passkey, is used(For details, please refer to the the table below.) The user may modify the example to set the I/O capabilities to use other than *No Input No Output*. Therefore, depending on the I/O capabilities of the remote device, a passkey might be generated on the ESP32 which is presented to the user with the `ESP_GAP_BLE_PASSKEY_NOTIF_EVT`:
In this example, the I/O capabilities are set to *No Input No Output*, therefore the *Just Works* pairing method, which doesn't not require the generation of a random 6-digit passkey, is used (For details, please refer to the the table below). The user may modify the example to set the I/O capabilities to use other than *No Input No Output*. Therefore, depending on the I/O capabilities of the remote device, a passkey might be generated on the ESP32 which is presented to the user with the `ESP_GAP_BLE_PASSKEY_NOTIF_EVT`:
```c
case ESP_GAP_BLE_PASSKEY_NOTIF_EVT:
@ -121,6 +120,14 @@ case ESP_GAP_BLE_PASSKEY_NOTIF_EVT:
The combination of input and output capabilities that determine which algorithm is used are:
| | Display Only | Display Yes/No | Keyboard Only | No Input No Output | Keyboard Display|
| :-- | :------------- | :------------- | :------------- | :----------------- | :-------------- |
| **Display Only** | Just Works | Just Works | Passkey Entry | Just Works | Passkey Entry |
| **Display Yes/No** | Just Works | Just Works | Passkey Entry | Just Works | Passkey Entry |
| **Keyboard Only** | Passkey Entry | Passkey Entry | Passkey Entry | Just Works | Passkey Entry |
| **No Input No Output** | Just Works | Just Works | Just Works | Just Works | Just Works |
| **Keyboard Display** | Passkey Entry | Passkey Entry | Passkey Entry | Just Works | Passkey Entry |
## Exchanging Keys
@ -128,7 +135,7 @@ When the client connects to the server and the pairing is done successfully, the
* Local device LTK
* Local device IRK
* Local device CSRK
* Local device CSRK
* Peer device LTK
* Peer device IRK
@ -192,8 +199,8 @@ When creating the services table, each attribute can have permissions to read or
[HRS_IDX_BOBY_SENSOR_LOC_VAL] = {
{ESP_GATT_AUTO_RSP},
{ESP_UUID_LEN_16,
(uint8_t *)&body_sensor_location_uuid,
ESP_GATT_PERM_READ_ENCRYPTED,
(uint8_t *)&body_sensor_location_uuid,
ESP_GATT_PERM_READ_ENCRYPTED,
sizeof(uint8_t),
sizeof(body_sensor_loc_val),
(uint8_t *)body_sensor_loc_val}
@ -217,7 +224,7 @@ When creating the services table, each attribute can have permissions to read or
During the communication between a master and a slave device, the slave might request to start encryption at any moment by issuing a security request command. This command will trigger an `ESP_GAP_BLE_SEC_REQ_EVT` event on the master, which will reply a positive (true) security response to the peer device to accept the request or a negative (false) one to reject the request. In this example, this event is used to reply a start encryption response by using the `esp_ble_gap_security_rsp()` API call.
```c
case ESP_GAP_BLE_SEC_REQ_EVT:
case ESP_GAP_BLE_SEC_REQ_EVT:
/* send the positive (true) security response to the peer device to accept the security request.
If not accept the security request, should sent the security response with negative(false) accept value*/
esp_ble_gap_security_rsp(param->ble_security.ble_req.bd_addr, true);
@ -225,5 +232,4 @@ During the communication between a master and a slave device, the slave might re
```
## Conclusion
In this document, a review of the security aspects of the GATT Server has been realized. BLE security encompasses Pairing, Bonding and Encryption. In order to establish a secured link between a master and a slave device, security parameters that define the capabilities and features each device possess are set. The combination of features and capabilities of the peer devices results in the selection of the appropriate pairing method which the BLE stack then executes. Immediately, the required keys are generated and exchanged, and the encryption of subsequent messages is started using the AES-128 engine and these keys. These steps trigger different events that are managed by the GATT and GAP event handlers which can be used to print useful information such as the types of keys exchanged and the pairing status. In addition, attribute permissions are appointed to allow only encrypted read and write events when needed. The rest of the Security GATT server functionalities such as defining services and characteristics are implemented in the same way as presented in the GATT Server example.
In this document, a review of the security aspects of the GATT Server has been realized. BLE security encompasses Pairing, Bonding and Encryption. In order to establish a secured link between a master and a slave device, security parameters that define the capabilities and features each device possess are set. The combination of features and capabilities of the peer devices results in the selection of the appropriate pairing method which the BLE stack then executes. Immediately, the required keys are generated and exchanged, and the encryption of subsequent messages is started using the AES-128 engine and these keys. These steps trigger different events that are managed by the GATT and GAP event handlers which can be used to print useful information such as the types of keys exchanged and the pairing status. In addition, attribute permissions are appointed to allow only encrypted read and write events when needed. The rest of the Security GATT server functionalities such as defining services and characteristics are implemented in the same way as presented in the GATT Server example.

View file

@ -1,10 +1,13 @@
ESP-IDF GATT SERVER demo
ESP-IDF Gatt Server Demo
========================
This is the demo for user to use ESP_APIs to create a GATT Server.The demo can send adv data,
be connected by client. Run the gatt_client demo, the client demo will automatically connect
to the gatt_server demo. The client demo will enable gatt_server's notify after connection.
Then the two devices will exchange data.
This is the demo of APIs to create a GATT service by adding attributes one by one. However, this method is defined by Bluedroid and is difficult for users to use.
Please check the [tutorial] (./tutorial/GATT_SERVER_EXAMPLE_WALKTHROUGH.md) for more information about this example.
Hence, we also allow users to create a GATT service with an attribute table, which releases the user from adding attributes one by one. And it is recommended for users to use. For more information about this method, please refer to [gatt_server_service_table_demo](../gatt_server_service_table).
This demo creates GATT a service and then starts advertising, waiting to be connected to a GATT client.
To test this demo, we can run the [gatt_client_demo](../gatt_client), which can scan for and connect to this demo automatically. They will start exchanging data once the GATT client has enabled the notification function of the GATT server.
Please check the [tutorial](tutorial/Gatt_Server_Example_Walkthrough.md) for more information about this example.

View file

@ -57,7 +57,7 @@ The entry point to this example is the app_main() function:
return;
}
ret = esp_bt_controller_enable(ESP_BT_MODE_BTDM);
ret = esp_bt_controller_enable(ESP_BT_MODE_BLE);
if (ret) {
ESP_LOGE(GATTS_TAG, "%s enable controller failed\n", __func__);
return;
@ -98,7 +98,7 @@ The entry point to this example is the app_main() function:
ESP_LOGE(GATTS_TAG, "set local MTU failed, error code = %x", local_mtu_ret);
}
return;
}
}
```
The main function starts by initializing the non-volatile storage library. This library allows to save key-value pairs in flash memory and is used by some components such as the Wi-Fi library to save the SSID and password:
@ -113,11 +113,13 @@ The main function also initializes the BT controller by first creating a BT cont
esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT();
ret = esp_bt_controller_init(&bt_cfg);
```
Next, the controller is enabled in Dual Mode (BLE + BT Classic).
Next, the controller is enabled in BLE Mode.
```c
ret = esp_bt_controller_enable(ESP_BT_MODE_BTDM);
ret = esp_bt_controller_enable(ESP_BT_MODE_BLE);
```
>The controller should be enabled in `ESP_BT_MODE_BTDM`, if you want to use the dual mode (BLE + BT).
There are four Bluetooth modes supported:
@ -142,10 +144,10 @@ The functions `gatts_event_handler()` and `gap_event_handler()` handle all the e
## APPLICATION PROFILES
The GATT Server example application is organized by using Application Profiles as shown in Fig 1. Each Application Profile describes a way to group functionalities that are designed for one client application, such as a mobile app running on a smartphone or tablet. In this way, a single design, enabled by different Application Profiles, can behave differently when used by different smartphone apps, allowing the server to react differently according to the client app that is being used. In practice, each profile is seen by the client as an independent BLE service. It is up to the client to discriminate the services that it is interested in.
The GATT Server example application is organized by using Application Profiles as shown in the figure below. Each Application Profile describes a way to group functionalities that are designed for one client application, such as a mobile app running on a smartphone or tablet. In this way, a single design, enabled by different Application Profiles, can behave differently when used by different smartphone apps, allowing the server to react differently according to the client app that is being used. In practice, each profile is seen by the client as an independent BLE service. It is up to the client to discriminate the services that it is interested in.
<div align="center"><img src="image/GATT_Server_Figure_1.png" width = "400" alt="Application Profiles are used to organize a BLE application in order to implement different functionality for different clients." align=center /> </div>
![](image/GATT SERVER FIGURE 1.png)
<p align="center">Fig.1. Application Profiles are used to organize a BLE application in order to implement different functionality for different clients.</p>
Each profile is defined as a struct where the struct members depend on the services and characteristics that are implemented in that Application Profile. The members also include a GATT interface, Application ID, Connection ID and a callback function to handle profile events. In this example, each profile is composed by:
@ -194,7 +196,7 @@ static struct gatts_profile_inst gl_profile_tab[PROFILE_NUM] = {
},
};
```
Finally, the Application Profiles are registered using the Application ID, which is an user-assigned number to identify each profile. In this way, multiple Application Profiles can run in one server.
Finally, the Application Profiles are registered using the Application ID, which is an user-assigned number to identify each profile. In this way, multiple Application Profiles can run in one server.
```c
esp_ble_gatts_app_register(PROFILE_A_APP_ID);
@ -291,13 +293,13 @@ static void gatts_profile_a_event_handler(esp_gatts_cb_event_t event, esp_gatt_i
```
## GAP Event Handler
Once the advertising data have been set, the GAP event `ESP_GAP_BLE_ADV_DATA_SET_COMPLETE_EVT` is triggered. For the case of raw advertising data set, the event triggered is `ESP_GAP_BLE_ADV_DATA_RAW_SET_COMPLETE_EVT`. Additionally when the raw scan response data is set, `ESP_GAP_BLE_SCAN_RSP_DATA_RAW_SET_COMPLETE_EVT` event is triggered.
Once the advertising data have been set, the GAP event `ESP_GAP_BLE_ADV_DATA_SET_COMPLETE_EVT` is triggered. For the case of raw advertising data set, the event triggered is `ESP_GAP_BLE_ADV_DATA_RAW_SET_COMPLETE_EVT`. Additionally when the raw scan response data is set, `ESP_GAP_BLE_SCAN_RSP_DATA_RAW_SET_COMPLETE_EVT` event is triggered.
```c
static void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param)
{
switch (event) {
#ifdef CONFIG_SET_RAW_ADV_DATA
#ifdef CONFIG_SET_RAW_ADV_DATA
case ESP_GAP_BLE_ADV_DATA_RAW_SET_COMPLETE_EVT:
adv_config_done &= (~adv_config_flag);
if (adv_config_done==0){
@ -331,18 +333,17 @@ In any case, the server can start advertising using the `esp_ble_gap_start_adver
```c
/// Advertising parameters
typedef struct {
uint16_t adv_int_min;
uint16_t adv_int_min;
/*!< Minimum advertising interval for undirected and low duty cycle directed advertising.
Range: 0x0020 to 0x4000
Default: N = 0x0800 (1.28 second)
Time = N * 0.625 msec
Time Range: 20 ms to 10.24 sec */
uint16_t adv_int_max;
/*!< Maximum advertising interval for undirected and low duty
cycle directed advertising.
Range: 0x0020 to 0x4000
uint16_t adv_int_max;
/*!< Maximum advertising interval for undirected and low duty cycle directed advertising.
Range: 0x0020 to 0x4000
Default: N = 0x0800 (1.28 second)
Time = N * 0.625 msec
Time = N * 0.625 msec
Time Range: 20 ms to 10.24 sec */
esp_ble_adv_type_t adv_type; /*!< Advertising type */
esp_ble_addr_type_t own_addr_type; /*!< Owner bluetooth device address type */
@ -350,7 +351,7 @@ typedef struct {
esp_ble_addr_type_t peer_addr_type; /*!< Peer device bluetooth device address type */
esp_ble_adv_channel_t channel_map; /*!< Advertising channel map */
esp_ble_adv_filter_t adv_filter_policy; /*!< Advertising filter policy */
}
}
esp_ble_adv_params_t;
```
@ -390,11 +391,13 @@ If the advertising started successfully, an `ESP_GAP_BLE_ADV_START_COMPLETE_EVT`
When an Application Profile is registered, an `ESP_GATTS_REG_EVT` event is triggered. The parameters of the `ESP_GATTS_REG_EVT` are:
* `esp_gatt_status_t status; /*!< Operation status */`
* `uint16_t app_id;       /*!< Application id which input in register API */`
```c
esp_gatt_status_t status; /*!< Operation status */`
uint16_t app_id; /*!< Application id which input in register API */`
```
In addition to the previous parameters, the event also contains the GATT interface assigned by the BLE stack. The event is captured by the `gatts_event_handler()`, which used to store the generated interface in the profile table, and then the event is forwarded to the corresponding profile event handler.
```c
static void gatts_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param)
{
@ -424,7 +427,7 @@ static void gatts_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_
} while (0);
}
```
## Creating Services
The register event is also used to create a profile attributes table by using the `esp_ble_gatts_create_attr_tab()` function, which takes an `esp_gatts_attr_db_t` type structure that has all the information of the service table. The way to create this table is:
@ -448,7 +451,7 @@ The handles are:
3. Characteristic value handle
4. Characteristic descriptor handle
The service is defined as a primary service with a UUID length of 16 bits. The service ID is initialized with instance ID = 0 and UUID defined by `GATTS_SERVICE_UUID_TEST_A`.
The service is defined as a primary service with a UUID length of 16 bits. The service ID is initialized with instance ID = 0 and UUID defined by `GATTS_SERVICE_UUID_TEST_A`.
The service instance ID can be used to differentiate multiple services with the same UUID. In this example, since there is only one service for each Application Profiles and the services have different UUIDs, the service instance ID can be defined as 0 in both profile A and B. However if there was only one Application Profile with two services using the same UUID, then it would be necessary to use different instance IDs to refer to one service or the other.
@ -480,7 +483,7 @@ case ESP_GATTS_CREATE_EVT:
gl_profile_tab[PROFILE_A_APP_ID].service_handle = param->create.service_handle;
gl_profile_tab[PROFILE_A_APP_ID].char_uuid.len = ESP_UUID_LEN_16;
gl_profile_tab[PROFILE_A_APP_ID].char_uuid.uuid.uuid16 = GATTS_CHAR_UUID_TEST_A;
esp_ble_gatts_start_service(gl_profile_tab[PROFILE_A_APP_ID].service_handle);
a_property = ESP_GATT_CHAR_PROP_BIT_READ | ESP_GATT_CHAR_PROP_BIT_WRITE | ESP_GATT_CHAR_PROP_BIT_NOTIFY;
esp_err_t add_char_ret =
@ -541,10 +544,12 @@ Finally, the characteristic is configured in a way that it is required to send a
Adding a characteristic to a service triggers an `ESP_GATTS_ADD_CHAR_EVT` event, which returns the handle generated by the stack for the characteristic just added. The event includes the following parameters:
* `esp_gatt_status_t status;     /*!< Operation status */`
* `uint16_t attr_handle;       /*!< Characteristic attribute handle */`
* `uint16_t service_handle;     /*!< Service attribute handle */`
* `esp_bt_uuid_t char_uuid;     /*!< Characteristic uuid */`
```c
esp_gatt_status_t status; /*!< Operation status */
uint16_t attr_handle; /*!< Characteristic attribute handle */
uint16_t service_handle; /*!< Service attribute handle */
esp_bt_uuid_t char_uuid; /*!< Characteristic uuid */
```
The attribute handle returned by the event is stored in the profile table and the characteristic descriptor length and UUID are set as well. The characteristic length and value are read using the `esp_ble_gatts_get_attr_value()` function, and then printed for information purposes. Finally, the characteristic description is added using the `esp_ble_gatts_add_char_descr()` function. The parameters used are the service handle, the descriptor UUID, write and read permissions, an initial value and the auto response setting. The initial value for the characteristic descriptor can be a NULL pointer and the auto response parameter is set to NULL as well, which means that requests that require responses have to be replied manually.
@ -655,14 +660,16 @@ The `esp_ble_gap_update_conn_params()` function triggers a GAP event `ESP_GAP_BL
## Managing Read Events
Now that the services and characteristics are created and started, the program can receive read and write events. The read operations are represented by the `ESP_GATTS_READ_EVT` event, which has the following parameters:
* `uint16_t conn_id;     /*!< Connection id */`
* `uint32_t trans_id;     /*!< Transfer id */`
* `esp_bd_addr_t bda;     /*!< The bluetooth device address which been read */`
* `uint16_t handle;      /*!< The attribute handle */`
* `uint16_t offset;      /*!< Offset of the value, if the value is too long */`
* `bool is_long;        /*!< The value is too long or not */`
* `bool need_rsp;       /*!< The read operation need to do response */`
```c
uint16_t conn_id; /*!< Connection id */
uint32_t trans_id; /*!< Transfer id */
esp_bd_addr_t bda; /*!< The bluetooth device address which been read */
uint16_t handle; /*!< The attribute handle */
uint16_t offset; /*!< Offset of the value, if the value is too long */
bool is_long; /*!< The value is too long or not */
bool need_rsp; /*!< The read operation need to do response */
```
In this example, a response is constructed with dummy data and sent back to the host using the same handle given by the event. In addition to the response, the GATT interface, the connection ID and the transfer ID are also included as parameters in the `esp_ble_gatts_send_response()` function. This function is necessary if the auto response byte is set to NULL when creating the characteristic or descriptor.
@ -690,17 +697,19 @@ case ESP_GATTS_READ_EVT: {
The write events are represented by the `ESP_GATTS_WRITE_EVT` event, which has the following parameters:
* `uint16_t conn_id;     /*!< Connection id */`
* `uint32_t trans_id;    /*!< Transfer id */`
* `esp_bd_addr_t bda;    /*!< The bluetooth device address which been written */`
* `uint16_t handle;     /*!< The attribute handle */`
* `uint16_t offset;     /*!< Offset of the value, if the value is too long */`
* `bool need_rsp;      /*!< The write operation need to do response */`
* `bool is_prep;       /*!< This write operation is prepare write */`
* `uint16_t len;       /*!< The write attribute value length */`
* `uint8_t *value;      /*!< The write attribute value */`
```c
uint16_t conn_id; /*!< Connection id */
uint32_t trans_id; /*!< Transfer id */
esp_bd_addr_t bda; /*!< The bluetooth device address which been written */
uint16_t handle; /*!< The attribute handle */
uint16_t offset; /*!< Offset of the value, if the value is too long */
bool need_rsp; /*!< The write operation need to do response */
bool is_prep; /*!< This write operation is prepare write */
uint16_t len; /*!< The write attribute value length */
uint8_t *value; /*!< The write attribute value */
```
There are two types of write events implemented in this example, write characteristic value and write long characteristic value. The first type of write is used when the characteristic value can fit in one Attribute Protocol Maximum Transmission Unit (ATT MTU), which is usually 23 bytes long. The second type is used when the attribute to write is longer than what can be sent in one single ATT message by dividing the data into multiple chunks using Prepare Write Responses, after which an Executive Write Request is used to confirm or cancel the complete write request. This behavior is defined in the [Bluetooth Specification Version 4.2](https://www.bluetooth.com/specifications/bluetooth-core-specification), Vol 3, Part G, section 4.9. The write long characteristic message flow is shown in Fig. 2.
There are two types of write events implemented in this example, write characteristic value and write long characteristic value. The first type of write is used when the characteristic value can fit in one Attribute Protocol Maximum Transmission Unit (ATT MTU), which is usually 23 bytes long. The second type is used when the attribute to write is longer than what can be sent in one single ATT message by dividing the data into multiple chunks using Prepare Write Responses, after which an Executive Write Request is used to confirm or cancel the complete write request. This behavior is defined in the [Bluetooth Specification Version 4.2](https://www.bluetooth.com/specifications/bluetooth-core-specification), Vol 3, Part G, section 4.9. The write long characteristic message flow is shown in the figure below.
When a write event is triggered, this example prints logging messages, and then executes the `example_write_event_env()` function.
@ -748,13 +757,12 @@ case ESP_GATTS_WRITE_EVT: {
}
}
}
example_write_event_env(gatts_if, &a_prepare_write_env, param);
example_write_event_env(gatts_if, &a_prepare_write_env, param);
break;
}
```
![](image/GATT SERVER FIGURE 2.jpg)
<p align="center">Fig.2. Message flow for Write Long Characteristic</p>
<div align="center"> <img src="image/GATT_Server_Figure_2.png" width = "650" alt="Message flow for Write Long Characteristic" align=center /> </div>
The `example_write_event_env()` function contains the logic for the write long characteristic procedure:
@ -773,7 +781,7 @@ void example_write_event_env(esp_gatt_if_t gatts_if, prepare_type_env_t *prepare
} else {
if(param->write.offset > PREPARE_BUF_MAX_SIZE) {
status = ESP_GATT_INVALID_OFFSET;
}
}
else if ((param->write.offset + param->write.len) > PREPARE_BUF_MAX_SIZE) {
status = ESP_GATT_INVALID_ATTR_LEN;
}
@ -803,7 +811,7 @@ void example_write_event_env(esp_gatt_if_t gatts_if, prepare_type_env_t *prepare
esp_ble_gatts_send_response(gatts_if, param->write.conn_id, param->write.trans_id, status, NULL);
}
}
}
}
```
When the client sends a Write Request or a Prepare Write Request, the server shall respond. However, if the client sends a Write Without Response command, the server does not need to reply back a response. This is checked in the write procedure by examining the value of the `write.need_rsp parameter`. If a response is needed, the procedure continues doing the response preparation, if not present, the client does not need a response and therefore the procedure is ended.
@ -859,7 +867,7 @@ If the buffer is not NULL, which means initialization completed, the procedure t
else {
if(param->write.offset > PREPARE_BUF_MAX_SIZE) {
status = ESP_GATT_INVALID_OFFSET;
}
}
else if ((param->write.offset + param->write.len) > PREPARE_BUF_MAX_SIZE) {
status = ESP_GATT_INVALID_ATTR_LEN;
}
@ -916,7 +924,7 @@ void example_exec_write_event_env(prepare_type_env_t *prepare_write_env, esp_ble
free(prepare_write_env->prepare_buf);
prepare_write_env->prepare_buf = NULL;
}
prepare_write_env->prepare_len = 0;
#### prepare_write_env->prepare_len = 0;
}
```
@ -944,5 +952,3 @@ prepare_write_env->prepare_len = 0;
## Conclusion
In this document, we have gone through the GATT SERVER example code describing each section. The application is designed around the concept of Application Profiles. In addition, the procedures that this example uses to register event handlers are explained. The events follow a sequence of configuration steps, such as defining advertising parameters, updating connection parameters and creating services and characteristics. Finally, the way in which read and write events are handled, including Write Long characteristics by dividing the write into chunks that can fit the Attribute Protocol message is explained.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 146 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 145 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 251 KiB

View file

@ -1,11 +1,6 @@
ESP-IDF GATT SERVER create attribute table demo
ESP-IDF Gatt Server Service Table Demo
===============================================
This is the demo for user to use ESP_APIs to create a GATT Server attribute table.
The table is easy to use to create GATT server service database without use each "attribute create" functions.
Actually, there are two way to create server service and characteristics.
One is use the esp_gatts_create_service or esp_ble_gatts_add_char and etc.
The other way is use esp_ble_gatts_create_attr_tab.
The important things: the two ways cannot use in the same service, but can use in different service.
This demo shows how to create a GATT service with an attribute table defined in one place. Provided API releases the user from adding attributes one by one as implemented in BLUEDROID. A demo of the other method to create the attribute table is presented in [gatt_server_demo](../gatt_server).
Please check the [tutorial](tutorial/GATT_Server_Service_Table_Example_Walkthrough.md) for more information about this example.
Please check the [tutorial](tutorial/Gatt_Server_Service_Table_Example_Walkthrough.md) for more information about this example.

View file

@ -2,17 +2,17 @@
## Introduction
This document presents a walkthrough of the GATT Server Service Table example code for the ESP32. This example implements a Bluetooth Low Energy (BLE) Generic Attribute (GATT) Server using a table-like data structure to define the server services and characteristics such as the one shown in Fig. 1. Therefore, it demonstrates a practical way to define the server functionality in one place instead of adding services and characteristics one by one.
This document presents a walkthrough of the GATT Server Service Table example code for the ESP32. This example implements a Bluetooth Low Energy (BLE) Generic Attribute (GATT) Server using a table-like data structure to define the server services and characteristics such as the one shown in the figure below Therefore, it demonstrates a practical way to define the server functionality in one place instead of adding services and characteristics one by one.
This example implements the *Heart Rate Profile* as defined by the [Traditional Profile Specifications](https://www.bluetooth.com/specifications/profiles-overview).
![Table-like data structure representing the Heart Rate Service](image/Table-like data structure representing the Heart Rate Service.png)
<div align="center"><img src="image/Heart_Rate_Service.png" width = "450" alt="Table-like data structure representing the Heart Rate Service" align=center /> </div>
## Includes
Lets start by taking a look at the included headers in the ``gatts_table_creat_demo.c`` file:
Lets start by taking a look at the included headers in the [gatts_table_creat_demo.c](../main/gatts_table_creat_demo.c) file:
```
```c
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/event_groups.h"
@ -38,9 +38,9 @@ These includes are required for the *FreeRTOS* and underlaying system components
## Service Table
The header file ``gatts_table_creat_demo.h`` is where an enumeration of the services and characteristics is created:
The header file [gatts_table_creat_demo.h](../main/gatts_table_creat_demo.h) is where an enumeration of the services and characteristics is created:
```
```c
enum
{
HRS_IDX_SVC,
@ -60,21 +60,21 @@ enum
```
The enumeration elements are set up in the same order as the Heart Rate Profile attributes, starting with the service followed by the characteristics of that service. In addition, the Heart Rate Measurement characteristic has a Client Characteristic Configuration (CCC) descriptor which is an additional attribute that describes if the characteristic has notifications enabled. The enumeration index can be used to identify each element later when creating the actual attributes table. In summary, the elements are described as follows:
* HRS_IDX_SVC: Heart Rate Service index
* HRS_IDX_HR_MEAS_CHAR: Heart Rate Measurement characteristic index
* HRS_IDX_HR_MEAS_VAL: Heart Rate Measurement characteristic value index
* HRS_IDX_HR_MEAS_NTF_CFG: Heart Rate Measurement notifications configuration (CCC) index
* HRS_IDX_BOBY_SENSOR_LOC_CHAR: Heart Rate Body Sensor Location characteristic index
* HRS_IDX_BOBY_SENSOR_LOC_VAL: Heart Rate Body Sensor Location characteristic value index
* HRS_IDX_HR_CTNL_PT_CHAR: Heart Rate Control Point characteristic index
* HRS_IDX_HR_CTNL_PT_VAL: Heart Rate Control Point characteristic value index
* HRS_IDX_NB: Number of table elements.
* ``HRS_IDX_SVC``: Heart Rate Service index
* ``HRS_IDX_HR_MEAS_CHAR``: Heart Rate Measurement characteristic index
* ``HRS_IDX_HR_MEAS_VAL``: Heart Rate Measurement characteristic value index
* ``HRS_IDX_HR_MEAS_NTF_CFG``: Heart Rate Measurement notifications configuration (CCC) index
* ``HRS_IDX_BOBY_SENSOR_LOC_CHAR``: Heart Rate Body Sensor Location characteristic index
* ``HRS_IDX_BOBY_SENSOR_LOC_VAL``: Heart Rate Body Sensor Location characteristic value index
* ``HRS_IDX_HR_CTNL_PT_CHAR``: Heart Rate Control Point characteristic index
* ``HRS_IDX_HR_CTNL_PT_VAL``: Heart Rate Control Point characteristic value index
* ``HRS_IDX_NB``: Number of table elements.
## Main Entry Point
The entry point to this example is the ``app_main()`` function:
```
```c
void app_main()
{
esp_err_t ret;
@ -94,7 +94,7 @@ void app_main()
return;
}
ret = esp_bt_controller_enable(ESP_BT_MODE_BTDM);
ret = esp_bt_controller_enable(ESP_BT_MODE_BLE);
if (ret) {
ESP_LOGE(GATTS_TABLE_TAG, "%s enable controller failed\n", __func__);
return;
@ -121,20 +121,20 @@ void app_main()
The main function starts by initializing the non-volatile storage library in order to be able to save parameters in flash memory.
```
```c
ret = nvs_flash_init();
```
## BT Controller and Stack Initialization
See this section in **GATT Server Example Walkthrough**.
See this section in [GATT Server Example Walkthrough](../../gatt_server/tutorial/Gatt_Server_Example_Walkthrough.md).
## Application Profiles
This example implements one Application Profile for the Heart Rate Service. An Application Profile is a way to group functionality which is designed to be used by one client application, for example one smartphone mobile app. In this way, different types of profiles can be accommodated in one server. The Application Profile ID, which is an user-assigned number to identify each profile, is used to register the profile in the stack, in this example the ID is 0x55.
```
```c
#define HEART_PROFILE_NUM 1
#define HEART_PROFILE_APP_IDX 0
#define ESP_HEART_RATE_APP_ID 0x55
@ -142,7 +142,7 @@ This example implements one Application Profile for the Heart Rate Service. An A
The profiles are stored in the ``heart_rate_profile_tab`` array. Since there is only one profile in this example, one element is stored in the array with index zero as defined by the ``HEART_PROFILE_APP_IDX``. Additionally, the profile event handler callback function is initialized. Each application on the GATT server uses a different interface, represented by the gatts_if parameter. For initialization, this parameter is set to ``ESP_GATT_IF_NONE``, later when the application is registered, the gatts_if parameter is updated with the corresponding interface generated by the stack.
```
```c
/* One gatt-based profile one app_id and one gatts_if, this array will store the gatts_if returned by ESP_GATTS_REG_EVT */
static struct gatts_profile_inst heart_rate_profile_tab[HEART_PROFILE_NUM] = {
[HEART_PROFILE_APP_IDX] = {
@ -155,7 +155,7 @@ static struct gatts_profile_inst heart_rate_profile_tab[HEART_PROFILE_NUM] = {
The application registration takes place inside ``app_main()`` using the ``esp_ble_gatts_app_register()`` function:
```
```c
esp_ble_gatts_app_register(ESP_HEART_RATE_APP_ID);
```
@ -168,27 +168,27 @@ The register application event is the first one that is triggered during the lif
The function used to configure standard Bluetooth Specification advertisement parameters is ``esp_ble_gap_config_adv_data()`` which takes a pointer to an ``esp_ble_adv_data_t`` structure. The ``esp_ble_adv_data_t`` data structure for advertising data has the following definition:
```
```c
typedef struct {
bool set_scan_rsp; /*!< Set this advertising data as scan response or not*/
bool include_name; /*!< Advertising data include device name or not */
bool set_scan_rsp; /*!< Set this advertising data as scan response or not*/
bool include_name; /*!< Advertising data include device name or not */
bool include_txpower; /*!< Advertising data include TX power */
int min_interval; /*!< Advertising data show advertising min interval */
int max_interval; /*!< Advertising data show advertising max interval */
int appearance; /*!< External appearance of device */
int min_interval; /*!< Advertising data show advertising min interval */
int max_interval; /*!< Advertising data show advertising max interval */
int appearance; /*!< External appearance of device */
uint16_t manufacturer_len; /*!< Manufacturer data length */
uint8_t *p_manufacturer_data; /*!< Manufacturer data point */
uint16_t service_data_len; /*!< Service data length */
uint8_t *p_service_data; /*!< Service data point */
uint16_t service_uuid_len; /*!< Service uuid length */
uint8_t *p_service_uuid; /*!< Service uuid array point */
uint8_t flag; /*!< Advertising flag of discovery mode, see BLE_ADV_DATA_FLAG detail */
uint16_t service_data_len; /*!< Service data length */
uint8_t *p_service_data; /*!< Service data point */
uint16_t service_uuid_len; /*!< Service uuid length */
uint8_t *p_service_uuid; /*!< Service uuid array point */
uint8_t flag; /*!< Advertising flag of discovery mode, see BLE_ADV_DATA_FLAG detail */
} esp_ble_adv_data_t;
```
In this example, the structure is initialized as follows:
```
```c
static esp_ble_adv_data_t heart_rate_adv_config = {
.set_scan_rsp = false,
.include_name = true,
@ -210,19 +210,18 @@ The minimum and maximum advertisement intervals are set in units of 0.625 ms. In
An advertising payload can be up to 31 bytes of data. It is possible that some of the parameters surpass the 31-byte advertisement packet limit which causes the stack to cut the message and leave some of the parameters out. To solve this, usually the longer parameters are stored in the scan response, which can be configured using the same ``esp_ble_gap_config_adv_data()`` function and an additional esp_ble_adv_data_t type structure with the .set_scan_rsp parameter is set to true. Finally, to set the device name the ``esp_ble_gap_set_device_name()`` function is used. The registering event handler is shown as follows:
```
static void gatts_profile_event_handler(esp_gatts_cb_event_t event,
esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param)
```c
static void gatts_profile_event_handler(esp_gatts_cb_event_t event,
esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param)
{
ESP_LOGE(GATTS_TABLE_TAG, "event = %x\n",event);
switch (event) {
case ESP_GATTS_REG_EVT:
ESP_LOGI(GATTS_TABLE_TAG, "%s %d\n", __func__, __LINE__);
esp_ble_gap_set_device_name(SAMPLE_DEVICE_NAME);
ESP_LOGI(GATTS_TABLE_TAG, "%s %d\n", __func__, __LINE__);
esp_ble_gap_config_adv_data(&heart_rate_adv_config);
ESP_LOGI(GATTS_TABLE_TAG, "%s %d\n", __func__, __LINE__);
ESP_LOGI(GATTS_TABLE_TAG, "%s %d\n", __func__, __LINE__);
esp_ble_gap_set_device_name(SAMPLE_DEVICE_NAME);
ESP_LOGI(GATTS_TABLE_TAG, "%s %d\n", __func__, __LINE__);
esp_ble_gap_config_adv_data(&heart_rate_adv_config);
ESP_LOGI(GATTS_TABLE_TAG, "%s %d\n", __func__, __LINE__);
```
@ -230,7 +229,7 @@ static void gatts_profile_event_handler(esp_gatts_cb_event_t event,
Once the advertising data have been set, the ``ESP_GAP_BLE_ADV_DATA_SET_COMPLETE_EVT`` is triggered and managed by the GAP event handler. Moreover, an ``ESP_GAP_BLE_SCAN_RSP_DATA_SET_COMPLETE_EVT`` is triggered as well if the scan response is also set. Once the configuration of the advertising and scan response data has been set, the handler can use any of these events to start advertising, which is done using the ``esp_ble_gap_start_advertising()`` function:
```
```c
static void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param)
{
ESP_LOGE(GATTS_TABLE_TAG, "GAP_EVT, event %d\n", event);
@ -253,27 +252,25 @@ static void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param
The function to start advertising takes a structure of type ``esp_ble_adv_params_t`` with the advertising parameters required.
```
```c
/// Advertising parameters
typedef struct {
uint16_t adv_int_min; /*!< Minimum advertising interval for
undirected and low duty cycle directed advertising.
Range: 0x0020 to 0x4000
Default: N = 0x0800 (1.28 second)
Time = N * 0.625 msec
Time Range: 20 ms to 10.24 sec */
uint16_t adv_int_max; /*!< Maximum advertising interval for undirected and low duty
cycle directed advertising.
Range: 0x0020 to 0x4000
Default: N = 0x0800 (1.28 second)
Time = N * 0.625 msec
Time Range: 20 ms to 10.24 sec */
esp_ble_adv_type_t adv_type; /*!< Advertising type */
esp_ble_addr_type_t own_addr_type; /*!< Owner bluetooth device address type */
esp_bd_addr_t peer_addr; /*!< Peer device bluetooth device address */
esp_ble_addr_type_t peer_addr_type;/*!< Peer device bluetooth device address type */
esp_ble_adv_channel_t channel_map; /*!< Advertising channel map */
esp_ble_adv_filter_t adv_filter_policy; /*!< Advertising filter policy */
uint16_t adv_int_min; /*!< Minimum advertising interval for undirected and low duty cycle directed advertising.
Range: 0x0020 to 0x4000
Default: N = 0x0800 (1.28 second)
Time = N * 0.625 msec
Time Range: 20 ms to 10.24 sec */
uint16_t adv_int_max; /*!< Maximum advertising interval for undirected and low duty cycle directed advertising.
Range: 0x0020 to 0x4000
Default: N = 0x0800 (1.28 second)
Time = N * 0.625 msec
Time Range: 20 ms to 10.24 sec */
esp_ble_adv_type_t adv_type; /*!< Advertising type */
esp_ble_addr_type_t own_addr_type; /*!< Owner bluetooth device address type */
esp_bd_addr_t peer_addr; /*!< Peer device bluetooth device address */
esp_ble_addr_type_t peer_addr_type; /*!< Peer device bluetooth device address type */
esp_ble_adv_channel_t channel_map; /*!< Advertising channel map */
esp_ble_adv_filter_t adv_filter_policy; /*!< Advertising filter policy */
} esp_ble_adv_params_t;
```
@ -281,7 +278,7 @@ Note that ``esp_ble_gap_config_adv_data()`` configures the data that is advertis
For this example, the advertisement parameters are initialized as follows:
```
```c
static esp_ble_adv_params_t heart_rate_adv_params = {
.adv_int_min = 0x20,
.adv_int_max = 0x40,
@ -298,7 +295,7 @@ These parameters configure the advertising interval between 20 ms to 40 ms. The
If the advertising started successfully, an ``ESP_GAP_BLE_ADV_START_COMPLETE_EVT`` event is generated which in this example is used to check if the advertising status is indeed advertising or otherwise print an error message.
```
```c
case ESP_GAP_BLE_ADV_START_COMPLETE_EVT:
//advertising start complete event to indicate advertising start successfully or failed
@ -313,14 +310,15 @@ If the advertising started successfully, an ``ESP_GAP_BLE_ADV_START_COMPLETE_EVT
When an Application Profile is registered, an ``ESP_GATTS_REG_EVT`` event is triggered. The parameters of the ``ESP_GATTS_REG_EVT`` are:
* esp_gatt_status_t status; /*!< Operation status */
* uint16_t app_id; /*!< Application id which input in register API */
```c
esp_gatt_status_t status; /*!< Operation status */
uint16_t app_id; /*!< Application id which input in register API */
```
In addition to the previous parameters, the event also contains the GATT interface assigned by the BLE stack. The event is captured by the ``gatts_event_handler()`` which stores the generated interface in the profile table and then forwards it to the corresponding profile event handler.
```
static void gatts_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if,
esp_ble_gatts_cb_param_t *param)
```c
static void gatts_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param)
{
ESP_LOGI(GATTS_TABLE_TAG, "EVT %d, gatts if %d\n", event, gatts_if);
@ -340,7 +338,7 @@ static void gatts_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_
int idx;
for (idx = 0; idx < HEART_PROFILE_NUM; idx++) {
if (gatts_if == ESP_GATT_IF_NONE || /* ESP_GATT_IF_NONE, not specify a certain gatt_if, need to call every profile cb function */
gatts_if == heart_rate_profile_tab[idx].gatts_if) {
gatts_if == heart_rate_profile_tab[idx].gatts_if) {
if (heart_rate_profile_tab[idx].gatts_cb) {
heart_rate_profile_tab[idx].gatts_cb(event, gatts_if, param);
}
@ -356,23 +354,27 @@ The register event is used to create a table of profile attributes by employing
The ``esp_gatts_attr_db_t`` structure has two members:
* esp_attr_control_t attr_control; /*!< The attribute control type*/
* esp_attr_desc_t att_desc; /*!< The attribute type*/
```c
esp_attr_control_t attr_control; /*!< The attribute control type*/
esp_attr_desc_t att_desc; /*!< The attribute type*/
```
The attr_control is the auto-respond parameter which can be set as ``ESP_GATT_AUTO_RSP`` to allow the BLE stack to take care of responding messages when read or write events arrive. The other option is ``ESP_GATT_RSP_BY_APP`` which allows to manually respond to messages using the ``esp_ble_gatts_send_response()`` function.
The ``att_desc`` is the attribute description which is made of:
* ``uint16_t uuid_length``; /*!< UUID length */
* ``uint8_t *uuid_p``; /*!< UUID value */
* ``uint16_t perm``; /*!< Attribute permission */
* ``uint16_t max_length``; /*!< Maximum length of the element*/
* ``uint16_t length``; /*!< Current length of the element*/
* ``uint8_t *value``; /*!< Element value array*/
```c
uint16_t uuid_length; /*!< UUID length */
uint8_t *uuid_p; /*!< UUID value */
uint16_t perm; /*!< Attribute permission */
uint16_t max_length; /*!< Maximum length of the element*/
uint16_t length; /*!< Current length of the element*/
uint8_t *value; /*!< Element value array*/
```
For example, the first element of the table in this example is the service attribute:
```
```c
[HRS_IDX_SVC] =
{{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&primary_service_uuid, ESP_GATT_PERM_READ,
sizeof(uint16_t), sizeof(heart_rate_svc), (uint8_t *)&heart_rate_svc}},
@ -391,7 +393,7 @@ The initialization values are:
The rest of the attributes is initialized in the same way. Some attributes also have the *NOTIFY* property which is set by ``&char_prop_notify``. The complete table structure is initialized as follows:
```
```c
/// Full HRS Database Description - Used to add attributes into the database
static const esp_gatts_attr_db_t heart_rate_gatt_db[HRS_IDX_NB] =
{
@ -440,14 +442,16 @@ static const esp_gatts_attr_db_t heart_rate_gatt_db[HRS_IDX_NB] =
## Starting the Service
When the attribute table is created, an ``ESP_GATTS_CREAT_ATTR_TAB_EVT`` event is triggered. This event has the following parameters:
* ``esp_gatt_status_t status``; /*!< Operation status */
* ``esp_bt_uuid_t svc_uuid``; /*!< Service uuid type */
* ``uint16_t num_handle``; /*!< The number of the attribute handle to be added to the gatts database */
* ``uint16_t *handles``; /*!< The number to the handles */
```c
esp_gatt_status_t status; /*!< Operation status */
esp_bt_uuid_t svc_uuid; /*!< Service uuid type */
uint16_t num_handle; /*!< The number of the attribute handle to be added to the gatts database */
uint16_t *handles; /*!< The number to the handles */
```
This example uses this event to print information and to check that the size of the created table equals the number of elements in the enumeration HRS_IDX_NB. If the table is correctly created, the attribute handles are copied into the handle table heart_rate_handle_table and the service is started using the ``esp_ble_gatts_start_service()`` function:
```
```c
case ESP_GATTS_CREAT_ATTR_TAB_EVT:{
ESP_LOGI(GATTS_TABLE_TAG, "The number handle =%x\n",param->add_attr_tab.num_handle);
if (param->add_attr_tab.status != ESP_GATT_OK){
@ -468,7 +472,7 @@ The handles stored in the handles pointer of the event parameters are numbers th
Finally, the heart_rate_handle_table contains the Application Profile in the form of a structure with information about the attribute parameters as well as GATT interface, connection ID, permissions and application ID. The profile structure is shown as follows, note that not all members are used in this example:
```
```c
struct gatts_profile_inst {
esp_gatts_cb_t gatts_cb;
uint16_t gatts_if;

View file

@ -1,10 +1,10 @@
ESP-IDF GATT MULTIPLE CLIENT demo
ESP-IDF Gatt Client Multi Connection Demo
========================
This is the demo for user to use ESP_APIs to create a GATT Multi-connection Client.
The gattc_multi_connect demo can connect three ble slaves at the same time.
Modify the name of gatt_server demo named ESP_GATTS_DEMO_a, ESP_GATTS_DEMO_b and ESP_GATTS_DEMO_c, then run the three demos,
the gattc_multi_connect demo will connect the three gatt_server demos, and then exchange data.
Of course you can also modify the code to connect more devices, we default to connect up to 4 devices, more than 4 you need to modify menuconfig.
This is the demo of APIs to create a GATT multi-connection client. It can be used to connect to three GATT servers at the same time.
Please check the [tutorial](tutorial/GATT_Client_Multi-Connection_Example_Walkthrough.md) for more information about this example.
To test this demo, please run [gatt_server_demo](../gatt_server) to create three GATT server devices, namely ESP_GATTS_DEMO_a, ESP_GATTS_DEMO_b and ESP_GATTS_DEMO_c, `Gatt_client_multi_connection_demo` will connect to these three gatt server demos, and then exchange data.
The code can be modified to connect to more devices (up to 4 devices by default). If you need to connect to more devices (more than 4 devices), you need to change `BT/BLE MAX ACL CONNECTIONS` in menuconfig.
Please check the [tutorial](tutorial/Gatt_Client_Multi_Connection_Example_Walkthrough.md) for more information about this example.

View file

@ -1,22 +1,22 @@
# GATT Client Multi-Connection Example Walkthrough
# GATT Client Multi-connection Example Walkthrough
## Introduction
This document presents a description of the multi-connection BLE GATT client example for the ESP32. In this implementation, a single ESP32 working as a GATT client connects to three different GATT servers at the same time. This set up illustrates the use case of an ESP32 device acting in a way so that it receives data from different BLE sensors. The unique combination of ESP32s BLE + Wi-Fi capabilities in addition to connection to multiple peripherals makes it a great candidate to serve as an IoT gateway.
This examples workflow is similar to the **GATT Client Example Walkthrough** and is shown in Fig. 1. However, in the multi-connection implementation, a GATT client searches for three specific server names and once that it has found them it opens a connection to all three of them one after the other. In code, each connection is handled separately with one Application Profile.
This examples workflow is similar to the [GATT Client Example Walkthrough](../../gatt_client/tutorial/Gatt_Client_Example_Walkthrough.md) and is shown in the figure below. However, in the multi-connection implementation, a GATT client searches for three specific server names and once that it has found them it opens a connection to all three of them one after the other. In code, each connection is handled separately with one Application Profile.
Four ESP32 devices are needed in order to demonstrate this example, among which:
* one would be employed as a GATT Client flashed with the ``gattc_multi_connect`` demo, and,
* the rest run as GATT servers flashed with the ``gatt_server`` demo of the ESP-IDF Bluetooth examples folder.
* one would be employed as a GATT Client flashed with the [gattc_multi_connect](../../gattc_multi_connect) demo, and,
* the rest run as GATT servers flashed with the [gatt_server](../../gatt_server) demo of the ESP-IDF Bluetooth examples folder.
![Multi-Connection GATT Client Flowchart](image/Multi-connection GATT Client Flowchart.png)
<div align="center"><img src="image/Multi_Connection_GATT_Client_Flowchart.png" width = "800" alt="Multi-Connection GATT Client Flowchart" align=center/></div>
## Includes
The multi-connection examples main source file is ``gattc_multi_connect.c``. For details, see Section **Includes** in **GATT Client Example Walkthrough**.
The multi-connection examples main source file is [gattc_multi_connect.c](../main/gattc_multi_connect.c). For details, see Section [Includes](../../gatt_client/tutorial/Gatt_Client_Example_Walkthrough.md#includes) in [GATT Client Example Walkthrough](../../gatt_client/tutorial/Gatt_Client_Example_Walkthrough.md).
## Main Entry Point
See Section **Main Entry Point** in **GATT Client Example Walkthrough**.
See Section [Main Entry Point](../../gatt_client/tutorial/Gatt_Client_Example_Walkthrough.md#main-entry-point) in [GATT Client Example Walkthrough](../../gatt_client/tutorial/Gatt_Client_Example_Walkthrough.md).
## Implementation Procedure
The GATT Client implementation includes the following steps:
@ -28,53 +28,53 @@ The GATT Client implementation includes the following steps:
* registering for notifications.
### Initializing
See Section **Main Entry Point** in **GATT Client Example Walkthrough**.
See Section [Main Entry Point](../../gatt_client/tutorial/Gatt_Client_Example_Walkthrough.md#main-entry-point) in [GATT Client Example Walkthrough](../../gatt_client/tutorial/Gatt_Client_Example_Walkthrough.md).
#### Application Profiles
Application Profiles are a way to group functionality. They are designed so that each Application Profile connects to one peer device, that way the same ESP32 can connect to multiple devices by assigning one Application Profile to each one, as Fig. 2 shows. Each Application Profile creates a GATT interface to connect to other devices. The Application Profiles are defined by an ID number, there are three profiles in this example:
Application Profiles are a way to group functionality. They are designed so that each Application Profile connects to one peer device, that way the same ESP32 can connect to multiple devices by assigning one Application Profile to each one, as figure below shows. Each Application Profile creates a GATT interface to connect to other devices. The Application Profiles are defined by an ID number, there are three profiles in this example:
```
```c
#define PROFILE_NUM 3
#define PROFILE_A_APP_ID 0
#define PROFILE_A_APP_ID 0
#define PROFILE_B_APP_ID 1
#define PROFILE_C_APP_ID 2
```
![ESP32 GATT Multi-Connect Client Application Profiles](image/ESP32 GATT Multi-connect Client Application Profiles.png)
<div align="center"><img src="image/ESP32_GATT_Multi_Connect_Client_Application_Profiles.png" width = "450" alt="Multi-Connection GATT Client Flowchart" align=center/></div>
The ``esp_ble_gattc_app_register()`` function is used to register each Application Profile to the BLE stack. The registration operation generates a GATT interface that is returned as a parameter in a registration event. In addition, each Application Profile is also defined by a structure that can be used to keep the state of the application and update its parameters when new data is propagated by the stack.
The Application Profiles in code are instances of a ``gattc_profile_inst`` structure. For details, see Section **Application Profiles** in **GATT Client Example Walkthrough**.
The Application Profiles in code are instances of a ``gattc_profile_inst`` structure. For details, see Section [Application Profiles](../../gatt_client/tutorial/Gatt_Client_Example_Walkthrough.md#application-profiles) in [GATT Client Example Walkthrough](../../gatt_client/tutorial/Gatt_Client_Example_Walkthrough.md).
### Scanning
#### Setting Scan Parameters
See Section **Setting Scan Parameters** in **GATT Client Example Walkthrough**.
See Section [Setting Scan Parameters](../../gatt_client/tutorial/Gatt_Client_Example_Walkthrough.md#setting-scan-parameters) in [GATT Client Example Walkthrough](../../gatt_client/tutorial/Gatt_Client_Example_Walkthrough.md).
#### Starting to Scan
See Section **Start Scanning** in **GATT Client Example Walkthrough**.
See Section [Start Scanning](../../gatt_client/tutorial/Gatt_Client_Example_Walkthrough.md#start-scanning) in [GATT Client Example Walkthrough](../../gatt_client/tutorial/Gatt_Client_Example_Walkthrough.md).
#### Getting Scan Results
See Section **Getting Scan Results** in **GATT Client Example Walkthrough**.
See Section [Getting Scan Results](../../gatt_client/tutorial/Gatt_Client_Example_Walkthrough.md#start-scanning) in [GATT Client Example Walkthrough](../../gatt_client/tutorial/Gatt_Client_Example_Walkthrough.md).
#### Name Comparison
* First, the name of the device is extracted from the advertised data and stored in the ``adv_name`` variable:
```
adv_name = esp_ble_resolve_adv_data(scan_result->scan_rst.ble_adv,
ESP_BLE_AD_TYPE_NAME_CMPL, &adv_name_len);
```c
adv_name = esp_ble_resolve_adv_data(scan_result->scan_rst.ble_adv, ESP_BLE_AD_TYPE_NAME_CMPL, &adv_name_len);
```
* Then, the device name found is compared to the server names that the client wants to connect to. The server names are defined in the ``remote_device_name`` array:
```c
static const char remote_device_name[3][20] = {"ESP_GATTS_DEMO_1", "ESP_GATTS_DEMO_2", “ESP_GATTS_DEMO_3"};
```
static const char remote_device_name[3][20] = {"ESP_GATTS_DEMO_1", "ESP_GATTS_DEMO_2", “ESP_GATTS_DEMO_3"};
```
The name comparison takes places as follows:
```
if (strlen(remote_device_name[0]) == adv_name_len && strncmp((char *)adv_name, remote_device_name[0], adv_name_len) == 0) {
```c
if (strlen(remote_device_name[0]) == adv_name_len && strncmp((char *)adv_name, remote_device_name[0], adv_name_len) == 0) {
if (find_device_1 == false) {
find_device_1 = true;
ESP_LOGI(GATTC_TAG, "Searched device %s", remote_device_name[0]);
@ -106,37 +106,36 @@ if (strlen(remote_device_name[0]) == adv_name_len && strncmp((char *)adv_name, r
#### Connecting to the First Remote Device
Once all devices have been found, the client stops scanning:
```
if (find_device_1 && find_device_2 && find_device_3 && stop_scan == false)
{
stop_scan = true;
esp_ble_gap_stop_scanning();
}
```c
if (find_device_1 && find_device_2 && find_device_3 && stop_scan == false {
stop_scan = true;
esp_ble_gap_stop_scanning();
}
```
The scan stop triggers an ``ESP_GAP_BLE_SCAN_STOP_COMPLETE_EVT`` event which is used to open a connection to the first remote device. The second and third devices get connected once the client searches for services, gets characteristics and registers for notifications on the first device. This workflow is designed to test that the communication between each remote device is working correctly before trying to connect to the next device or in case of error, skip to the next device.
```
```c
case ESP_GAP_BLE_SCAN_STOP_COMPLETE_EVT:
if (param->scan_stop_cmpl.status != ESP_BT_STATUS_SUCCESS){
ESP_LOGE(GATTC_TAG, "Scan stop failed");
break;
}
ESP_LOGI(GATTC_TAG, "Stop scan successfully");
if (!stop_scan){
ESP_LOGE(GATTC_TAG, "Did not find all devices");
}
if (find_device_1){
esp_ble_gattc_open(gl_profile_tab[PROFILE_A_APP_ID].gattc_if, gl_profile_tab[PROFILE_A_APP_ID].remote_bda, true);
}
if (param->scan_stop_cmpl.status != ESP_BT_STATUS_SUCCESS){
ESP_LOGE(GATTC_TAG, "Scan stop failed");
break;
}
ESP_LOGI(GATTC_TAG, "Stop scan successfully");
if (!stop_scan){
ESP_LOGE(GATTC_TAG, "Did not find all devices");
}
if (find_device_1){
esp_ble_gattc_open(gl_profile_tab[PROFILE_A_APP_ID].gattc_if, gl_profile_tab[PROFILE_A_APP_ID].remote_bda, true);
}
break;
```
* The connection is opened with the ``esp_ble_gattc_open()`` function which takes the GATT interface, the remote device address and a boolean value set to true for direct connection or false for background auto connection. To disconnect the physical connection, the GAP API function ``esp_ble_gap_disconnect()`` is used.
When connecting to the first device, an ``ESP_GATTC_CONNECT_EVT`` event is generated which is forwarded to all profiles. It also triggers an ``ESP_GATTC_OPEN_EVT`` event that is forwarded to the Profile A event handler only, or ``gattc_profile_a_event_handler()`` function. The event checks that the connection is opened successfully, if not, the device is ignored and the client tries to open a connection to the second device:
When connecting to the first device, an ``ESP_GATTC_CONNECT_EVT`` event is generated which is forwarded to all profiles. It also triggers an ``ESP_GATTC_OPEN_EVT`` event that is forwarded to the Profile A event handler only, or ``gattc_profile_a_event_handler()`` function. The event checks that the connection is opened successfully, if not, the device is ignored and the client tries to open a connection to the second device:
```
```c
case ESP_GATTC_OPEN_EVT:
if (p_data->open.status != ESP_GATT_OK){
//open failed, ignore the first device, connect the second device
@ -146,35 +145,32 @@ case ESP_GAP_BLE_SCAN_STOP_COMPLETE_EVT:
}
break;
}
```
If the connection is successful the client saves the connection ID, prints the remote device information and configures the MTU size to 200 bytes.
```
gl_profile_tab[PROFILE_A_APP_ID].conn_id = p_data->open.conn_id;
ESP_LOGI(GATTC_TAG, "ESP_GATTC_OPEN_EVT conn_id %d, if %d, status %d, mtu %d", p_data->open.conn_id, gattc_if, p_data->open.status, p_data->open.mtu);
ESP_LOGI(GATTC_TAG, "REMOTE BDA:");
esp_log_buffer_hex(GATTC_TAG, p_data->open.remote_bda, sizeof(esp_bd_addr_t));
esp_err_t mtu_ret = esp_ble_gattc_config_mtu (gattc_if, p_data->open.conn_id, 200);
if (mtu_ret){
ESP_LOGE(GATTC_TAG, "config MTU error, error code = %x", mtu_ret);
}
break;
```
```
If the connection is successful the client saves the connection ID, prints the remote device information and configures the MTU size to 200 bytes.
```c
gl_profile_tab[PROFILE_A_APP_ID].conn_id = p_data->open.conn_id;
ESP_LOGI(GATTC_TAG, "ESP_GATTC_OPEN_EVT conn_id %d, if %d, status %d, mtu %d", p_data->open.conn_id, gattc_if, p_data->open.status, p_data->open.mtu);
ESP_LOGI(GATTC_TAG, "REMOTE BDA:");
esp_log_buffer_hex(GATTC_TAG, p_data->open.remote_bda, sizeof(esp_bd_addr_t));
esp_err_t mtu_ret = esp_ble_gattc_config_mtu (gattc_if, p_data->open.conn_id, 200);
if (mtu_ret){
ESP_LOGE(GATTC_TAG, "config MTU error, error code = %x", mtu_ret);
}
break;
```
* After configuration of the MTU size, an ``ESP_GATTC_CFG_MTU_EVT`` is generated. This event is used to search for available known services on the remote device. The search is performed by using the ``esp_ble_gattc_search_service()`` function and a service ID defined by:
```
static esp_bt_uuid_t remote_filter_service_uuid = {
```c
static esp_bt_uuid_t remote_filter_service_uuid = {
.len = ESP_UUID_LEN_16,
.uuid = {.uuid16 = REMOTE_SERVICE_UUID,},
};
```
};
```
* The handler then searches for the service:
```
case ESP_GATTC_CFG_MTU_EVT:
```c
case ESP_GATTC_CFG_MTU_EVT:
if (param->cfg_mtu.status != ESP_GATT_OK){
ESP_LOGE(GATTC_TAG,"Config mtu failed");
}
@ -182,13 +178,12 @@ case ESP_GATTC_CFG_MTU_EVT:
esp_ble_gattc_search_service(gattc_if, param->cfg_mtu.conn_id, &remote_filter_service_uuid);
break;
```
If the service is found, an ``ESP_GATTC_SEARCH_RES_EVT`` event is triggered which allows to set the ``get_service_1 flag`` to true. This flag is used to print information and later get the characteristic that the client is interested in.
* Once the search for all services is completed, an ``ESP_GATTC_SEARCH_CMPL_EVT`` event is generated which is used to get the characteristics of the service just discovered. This is done with the ``esp_ble_gattc_get_characteristic()`` function:
```
case ESP_GATTC_SEARCH_CMPL_EVT:
```c
case ESP_GATTC_SEARCH_CMPL_EVT:
if (p_data->search_cmpl.status != ESP_GATT_OK){
ESP_LOGE(GATTC_TAG, "search service failed, error status = %x", p_data->search_cmpl.status);
break;
@ -198,16 +193,14 @@ case ESP_GATTC_CFG_MTU_EVT:
}
break;
```
The ``esp_ble_gattc_get_characteristic()`` function takes the GATT interface, the connection ID and the remote service ID as parameters. In addition, a NULL value is passed to indicate that we want all the characteristics starting from the first one. If the client is interested in a specific characteristic it could pass the characteristic ID in this field to specify that.
An ``ESP_GATTC_GET_CHAR_EVT`` event is triggered when a characteristic is discovered. This event is used to print information about the characteristic.
The ``esp_ble_gattc_get_characteristic()`` function takes the GATT interface, the connection ID and the remote service ID as parameters. In addition, a NULL value is passed to indicate that we want all the characteristics starting from the first one. If the client is interested in a specific characteristic it could pass the characteristic ID in this field to specify that.
An ``ESP_GATTC_GET_CHAR_EVT`` event is triggered when a characteristic is discovered. This event is used to print information about the characteristic.
* If the characteristic ID is the same as the one defined by ``REMOTE_NOTIFY_CHAR_UUID``, the client registers for notifications on that characteristic value.
* Finally, the next characteristic is requested using the same ``esp_ble_gattc_get_characteristic()`` function, this time, the last parameter is set to the current characteristic. This triggers another ``ESP_GATTC_GET_CHAR_EVT`` and the process is repeated until all characteristics are obtained.
```
case ESP_GATTC_GET_CHAR_EVT:
```c
case ESP_GATTC_GET_CHAR_EVT:
if (p_data->get_char.status != ESP_GATT_OK) {
break;
}
@ -228,25 +221,24 @@ At this point the client has acquired all characteristics from the remote device
#### Connecting to the Next Remote Device
* In order to pass this descriptor as a parameter we first define it as:
```
static esp_gatt_id_t notify_descr_id = {
```c
static esp_gatt_id_t notify_descr_id = {
.uuid = {
.len = ESP_UUID_LEN_16,
.uuid = {.uuid16 = ESP_GATT_UUID_CHAR_CLIENT_CONFIG,},
},
},
.inst_id = 0,
};
};
```
Where ``ESP_GATT_UUID_CHAR_CLIENT_CONFIG`` is defined as the UUID to identify the CCC:
```
Where ``ESP_GATT_UUID_CHAR_CLIENT_CONFIG`` is defined as the UUID to identify the CCC:
```c
#define ESP_GATT_UUID_CHAR_CLIENT_CONFIG 0x2902 /* Client Characteristic Configuration */
```
The value to write is “1” to enable notifications. The parameter ``ESP_GATT_WRITE_TYPE_RSP`` is also passed to request that the server responds to the write request, as well as the ``ESP_GATT_AUTH_REQ_NONE`` parameter to indicate that the write request does not need authorization:
```
The value to write is “1” to enable notifications. The parameter ``ESP_GATT_WRITE_TYPE_RSP`` is also passed to request that the server responds to the write request, as well as the ``ESP_GATT_AUTH_REQ_NONE`` parameter to indicate that the write request does not need authorization:
```c
case ESP_GATTC_REG_FOR_NOTIFY_EVT: {
if (p_data->reg_for_notify.status != ESP_GATT_OK){
ESP_LOGE(GATTC_TAG, "reg notify failed, error status =%x", p_data->reg_for_notify.status);
@ -269,12 +261,12 @@ static esp_gatt_id_t notify_descr_id = {
ESP_GATT_AUTH_REQ_NONE);
break;
}
```
* Once notifications are enabled, the remote device sends a notification which triggers the ``ESP_GATTC_NOTIFY_EVT`` event on the client. This event is handled to write back to the characteristic using the ``esp_ble_gattc_write_char()`` function:
```
case ESP_GATTC_NOTIFY_EVT:
```c
case ESP_GATTC_NOTIFY_EVT:
ESP_LOGI(GATTC_TAG, "ESP_GATTC_NOTIFY_EVT, Receive notify value:");
esp_log_buffer_hex(GATTC_TAG, p_data->notify.value, p_data->notify.value_len);
//write back
@ -287,12 +279,12 @@ case ESP_GATTC_NOTIFY_EVT:
ESP_GATT_WRITE_TYPE_RSP,
ESP_GATT_AUTH_REQ_NONE);
break;
```
```
* If the writing procedure is acknowledged then the remote device has connected successfully and communication is established without error. Immediately, the write procedure generates an ``ESP_GATTC_WRITE_CHAR_EVT`` event which in this example is used to print information and connect to the second remote device:
```
case ESP_GATTC_WRITE_CHAR_EVT:
```c
case ESP_GATTC_WRITE_CHAR_EVT:
if (p_data->write.status != ESP_GATT_OK){
ESP_LOGE(GATTC_TAG, "write char failed, error status = %x", p_data->write.status);
}else{
@ -304,10 +296,11 @@ case ESP_GATTC_WRITE_CHAR_EVT:
}
break;
```
* This triggers an open event which is handled by the Profile B event handler. This handler follows the same steps to search for services, get characteristics, register for notifications and write to the characteristic as the first device. The sequence for the second device also ends with an ``ESP_GATTC_WRITE_CHAR_EVT`` event which in turn is used to connect to the third device:
```
case ESP_GATTC_WRITE_CHAR_EVT:
```c
case ESP_GATTC_WRITE_CHAR_EVT:
if (p_data->write.status != ESP_GATT_OK){
ESP_LOGE(GATTC_TAG, "Write char failed, error status = %x", p_data->write.status);
}else{

Binary file not shown.

After

Width:  |  Height:  |  Size: 122 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 136 KiB