mirror of
https://github.com/espressif/esp-idf.git
synced 2024-10-05 20:47:46 -04:00
component/bt: Modify docs to be compatible with "Github Standard Markdown"
This commit is contained in:
parent
e54f8a96a0
commit
6ba817038c
@ -1,10 +1,12 @@
|
||||
#GATT CLIENT EXAMPLE WALKTHROUGH
|
||||
# Gatt Client Example Walkthrough
|
||||
|
||||
## Introduction
|
||||
|
||||
##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.
|
||||
|
||||
#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:
|
||||
# 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:
|
||||
|
||||
```c
|
||||
#include <stdint.h>
|
||||
@ -23,14 +25,15 @@ This example is located in the examples folder of the ESP-IDF under the bluetoot
|
||||
#include "esp_gatt_common_api.h"
|
||||
```
|
||||
|
||||
These "includes" are required for the FreeRTOS and underlaying system components to run, including the logging functionality and a library to store data in non-volatile flash memory. We are interested in `“bt.h”`, `“esp_bt_main.h”`, `"esp_gap_ble_api.h"` and `“esp_gattc_api.h”`, which expose the BLE APIs required to implement this example.
|
||||
These `includes` are required for the FreeRTOS and underlaying system components to run, including the logging functionality and a library to store data in non-volatile flash memory. We are interested in `“bt.h”`, `“esp_bt_main.h”`, `"esp_gap_ble_api.h"` and `“esp_gattc_api.h”`, which expose the BLE APIs required to implement this example.
|
||||
|
||||
* `bt.h`: configures the BT controller and VHCI from the host side.
|
||||
* `esp_bt_main.h`: initializes and enables the Bluedroid stack.
|
||||
* `esp_gap_ble_api.h`: implements the GAP configuration, such as advertising and connection parameters.
|
||||
* `esp_gattc_api.h`: implements the GATT Client configuration, such as connecting to peripherals and searching for services.
|
||||
|
||||
##MAIN ENTRY POINT
|
||||
## Main Entry Point
|
||||
|
||||
The program’s entry point is the app_main() function:
|
||||
|
||||
```c
|
||||
@ -107,7 +110,7 @@ The main function starts by initializing the non-volatile storage library. This
|
||||
ESP_ERROR_CHECK( ret );
|
||||
```
|
||||
|
||||
##BT CONTROLLER AND STACK INITIALIZATION
|
||||
## BT Controller and Stack Initialization
|
||||
|
||||
The main function also initializes the BT controller by first creating a BT controller configuration structure named `esp_bt_controller_config_t` with default settings generated by the `BT_CONTROLLER_INIT_CONFIG_DEFAULT()` macro. The BT controller implements the Host Controller Interface (HCI) on the controller side, the Link Layer (LL) and the Physical Layer (PHY). The BT Controller is invisible to the user applications and deals with the lower layers of the BLE stack. The controller configuration includes setting the BT controller stack size, priority and HCI baud rate. With the settings created, the BT controller is initialized and enabled with the `esp_bt_controller_init()` function:
|
||||
|
||||
@ -123,6 +126,7 @@ ret = esp_bt_controller_enable(ESP_BT_MODE_BTDM);
|
||||
```
|
||||
|
||||
There are four Bluetooth modes supported:
|
||||
|
||||
1. `ESP_BT_MODE_IDLE`: Bluetooth not running
|
||||
2. `ESP_BT_MODE_BLE`: BLE mode
|
||||
3. `ESP_BT_MODE_CLASSIC_BT`: BT Classic mode
|
||||
@ -159,7 +163,7 @@ esp_ble_gattc_register_callback();
|
||||
```
|
||||
The functions `esp_gap_cb()` and `esp_gattc_cb()` handle all the events generated by the BLE stack.
|
||||
|
||||
##APPLICATION PROFILES
|
||||
## Application Profiles
|
||||
|
||||
The Application Profiles are a way to group functionalities that are designed for one or more server applications. For example, you can have an Application Profile connected to the Heart Rate Sensors, and another one connected to the Temperature Sensors. Each Application Profile creates a GATT interface to connect to other devices. The Application Profiles in the code are instances of the `gattc_profile_inst` structure, which is defined as:
|
||||
|
||||
@ -177,14 +181,15 @@ struct gattc_profile_inst {
|
||||
```
|
||||
|
||||
The Application Profile structure contains:
|
||||
* `gattc_cb`: GATT client callback function
|
||||
* `gattc_if`: GATT client interface number for this profile
|
||||
* `app_id`: Application Profile ID number
|
||||
* `conn_id`: Connection ID
|
||||
* `service_start_handle`: Service start handle
|
||||
* `service_end_handle`: Service end handle
|
||||
* `char_handle`: Char handle
|
||||
* `remote_bda`: Remote device address connected to this client.
|
||||
|
||||
* `gattc_cb`: GATT client callback function
|
||||
* `gattc_if`: GATT client interface number for this profile
|
||||
* `app_id`: Application Profile ID number
|
||||
* `conn_id`: Connection ID
|
||||
* `service_start_handle`: Service start handle
|
||||
* `service_end_handle`: Service end handle
|
||||
* `char_handle`: Char handle
|
||||
* `remote_bda`: Remote device address connected to this client.
|
||||
|
||||
In this example there is one Application Profile and its ID is defined as:
|
||||
|
||||
@ -203,6 +208,7 @@ static struct gattc_profile_inst gl_profile_tab[PROFILE_NUM] = {
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
The initialization of the Application Profile table array includes defining the callback functions for each Profile. These are `gattc_profile_a_event_handler()` and `gattc_profile_a_event_handler()` respectively. In addition, the GATT interface is initialized to the default value of `ESP_GATT_IF_NONE`. Later on, when the Application Profile is registered, the BLE stack returns a GATT interface instance to use with that Application Profile.
|
||||
|
||||
The profile registration triggers an `ESP_GATTC_REG_EVT` event, which is handled by the `esp_gattc_cb()` event handler. The handler takes the GATT interface returned by the event and stores it in the profile table:
|
||||
@ -245,7 +251,7 @@ Finally, the callback function invokes the corresponding event handler for each
|
||||
} while (0);
|
||||
}
|
||||
```
|
||||
##SETTING SCAN PARAMETERS
|
||||
## Setting Scan Parameters
|
||||
|
||||
The GATT client normally scans for nearby servers and tries connect to them, if interested. However, in order to perform the scanning, first the configuration parameters need to be set. This is done after the registration of the Application Profiles, because the registration, once completed, triggers an `ESP_GATTC_REG_EVT` event. The first time this event is triggered, the GATT event handler captures it and assigns a GATT interface to Profile A, then the event is forwarded to the GATT event handler of Profile A. One in this event handler, the event is used to call the `esp_ble_gap_set_scan_params()` function, which takes a `ble_scan_params` structure instance as parameter. This structure is defined as:
|
||||
|
||||
@ -302,8 +308,10 @@ case ESP_GATTC_REG_EVT:
|
||||
break;
|
||||
```
|
||||
|
||||
##START SCANNING
|
||||
## Start Scanning
|
||||
|
||||
Once the scanning parameters are set, an `ESP_GAP_BLE_SCAN_PARAM_SET_COMPLETE_EVT` event is triggered, which is handled by the GAP event handler `esp_gap_cb()`. This event is used to start the scanning of nearby GATT servers:
|
||||
|
||||
```c
|
||||
case ESP_GAP_BLE_SCAN_PARAM_SET_COMPLETE_EVT: {
|
||||
//the unit of the duration is second
|
||||
@ -312,9 +320,10 @@ Once the scanning parameters are set, an `ESP_GAP_BLE_SCAN_PARAM_SET_COMPLETE_EV
|
||||
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.
|
||||
|
||||
##GETTING SCAN RESULTS
|
||||
## Getting Scan Results
|
||||
|
||||
The results of the scanning are displayed as soon as they arrive with the `ESP_GAP_BLE_SCAN_RESULT_EVT` event, which includes the following parameters:
|
||||
|
||||
@ -387,7 +396,8 @@ We are interested in the `ESP_GAP_SEARCH_INQ_RES_EVT` event, which is called eve
|
||||
First the device name is resolved and compared to the one defined in `remote_device_name`. If it equals to the device name of the GATT Server we are interested in, then the scanning is stopped.
|
||||
|
||||
|
||||
##CONNECTING TO A GATT SERVER
|
||||
## Connecting to A GATT Server
|
||||
|
||||
Every time we receive a result from the `ESP_GAP_SEARCH_INQ_RES_EVT` event, the code first prints the address of the remote device:
|
||||
|
||||
```c
|
||||
@ -413,7 +423,7 @@ esp_log_buffer_char(GATTC_TAG, adv_name, adv_name_len);
|
||||
|
||||
Finally if the remote device name is the same as we have defined above, the local device stops scanning and tries to open a connection to the remote device using the `esp_ble_gattc_open()` function. This function takes as parameters the Application Profile GATT interface, the remote server address and a boolean value. The boolean value is used to indicate if the connection is done directly or if it’s done in the background (auto-connection), at the moment this boolean value must be set to true in order to establish the connection. Notice that the client opens a virtual connection to the server. The virtual connection returns a connection ID. The virtual connection is the connection between the Application Profile and the remote server. Since many Application Profiles can run on one ESP32, there could be many virtual connection opened to the same remote server. There is also the physical connection which is the actual BLE link between the client and the server. Therefore, if the physical connection is disconnected with the `esp_ble_gap_disconnect()` function, all other virtual connections are closed as well. In this example, each Application Profile creates a virtual connection to the same server with the `esp_ble_gattc_open()` function, so when the close function is called, only that connection from the Application Profile is closed, while if the gap disconnect function is called, both connections will be closed. In addition, connect events are propagated to all profiles because it relates to the physical connection, while open events are propagated only to the profile that creates the virtual connection.
|
||||
|
||||
##CONFIGURING MTU SIZE
|
||||
## Configuring the MTU Size
|
||||
|
||||
ATT_MTU is defined as the maximum size of any packet sent between a client and a server. When the client connects to the server, it informs the server which MTU size to use by exchanging MTU Request and Response protocol data units (PDUs). This is done after the opening of a connection. After opening the connection, an `ESP_GATTC_CONNECT_EVT` event is triggered:
|
||||
|
||||
@ -455,7 +465,7 @@ if (mtu_ret){
|
||||
break;
|
||||
```
|
||||
|
||||
The connection opening also triggers an ESP_GATTC_OPEN_EVT, which is used to check that the opening of the connection was done successfully, otherwise print an error and exit.
|
||||
The connection opening also triggers an `ESP_GATTC_OPEN_EVT`, which is used to check that the opening of the connection was done successfully, otherwise print an error and exit.
|
||||
|
||||
```c
|
||||
case ESP_GATTC_OPEN_EVT:
|
||||
@ -477,7 +487,7 @@ case ESP_GATTC_CFG_MTU_EVT:
|
||||
…
|
||||
```
|
||||
|
||||
##DISCOVERING SERVICES
|
||||
## Discovering Services
|
||||
|
||||
The MTU configuration event is also used to start discovering the services available in the server that the client just connected to. To discover the services, the function `esp_ble_gattc_search_service()` is used. The parameters of the function are the GATT interface, the Application Profile connection ID and the UUID of the service application that the client is interested in. The service we are looking for is defined as:
|
||||
|
||||
@ -517,7 +527,7 @@ REMOTE_SERVICE_UUID) {
|
||||
|
||||
In case that the client finds the service that it is looking for, the flag get_server is set to true, and the start handle value and end handle value, which will be used later to get all the characteristics of that service, are saved. After all service results are returned, the search is completed and an `ESP_GATTC_SEARCH_CMPL_EVT` event is triggered.
|
||||
|
||||
##GETTING CHARACTERISTICS
|
||||
## Getting Characteristics
|
||||
|
||||
This examples implements getting characteristic data from a predefined service. The service that we want the characteristics from has an UUID of 0x00FF, and the characteristic we are interested in has an UUID of 0xFF01:
|
||||
|
||||
@ -608,7 +618,7 @@ Once defined, we can get the characteristics from that service using the `esp_bl
|
||||
|
||||
`esp_ble_gattc_get_attr_count()` gets the attribute count with the given service or characteristic in the gattc cache. The parameters of `esp_ble_gattc_get_attr_count()` function are the GATT interface, the connection ID, the attribute type defined in `esp_gatt_db_attr_type_t`, the attribute start handle, the attribute end handle, the characteristic handle (this parameter is only valid when the type is set to `ESP_GATT_DB_DESCRIPTOR`.) and output the number of attribute has been found in the gattc cache with the given attribute type. Then we allocate a buffer to save the char information for `esp_ble_gattc_get_char_by_uuid()` function. The function finds the characteristic with the given characteristic UUID in the gattc cache. It just gets characteristic from local cache, instead of the remote devices. In a server, there might be more than one chars sharing the same UUID. However, in our gatt_server demo, every char has an unique UUID and that’s why we only use the first char in `char_elem_result`, which is the pointer to the characteristic of the service. Count initially stores the number of the characteristics that the client wants to find, and will be updated with the number of the characteristics that have been actually found in the gattc cache with `esp_ble_gattc_get_char_by_uuid`.
|
||||
|
||||
##REGISTERING FOR NOTIFICATIONS
|
||||
## Registering for Notifications
|
||||
|
||||
The client can register to receive notifications from the server every time the characteristic value changes. In this example, we want to register for notifications of the characteristic identified with an UUID of 0xff01. After getting all the characteristics, we check the properties of the received characteristic, then use the `esp_ble_gattc_register_for_notify()` function to register notifications. The function arguments are the GATT interface, the address of the remote server, and the handle we want to register for notifications.
|
||||
|
||||
@ -712,7 +722,7 @@ The value to write is “1” to enable notifications. We also pass `ESP_GATT_WR
|
||||
|
||||
|
||||
|
||||
##CONCLUSION
|
||||
## Conclusion
|
||||
|
||||
We have reviewed the GATT Client example code for the ESP32. This example scans for nearby devices and searches for services and characteristics of servers of interest. When the server of interest is found, a connection is made with that server and a search for services is performed. Finally, the client looks for a specific characteristic in the services found, if found, gets the characteristic value and registers for notifications to that characteristic. This is done by registering one Application Profile and following a sequence of events to configure the GAP and GATT parameters required.
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
#GATT SECURITY SERVER EXAMPLE WALKTHROUGH
|
||||
# Gatt Security Server Example Walkthrough
|
||||
|
||||
##INTRODUCTION
|
||||
## 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.
|
||||
|
||||
@ -8,7 +8,7 @@ BLE security involves three interrelated concepts: pairing, bonding and encrypti
|
||||
|
||||
This document only describes the security configuration. The rest of the GATT server functionalities, such as defining the service table, are explained in the GATT Server example walkthrough documentation. For a better understanding of this example workflow, it is recommended that the reader is familiar with the pairing feature exchange and key generation defined in the section 3.5 of the [Bluetooth Specification Version 4.2](https://www.bluetooth.com/specifications/bluetooth-core-specification) [Vol 3, Part H].
|
||||
|
||||
##SETTING SECURITY PARAMETERS
|
||||
## Setting Security Parameters
|
||||
|
||||
The ESP32 requires a series of security parameters in order to define how the pairing request and response are going to be built. The Pairing Response packet built by the GATT Server includes fields such as the input/output capabilities, Secure Connections pairing, authenticated Man-In-The-Middle (MITM) protection or no security requirements (see Section 2.3.1 of the [Bluetooth Specification Version 4.2](https://www.bluetooth.com/specifications/bluetooth-core-specification) [Vol 3, Part H]). In this example, this procedure is done in the `app_main()` function. The pairing request is sent by the initiator which in this case is a remote GATT client. The ESP32 server implemented in this example receives this request and replies with a pairing response, which contains the same security parameters in order for both devices to agree on the resources available and the applicable pairing algorithm (*Just Works* or *Passkey Entry*). Both the pairing request and response commands have the following parameters:
|
||||
|
||||
@ -23,74 +23,74 @@ In code, these parameters are defined as follows:
|
||||
|
||||
* *IO Capability*:
|
||||
|
||||
```c
|
||||
esp_ble_io_cap_t iocap = ESP_IO_CAP_NONE;//set the IO capability to No Input No Output
|
||||
```
|
||||
```c
|
||||
esp_ble_io_cap_t iocap = ESP_IO_CAP_NONE;//set the IO capability to No Input No Output
|
||||
```
|
||||
|
||||
The possible values for *IO Capabilities* are:
|
||||
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 */
|
||||
```
|
||||
```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 */
|
||||
```
|
||||
|
||||
* *Authorization Request*:
|
||||
|
||||
```c
|
||||
esp_ble_auth_req_t auth_req = ESP_LE_AUTH_BOND;
|
||||
```c
|
||||
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:
|
||||
The possible values for *Authorization Request* are a combination of Bonding, MITM protection and Secure Connections requests:
|
||||
|
||||
```c
|
||||
ESP_LE_AUTH_NO_BOND: No bonding.
|
||||
ESP_LE_AUTH_BOND: Bonding is performed.
|
||||
ESP_LE_AUTH_REQ_MITM: MITM Protection is enabled.
|
||||
ESP_LE_AUTH_REQ_SC_ONLY: Secure Connections without bonding enabled.
|
||||
ESP_LE_AUTH_REQ_SC_BOND: Secure Connections with bonding enabled.
|
||||
ESP_LE_AUTH_REQ_SC_MITM: Secure Connections with MITM Protection and no bonding enabled.
|
||||
ESP_LE_AUTH_REQ_SC_MITM_BOND: Secure Connections with MITM Protection and bonding enabled.
|
||||
```
|
||||
```c
|
||||
ESP_LE_AUTH_NO_BOND: No bonding.
|
||||
ESP_LE_AUTH_BOND: Bonding is performed.
|
||||
ESP_LE_AUTH_REQ_MITM: MITM Protection is enabled.
|
||||
ESP_LE_AUTH_REQ_SC_ONLY: Secure Connections without bonding enabled.
|
||||
ESP_LE_AUTH_REQ_SC_BOND: Secure Connections with bonding enabled.
|
||||
ESP_LE_AUTH_REQ_SC_MITM: Secure Connections with MITM Protection and no bonding enabled.
|
||||
ESP_LE_AUTH_REQ_SC_MITM_BOND: Secure Connections with MITM Protection and bonding enabled.
|
||||
```
|
||||
|
||||
* *Maximum Encryption Key Size*:
|
||||
|
||||
```c
|
||||
uint8_t key_size = 16; //the key size should be 7~16 bytes
|
||||
```
|
||||
```c
|
||||
uint8_t key_size = 16; //the key size should be 7~16 bytes
|
||||
```
|
||||
|
||||
* *Initiator Key Distribution/Generation*:
|
||||
|
||||
```c
|
||||
uint8_t init_key = ESP_BLE_ENC_KEY_MASK | ESP_BLE_ID_KEY_MASK;
|
||||
```
|
||||
```c
|
||||
uint8_t init_key = ESP_BLE_ENC_KEY_MASK | ESP_BLE_ID_KEY_MASK;
|
||||
```
|
||||
|
||||
The initiator distributes the LTK and IRK keys by the setting the EncKey and IdKey masks.
|
||||
The initiator distributes the LTK and IRK keys by the setting the EncKey and IdKey masks.
|
||||
|
||||
* *Responder Key Distribution/Generation*:
|
||||
|
||||
```c
|
||||
uint8_t rsp_key = ESP_BLE_ENC_KEY_MASK | ESP_BLE_ID_KEY_MASK;
|
||||
```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.
|
||||
The responder distributes the LTK and IRK keys by the setting the *EncKey* and *IdKey* masks.
|
||||
|
||||
Once defined, the parameters are set using the `esp_ble_gap_set_security_param()` function. This function sets the parameter type, the parameter value and the parameter length:
|
||||
Once defined, the parameters are set using the `esp_ble_gap_set_security_param()` function. This function sets the parameter type, the parameter value and the parameter length:
|
||||
|
||||
```c
|
||||
esp_ble_gap_set_security_param(ESP_BLE_SM_AUTHEN_REQ_MODE, &auth_req, sizeof(uint8_t));
|
||||
esp_ble_gap_set_security_param(ESP_BLE_SM_IOCAP_MODE, &iocap, sizeof(uint8_t));
|
||||
esp_ble_gap_set_security_param(ESP_BLE_SM_MAX_KEY_SIZE, &key_size, sizeof(uint8_t));
|
||||
esp_ble_gap_set_security_param(ESP_BLE_SM_SET_INIT_KEY, &init_key, sizeof(uint8_t));
|
||||
esp_ble_gap_set_security_param(ESP_BLE_SM_SET_RSP_KEY, &rsp_key, sizeof(uint8_t));
|
||||
```
|
||||
```c
|
||||
esp_ble_gap_set_security_param(ESP_BLE_SM_AUTHEN_REQ_MODE, &auth_req, sizeof(uint8_t));
|
||||
esp_ble_gap_set_security_param(ESP_BLE_SM_IOCAP_MODE, &iocap, sizeof(uint8_t));
|
||||
esp_ble_gap_set_security_param(ESP_BLE_SM_MAX_KEY_SIZE, &key_size, sizeof(uint8_t));
|
||||
esp_ble_gap_set_security_param(ESP_BLE_SM_SET_INIT_KEY, &init_key, sizeof(uint8_t));
|
||||
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.
|
||||
|
||||
##CONNECTING AND BONDING TO A PEER DEVICE
|
||||
## Connecting and Bonding to a Peer Device
|
||||
|
||||
The security parameters set previously are stored locally to be used later when the master device connects to the slave. Every time a remote device connects to the local GATT server, the connection event `ESP_GATTS_CONNECT_EVT` is triggered. This event is employed to perform the pairing and bonding process by invoking the `esp_ble_set_encryption()` function which takes as parameters the remote device address and the type of encryption that will be performed. The BLE stack then executes the actual pairing process in the background. In this example, the encryption includes MITM protection.
|
||||
|
||||
@ -122,7 +122,7 @@ case ESP_GAP_BLE_PASSKEY_NOTIF_EVT:
|
||||
The combination of input and output capabilities that determine which algorithm is used are:
|
||||
|
||||
|
||||
##EXCHANGING KEYS
|
||||
## Exchanging Keys
|
||||
|
||||
When the client connects to the server and the pairing is done successfully, the keys indicated by the `init_key` and `rsp_key` security parameters are exchanged. In this example the following keys are generated and distributed:
|
||||
|
||||
@ -160,7 +160,7 @@ case ESP_GAP_BLE_AUTH_CMPL_EVT: {
|
||||
}
|
||||
```
|
||||
|
||||
##SECURITY PERMISSIONS FOR ATTRIBUTES
|
||||
## Security Permission for Attributes
|
||||
|
||||
When defining the attributes of the server, different permissions can be set to the write and read events. Attributes permissions can be:
|
||||
|
||||
@ -212,7 +212,7 @@ When creating the services table, each attribute can have permissions to read or
|
||||
…
|
||||
```
|
||||
|
||||
##SECURITY REQUESTS
|
||||
## Security Requests
|
||||
|
||||
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.
|
||||
|
||||
@ -223,7 +223,7 @@ During the communication between a master and a slave device, the slave might re
|
||||
esp_ble_gap_security_rsp(param->ble_security.ble_req.bd_addr, true);
|
||||
break;
|
||||
```
|
||||
##CONCLUSION
|
||||
## 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.
|
||||
|
||||
|
@ -1,8 +1,11 @@
|
||||
# GATT SERVER EXAMPLE WALKTHROUGH
|
||||
##INTRODUCTION
|
||||
# Gatt Server Example Walkthrough
|
||||
|
||||
## Introduction
|
||||
|
||||
In this document, we review the GATT SERVER example code which implements a Bluetooth Low Energy (BLE) Generic Attribute Profile (GATT) Server on the ESP32. This example is designed around two Application Profiles and a series of events that are handled in order to execute a sequence of configuration steps, such as defining advertising parameters, updating connection parameters and creating services and characteristics. In addition, this example handles read and write events, including a Write Long Characteristic request, which divides the incoming data into chunks so that the data can fit in the Attribute Protocol (ATT) message. This document follows the program workflow and breaks down the code in order to make sense of every section and reasoning behind the implementation.
|
||||
|
||||
#INCLUDES
|
||||
## Includes
|
||||
|
||||
First, let’s take a look at the includes:
|
||||
|
||||
```c
|
||||
@ -30,7 +33,8 @@ These includes are required for the FreeRTOS and underlaying system components t
|
||||
* `esp_gap_ble_api.h`: implements GAP configuration, such as advertising and connection parameters.
|
||||
* `esp_gatts_api.h`: implements GATT configuration, such as creating services and characteristics.
|
||||
|
||||
## MAIN ENTRY POINT
|
||||
## Main Entry Point
|
||||
|
||||
The entry point to this example is the app_main() function:
|
||||
|
||||
```c
|
||||
@ -101,7 +105,8 @@ The main function starts by initializing the non-volatile storage library. This
|
||||
```c
|
||||
ret = nvs_flash_init();
|
||||
```
|
||||
##BT CONTROLLER AND STACK INITIALIZATION
|
||||
## BT Controller and Stack Initialization
|
||||
|
||||
The main function also initializes the BT controller by first creating a BT controller configuration structure named `esp_bt_controller_config_t` with default settings generated by the `BT_CONTROLLER_INIT_CONFIG_DEFAULT()` macro. The BT controller implements the Host Controller Interface (HCI) on the controller side, the Link Layer (LL) and the Physical Layer (PHY). The BT Controller is invisible to the user applications and deals with the lower layers of the BLE stack. The controller configuration includes setting the BT controller stack size, priority and HCI baud rate. With the settings created, the BT controller is initialized and enabled with the `esp_bt_controller_init()` function:
|
||||
|
||||
```c
|
||||
@ -135,13 +140,13 @@ esp_ble_gap_register_callback(gap_event_handler);
|
||||
```
|
||||
The functions `gatts_event_handler()` and `gap_event_handler()` handle all the events that are pushed to the application from the BLE stack.
|
||||
|
||||
##APPLICATION PROFILES
|
||||
## 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.
|
||||
|
||||
![](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:
|
||||
|
||||
* GATT interface
|
||||
@ -196,7 +201,8 @@ esp_ble_gatts_app_register(PROFILE_A_APP_ID);
|
||||
esp_ble_gatts_app_register(PROFILE_B_APP_ID);
|
||||
```
|
||||
|
||||
##SETTING GAP PARAMETERS
|
||||
## Setting GAP Parameters
|
||||
|
||||
The register application event is the first one that is triggered during the lifetime of the program, this example uses the Profile A GATT event handle to configure the advertising parameters upon registration. This example has the option to use both standard Bluetooth Core Specification advertising parameters or a customized raw buffer. The option can be selected with the `CONFIG_SET_RAW_ADV_DATA` define. The raw advertising data can be used to implement iBeacons, Eddystone or other proprietaries, and custom frame types such as the ones used for Indoor Location Services that are different from the standard specifications.
|
||||
|
||||
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:
|
||||
@ -283,7 +289,8 @@ static void gatts_profile_a_event_handler(esp_gatts_cb_event_t event, esp_gatt_i
|
||||
adv_config_done |= scan_rsp_config_flag;
|
||||
#endif
|
||||
```
|
||||
##GAP EVENT HANDLER
|
||||
## 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.
|
||||
|
||||
```c
|
||||
@ -379,7 +386,8 @@ If the advertising started successfully, an `ESP_GAP_BLE_ADV_START_COMPLETE_EVT`
|
||||
…
|
||||
```
|
||||
|
||||
##GATT EVENT HANDLERS
|
||||
## GATT Event Handlers
|
||||
|
||||
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 */`
|
||||
@ -417,7 +425,8 @@ static void gatts_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_
|
||||
}
|
||||
```
|
||||
|
||||
##CREATING SERVICES
|
||||
## 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:
|
||||
|
||||
```c
|
||||
@ -461,7 +470,8 @@ static void gatts_profile_b_event_handler(esp_gatts_cb_event_t event, esp_gatt_i
|
||||
}
|
||||
```
|
||||
|
||||
##STARTING SERVICES AND CREATING CHARACTERISTICS
|
||||
## Starting Services and Creating Characteristics
|
||||
|
||||
When a service is created successfully, an `ESP_GATTS_CREATE_EVT` event managed by the profile GATT handler is triggered, and can be used to start the service and add characteristics to the service. For the case of Profile A, the service is started and the characteristics are added as follows:
|
||||
|
||||
```c
|
||||
@ -527,7 +537,7 @@ The characteristic initial value must be a non-null object and the characteristi
|
||||
|
||||
Finally, the characteristic is configured in a way that it is required to send a response manually every time the characteristic is read or written, instead of letting the stack auto respond. This is configured by setting the last parameter of the `esp_ble_gatts_add_char()` function, representing the attribute response control parameter, to `ESP_GATT_RSP_BY_APP` or NULL.
|
||||
|
||||
##CREATING THE CHARACTERISTIC DESCRIPTOR
|
||||
## Creating the Characteristic Descriptor
|
||||
|
||||
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:
|
||||
|
||||
@ -579,7 +589,8 @@ Once the descriptor is added, the `ESP_GATTS_ADD_CHAR_DESCR_EVT` event is trigge
|
||||
```
|
||||
This procedure is repeated in the Profile B event handler, in order to create the services and characteristics for that profile.
|
||||
|
||||
##CONNECTION EVENT
|
||||
## Connection Event
|
||||
|
||||
An `ESP_GATTS_CONNECT_EVT` is triggered when a client has connected to the GATT server. This event is used to update the connection parameters, such as latency, minimum connection interval, maximum connection interval and time out. The connection parameters are stored into an `esp_ble_conn_update_params_t` structure, and then passed to the `esp_ble_gap_update_conn_params()` function. The update connection parameters procedure needs to be done only once, therefore the Profile B connection event handler does not include the `esp_ble_gap_update_conn_params()` function. Finally, the connection ID returned by the event is stored in the profile table.
|
||||
|
||||
Profile A connection event:
|
||||
@ -641,7 +652,7 @@ The `esp_ble_gap_update_conn_params()` function triggers a GAP event `ESP_GAP_BL
|
||||
break;
|
||||
```
|
||||
|
||||
##MANAGING READ EVENTS
|
||||
## 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:
|
||||
|
||||
@ -675,7 +686,8 @@ case ESP_GATTS_READ_EVT: {
|
||||
}
|
||||
```
|
||||
|
||||
##MANAGING WRITE EVENTS
|
||||
## Managing Write Events
|
||||
|
||||
The write events are represented by the `ESP_GATTS_WRITE_EVT` event, which has the following parameters:
|
||||
|
||||
* `uint16_t conn_id; /*!< Connection id */`
|
||||
@ -815,6 +827,7 @@ if (param->write.is_prep){
|
||||
}
|
||||
…
|
||||
```
|
||||
|
||||
To handle long characteristic write, a prepare buffer structure is defined and instantiated:
|
||||
|
||||
```c
|
||||
@ -906,6 +919,7 @@ void example_exec_write_event_env(prepare_type_env_t *prepare_write_env, esp_ble
|
||||
prepare_write_env->prepare_len = 0;
|
||||
}
|
||||
```
|
||||
|
||||
The executive write is used to either confirm or cancel the write procedure done before, by the Long Characteristic Write procedure. In order to do this, the function checks for the `exec_write_flag` in the parameters received with the event. If the flag equals the execute flag represented by `exec_write_flag`, the write is confirmed and the buffer is printed in the log; if not, then it means the write is canceled and all the data that has been written is deleted.
|
||||
|
||||
```c
|
||||
@ -928,7 +942,7 @@ if (prepare_write_env->prepare_buf) {
|
||||
prepare_write_env->prepare_len = 0;
|
||||
```
|
||||
|
||||
##CONCLUSION
|
||||
## 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.
|
||||
|
||||
|
||||
|
@ -3,11 +3,13 @@
|
||||
## 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 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.pdf)
|
||||
![Table-like data structure representing the Heart Rate Service](image/Table-like data structure representing the Heart Rate Service.png)
|
||||
|
||||
## Includes
|
||||
|
||||
Let’s start by taking a look at the included headers in the ``gatts_table_creat_demo.c`` file:
|
||||
|
||||
```
|
||||
@ -35,6 +37,7 @@ These includes are required for the *FreeRTOS* and underlaying system components
|
||||
* ``esp_gatts_api.h``: implements GATT Server configuration such as creating services and characteristics.
|
||||
|
||||
## Service Table
|
||||
|
||||
The header file ``gatts_table_creat_demo.h`` is where an enumeration of the services and characteristics is created:
|
||||
|
||||
```
|
||||
@ -68,6 +71,7 @@ The enumeration elements are set up in the same order as the Heart Rate Profile
|
||||
* HRS_IDX_NB: Number of table elements.
|
||||
|
||||
## Main Entry Point
|
||||
|
||||
The entry point to this example is the ``app_main()`` function:
|
||||
|
||||
```
|
||||
@ -156,6 +160,7 @@ esp_ble_gatts_app_register(ESP_HEART_RATE_APP_ID);
|
||||
```
|
||||
|
||||
## Setting GAP Parameters
|
||||
|
||||
The register application event is the first one that is triggered during the lifetime of the program. This example uses this event to configure advertising parameters upon registration in the profile event handler. The functions used to achieve this are:
|
||||
|
||||
* ``esp_ble_gap_set_device_name()``: used to set the advertised device name.
|
||||
@ -222,6 +227,7 @@ static void gatts_profile_event_handler(esp_gatts_cb_event_t event,
|
||||
```
|
||||
|
||||
## GAP Event Handler
|
||||
|
||||
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:
|
||||
|
||||
```
|
||||
@ -304,6 +310,7 @@ If the advertising started successfully, an ``ESP_GAP_BLE_ADV_START_COMPLETE_EVT
|
||||
```
|
||||
|
||||
## GATT Event Handlers
|
||||
|
||||
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 */
|
||||
|
@ -10,7 +10,7 @@ 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.
|
||||
|
||||
![Multi-Connection GATT Client Flowchart](image/Multi-connection GATT Client Flowchart.pdf)
|
||||
![Multi-Connection GATT Client Flowchart](image/Multi-connection GATT Client Flowchart.png)
|
||||
|
||||
## Includes
|
||||
The multi-connection example’s main source file is ``gattc_multi_connect.c``. For details, see Section **Includes** in **GATT Client Example Walkthrough**.
|
||||
@ -39,7 +39,7 @@ Application Profiles are a way to group functionality. They are designed so that
|
||||
#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.pdf)
|
||||
![ESP32 GATT Multi-Connect Client Application Profiles](image/ESP32 GATT Multi-connect Client Application Profiles.png)
|
||||
|
||||
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.
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user