Skip to content

Instantly share code, notes, and snippets.

@heiko-r
Created October 26, 2017 12:39
Show Gist options
  • Star 18 You must be signed in to star a gist
  • Fork 5 You must be signed in to fork a gist
  • Save heiko-r/f284d95141871e12ca0164d9070d61b4 to your computer and use it in GitHub Desktop.
Save heiko-r/f284d95141871e12ca0164d9070d61b4 to your computer and use it in GitHub Desktop.
ESP32 BLE GATT server example
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "esp_system.h"
#include "esp_log.h"
#include "bt.h"
#include "bta_api.h"
#include "esp_gap_ble_api.h"
#include "esp_bt_main.h"
#include "esp_bt_main.h"
#include "ble_config.h"
#include "version.h"
/* The "layout" of the services, characteristics and descriptors is as follows:
* - Service: Battery Service
* - Characteristic: Battery Level
* - Descriptor: Client Characteristic Configuration
* - Descriptor: Characteristic Presentation Format (8bit int, percent)
* - Service: Teacher's Button Service
* - Characteristic: Button number
* - Descriptor: Characteristic User Description
* - Descriptor: Client Characteristic Configuration
* - Descriptor: Characteristic Presentation Format (8bit int, unitless)
* - Characteristic: Reference voltage
* - Descriptor: Characteristic User Description
* - Descriptor: Client Characteristic Configuration
* - Descriptor: Characteristic Presentation Format (16bit int, unitless)
* - Characteristic: Firmware version
* - Descriptor: Characteristic User Description
* - Descriptor: Client Characteristic Configuration
* - Descriptor: Characteristic Presentation Format (UTF-8 string, unitless)
*/
/* First, we define the variables we want to share via GATT.*/
/* Characteristic/descriptor values: Battery level */
// battery level in percent as integer
uint8_t battery_service_char_battery_level_int = 42;
// Client Characteristic Configuration: 0x00: notifications disabled; 0x00: indications disabled
uint8_t battery_service_char_battery_level_descr_config_str[2] = {0x00,0x00};
// Characteristic Presentation Format: 0x04: unsigned 8 bit integer; 0x00: no exponent; 0x27AD: unit = percentage; 0x01: Bluetooth SIG namespace; 0x0000: No description
uint8_t battery_service_char_battery_level_descr_present_str[7] = {0x04, 0x00, 0xAD, 0x27, 0x01, 0x00, 0x00};
/* Characteristic/descriptor values: Button number */
// pushbutton number to connect to as integer
uint8_t teacherbutton_service_char_button_number_int = 0; // button number will default to 0 (i.e. bind to nearest button upon startup) if it is not yet set in NVS
// Characteristic User Description:
uint8_t teacherbutton_service_char_button_number_descr_user_str[13] = "Button number";
// Client Characteristic Configuration: 0x00: notifications disabled; 0x00: indications disabled
uint8_t teacherbutton_service_char_button_number_descr_config_str[2] = {0x00,0x00};
// Characteristic Presentation Format: 0x04: unsigned 8 bit integer; 0x00: no exponent; 0x2700: unit = unitless; 0x01: Bluetooth SIG namespace; 0x0000: No description
uint8_t teacherbutton_service_char_button_number_descr_present_str[7] = {0x04, 0x00, 0x00, 0x27, 0x01, 0x00, 0x00};
/* Characteristic/descriptor values: Reference voltage */
// reference voltage for ADC claibration
#define REF_VOLTAGE_DEFAULT 1100
uint8_t teacherbutton_service_char_ref_voltage_int[2] = {REF_VOLTAGE_DEFAULT & 0x00FF, (REF_VOLTAGE_DEFAULT >> 8) & 0x00FF }; // ADC reference voltage in mV as measured externally; default: 110mV
// Characteristic User Description:
uint8_t teacherbutton_service_char_ref_voltage_descr_user_str[18] = "Reference voltage";
// Client Characteristic Configuration: 0x00: notifications disabled; 0x00: indications disabled
uint8_t teacherbutton_service_char_ref_voltage_descr_config_str[2] = {0x00,0x00};
// Characteristic Presentation Format: 0x06: unsigned 16 bit integer; -3: 10^(-3); 0x2728: unit = volt; 0x01: Bluetooth SIG namespace; 0x0000: No description
uint8_t teacherbutton_service_char_ref_voltage_descr_present_str[7] = {0x06, -3, 0x28, 0x27, 0x01, 0x00, 0x00};
/* Characteristic/descriptor values: Firmware version */
// 3 bytes firmware version
uint8_t teacherbutton_service_char_firmware_version_str[3] = {EG_VERSION_MAJOR, EG_VERSION_MINOR, EG_VERSION_REVISION};
// Characteristic User Description:
uint8_t teacherbutton_service_char_firmware_version_descr_user_str[17] = "Firmware version";
// Client Characteristic Configuration: 0x00: notifications disabled; 0x00: indications disabled
uint8_t teacherbutton_service_char_firmware_version_descr_config_str[2] = {0x00,0x00};
// Characteristic Presentation Format: 0x1B: opaque structure; 0x00: no exponent; 0x2700: unit = unitless; 0x01: Bluetooth SIG namespace; 0x0000: No description
uint8_t teacherbutton_service_char_firmware_version_descr_present_str[7] = {0x1B, 0x00, 0x00, 0x27, 0x01, 0x00, 0x00};
/* The ESP GATTS API doesn't take the above characteristic and descriptor
* values directly, but needs them wrapped in these structures:
*/
esp_attr_value_t gatts_battery_service_char_battery_level_val = {
.attr_max_len = GATTS_CHAR_VAL_LEN_MAX,
.attr_len = sizeof(battery_service_char_battery_level_int),
.attr_value = &battery_service_char_battery_level_int,
};
esp_attr_value_t gatts_battery_service_char_battery_level_descr_config_val = {
.attr_max_len = GATTS_CHAR_VAL_LEN_MAX,
.attr_len = sizeof(battery_service_char_battery_level_descr_config_str),
.attr_value = battery_service_char_battery_level_descr_config_str,
};
esp_attr_value_t gatts_battery_service_char_battery_level_descr_present_val = {
.attr_max_len = GATTS_CHAR_VAL_LEN_MAX,
.attr_len = sizeof(battery_service_char_battery_level_descr_present_str),
.attr_value = battery_service_char_battery_level_descr_present_str,
};
esp_attr_value_t gatts_teacherbutton_service_char_button_number_val = {
.attr_max_len = GATTS_CHAR_VAL_LEN_MAX,
.attr_len = sizeof(teacherbutton_service_char_button_number_int),
.attr_value = &teacherbutton_service_char_button_number_int,
};
esp_attr_value_t gatts_teacherbutton_service_char_button_number_descr_user_val = {
.attr_max_len = GATTS_CHAR_VAL_LEN_MAX,
.attr_len = sizeof(teacherbutton_service_char_button_number_descr_user_str),
.attr_value = teacherbutton_service_char_button_number_descr_user_str,
};
esp_attr_value_t gatts_teacherbutton_service_char_button_number_descr_config_val = {
.attr_max_len = GATTS_CHAR_VAL_LEN_MAX,
.attr_len = sizeof(teacherbutton_service_char_button_number_descr_config_str),
.attr_value = teacherbutton_service_char_button_number_descr_config_str,
};
esp_attr_value_t gatts_teacherbutton_service_char_button_number_descr_present_val = {
.attr_max_len = GATTS_CHAR_VAL_LEN_MAX,
.attr_len = sizeof(teacherbutton_service_char_button_number_descr_present_str),
.attr_value = teacherbutton_service_char_button_number_descr_present_str,
};
esp_attr_value_t gatts_teacherbutton_service_char_ref_voltage_val = {
.attr_max_len = GATTS_CHAR_VAL_LEN_MAX,
.attr_len = sizeof(teacherbutton_service_char_ref_voltage_int),
.attr_value = teacherbutton_service_char_ref_voltage_int,
};
esp_attr_value_t gatts_teacherbutton_service_char_ref_voltage_descr_user_val = {
.attr_max_len = GATTS_CHAR_VAL_LEN_MAX,
.attr_len = sizeof(teacherbutton_service_char_ref_voltage_descr_user_str),
.attr_value = teacherbutton_service_char_ref_voltage_descr_user_str,
};
esp_attr_value_t gatts_teacherbutton_service_char_ref_voltage_descr_config_val = {
.attr_max_len = GATTS_CHAR_VAL_LEN_MAX,
.attr_len = sizeof(teacherbutton_service_char_ref_voltage_descr_config_str),
.attr_value = teacherbutton_service_char_ref_voltage_descr_config_str,
};
esp_attr_value_t gatts_teacherbutton_service_char_ref_voltage_descr_present_val = {
.attr_max_len = GATTS_CHAR_VAL_LEN_MAX,
.attr_len = sizeof(teacherbutton_service_char_ref_voltage_descr_present_str),
.attr_value = teacherbutton_service_char_ref_voltage_descr_present_str,
};
esp_attr_value_t gatts_teacherbutton_service_char_firmware_version_val = {
.attr_max_len = GATTS_CHAR_VAL_LEN_MAX,
.attr_len = sizeof(teacherbutton_service_char_firmware_version_str),
.attr_value = teacherbutton_service_char_firmware_version_str,
};
esp_attr_value_t gatts_teacherbutton_service_char_firmware_version_descr_user_val = {
.attr_max_len = GATTS_CHAR_VAL_LEN_MAX,
.attr_len = sizeof(teacherbutton_service_char_firmware_version_descr_user_str),
.attr_value = teacherbutton_service_char_firmware_version_descr_user_str,
};
esp_attr_value_t gatts_teacherbutton_service_char_firmware_version_descr_config_val = {
.attr_max_len = GATTS_CHAR_VAL_LEN_MAX,
.attr_len = sizeof(teacherbutton_service_char_firmware_version_descr_config_str),
.attr_value = teacherbutton_service_char_firmware_version_descr_config_str,
};
esp_attr_value_t gatts_teacherbutton_service_char_firmware_version_descr_present_val = {
.attr_max_len = GATTS_CHAR_VAL_LEN_MAX,
.attr_len = sizeof(teacherbutton_service_char_firmware_version_descr_present_str),
.attr_value = teacherbutton_service_char_firmware_version_descr_present_str,
};
/* To define each of our services, we just set the .gatts_if to none and the
* number of handles this service will use as calculated in ble_config.h
* The gatts_event_handler() below will take care of setting all the values
* when the service is initialised.
*
* 1st service: Battery Service
* 2nd service: Teacher's Button Service
*/
struct gatts_service_inst gatts_service[GATTS_SERVICE_NUM] = {
{
.gatts_if = ESP_GATT_IF_NONE, /* gatts_if not known yet, so initial is ESP_GATT_IF_NONE */
.num_handles = GATTS_BATTERY_SERVICE_NUM_HANDLES
},
{
.gatts_if = ESP_GATT_IF_NONE, /* gatts_if not known yet, so initial is ESP_GATT_IF_NONE */
.num_handles = GATTS_TEACHERBUTTON_SERVICE_NUM_HANDLES
}
};
/* Here we define all the characteristics for all the services.
* To associate a characteristic with its service, set the .service_pos to the
* corresponding index in the gatts_service array above:
* 0 = 1st service, 1 = 2nd service, ...
*
* Standard bluetooth characteristics such as "Battery level" (see
* https://www.bluetooth.com/specifications/gatt/characteristics) use a 16 bit
* UUID. For custom characteristics, we have to use a random 128 bit UUID as
* generated by https://www.uuidgenerator.net/ .
* For better readability, our random UUIDs are defined in ble_server.h .
*
* The .char_handle is set automatically by the gatts_check_add_char() function
* below, once the characteristic has been added to the service.
*
* .char_nvs is the key under which the characteristic's value is stored in NVS.
* Its maximum length is 15 bytes (=15 characters). The array is 16 bytes long
* instead of just 15, because a string we add to it will be null-terminated
* (\0 is automatically added as last element. Set it to "" when the
* value is not stored in NVS.
*/
struct gatts_char_inst gatts_char[GATTS_CHAR_NUM] = {
{
/* Battery Service -> Battery Level */
.service_pos = 0, // Battery service
.char_uuid.len = ESP_UUID_LEN_16, // Battery Level
.char_uuid.uuid.uuid16 = ESP_GATT_UUID_BATTERY_LEVEL,
.char_perm = ESP_GATT_PERM_READ,
.char_property = ESP_GATT_CHAR_PROP_BIT_READ | ESP_GATT_CHAR_PROP_BIT_NOTIFY,
.char_val = &gatts_battery_service_char_battery_level_val,
.char_control = NULL,
.char_handle = 0,
.char_nvs = ""
},
{
/* Teacher's Button Service -> Button number */
.service_pos = 1, // Teacher's Button service
.char_uuid.len = ESP_UUID_LEN_128, // Custom characteristic -> 128bit UUID
.char_uuid.uuid.uuid128 = GATTS_TEACHERBUTTON_BUTTON_NUMBER_CHAR_UUID,
.char_perm = ESP_GATT_PERM_READ | ESP_GATT_PERM_WRITE,
.char_property = ESP_GATT_CHAR_PROP_BIT_READ | ESP_GATT_CHAR_PROP_BIT_WRITE,
.char_val = &gatts_teacherbutton_service_char_button_number_val,
.char_control = NULL,
.char_handle = 0,
.char_nvs = "button_number"
},
{
/* Teacher's Button Service -> Reference voltage */
.service_pos = 1, // Teacher's Button service
.char_uuid.len = ESP_UUID_LEN_128, // Custom characteristic -> 128bit UUID
.char_uuid.uuid.uuid128 = GATTS_TEACHERBUTTON_REF_VOLTAGE_CHAR_UUID,
.char_perm = ESP_GATT_PERM_READ | ESP_GATT_PERM_WRITE,
.char_property = ESP_GATT_CHAR_PROP_BIT_READ | ESP_GATT_CHAR_PROP_BIT_WRITE | ESP_GATT_CHAR_PROP_BIT_NOTIFY,
.char_val = &gatts_teacherbutton_service_char_ref_voltage_val,
.char_control = NULL,
.char_handle = 0,
.char_nvs = "ref_voltage"
},
{
/* Teacher's Button Service -> Firmware version */
.service_pos = 1, // Teacher's Button service
.char_uuid.len = ESP_UUID_LEN_128, // Custom characteristic -> 128bit UUID
.char_uuid.uuid.uuid128 = GATTS_TEACHERBUTTON_FIRMWARE_VERSION_CHAR_UUID,
.char_perm = ESP_GATT_PERM_READ,
.char_property = ESP_GATT_CHAR_PROP_BIT_READ,
.char_val = &gatts_teacherbutton_service_char_firmware_version_val,
.char_control = NULL,
.char_handle = 0,
.char_nvs = ""
}
};
/* Here we define all the descriptors for all the characteristics.
* To associate a descriptor to a characteristic, set the .char_pos to the
* corresponding index in the gl_char array above:
* 0 = 1st characteristic, 1 = 2nd characteristic, ...
*
* All we use here are standard bluetooth descriptors (see
* https://www.bluetooth.com/specifications/gatt/descriptors) with a 16 bit
* UUID.
*
* The .descr_handle is set automatically by the gatts_check_add_descr()
* function below, once the descriptor has been added to the characteristic.
*/
struct gatts_descr_inst gatts_descr[GATTS_DESCR_NUM] = {
{
/* Battery Level -> Client Characteristic Configuration */
.char_pos = 0,
.descr_uuid.len = ESP_UUID_LEN_16,
.descr_uuid.uuid.uuid16 = ESP_GATT_UUID_CHAR_CLIENT_CONFIG,
.descr_perm=ESP_GATT_PERM_READ | ESP_GATT_PERM_WRITE,
.descr_val = &gatts_battery_service_char_battery_level_descr_config_val,
.descr_control=NULL,
.descr_handle=0
},
{
/* Battery Level -> Characteristic Presentation Format */
.char_pos=0,
.descr_uuid.len = ESP_UUID_LEN_16,
.descr_uuid.uuid.uuid16 = ESP_GATT_UUID_CHAR_PRESENT_FORMAT,
.descr_perm=ESP_GATT_PERM_READ,
.descr_val = &gatts_battery_service_char_battery_level_descr_present_val,
.descr_control=NULL,
.descr_handle=0
},
{
/* Button number -> Characteristic User Description */
.char_pos=1,
.descr_uuid.len = ESP_UUID_LEN_16,
.descr_uuid.uuid.uuid16 = ESP_GATT_UUID_CHAR_DESCRIPTION,
.descr_perm=ESP_GATT_PERM_READ,
.descr_val = &gatts_teacherbutton_service_char_button_number_descr_user_val,
.descr_control=NULL,
.descr_handle=0
},
{
/* Button number -> Client Characteristic Configuration */
.char_pos = 1,
.descr_uuid.len = ESP_UUID_LEN_16,
.descr_uuid.uuid.uuid16 = ESP_GATT_UUID_CHAR_CLIENT_CONFIG,
.descr_perm=ESP_GATT_PERM_READ | ESP_GATT_PERM_WRITE,
.descr_val = &gatts_teacherbutton_service_char_button_number_descr_config_val,
.descr_control=NULL,
.descr_handle=0
},
{
/* Button number -> Characteristic Presentation Format */
.char_pos = 1,
.descr_uuid.len = ESP_UUID_LEN_16,
.descr_uuid.uuid.uuid16 = ESP_GATT_UUID_CHAR_PRESENT_FORMAT,
.descr_perm=ESP_GATT_PERM_READ,
.descr_val = &gatts_teacherbutton_service_char_button_number_descr_present_val,
.descr_control=NULL,
.descr_handle=0
},
{
/* Reference voltage -> Characteristic User Description */
.char_pos=2,
.descr_uuid.len = ESP_UUID_LEN_16,
.descr_uuid.uuid.uuid16 = ESP_GATT_UUID_CHAR_DESCRIPTION,
.descr_perm=ESP_GATT_PERM_READ,
.descr_val = &gatts_teacherbutton_service_char_ref_voltage_descr_user_val,
.descr_control=NULL,
.descr_handle=0
},
{
/* Reference voltage -> Client Characteristic Configuration */
.char_pos = 2,
.descr_uuid.len = ESP_UUID_LEN_16,
.descr_uuid.uuid.uuid16 = ESP_GATT_UUID_CHAR_CLIENT_CONFIG,
.descr_perm=ESP_GATT_PERM_READ | ESP_GATT_PERM_WRITE,
.descr_val = &gatts_teacherbutton_service_char_ref_voltage_descr_config_val,
.descr_control=NULL,
.descr_handle=0
},
{
/* Reference voltage -> Characteristic Presentation Format */
.char_pos = 2,
.descr_uuid.len = ESP_UUID_LEN_16,
.descr_uuid.uuid.uuid16 = ESP_GATT_UUID_CHAR_PRESENT_FORMAT,
.descr_perm=ESP_GATT_PERM_READ,
.descr_val = &gatts_teacherbutton_service_char_ref_voltage_descr_present_val,
.descr_control=NULL,
.descr_handle=0
},
{
/* Firmware version -> Characteristic User Description */
.char_pos=3,
.descr_uuid.len = ESP_UUID_LEN_16,
.descr_uuid.uuid.uuid16 = ESP_GATT_UUID_CHAR_DESCRIPTION,
.descr_perm=ESP_GATT_PERM_READ,
.descr_val = &gatts_teacherbutton_service_char_firmware_version_descr_user_val,
.descr_control=NULL,
.descr_handle=0
},
{
/* Firmware version -> Client Characteristic Configuration */
.char_pos = 3,
.descr_uuid.len = ESP_UUID_LEN_16,
.descr_uuid.uuid.uuid16 = ESP_GATT_UUID_CHAR_CLIENT_CONFIG,
.descr_perm=ESP_GATT_PERM_READ | ESP_GATT_PERM_WRITE,
.descr_val = &gatts_teacherbutton_service_char_firmware_version_descr_config_val,
.descr_control=NULL,
.descr_handle=0
},
{
/* Firmware version -> Characteristic Presentation Format */
.char_pos = 3,
.descr_uuid.len = ESP_UUID_LEN_16,
.descr_uuid.uuid.uuid16 = ESP_GATT_UUID_CHAR_PRESENT_FORMAT,
.descr_perm=ESP_GATT_PERM_READ,
.descr_val = &gatts_teacherbutton_service_char_firmware_version_descr_present_val,
.descr_control=NULL,
.descr_handle=0
}
};
#include "esp_gatts_api.h"
#define GATTS_TAG "EGTeachersButton" // Prepended to all logging output
#define GATTS_CHAR_VAL_LEN_MAX 22 // maximum length in bytes of a characteristic's value. TODO: find out how to determine this value?
/* Number of services, characteristics and descriptors used in ble_server.c.
* Needs to be equal to the size of the gatts_service, gatts_char and gatts_descr arrays.
*/
#define GATTS_SERVICE_NUM 2 // Battery Service and Teacher's Button Service
#define GATTS_CHAR_NUM 4 // Combined number of characteristics for all services
#define GATTS_DESCR_NUM 11 // Combined number of descriptors for all characteristics
#define GATTS_BATTERY_SERVICE_UUID 0xfb, 0x34, 0x9b, 0x5f, 0x80, 0x00, 0x00, 0x80, 0x00, 0x10, 0x00, 0x00, 0x0F, 0x18, 0x00, 0x00 // 16bit shortened UUID for Battery Service 0x180F expanded to 128 bit with Bluetooth Base UUID, in "reverse" LSB<->MSB order
#define GATTS_BATTERY_SERVICE_NUM_HANDLES 1+2*1+2 // 1 for Service Declaration, 2 for Battery Level characteristic, 1 for each descriptor
#define GATTS_TEACHERBUTTON_SERVICE_UUID 0xc5, 0xb9, 0x31, 0x68, 0xfa, 0x31, 0x09, 0xa0, 0x8f, 0x42, 0xa5, 0xd3, 0xe9, 0x17, 0xe2, 0x0b // random 128bit UUID for custom Teacher's Button Service: 0be217e9-d3a5-428f-a009-31fa6831b9c5
#define GATTS_TEACHERBUTTON_SERVICE_NUM_HANDLES 1+2*3+9 // 1 for Service Declaration, 2 for each characteristic, 1 for each descriptor
#define GATTS_TEACHERBUTTON_BUTTON_NUMBER_CHAR_UUID {0x76, 0xf6, 0x15, 0x1d, 0xd9, 0x2f, 0x0f, 0x8d, 0x4c, 0x46, 0xf0, 0xe5, 0x89, 0x5b, 0x0b, 0x67} // random 128bit UUID: 670b5b89-e5f0-464c-8d0f-2fd91d15f676
#define GATTS_TEACHERBUTTON_REF_VOLTAGE_CHAR_UUID {0xa0, 0x9e, 0x78, 0x63, 0xf0, 0x0e, 0x75, 0xbf, 0x55, 0x48, 0x22, 0x85, 0xa6, 0xf2, 0x16, 0x32} // random 128but UUID: 3216f2a6-8522-4855-bf75-0ef063789ea0
#define GATTS_TEACHERBUTTON_FIRMWARE_VERSION_CHAR_UUID {0x64, 0x5d, 0xff, 0x33, 0xd5, 0x2b, 0x42, 0xa1, 0xdc, 0x43, 0xce, 0x9f, 0x73, 0x03, 0x81, 0xc2} // random 128bit UUID: c2810373-9fce-43dc-a142-2bd533ff5d64
/* Profile IDs corresponding to the position in the gatts_service array (see
* ble_server.c).
*/
#define APP_ID_BATTERY_SERVICE 0
#define APP_ID_TEACHERBUTTON_SERVICE 1
/* Positions of the respective characteristics in the gatts_char array,
* counting from 0, so that we can more easily access them in the main code.
*/
#define GATTS_BATTERY_LEVEL_CHAR_POS 0
#define GATTS_BUTTON_NUMBER_CHAR_POS 1
#define GATTS_REF_VOLTAGE_CHAR_POS 2
/* GAP advertising configuration, determines how the teacher's button will
* advertise itself via BLE in configuration mode.
*/
#define BLE_DEVICE_NAME_LEN 23 // Length of the device name below. Count 2 bytes for the button number and add one for the (invisible) \0 at the end of the string
#define BLE_DEVICE_NAME "EG Teacher's Button %02u" // Base name used as device name for BLE advertising; %02u will be replaced by the configured button number
#define BLE_MANUFACTURER_DATA_LEN 2 // Length of the manufacturer specific advertising payload. Currently contains only the manufacturer ID (2 bytes)
#define BLE_MANUFACTURER_DATA {0xFF, 0xFF} // No manufacturer ID (see https://www.bluetooth.com/specifications/assigned-numbers/company-identifiers)
#define BLE_APPEARANCE 0x4C3 // External appearance of the device. 0x4C3 = 1219 = Button (Control Device), see https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.characteristic.gap.appearance.xml
#define BLE_SERVICE_DATA_LEN 0
#define BLE_SERVICE_DATA NULL // TODO: figure out what "service data" can be used for
#define BLE_ADV_MIN_INTERVAL 800 // Minimum interval in which the advertising data should be sent out. Range: 0x0020 to 0x4000 (X * 0.625ms) -> 500ms
#define BLE_ADV_MAX_INTERVAL 1600 // Maximum interval in which the advertising data should be sent out. Range: 0x0020 to 0x4000 (X * 0.625ms) -> 1000ms
/* The BLE connection parameters will change to these when another device
* connects to this teacher's button. Advertising is stopped in this case.
* TODO: figure out what these values mean
*/
#define BLE_CONNECTED_MAX_INTERVAL 400 // Min connection interval (X * 1.25ms, TODO: to be confirmed) -> 500ms
#define BLE_CONNECTED_MIN_INTERVAL 800 // Max connection interval (X * 1.25ms, TODO: to be confirmed) -> 1000ms
#define BLE_CONNECTED_TIMEOUT 400 // Supervision timeout for the LE Link. Range: 0x000A to 0x0C80 (X * 10ms) -> 4000ms
/* These structs define which attributes each service, characteristic or
* descriptor have.
* The individual services, characteristics and descriptors are then defined in
* gatt_config.c
*/
struct gatts_service_inst {
uint16_t gatts_if;
uint16_t app_id;
uint16_t conn_id;
uint16_t service_handle;
esp_gatt_srvc_id_t service_id;
uint16_t num_handles;
};
struct gatts_char_inst {
uint32_t service_pos;
esp_bt_uuid_t char_uuid;
esp_gatt_perm_t char_perm;
esp_gatt_char_prop_t char_property;
esp_attr_value_t *char_val;
esp_attr_control_t *char_control;
uint16_t char_handle;
char char_nvs[16];
};
struct gatts_descr_inst {
uint32_t char_pos;
esp_bt_uuid_t descr_uuid;
esp_gatt_perm_t descr_perm;
esp_attr_value_t *descr_val;
esp_attr_control_t *descr_control;
uint16_t descr_handle;
};
extern struct gatts_service_inst gatts_service[];
extern struct gatts_char_inst gatts_char[];
extern struct gatts_descr_inst gatts_descr[];
/* Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
*
* Modifications copyright 2017 Heiko Rothkranz
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/event_groups.h"
#include "esp_system.h"
#include "esp_log.h"
#include "nvs_flash.h"
#include "bt.h"
#include "bta_api.h"
#include "esp_gap_ble_api.h"
#include "esp_gatts_api.h"
#include "esp_bt_defs.h"
#include "esp_bt_main.h"
#include "esp_bt_main.h"
#include "sdkconfig.h"
#include "ble_config.h"
#include "ble_server.h"
/* ************************************************************ */
/* *************************** GAP **************************** */
/* ************************************************************ */
/* GAP is the BLE protocol for advertising. This section defines how the
* pushbutton will present itself to other devices. */
/* BLE services use 128 bit UUIDs for identification.
* If the service is one of those predefined by the Bluetooth SIG (see
* https://www.bluetooth.com/specifications/gatt/services), a shorter 16 or 32
* bit identifier can be used. The 16 bit UUID (e.g. 0x180F for the standard
* "Battery Service") is then extended with the Bluetooth Base UUID:
* xxxxxxxx-0000-1000-8000-00805F9B34FB (MSB->LSB notation).
* Custom services require a full 128 bit UUID which can be randomly generated
* here: https://www.uuidgenerator.net/
*
* Because we are using a custom service for the pushbutton in addition to the
* standard Battery Service, we are using 128 bit UUIDs for both, for
* simplicity.
* Note that LSB->MSB notation is used below, so the UUIDs below are "in
* reverse".
*
* To define an additional service, increase GATTS_SERVICE_NUM in ble_server.h,
* add its 128 bit UUID to the ble_service_uuid128 array below (in reverse),
* add another element to the gatts_service element and add another call to
* esp_ble_gatts_app_register() in eg_pushbutton.c.
*/
#define BLE_SERVICE_UUID_SIZE GATTS_SERVICE_NUM*ESP_UUID_LEN_128 // 2 services -> 2 128bit service UUIDs
static uint8_t ble_service_uuid128[BLE_SERVICE_UUID_SIZE] = {
GATTS_BATTERY_SERVICE_UUID,
GATTS_TEACHERBUTTON_SERVICE_UUID
};
static uint8_t ble_manufacturer[BLE_MANUFACTURER_DATA_LEN] = BLE_MANUFACTURER_DATA;
/* BLE GAP advertising data; TODO: figure out meaning of the flags */
static esp_ble_adv_data_t ble_adv_data = {
.set_scan_rsp = false,
.include_name = true,
.include_txpower = true,
.min_interval = BLE_ADV_MIN_INTERVAL,
.max_interval = BLE_ADV_MAX_INTERVAL,
.appearance = BLE_APPEARANCE,
.manufacturer_len = BLE_MANUFACTURER_DATA_LEN,
.p_manufacturer_data = (uint8_t *)ble_manufacturer,
.service_data_len = BLE_SERVICE_DATA_LEN,
.p_service_data = BLE_SERVICE_DATA,
.service_uuid_len = BLE_SERVICE_UUID_SIZE,
.p_service_uuid = ble_service_uuid128,
.flag = (ESP_BLE_ADV_FLAG_GEN_DISC | ESP_BLE_ADV_FLAG_BREDR_NOT_SPT),
};
/* Parameters defining how the pushbutton advertises the data defined above */
static esp_ble_adv_params_t ble_adv_params = {
.adv_int_min = BLE_ADV_MIN_INTERVAL,
.adv_int_max = BLE_ADV_MAX_INTERVAL,
.adv_type = ADV_TYPE_IND,
.own_addr_type = BLE_ADDR_TYPE_PUBLIC,
.channel_map = ADV_CHNL_ALL,
.adv_filter_policy = ADV_FILTER_ALLOW_SCAN_ANY_CON_ANY,
};
/* This function initialises the GAP data.
* ESP_GAP_BLE_ADV_DATA_SET_COMPLETE_EVT is sent to gap_event_handler() below
* afterwards.
*/
void gaps_init() {
esp_err_t ret;
char device_name[BLE_DEVICE_NAME_LEN];
sprintf(device_name, BLE_DEVICE_NAME, *gatts_char[GATTS_BUTTON_NUMBER_CHAR_POS].char_val->attr_value); // copy configured button number into the device name
esp_ble_gap_set_device_name(device_name);
ret=esp_ble_gap_config_adv_data(&ble_adv_data);
ESP_LOGI(GATTS_TAG, "esp_ble_gap_config_adv_data %d", ret);
}
/* In server (config) mode, this function is called whenever the ESP32
* bluetooth stack generates a GAP event.
*/
void gaps_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) {
switch (event) {
case ESP_GAP_BLE_ADV_DATA_SET_COMPLETE_EVT:
esp_ble_gap_start_advertising(&ble_adv_params);
break;
case ESP_GAP_BLE_ADV_START_COMPLETE_EVT:
//advertising start complete event to indicate advertising start successfully or failed
if (param->adv_start_cmpl.status != ESP_BT_STATUS_SUCCESS) {
ESP_LOGE(GATTS_TAG, "Advertising start failed\n");
}
break;
case ESP_GAP_BLE_ADV_STOP_COMPLETE_EVT:
if (param->adv_stop_cmpl.status != ESP_BT_STATUS_SUCCESS) {
ESP_LOGE(GATTS_TAG, "Advertising stop failed\n");
} else {
ESP_LOGI(GATTS_TAG, "Stop adv successfully\n");
}
break;
case ESP_GAP_BLE_UPDATE_CONN_PARAMS_EVT:
ESP_LOGI(GATTS_TAG, "update connetion params status = %d, min_int = %d, max_int = %d,conn_int = %d,latency = %d, timeout = %d",
param->update_conn_params.status,
param->update_conn_params.min_int,
param->update_conn_params.max_int,
param->update_conn_params.conn_int,
param->update_conn_params.latency,
param->update_conn_params.timeout);
break;
default:
break;
}
}
/* ************************************************************ */
/* *************************** GATT *************************** */
/* ************************************************************ */
/* GATT is the BLE protocol for reading from and writing to characteristics.
* Characteristics are variables on the pushbutton, which can be accessed
* remotely via BLE. Each characteristic can have multiple descriptors
* containing additional information on the characteristic, e.g. the format.
* This section defines the characteristics and their descriptors for each
* service, as well as the functions to respond to GATT requests.
*/
/* Services, characteristics and descriptors are defined in arrays below, so
* that we can have several of each. Which characteristic belongs to which
* service, and which descriptor belongs to which characteristic, depends on
* the sequence in which they are added. It needs to be as in the "layout"
* above.
* These variables are used to count through the items we're adding so that we
* always know where in the "layout" we currently are.
*/
static uint16_t ble_add_service_pos;
static uint32_t ble_add_char_pos;
static uint32_t ble_add_descr_pos;
/* This function is called by gatts_event_handler() in case of an
* ESP_GATTS_READ_EVT:
* It walks through all of the gatts_char and gatts_descr arrays until the
* characteristic or descriptor with the correct handle is found. Then its
* value is copied byte-by-byte into the response variable (rsp) which is
* finally sent to the client.
*/
static void gatts_read_value_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param) {
ESP_LOGI(GATTS_TAG, "gatts_read_value_handler: handle %d\n", param->read.handle);
// prepare the response to this read request:
esp_gatt_rsp_t rsp;
memset(&rsp, 0, sizeof(esp_gatt_rsp_t));
// set the handle to which we are responding:
rsp.attr_value.handle = param->read.handle;
// find the requested attribute among the chars and descrs and copy its value into the response:
for (uint32_t pos=0;pos<GATTS_CHAR_NUM;pos++) {
if (gatts_char[pos].char_handle==param->read.handle) {
ESP_LOGI(GATTS_TAG, "gatts_read_value_handler: found requested handle at char pos %d\n", pos);
// set the attribute value of the response:
if (gatts_char[pos].char_val!=NULL) {
ESP_LOGI(GATTS_TAG, "gatts_read_value_handler: char_val length %d\n",gatts_char[pos].char_val->attr_len);
rsp.attr_value.len = gatts_char[pos].char_val->attr_len;
for (uint32_t valpos=0;valpos<gatts_char[pos].char_val->attr_len&&valpos<gatts_char[pos].char_val->attr_max_len;valpos++) {
rsp.attr_value.value[valpos] = gatts_char[pos].char_val->attr_value[valpos];
}
break;
}
}
}
for (uint32_t pos=0;pos<GATTS_DESCR_NUM;pos++) {
if (gatts_descr[pos].descr_handle==param->read.handle) {
ESP_LOGI(GATTS_TAG, "gatts_read_value_handler: found requested handle at descr pos %d\n", pos);
// set the attribute value of the response:
if (gatts_descr[pos].descr_val!=NULL) {
ESP_LOGI(GATTS_TAG, "gatts_read_value_handler: descr_val length %d\n",gatts_descr[pos].descr_val->attr_len);
rsp.attr_value.len = gatts_descr[pos].descr_val->attr_len;
for (uint32_t valpos=0;valpos<gatts_descr[pos].descr_val->attr_len&&valpos<gatts_descr[pos].descr_val->attr_max_len;valpos++) {
rsp.attr_value.value[valpos] = gatts_descr[pos].descr_val->attr_value[valpos];
}
break;
}
}
}
esp_ble_gatts_send_response(gatts_if, param->read.conn_id, param->read.trans_id, ESP_GATT_OK, &rsp);
}
/* This function is called by gatts_event_handler() in case of an
* ESP_GATTS_WRITE_EVT:
* It walks through all of the gatts_char and gatts_descr arrays until the
* characteristic or descriptor with the correct handle is found. Then the
* requested value is copied byte-by-byte into the characteristic's or
* descriptor's variable. If .char_nvs is set, the requested value is also
* written to Non-Volatile Storage (NVS) using the value of .char_nvs as the
* key.
* Finally, an empty response is sent to the client.
*/
static void gatts_write_value_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param) {
ESP_LOGI(GATTS_TAG, "gatts_write_value_handler: handle %d\n", param->write.handle);
esp_err_t ret;
// find the requested attribute among the chars and descrs and copy the request value into it:
for (uint32_t pos=0;pos<GATTS_CHAR_NUM;pos++) {
if (gatts_char[pos].char_handle==param->write.handle) {
ESP_LOGI(GATTS_TAG, "gatts_write_value_handler: found requested handle at char pos %d\n", pos);
// set the attribute value:
if (gatts_char[pos].char_val!=NULL) {
ESP_LOGI(GATTS_TAG, "gatts_write_value_handler: char_val length %d\n", param->write.len);
gatts_char[pos].char_val->attr_len = param->write.len;
for (uint32_t valpos=0; valpos<param->write.len && valpos<gatts_char[pos].char_val->attr_max_len;valpos++) {
gatts_char[pos].char_val->attr_value[valpos]=param->write.value[valpos];
}
if (gatts_char[pos].char_nvs != '\0') {
// char_nvs is set, so the new value should also be stored in NVS
if (gatts_char[pos].char_val->attr_len == 1) { // TODO: this currently only works for single bytes (i.e. uint8_t) and uint16_t
ret = nvs_set_u8(eg_nvs, gatts_char[pos].char_nvs, *param->write.value);
} else if (gatts_char[pos].char_val->attr_len == 2) {
uint16_t val = *(param->write.value+1) << 8 | *param->write.value;
ret = nvs_set_u16(eg_nvs, gatts_char[pos].char_nvs, val);
} else {
ret = ESP_FAIL;
ESP_LOGE(GATTS_TAG, "Attempt to write unsupported type to NVS (handle: %s)", gatts_char[pos].char_nvs);
}
if (ret == ESP_OK) {
if (gatts_char[pos].char_val->attr_len == 1) {
ESP_LOGI(GATTS_TAG, "NVS write successful: %s: %d", gatts_char[pos].char_nvs, *param->write.value);
} else if (gatts_char[pos].char_val->attr_len == 2) {
uint16_t val = *(param->write.value+1) << 8 | *param->write.value;
ESP_LOGI(GATTS_TAG, "NVS write successful: %s: %d", gatts_char[pos].char_nvs, val);
}
} else {
ESP_LOGE(GATTS_TAG, "NVS write failed (error %d): %s: %d", ret, gatts_char[pos].char_nvs, *param->write.value);
}
}
ESP_LOGI(TAG, "gatts_write_value_handler %.*s", gatts_char[pos].char_val->attr_len, (char*)gatts_char[pos].char_val->attr_value);
esp_ble_gatts_send_response(gatts_if, param->write.conn_id, param->write.trans_id, ESP_GATT_OK, NULL);
// TODO: notify?
break;
}
}
}
for (uint32_t pos=0;pos<GATTS_DESCR_NUM;pos++) {
if (gatts_descr[pos].descr_handle==param->write.handle) {
ESP_LOGI(GATTS_TAG, "gatts_write_value_handler: found requested handle at descr pos %d\n", pos);
// set the attribute value:
if (gatts_descr[pos].descr_val!=NULL) {
ESP_LOGI(GATTS_TAG, "gatts_write_value_handler: descr_val length %d\n", param->write.len);
gatts_descr[pos].descr_val->attr_len = param->write.len;
for (uint32_t valpos=0; valpos<param->write.len && valpos<gatts_descr[pos].descr_val->attr_max_len;valpos++) {
gatts_descr[pos].descr_val->attr_value[valpos]=param->write.value[valpos];
}
ESP_LOGI(TAG, "gatts_write_value_handler: wrote: %.*s", gatts_descr[pos].descr_val->attr_len, (char*)gatts_descr[pos].descr_val->attr_value);
esp_ble_gatts_send_response(gatts_if, param->write.conn_id, param->write.trans_id, ESP_GATT_OK, NULL);
// TODO: notify?
break;
}
}
}
}
/* This function is first called by gatts_event_handler() in case of an
* ESP_GATTS_CREATE_EVT (after a service has been added) and then again when
* there are no more descriptors to add to a characteristic:
* It walks through all of the gatts_char array until a characteristic is found
* which belongs to the current service (checking .service_pos) and does not
* yet have a handle (i.e. hasn't been added yet).
* After the characteristic has been added, an ESP_GATTS_ADD_CHAR_EVT event is
* generated, which causes gatts_check_add_char() to be called below.
*/
static void gatts_add_char() {
ESP_LOGI(GATTS_TAG, "gatts_add_char: service %d", ble_add_service_pos);
for (uint32_t pos=0;pos<GATTS_CHAR_NUM;pos++) {
if (gatts_char[pos].service_pos==ble_add_service_pos && gatts_char[pos].char_handle==0) {
ESP_LOGI(GATTS_TAG, "gatts_add_char: adding char pos %d to service pos %d (service handle %d)", pos, ble_add_service_pos, gatts_service[ble_add_service_pos].service_handle);
ble_add_char_pos=pos;
esp_ble_gatts_add_char(gatts_service[ble_add_service_pos].service_handle, &gatts_char[pos].char_uuid,
gatts_char[pos].char_perm,gatts_char[pos].char_property,gatts_char[pos].char_val, gatts_char[pos].char_control);
break;
}
}
}
/* This function is first called by gatts_check_add_char() below after a
* characteristic has been added and then again after each added descriptor.
* It walks through all of the gatts_descr array until a descriptor is found
* which belongs to the current characteristic (checking .char_pos) and does
* not yet have a handle (i.e. hasn't been added yet).
* After the descriptor has been added, an ESP_GATTS_ADD_CHAR_DESCR_EVT event
* is generated, which causes gatts_check_add_descr() to be called below.
* If there are no more descriptors left for the current characteristic,
* gatts_add_char() is called in order to add the next characteristic.
*/
static void gatts_add_descr() {
ESP_LOGI(GATTS_TAG, "gatts_add_descr: service %d, char %d", ble_add_service_pos, ble_add_char_pos);
for (uint32_t pos=0;pos<GATTS_DESCR_NUM;pos++) {
if (gatts_descr[pos].descr_handle==0 && gatts_descr[pos].char_pos==ble_add_char_pos) {
ESP_LOGI(GATTS_TAG, "gatts_add_descr: adding descr pos %d to char pos %d (handle %d) on service %d (handle %d)", pos, ble_add_char_pos, gatts_char[ble_add_char_pos].char_handle, ble_add_service_pos, gatts_service[ble_add_service_pos].service_handle);
ble_add_descr_pos=pos;
esp_ble_gatts_add_char_descr(gatts_service[ble_add_service_pos].service_handle, &gatts_descr[pos].descr_uuid,
gatts_descr[pos].descr_perm, gatts_descr[pos].descr_val, gatts_descr[pos].descr_control);
break;
} else if (pos == GATTS_DESCR_NUM-1) {
// went through all of the descriptors without finding one to add -> add next characteristic
gatts_add_char();
}
}
}
/* This function is called by gatts_event_handler() in case of an
* ESP_GATTS_ADD_CHAR_EVT, i.e. after a characteristic has been added.
* It sets the .char_handle variable of the current characteristic to the new
* handle generated by the ESP32. Finally, gatts_add_descr() is called to add
* the descriptors, if any, to this new characteristic.
*/
static void gatts_check_add_char(esp_bt_uuid_t char_uuid, uint16_t attr_handle) {
ESP_LOGI(GATTS_TAG, "gatts_check_add_char: char handle %d", attr_handle);
if (attr_handle != 0) {
if (char_uuid.len == ESP_UUID_LEN_16) {
ESP_LOGI(GATTS_TAG, "Char UUID16: %x", char_uuid.uuid.uuid16);
} else if (char_uuid.len == ESP_UUID_LEN_32) {
ESP_LOGI(GATTS_TAG, "Char UUID32: %x", char_uuid.uuid.uuid32);
} else if (char_uuid.len == ESP_UUID_LEN_128) {
ESP_LOGI(GATTS_TAG, "Char UUID128: %x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x", char_uuid.uuid.uuid128[0],
char_uuid.uuid.uuid128[1], char_uuid.uuid.uuid128[2], char_uuid.uuid.uuid128[3],
char_uuid.uuid.uuid128[4], char_uuid.uuid.uuid128[5], char_uuid.uuid.uuid128[6],
char_uuid.uuid.uuid128[7], char_uuid.uuid.uuid128[8], char_uuid.uuid.uuid128[9],
char_uuid.uuid.uuid128[10], char_uuid.uuid.uuid128[11], char_uuid.uuid.uuid128[12],
char_uuid.uuid.uuid128[13], char_uuid.uuid.uuid128[14], char_uuid.uuid.uuid128[15]);
} else {
ESP_LOGE(GATTS_TAG, "Char UNKNOWN LEN %d\n", char_uuid.len);
}
ESP_LOGI(GATTS_TAG, "gatts_check_add_char: found char pos %d, handle %d\n", ble_add_char_pos, attr_handle);
gatts_char[ble_add_char_pos].char_handle=attr_handle;
gatts_add_descr(); // try to add descriptors to this characteristic
}
}
/* This function is called by gatts_event_handler() in case of an
* ESP_GATTS_ADD_CHAR_DESCR_EVT, i.e. after a descriptor has been added.
* It sets the .descr_handle variable of the current descriptor to the new
* handle generated by the ESP32. Finally, gatts_add_descr() is called again to
* add any further descriptors to the current characteristic.
*/
static void gatts_check_add_descr(esp_bt_uuid_t descr_uuid, uint16_t attr_handle) {
ESP_LOGI(GATTS_TAG, "gatts_check_add_descr: descr handle %d", attr_handle);
if (attr_handle != 0) {
if (descr_uuid.len == ESP_UUID_LEN_16) {
ESP_LOGI(GATTS_TAG, "Char UUID16: %x", descr_uuid.uuid.uuid16);
} else if (descr_uuid.len == ESP_UUID_LEN_32) {
ESP_LOGI(GATTS_TAG, "Char UUID32: %x", descr_uuid.uuid.uuid32);
} else if (descr_uuid.len == ESP_UUID_LEN_128) {
ESP_LOGI(GATTS_TAG, "Char UUID128: %x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x", descr_uuid.uuid.uuid128[0],
descr_uuid.uuid.uuid128[1], descr_uuid.uuid.uuid128[2], descr_uuid.uuid.uuid128[3],
descr_uuid.uuid.uuid128[4], descr_uuid.uuid.uuid128[5], descr_uuid.uuid.uuid128[6],
descr_uuid.uuid.uuid128[7], descr_uuid.uuid.uuid128[8], descr_uuid.uuid.uuid128[9],
descr_uuid.uuid.uuid128[10], descr_uuid.uuid.uuid128[11], descr_uuid.uuid.uuid128[12],
descr_uuid.uuid.uuid128[13], descr_uuid.uuid.uuid128[14], descr_uuid.uuid.uuid128[15]);
} else {
ESP_LOGE(GATTS_TAG, "Descriptor UNKNOWN LEN %d\n", descr_uuid.len);
}
ESP_LOGI(GATTS_TAG, "gatts_check_add_descr: found descr pos %d, handle %d\n", ble_add_descr_pos, attr_handle);
gatts_descr[ble_add_descr_pos].descr_handle=attr_handle;
}
gatts_add_descr(); // try to add more descriptors
}
/* This function is called whenever the ESP32 bluetooth stack generates a GATT
* event.
*/
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_TAG, "gatts_event_handler: reg.app_id: %d", param->reg.app_id);
switch (event) {
case ESP_GATTS_REG_EVT:
ESP_LOGI(GATTS_TAG, "REGISTER_APP_EVT, status %d, app_id %d", param->reg.status, param->reg.app_id);
if (param->reg.status == ESP_GATT_OK) {
gatts_service[param->reg.app_id].gatts_if = gatts_if;
} else {
ESP_LOGI(GATTS_TAG, "Reg app failed, app_id %04x, status %d\n", param->reg.app_id, param->reg.status);
return;
}
ble_add_service_pos = param->reg.app_id;
gatts_service[param->reg.app_id].service_id.is_primary = true;
gatts_service[param->reg.app_id].service_id.id.inst_id = 0x00;
gatts_service[param->reg.app_id].service_id.id.uuid.len = ESP_UUID_LEN_128;
for (uint8_t pos=0;pos<ESP_UUID_LEN_128;pos++) {
// copy correct part of ble_service_uuid128 byte by byte into the service struct
gatts_service[param->reg.app_id].service_id.id.uuid.uuid.uuid128[pos]=ble_service_uuid128[pos+16*param->reg.app_id];
ESP_LOGI(GATTS_TAG, "Service %d UUID pos %d: %02x", param->reg.app_id, pos, ble_service_uuid128[pos+16*param->reg.app_id]);
}
ESP_LOGI(GATTS_TAG, "ble_service_uuid128[0] %d, gatts_service[param].uuid128[0] %d, gatts_service[ble_pos].uuid128[0] %d", ble_service_uuid128[0], gatts_service[param->reg.app_id].service_id.id.uuid.uuid.uuid128[0], gatts_service[ble_add_service_pos].service_id.id.uuid.uuid.uuid128[0]);
esp_ble_gatts_create_service(gatts_if, &gatts_service[param->reg.app_id].service_id, gatts_service[param->reg.app_id].num_handles);
break;
case ESP_GATTS_READ_EVT: {
ESP_LOGI(GATTS_TAG, "GATT_READ_EVT, conn_id %d, trans_id %d, handle %d", param->read.conn_id, param->read.trans_id, param->read.handle);
gatts_read_value_handler(event, gatts_if, param);
break;
}
case ESP_GATTS_WRITE_EVT: {
ESP_LOGI(GATTS_TAG, "GATT_WRITE_EVT, conn_id %d, trans_id %d, handle %d", param->write.conn_id, param->write.trans_id, param->write.handle);
ESP_LOGI(GATTS_TAG, "GATT_WRITE_EVT, value len %d, value %08x", param->write.len, *(uint32_t *)param->write.value);
gatts_write_value_handler(event, gatts_if, param);
break;
}
case ESP_GATTS_EXEC_WRITE_EVT:
case ESP_GATTS_MTU_EVT:
case ESP_GATTS_CONF_EVT:
case ESP_GATTS_UNREG_EVT:
break;
case ESP_GATTS_CREATE_EVT:
ESP_LOGI(GATTS_TAG, "CREATE_SERVICE_EVT, service %d, status %d, service_handle %d", ble_add_service_pos, param->create.status, param->create.service_handle);
ESP_LOGI(GATTS_TAG, "ble_add_service_pos: %d, param->reg.app_id: %d", ble_add_service_pos, param->reg.app_id);
gatts_service[ble_add_service_pos].service_handle = param->create.service_handle;
ESP_LOGI(GATTS_TAG, "param->create.service_handle %d, gatts_service[param->reg.app_id].service_handle %d, gatts_service[ble_add_service_pos].service_handle %d\n", param->create.service_handle, gatts_service[param->reg.app_id].service_handle, gatts_service[ble_add_service_pos].service_handle);
esp_ble_gatts_start_service(gatts_service[ble_add_service_pos].service_handle);
gatts_add_char();
break;
case ESP_GATTS_ADD_INCL_SRVC_EVT:
break;
case ESP_GATTS_ADD_CHAR_EVT: {
ESP_LOGI(GATTS_TAG, "ADD_CHAR_EVT, status 0x%X, attr_handle %d, service_handle %d",
param->add_char.status, param->add_char.attr_handle, param->add_char.service_handle);
if (param->add_char.status==ESP_GATT_OK) {
gatts_check_add_char(param->add_char.char_uuid,param->add_char.attr_handle);
}
break;
}
case ESP_GATTS_ADD_CHAR_DESCR_EVT:
ESP_LOGI(GATTS_TAG, "ADD_DESCR_EVT char, status %d, attr_handle %d, service_handle %d",
param->add_char.status, param->add_char.attr_handle, param->add_char.service_handle);
ESP_LOGI(GATTS_TAG, "ADD_DESCR_EVT desc, status %d, attr_handle %d, service_handle %d\n",
param->add_char_descr.status, param->add_char_descr.attr_handle, param->add_char_descr.service_handle);
if (param->add_char_descr.status==ESP_GATT_OK) {
gatts_check_add_descr(param->add_char.char_uuid,param->add_char.attr_handle);
}
break;
case ESP_GATTS_DELETE_EVT:
break;
case ESP_GATTS_START_EVT:
ESP_LOGI(GATTS_TAG, "SERVICE_START_EVT, status %d, service_handle %d\n",
param->start.status, param->start.service_handle);
break;
case ESP_GATTS_STOP_EVT:
break;
case ESP_GATTS_CONNECT_EVT: {
esp_ble_conn_update_params_t conn_params = {0};
memcpy(conn_params.bda, param->connect.remote_bda, sizeof(esp_bd_addr_t));
/* For the IOS system, please reference the apple official documents about the ble connection parameters restrictions. */
conn_params.latency = 0;
conn_params.max_int = BLE_CONNECTED_MAX_INTERVAL;
conn_params.min_int = BLE_CONNECTED_MIN_INTERVAL;
conn_params.timeout = BLE_CONNECTED_TIMEOUT;
ESP_LOGI(GATTS_TAG, "\nESP_GATTS_CONNECT_EVT, conn_id %d, remote %02x:%02x:%02x:%02x:%02x:%02x:, is_conn %d\n",
param->connect.conn_id,
param->connect.remote_bda[0], param->connect.remote_bda[1], param->connect.remote_bda[2],
param->connect.remote_bda[3], param->connect.remote_bda[4], param->connect.remote_bda[5],
param->connect.is_connected);
gatts_service[ble_add_service_pos].conn_id = param->connect.conn_id;
//start send the update connection parameters to the peer device.
esp_ble_gap_update_conn_params(&conn_params);
// update LED pattern:
status_connected = true;
break;
}
case ESP_GATTS_DISCONNECT_EVT:
status_connected = false; // for LED blinking pattern
esp_ble_gap_start_advertising(&ble_adv_params);
break;
case ESP_GATTS_OPEN_EVT:
case ESP_GATTS_CANCEL_OPEN_EVT:
case ESP_GATTS_CLOSE_EVT:
case ESP_GATTS_LISTEN_EVT:
case ESP_GATTS_CONGEST_EVT:
default:
break;
}
}
void gatts_init_values() {
esp_err_t ret;
for (uint32_t pos=0;pos<GATTS_CHAR_NUM;pos++) {
if (gatts_char[pos].char_nvs[0] != '\0') { // If char_nvs is empty, skip reading it from NVS. The value is already initialized.
if (gatts_char[pos].char_val->attr_len == 1) { // TODO: this currently only works for single bytes (i.e. uint8_t) and uint16_t
ret = nvs_get_u8(eg_nvs, gatts_char[pos].char_nvs, gatts_char[pos].char_val->attr_value);
} else if (gatts_char[pos].char_val->attr_len == 2) {
ret = nvs_get_u16(eg_nvs, gatts_char[pos].char_nvs, gatts_char[pos].char_val->attr_value);
} else {
ret = ESP_FAIL;
ESP_LOGE(GATTS_TAG, "Attempt to read unsupported type from NVS (handle: %s)", gatts_char[pos].char_nvs);
}
switch (ret) {
case ESP_OK:
if (gatts_char[pos].char_val->attr_len == 1) {
ESP_LOGI(GATTS_TAG, "Read %s from NVS: %d", gatts_char[pos].char_nvs, *gatts_char[pos].char_val->attr_value);
} else if (gatts_char[pos].char_val->attr_len == 2) {
uint16_t val = *(gatts_char[pos].char_val->attr_value+1) << 8 | *gatts_char[pos].char_val->attr_value;
ESP_LOGI(GATTS_TAG, "Read %s from NVS: %d", gatts_char[pos].char_nvs, val);
}
break;
case ESP_ERR_NVS_NOT_FOUND:
ESP_LOGI(GATTS_TAG, "%s not initialized yet in NVS.", gatts_char[pos].char_nvs);
break;
default:
ESP_LOGE(GATTS_TAG, "Error (%d) reading %s from NVS.", ret, gatts_char[pos].char_nvs);
}
}
}
}
/* Copyright 2017 Heiko Rothkranz
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef BLE_SERVER_H_
#define BLE_SERVER_H_
extern nvs_handle eg_nvs;
extern bool status_connected;
void gaps_init();
void gaps_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param);
void gatts_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param);
void gatts_init_values();
#endif /* BLE_SERVER_H_ */
@natanaeljr
Copy link

Awesome! I was looking for a good example. Thanks!

@vwitti
Copy link

vwitti commented Dec 11, 2018

Exzellente Beschreibung und Kommentierung, hat mir sehr geholfen. Vielen Dank,

Copy link

ghost commented Aug 6, 2020

How line 65 is suposed to work since -3 is not uin8_t?

@LRagji
Copy link

LRagji commented Feb 27, 2022

Where did you get line 43/44 from in bleconfig.c? can you provide link please for the 7bytes mentioned

@heiko-r
Copy link
Author

heiko-r commented Mar 17, 2022

Hi @LRagji , I wrote this a long time ago, and cannot find the nice source I had back then anymore, but from a quick search, there's this:

@LRagji
Copy link

LRagji commented Mar 17, 2022

@heiko-r First thanks for reverting back, and i understand this being long time back.. I too was looking into this website https://www.bluetooth.com/specifications/specs/ but they seemed to have changed a lot and section of what each standard descriptor is doing seems to be missing with its data format... so asked any how Thanks again will look deeper into it...

@erenuzz
Copy link

erenuzz commented Apr 6, 2022

Is there a program where I can set the bluetooth as an access point and connect to it with phones?

@erenuzz
Copy link

erenuzz commented Apr 6, 2022

I want it so that multiple devices can be connected

@heiko-r
Copy link
Author

heiko-r commented Apr 18, 2022

@erenuzz I believe this is possible with the ESP-IDF, but I have no idea how. It's been years since I last worked with BLE. I also assume that the ESP-IDF has evolved since then. Might make sense to look for a more recent example.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment