Skip to content

Instantly share code, notes, and snippets.

@heiko-r
Created October 26, 2017 12:39
Show Gist options
  • 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_ */
@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