ESP32+idf开发之蓝牙通信入门—ble数据收发(notify)
一、实现功能:
esp32作为蓝牙从机,与手机端蓝牙调试助手(如LightBlue)主机进行通信,实现数据的收发功能:
1、收:蓝牙调试助手发送数据控制esp32开发板led灯的亮灭;
2、发(notify):esp32将传感器数据(如温度数据)主动每隔2s发送给蓝牙调试助手,实现通知(notify)功能;
二、蓝牙BLE概述:
1、BLE(低功耗蓝牙)采用了client/server (C/S) 架构来进行数据交互。这里讲的C/S架构和前面tcp/udp编程时所讲的C/S架构是相同的。 一般而言蓝牙设备提供服务,因此设备是server,手机使用设备提供的服务,因此手机是client。比如蓝牙体温计,它可以提供 “体温” 数据服务,因此是一个server,而手机则可以请求“体温”数据以显示在手机上,因此手机是一个client。
中央设备(主机)即通信的发起者,只能与外围设备通信;外围设备(从机)无法启动通信,只能与中央设备通信;同一时间外围设备只能与一个中央设备通信;外围设备无法与其他外围设备通信。
2、蓝牙协议栈:
profile 可以理解为一种规范,建立的蓝牙应用任务,蓝牙任务实际上分为两类: 标准蓝牙任务规范 profile(公有任务),非标准蓝牙任务规范 profile(私有任务)。标准蓝牙任务规范 profile:指的是从蓝牙特别兴趣小组 SIG 的官网上已经发布的 GATT 规范列表,包括警告通知(alert notification),血压测量(blood pressure),心率(heart rate),电池(battery)等等。它们都是针对具体的低功耗蓝牙的应用实例来设计的。profile的结构如下:
service即服务:在 BLE 从机中有多个服务,例如:电量信息服务、系统信息服务等;每个 service 中又包含多个 characteristic 特征值;
characteristic即特征,上面讲到: Service通过characteristic对数据进行封装,每个service中又包含多个 characteristic 特征值。一个characteristic包含三种条目:characteristic声明,characteristic的值以及characteristic的描述符。Characteristic是在GATT规范中最小的逻辑数据单元,由一个Value和多个描述特性的Desciptior组成。实际上,在与蓝牙设备打交道,主要就是读写Characteristic的value来完成。
Attribute 是属于 ATT属性层的东西,它是 ATT层 的核心。Attribute其实就是一条一条的数据。前面说过,每个蓝牙设备就是用来提供服务的,而服务就是众多数据的合集,这个合集可以称为数据库,数据库里面每个条目都是一个attribute。Attribute 由以下 4 部分组成:属性句柄(Attribute Handler)、属性类型(AttributeType)、属性值(Attribute Value)、属性权限(Attribute Permissions)。
UUID(universally uniqueidentifier,通用唯一识别码)是一个软件构建标准,一个合法的UUID,一定是随机的、全球唯一的。(UUID并不是BLE独有的概念)上面提到的 service 和 characteristic,都需要一个唯一的uuid来标识。
GATT (通用属性配置,Generic Attribute Profile) ,它定义两个 BLE 设备通过 Service 和 Characteristic 进行通信;GATT 就是使用了 ATT(Attribute Protocol)协议,ATT层定义了一个通信的基本框架,数据的基本结构,以及通信的指令,Service 和 characteristic 就是GATT层定义的, GATT层用来赋予每个数据一个具体的内涵,让数据变得有结构和意义。一旦两个设备建立起了连接,GATT 就开始起作用了,这里需要说明的是,GATT 连接必需先经过 GAP 协议。
GAP(通用访问规范,Generic Access Profile)在用来控制设备连接和广播,用于提供蓝牙设备的通用访问功能,包括设备发现、连接、鉴权、服务发现等等。GATT是建立连接后通信规范, 而蓝牙是通过GAP建立通信的。GAP 使你的设备被其他设备可见,并决定了你的设备是否可以或者怎样与合同设备进行交互。
三、步骤
1、使用模板gatt_server_service_table创建新工程。
2、修改蓝牙名并不使用默认广播数据:
3、增加属性表配置特征值用来描述LED和温度的相关信息:(可仿照特征A来写),同时将相关的宏定义和相关保存LED灯状态和温度值的变量进行定义完善。
2、获取得到主机蓝牙调试助手发来的数据,并进行判断,封装led组件,调用组件接口驱动LED亮灭
3、收到蓝牙主机调试助手的notify订阅后创建任务进行温度数据采集并发送到主机,此处采用温度数据模拟;收到取消notify后则任务删除。
四、完整代码
gatts_table_creat_demo.c:
/*
This example code is in the Public Domain (or CC0 licensed, at your option.)
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
*//****************************************************************************
*
* This demo showcases creating a GATT database using a predefined attribute table.
* It acts as a GATT server and can send adv data, be connected by client.
* Run the gatt_client demo, the client demo will automatically connect to the gatt_server_service_table demo.
* Client demo will enable GATT server's notify after connection. The two devices will then exchange
* data.
*
****************************************************************************/#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"esp_bt.h"#include"esp_gap_ble_api.h"#include"esp_gatts_api.h"#include"esp_bt_main.h"#include"gatts_table_creat_demo.h"#include"esp_gatt_common_api.h"#include<led.h>#defineGATTS_TABLE_TAG"GATTS_TABLE_DEMO"#definePROFILE_NUM1#definePROFILE_APP_IDX0#defineESP_APP_ID0x55#defineSAMPLE_DEVICE_NAME"rocket_iot"// 修改蓝牙名#defineSVC_INST_ID0/* The max length of characteristic value. When the GATT client performs a write or prepare write operation,
* the data length must be less than GATTS_DEMO_CHAR_VAL_LEN_MAX.
*/#defineGATTS_DEMO_CHAR_VAL_LEN_MAX500#definePREPARE_BUF_MAX_SIZE1024#defineCHAR_DECLARATION_SIZE(sizeof(uint8_t))#defineADV_CONFIG_FLAG(1<<0)#defineSCAN_RSP_CONFIG_FLAG(1<<1)staticuint8_t adv_config_done =0;uint16_t heart_rate_handle_table[HRS_IDX_NB];typedefstruct{uint8_t*prepare_buf;int prepare_len;}prepare_type_env_t;staticprepare_type_env_t prepare_write_env;// #define CONFIG_SET_RAW_ADV_DATA#ifdefCONFIG_SET_RAW_ADV_DATAstaticuint8_t raw_adv_data[]={/* flags */0x02,0x01,0x06,/* tx power*/0x02,0x0a,0xeb,/* service uuid */0x03,0x03,0xFF,0x00,/* device name */0x0f,0x09,'E','S','P','_','G','A','T','T','S','_','D','E','M','O'};staticuint8_t raw_scan_rsp_data[]={/* flags */0x02,0x01,0x06,/* tx power */0x02,0x0a,0xeb,/* service uuid */0x03,0x03,0xFF,0x00};#elsestaticuint8_t service_uuid[16]={/* LSB <--------------------------------------------------------------------------------> MSB */// first uuid, 16bit, [12],[13] is the value0xfb,0x34,0x9b,0x5f,0x80,0x00,0x00,0x80,0x00,0x10,0x00,0x00,0xFF,0x00,0x00,0x00,};/* The length of adv data must be less than 31 bytes */staticesp_ble_adv_data_t adv_data ={.set_scan_rsp = false,.include_name = true,.include_txpower = true,.min_interval =0x0006,// slave connection min interval, Time = min_interval * 1.25 msec.max_interval =0x0010,// slave connection max interval, Time = max_interval * 1.25 msec.appearance =0x00,.manufacturer_len =0,// TEST_MANUFACTURER_DATA_LEN,.p_manufacturer_data =NULL,// test_manufacturer,.service_data_len =0,.p_service_data =NULL,.service_uuid_len =sizeof(service_uuid),.p_service_uuid = service_uuid,.flag =(ESP_BLE_ADV_FLAG_GEN_DISC | ESP_BLE_ADV_FLAG_BREDR_NOT_SPT),};// scan response datastaticesp_ble_adv_data_t scan_rsp_data ={.set_scan_rsp = true,.include_name = true,.include_txpower = true,.min_interval =0x0006,.max_interval =0x0010,.appearance =0x00,.manufacturer_len =0,// TEST_MANUFACTURER_DATA_LEN,.p_manufacturer_data =NULL,//&test_manufacturer[0],.service_data_len =0,.p_service_data =NULL,.service_uuid_len =sizeof(service_uuid),.p_service_uuid = service_uuid,.flag =(ESP_BLE_ADV_FLAG_GEN_DISC | ESP_BLE_ADV_FLAG_BREDR_NOT_SPT),};#endif/* CONFIG_SET_RAW_ADV_DATA */staticesp_ble_adv_params_t adv_params ={.adv_int_min =0x20,.adv_int_max =0x40,.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,};structgatts_profile_inst{esp_gatts_cb_t gatts_cb;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 char_handle;esp_bt_uuid_t char_uuid;esp_gatt_perm_t perm;esp_gatt_char_prop_t property;uint16_t descr_handle;esp_bt_uuid_t descr_uuid;};staticvoidgatts_profile_event_handler(esp_gatts_cb_event_t event,esp_gatt_if_t gatts_if,esp_ble_gatts_cb_param_t*param);/* One gatt-based profile one app_id and one gatts_if, this array will store the gatts_if returned by ESP_GATTS_REG_EVT */staticstructgatts_profile_inst heart_rate_profile_tab[PROFILE_NUM]={[PROFILE_APP_IDX]={.gatts_cb = gatts_profile_event_handler,.gatts_if = ESP_GATT_IF_NONE,/* Not get the gatt_if, so initial is ESP_GATT_IF_NONE */},};/* Service */staticconstuint16_t GATTS_SERVICE_UUID_TEST =0x00FF;staticconstuint16_t GATTS_CHAR_UUID_TEST_A =0xFF01;staticconstuint16_t GATTS_CHAR_UUID_TEST_B =0xFF02;staticconstuint16_t GATTS_CHAR_UUID_TEST_C =0xFF03;staticconstuint16_t GATTS_CHAR_UUID_TEST_LED =0xFF04;staticconstuint16_t GATTS_CHAR_UUID_TEST_TEMP =0xFF05;staticconstuint16_t primary_service_uuid = ESP_GATT_UUID_PRI_SERVICE;staticconstuint16_t character_declaration_uuid = ESP_GATT_UUID_CHAR_DECLARE;staticconstuint16_t character_client_config_uuid = ESP_GATT_UUID_CHAR_CLIENT_CONFIG;staticconstuint8_t char_prop_read = ESP_GATT_CHAR_PROP_BIT_READ;staticconstuint8_t char_prop_write = ESP_GATT_CHAR_PROP_BIT_WRITE;staticconstuint8_t char_prop_read_notify = ESP_GATT_CHAR_PROP_BIT_READ | ESP_GATT_CHAR_PROP_BIT_NOTIFY;staticconstuint8_t char_prop_read_write_notify = ESP_GATT_CHAR_PROP_BIT_WRITE | ESP_GATT_CHAR_PROP_BIT_READ | ESP_GATT_CHAR_PROP_BIT_NOTIFY;staticconstuint8_t heart_measurement_ccc[2]={0x00,0x00};staticconstuint8_t char_value[4]={0x11,0x22,0x33,0x44};staticconstuint8_t led_value[1]={0x00};staticconstuint8_t temp_value[1]={0x00};/* Full Database Description - Used to add attributes into the database */staticconstesp_gatts_attr_db_t gatt_db[HRS_IDX_NB]={// Service Declaration[IDX_SVC]={{ESP_GATT_AUTO_RSP},{ESP_UUID_LEN_16,(uint8_t*)&primary_service_uuid, ESP_GATT_PERM_READ,sizeof(uint16_t),sizeof(GATTS_SERVICE_UUID_TEST),(uint8_t*)&GATTS_SERVICE_UUID_TEST}},/* Characteristic Declaration */[IDX_CHAR_A]={{ESP_GATT_AUTO_RSP},{ESP_UUID_LEN_16,(uint8_t*)&character_declaration_uuid, ESP_GATT_PERM_READ, CHAR_DECLARATION_SIZE, CHAR_DECLARATION_SIZE,(uint8_t*)&char_prop_read_write_notify}},/* Characteristic Value */[IDX_CHAR_VAL_A]={{ESP_GATT_AUTO_RSP},{ESP_UUID_LEN_16,(uint8_t*)&GATTS_CHAR_UUID_TEST_A, ESP_GATT_PERM_READ | ESP_GATT_PERM_WRITE, GATTS_DEMO_CHAR_VAL_LEN_MAX,sizeof(char_value),(uint8_t*)char_value}},/* Client Characteristic Configuration Descriptor */[IDX_CHAR_CFG_A]={{ESP_GATT_AUTO_RSP},{ESP_UUID_LEN_16,(uint8_t*)&character_client_config_uuid, ESP_GATT_PERM_READ | ESP_GATT_PERM_WRITE,sizeof(uint16_t),sizeof(heart_measurement_ccc),(uint8_t*)heart_measurement_ccc}},/* Characteristic Declaration */[IDX_CHAR_LED]={{ESP_GATT_AUTO_RSP},{ESP_UUID_LEN_16,(uint8_t*)&character_declaration_uuid, ESP_GATT_PERM_READ, CHAR_DECLARATION_SIZE, CHAR_DECLARATION_SIZE,(uint8_t*)&char_prop_read_write_notify}},/* Characteristic Value */[IDX_CHAR_VAL_LED]={{ESP_GATT_AUTO_RSP},{ESP_UUID_LEN_16,(uint8_t*)&GATTS_CHAR_UUID_TEST_LED, ESP_GATT_PERM_READ | ESP_GATT_PERM_WRITE, GATTS_DEMO_CHAR_VAL_LEN_MAX,sizeof(led_value),(uint8_t*)led_value}},/* Characteristic Declaration */[IDX_CHAR_TEMP]={{ESP_GATT_AUTO_RSP},{ESP_UUID_LEN_16,(uint8_t*)&character_declaration_uuid, ESP_GATT_PERM_READ, CHAR_DECLARATION_SIZE, CHAR_DECLARATION_SIZE,(uint8_t*)&char_prop_read_notify}},/* Characteristic Value */[IDX_CHAR_VAL_TEMP]={{ESP_GATT_AUTO_RSP},{ESP_UUID_LEN_16,(uint8_t*)&GATTS_CHAR_UUID_TEST_TEMP, ESP_GATT_PERM_READ, GATTS_DEMO_CHAR_VAL_LEN_MAX,sizeof(temp_value),(uint8_t*)temp_value}},/* Client Characteristic Configuration Descriptor */[IDX_CHAR_CFG_TEMP]={{ESP_GATT_AUTO_RSP},{ESP_UUID_LEN_16,(uint8_t*)&character_client_config_uuid, ESP_GATT_PERM_READ | ESP_GATT_PERM_WRITE,sizeof(uint16_t),sizeof(heart_measurement_ccc),(uint8_t*)heart_measurement_ccc}},/* Characteristic Declaration */[IDX_CHAR_B]={{ESP_GATT_AUTO_RSP},{ESP_UUID_LEN_16,(uint8_t*)&character_declaration_uuid, ESP_GATT_PERM_READ, CHAR_DECLARATION_SIZE, CHAR_DECLARATION_SIZE,(uint8_t*)&char_prop_read}},/* Characteristic Value */[IDX_CHAR_VAL_B]={{ESP_GATT_AUTO_RSP},{ESP_UUID_LEN_16,(uint8_t*)&GATTS_CHAR_UUID_TEST_B, ESP_GATT_PERM_READ | ESP_GATT_PERM_WRITE, GATTS_DEMO_CHAR_VAL_LEN_MAX,sizeof(char_value),(uint8_t*)char_value}},/* Characteristic Declaration */[IDX_CHAR_C]={{ESP_GATT_AUTO_RSP},{ESP_UUID_LEN_16,(uint8_t*)&character_declaration_uuid, ESP_GATT_PERM_READ, CHAR_DECLARATION_SIZE, CHAR_DECLARATION_SIZE,(uint8_t*)&char_prop_write}},/* Characteristic Value */[IDX_CHAR_VAL_C]={{ESP_GATT_AUTO_RSP},{ESP_UUID_LEN_16,(uint8_t*)&GATTS_CHAR_UUID_TEST_C, ESP_GATT_PERM_READ | ESP_GATT_PERM_WRITE, GATTS_DEMO_CHAR_VAL_LEN_MAX,sizeof(char_value),(uint8_t*)char_value}},};staticvoidgap_event_handler(esp_gap_ble_cb_event_t event,esp_ble_gap_cb_param_t*param){switch(event){#ifdefCONFIG_SET_RAW_ADV_DATAcase ESP_GAP_BLE_ADV_DATA_RAW_SET_COMPLETE_EVT:
adv_config_done &=(~ADV_CONFIG_FLAG);if(adv_config_done ==0){esp_ble_gap_start_advertising(&adv_params);}break;case ESP_GAP_BLE_SCAN_RSP_DATA_RAW_SET_COMPLETE_EVT:
adv_config_done &=(~SCAN_RSP_CONFIG_FLAG);if(adv_config_done ==0){esp_ble_gap_start_advertising(&adv_params);}break;#elsecase ESP_GAP_BLE_ADV_DATA_SET_COMPLETE_EVT:
adv_config_done &=(~ADV_CONFIG_FLAG);if(adv_config_done ==0){esp_ble_gap_start_advertising(&adv_params);}break;case ESP_GAP_BLE_SCAN_RSP_DATA_SET_COMPLETE_EVT:
adv_config_done &=(~SCAN_RSP_CONFIG_FLAG);if(adv_config_done ==0){esp_ble_gap_start_advertising(&adv_params);}break;#endifcase 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_TABLE_TAG,"advertising start failed");}else{ESP_LOGI(GATTS_TABLE_TAG,"advertising start successfully");}break;case ESP_GAP_BLE_ADV_STOP_COMPLETE_EVT:if(param->adv_stop_cmpl.status != ESP_BT_STATUS_SUCCESS){ESP_LOGE(GATTS_TABLE_TAG,"Advertising stop failed");}else{ESP_LOGI(GATTS_TABLE_TAG,"Stop adv successfully\n");}break;case ESP_GAP_BLE_UPDATE_CONN_PARAMS_EVT:ESP_LOGI(GATTS_TABLE_TAG,"update connection 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;}}voidexample_prepare_write_event_env(esp_gatt_if_t gatts_if,prepare_type_env_t*prepare_write_env,esp_ble_gatts_cb_param_t*param){ESP_LOGI(GATTS_TABLE_TAG,"prepare write, handle = %d, value len = %d", param->write.handle, param->write.len);esp_gatt_status_t status = ESP_GATT_OK;if(prepare_write_env->prepare_buf ==NULL){
prepare_write_env->prepare_buf =(uint8_t*)malloc(PREPARE_BUF_MAX_SIZE *sizeof(uint8_t));
prepare_write_env->prepare_len =0;if(prepare_write_env->prepare_buf ==NULL){ESP_LOGE(GATTS_TABLE_TAG,"%s, Gatt_server prep no mem",__func__);
status = ESP_GATT_NO_RESOURCES;}}else{if(param->write.offset > PREPARE_BUF_MAX_SIZE){
status = ESP_GATT_INVALID_OFFSET;}elseif((param->write.offset + param->write.len)> PREPARE_BUF_MAX_SIZE){
status = ESP_GATT_INVALID_ATTR_LEN;}}/*send response when param->write.need_rsp is true */if(param->write.need_rsp){esp_gatt_rsp_t*gatt_rsp =(esp_gatt_rsp_t*)malloc(sizeof(esp_gatt_rsp_t));if(gatt_rsp !=NULL){
gatt_rsp->attr_value.len = param->write.len;
gatt_rsp->attr_value.handle = param->write.handle;
gatt_rsp->attr_value.offset = param->write.offset;
gatt_rsp->attr_value.auth_req = ESP_GATT_AUTH_REQ_NONE;memcpy(gatt_rsp->attr_value.value, param->write.value, param->write.len);esp_err_t response_err =esp_ble_gatts_send_response(gatts_if, param->write.conn_id, param->write.trans_id, status, gatt_rsp);if(response_err != ESP_OK){ESP_LOGE(GATTS_TABLE_TAG,"Send response error");}free(gatt_rsp);}else{ESP_LOGE(GATTS_TABLE_TAG,"%s, malloc failed",__func__);}}if(status != ESP_GATT_OK){return;}memcpy(prepare_write_env->prepare_buf + param->write.offset,
param->write.value,
param->write.len);
prepare_write_env->prepare_len += param->write.len;}voidexample_exec_write_event_env(prepare_type_env_t*prepare_write_env,esp_ble_gatts_cb_param_t*param){if(param->exec_write.exec_write_flag == ESP_GATT_PREP_WRITE_EXEC && prepare_write_env->prepare_buf){esp_log_buffer_hex(GATTS_TABLE_TAG, prepare_write_env->prepare_buf, prepare_write_env->prepare_len);}else{ESP_LOGI(GATTS_TABLE_TAG,"ESP_GATT_PREP_WRITE_CANCEL");}if(prepare_write_env->prepare_buf){free(prepare_write_env->prepare_buf);
prepare_write_env->prepare_buf =NULL;}
prepare_write_env->prepare_len =0;}
TaskHandle_t *pTask =NULL;volatile bool notify_flag = false;staticvoidget_temp(void*arg){while(1){if(notify_flag == true){uint8_t temp =20+rand()%11;esp_ble_gatts_set_attr_value(heart_rate_handle_table[IDX_CHAR_VAL_TEMP],1,&temp);esp_ble_gatts_send_indicate(heart_rate_profile_tab[0].gatts_if, heart_rate_profile_tab[0].conn_id,
heart_rate_handle_table[IDX_CHAR_VAL_TEMP],1,&temp, false);}else{vTaskDelete(NULL);}vTaskDelay(pdMS_TO_TICKS(2000));}}staticvoidgatts_profile_event_handler(esp_gatts_cb_event_t event,esp_gatt_if_t gatts_if,esp_ble_gatts_cb_param_t*param){switch(event){case ESP_GATTS_REG_EVT:{esp_err_t set_dev_name_ret =esp_ble_gap_set_device_name(SAMPLE_DEVICE_NAME);if(set_dev_name_ret){ESP_LOGE(GATTS_TABLE_TAG,"set device name failed, error code = %x", set_dev_name_ret);}#ifdefCONFIG_SET_RAW_ADV_DATAesp_err_t raw_adv_ret =esp_ble_gap_config_adv_data_raw(raw_adv_data,sizeof(raw_adv_data));if(raw_adv_ret){ESP_LOGE(GATTS_TABLE_TAG,"config raw adv data failed, error code = %x ", raw_adv_ret);}
adv_config_done |= ADV_CONFIG_FLAG;esp_err_t raw_scan_ret =esp_ble_gap_config_scan_rsp_data_raw(raw_scan_rsp_data,sizeof(raw_scan_rsp_data));if(raw_scan_ret){ESP_LOGE(GATTS_TABLE_TAG,"config raw scan rsp data failed, error code = %x", raw_scan_ret);}
adv_config_done |= SCAN_RSP_CONFIG_FLAG;#else// config adv dataesp_err_t ret =esp_ble_gap_config_adv_data(&adv_data);if(ret){ESP_LOGE(GATTS_TABLE_TAG,"config adv data failed, error code = %x", ret);}
adv_config_done |= ADV_CONFIG_FLAG;// config scan response data
ret =esp_ble_gap_config_adv_data(&scan_rsp_data);if(ret){ESP_LOGE(GATTS_TABLE_TAG,"config scan response data failed, error code = %x", ret);}
adv_config_done |= SCAN_RSP_CONFIG_FLAG;#endifesp_err_t create_attr_ret =esp_ble_gatts_create_attr_tab(gatt_db, gatts_if, HRS_IDX_NB, SVC_INST_ID);if(create_attr_ret){ESP_LOGE(GATTS_TABLE_TAG,"create attr table failed, error code = %x", create_attr_ret);}}break;case ESP_GATTS_READ_EVT:ESP_LOGI(GATTS_TABLE_TAG,"ESP_GATTS_READ_EVT");break;case ESP_GATTS_WRITE_EVT:if(!param->write.is_prep){// the data length of gattc write must be less than GATTS_DEMO_CHAR_VAL_LEN_MAX.ESP_LOGI(GATTS_TABLE_TAG,"GATT_WRITE_EVT, handle = %d, value len = %d, value :", param->write.handle, param->write.len);esp_log_buffer_hex(GATTS_TABLE_TAG, param->write.value, param->write.len);if(heart_rate_handle_table[IDX_CHAR_VAL_LED]== param->write.handle){ESP_LOGI(GATTS_TABLE_TAG,"write:0x%x", param->write.value[0]);if(param->write.value[0]==0x00){led_off();// 收到数据0x00关灯}elseif(param->write.value[0]==0x01){led_on();// 收到数据0x01开灯}}if(heart_rate_handle_table[IDX_CHAR_CFG_TEMP]== param->write.handle && param->write.len ==2){uint16_t descr_value = param->write.value[1]<<8| param->write.value[0];if(descr_value ==0x0001){ESP_LOGI(GATTS_TABLE_TAG,"notify enable");xTaskCreate(get_temp,"get temp",8192,NULL,10, pTask);
notify_flag=true;// ESP_LOGI(GATTS_TABLE_TAG, "notify enable");// uint8_t notify_data[15];// for (int i = 0; i < sizeof(notify_data); ++i)// {// notify_data[i] = i % 0xff;// }// // the size of notify_data[] need less than MTU size// esp_ble_gatts_send_indicate(gatts_if, param->write.conn_id, heart_rate_handle_table[IDX_CHAR_VAL_A],// sizeof(notify_data), notify_data, false);}elseif(descr_value ==0x0002){ESP_LOGI(GATTS_TABLE_TAG,"indicate enable");uint8_t indicate_data[15];for(int i =0; i <sizeof(indicate_data);++i){
indicate_data[i]= i %0xff;}// the size of indicate_data[] need less than MTU sizeesp_ble_gatts_send_indicate(gatts_if, param->write.conn_id, heart_rate_handle_table[IDX_CHAR_VAL_A],sizeof(indicate_data), indicate_data, true);}elseif(descr_value ==0x0000){ESP_LOGI(GATTS_TABLE_TAG,"notify/indicate disable ");
notify_flag=false;//vTaskDelete(pTask);}else{ESP_LOGE(GATTS_TABLE_TAG,"unknown descr value");esp_log_buffer_hex(GATTS_TABLE_TAG, param->write.value, param->write.len);}}/* send response when param->write.need_rsp is true*/if(param->write.need_rsp){esp_ble_gatts_send_response(gatts_if, param->write.conn_id, param->write.trans_id, ESP_GATT_OK,NULL);}}else{/* handle prepare write */example_prepare_write_event_env(gatts_if,&prepare_write_env, param);}break;case ESP_GATTS_EXEC_WRITE_EVT:// the length of gattc prepare write data must be less than GATTS_DEMO_CHAR_VAL_LEN_MAX.ESP_LOGI(GATTS_TABLE_TAG,"ESP_GATTS_EXEC_WRITE_EVT");example_exec_write_event_env(&prepare_write_env, param);break;case ESP_GATTS_MTU_EVT:ESP_LOGI(GATTS_TABLE_TAG,"ESP_GATTS_MTU_EVT, MTU %d", param->mtu.mtu);break;case ESP_GATTS_CONF_EVT:ESP_LOGI(GATTS_TABLE_TAG,"ESP_GATTS_CONF_EVT, status = %d, attr_handle %d", param->conf.status, param->conf.handle);break;case ESP_GATTS_START_EVT:ESP_LOGI(GATTS_TABLE_TAG,"SERVICE_START_EVT, status %d, service_handle %d", param->start.status, param->start.service_handle);break;case ESP_GATTS_CONNECT_EVT:ESP_LOGI(GATTS_TABLE_TAG,"ESP_GATTS_CONNECT_EVT, conn_id = %d", param->connect.conn_id);esp_log_buffer_hex(GATTS_TABLE_TAG, param->connect.remote_bda,6);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 refer to Apple official documents about the BLE connection parameters restrictions. */
conn_params.latency =0;
conn_params.max_int =0x20;// max_int = 0x20*1.25ms = 40ms
conn_params.min_int =0x10;// min_int = 0x10*1.25ms = 20ms
conn_params.timeout =400;// timeout = 400*10ms = 4000ms
heart_rate_profile_tab[0].conn_id = param->connect.conn_id;// start sent the update connection parameters to the peer device.esp_ble_gap_update_conn_params(&conn_params);break;case ESP_GATTS_DISCONNECT_EVT:ESP_LOGI(GATTS_TABLE_TAG,"ESP_GATTS_DISCONNECT_EVT, reason = 0x%x", param->disconnect.reason);esp_ble_gap_start_advertising(&adv_params);break;case ESP_GATTS_CREAT_ATTR_TAB_EVT:{if(param->add_attr_tab.status != ESP_GATT_OK){ESP_LOGE(GATTS_TABLE_TAG,"create attribute table failed, error code=0x%x", param->add_attr_tab.status);}elseif(param->add_attr_tab.num_handle != HRS_IDX_NB){ESP_LOGE(GATTS_TABLE_TAG,"create attribute table abnormally, num_handle (%d) \
doesn't equal to HRS_IDX_NB(%d)",
param->add_attr_tab.num_handle, HRS_IDX_NB);}else{ESP_LOGI(GATTS_TABLE_TAG,"create attribute table successfully, the number handle = %d\n", param->add_attr_tab.num_handle);memcpy(heart_rate_handle_table, param->add_attr_tab.handles,sizeof(heart_rate_handle_table));esp_ble_gatts_start_service(heart_rate_handle_table[IDX_SVC]);}break;}case ESP_GATTS_STOP_EVT: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:case ESP_GATTS_UNREG_EVT:case ESP_GATTS_DELETE_EVT:default:break;}}staticvoidgatts_event_handler(esp_gatts_cb_event_t event,esp_gatt_if_t gatts_if,esp_ble_gatts_cb_param_t*param){/* If event is register event, store the gatts_if for each profile */if(event == ESP_GATTS_REG_EVT){if(param->reg.status == ESP_GATT_OK){
heart_rate_profile_tab[PROFILE_APP_IDX].gatts_if = gatts_if;}else{ESP_LOGE(GATTS_TABLE_TAG,"reg app failed, app_id %04x, status %d",
param->reg.app_id,
param->reg.status);return;}}do{int idx;for(idx =0; idx < PROFILE_NUM; idx++){/* ESP_GATT_IF_NONE, not specify a certain gatt_if, need to call every profile cb function */if(gatts_if == ESP_GATT_IF_NONE || gatts_if == heart_rate_profile_tab[idx].gatts_if){if(heart_rate_profile_tab[idx].gatts_cb){
heart_rate_profile_tab[idx].gatts_cb(event, gatts_if, param);}}}}while(0);}voidapp_main(void){esp_err_t ret;led_init();/* Initialize NVS. */
ret =nvs_flash_init();if(ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND){ESP_ERROR_CHECK(nvs_flash_erase());
ret =nvs_flash_init();}ESP_ERROR_CHECK(ret);ESP_ERROR_CHECK(esp_bt_controller_mem_release(ESP_BT_MODE_CLASSIC_BT));esp_bt_controller_config_t bt_cfg =BT_CONTROLLER_INIT_CONFIG_DEFAULT();
ret =esp_bt_controller_init(&bt_cfg);if(ret){ESP_LOGE(GATTS_TABLE_TAG,"%s enable controller failed: %s",__func__,esp_err_to_name(ret));return;}
ret =esp_bt_controller_enable(ESP_BT_MODE_BLE);if(ret){ESP_LOGE(GATTS_TABLE_TAG,"%s enable controller failed: %s",__func__,esp_err_to_name(ret));return;}
ret =esp_bluedroid_init();if(ret){ESP_LOGE(GATTS_TABLE_TAG,"%s init bluetooth failed: %s",__func__,esp_err_to_name(ret));return;}
ret =esp_bluedroid_enable();if(ret){ESP_LOGE(GATTS_TABLE_TAG,"%s enable bluetooth failed: %s",__func__,esp_err_to_name(ret));return;}
ret =esp_ble_gatts_register_callback(gatts_event_handler);if(ret){ESP_LOGE(GATTS_TABLE_TAG,"gatts register error, error code = %x", ret);return;}
ret =esp_ble_gap_register_callback(gap_event_handler);if(ret){ESP_LOGE(GATTS_TABLE_TAG,"gap register error, error code = %x", ret);return;}
ret =esp_ble_gatts_app_register(ESP_APP_ID);if(ret){ESP_LOGE(GATTS_TABLE_TAG,"gatts app register error, error code = %x", ret);return;}esp_err_t local_mtu_ret =esp_ble_gatt_set_local_mtu(500);if(local_mtu_ret){ESP_LOGE(GATTS_TABLE_TAG,"set local MTU failed, error code = %x", local_mtu_ret);}}
组件led相关代码有:led.c
#include<stdio.h>#include"led.h"#include<driver/gpio.h>voidled_init(){gpio_config_t cfg={.pin_bit_mask=1<<LED_PIN,.mode=GPIO_MODE_OUTPUT,.intr_type=GPIO_INTR_DISABLE,.pull_up_en=GPIO_PULLUP_DISABLE,.pull_down_en=GPIO_PULLDOWN_DISABLE,};ESP_ERROR_CHECK(gpio_config(&cfg));}voidled_on(){ESP_ERROR_CHECK(gpio_set_level(LED_PIN,1));}voidled_off(){ESP_ERROR_CHECK(gpio_set_level(LED_PIN,0));}
led.h
#ifndef __LED_H__
#define __LED_H__
#define LED_PIN GPIO_NUM_2
void led_init();
void led_on();
void led_off();
#endif
五、测试
1、控制led灯测试,默认不亮,蓝牙调试APP发送0x01,esp灯亮;发送0x00,led灯灭。
2、调试助手APP点击notify开关监听ESP32发来的温度数据并16进制显示,关闭后则停止,同时esp32终端打印相关信息。
版权归原作者 rocket_iot 所有, 如有侵权,请联系我们删除。