一、GAP(Geneirc Access Profile)

1.GAP的作用

esp32 spiffs 分区表_服务器

2.一个可扫描可连接的定向蓝牙连接过程

esp32 spiffs 分区表_esp32 spiffs 分区表_02

 二、GATT(Generic Attribute Profile)

1.GATT作用 

  • GATT是基于ATT的上层协议;

esp32 spiffs 分区表_linux_03

  • 数据是存储在服务器Server上的,数据格式是以一种Profile配置文件方式来存储的;
  • Profile下会有不同的Service服务,Service底下又有各种各样的Characteristic特征值;

esp32 spiffs 分区表_运维_04

2.Service的构成

  • 首先是Service的声明;
  • 然后是Serivice Include用于引用其他服务;
  • Characteristic特征;

esp32 spiffs 分区表_linux_05

2.1 Service的声明
  • Service声明的数据格式:(从左到右)
  • 属性的句柄;
  • 属性的类别;
  • 当客户端读到属性类型为2800或者2801就知道这一串数据就是服务声明的数据;
  • 2800指主服务PrimaryService它是可以被发现的;2801指次要服务Secondary Service是不可以被发现只能被引用;
  • 属性值;
  • 当知道属性类别是2800或者2801后就知道这里的属性值是代表当前Service UUID
  • 属性权限;

esp32 spiffs 分区表_esp32 spiffs 分区表_06

2.2 Service的包含
  • 表明当前的服务可以包含其他的Service;
  • 数据格式从左到右;
  • 属性的句柄;
  • 属性的类别;
  • 读到的属性类型为2802表明当前服务包含其他Service;
  • 属性值;
  • 当属性类型为2802时表示的是其他服务的信息;
  • 首先是包含其他servic起始句柄位置;
  • 然后是结束句柄位置;
  • 所包含服务的UUID
  • 属性权限;

esp32 spiffs 分区表_运维_07

2.3 Service的特征值
2.3.1特征值的构成
2.3.1.1 特征声明
  • 属性的句柄;
  • 属性的类别;
  • 当读到的值为2803时表示当前这串数据描述的是特征值;
  • 属性值;
  • 当属性类别为2803时属性值表;
  • 第一个是它的属性;
  • 第二是特征值的句柄;
  • 第三是特征值的UUID;
  • 属性权限;

esp32 spiffs 分区表_GAP_08

2.3.1.2 特征值声明
  • 属性的句柄;
  • 属性的类别;
  • 对应的是特征声明中属性值中第三个成员的UUID
  • 属性值;
  • 即这个特征的特征值
  • 属性权限;

esp32 spiffs 分区表_esp32 spiffs 分区表_09

2.3.1.3 特征描述描述声明
  • 这个是用于针对特征值的可选补充信息声明,下面6条这些是可选配置;
  • 扩展属性:识别码2900;
  • 用户属性:识别码2901;
  • 客户端配置属性:识别码2902,客户端可以配置service端是否能发通知的行为;
  • service配置属性:识别码2903;
  • 显示属性:识别码2904;代表了如何解析我们的特征值
  • Attrubute Value从左到右
  • Format:特征值用什么格式表示
  • Exponent:特征值是否有指数的形式
  • Unit:特征值的单位;
  • 集合属性:识别码2905;

esp32 spiffs 分区表_esp32 spiffs 分区表_10

esp32 spiffs 分区表_GAP_11

esp32 spiffs 分区表_linux_12

esp32 spiffs 分区表_运维_13

esp32 spiffs 分区表_GAP_14

esp32 spiffs 分区表_运维_15

2.4 统一格式

  • Attribute Handle:属性句柄;
  • 属性类别;
  • 属性值;
  • 属性权限;
  • 上述格式即ATT属性协议规定的;
  • GATT赋予了属性不同的Attribute Type类别,根据这个类别来判断当前是哪一种类型的数据,从而继续Attribute Value的格式和内以及还可以读取下一条数据;

esp32 spiffs 分区表_esp32 spiffs 分区表_16

  • GATT不单单是ATT协议来来存储数据,还使用ATT协议来发送命令和数据
  • 命令的代码
  • 发送命令的输入参数
  • 发送命令的授权

esp32 spiffs 分区表_GAP_17

三、GATT服务端工作流程

  •  初始化Flash
  • 释放控制器中经典蓝牙的内存空间
  • 初始化控制器的缺省配置
  • 初始化底层的蓝牙控制器
  • 使能控制器
  • 初始化bluedroid协议栈
  • 使能bluedroid协议栈
  • 调用gatts_register_callback,注册GATT_EVENT回调处理函数处理所有的GATT事件
  • 调用gap_register_callback,注册GAP_EVENT回调处理函数处理所有的GAP事件
  • 调用ggatts_app_register,注册一个API,ESP_APP_ID是底层协议栈用来区分profile配置文件,每一个配置文件对应不同的ID
  • 设置最大可传输单元MTU

esp32 spiffs 分区表_服务器_18

  •  gap_event_handler中会处理客户端的扫描、连接等等GAP事件,确保客户端连接到服务器;
  • 在客户端连接成功后底层协议栈会上发GATT事件到gatts_event_handler中,在这里处理不同的GATT事件,完成客户端对服务器数据的读写修改操作;

esp32 spiffs 分区表_GAP_19

 1.1 app_main

void app_main(void) { esp_err_t ret; /* Initialize NVS. */ ret = nvs_flash_init(); //初始化Flash 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(); //初始化bluedroid协议栈 if (ret) { ESP_LOGE(GATTS_TABLE_TAG, "%s init bluetooth failed: %s", __func__, esp_err_to_name(ret)); return; } ret = esp_bluedroid_enable(); //使能bluedroid协议栈 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); //调用gatts_register_callback,注册GATT_EVENT回调处理函数处理所有的GATT事件 if (ret){ ESP_LOGE(GATTS_TABLE_TAG, "gatts register error, error code = %x", ret); return; } ret = esp_ble_gap_register_callback(gap_event_handler);//调用gap_register_callback,注册GAP_EVENT回调处理函数处理所有的GAP事件 if (ret){ ESP_LOGE(GATTS_TABLE_TAG, "gap register error, error code = %x", ret); return; } ret = esp_ble_gatts_app_register(ESP_APP_ID);//调用ggatts_app_register,注册一个API,ESP_APP_ID是底层协议栈用来区分profile配置文件,每一个配置文件对应不同的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);//设置最大可传输单元MTU if (local_mtu_ret){ ESP_LOGE(GATTS_TABLE_TAG, "set local MTU failed, error code = %x", local_mtu_ret); } }

1.2 gatts_event_handler

static void gatts_profile_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param); /* One gatt-based profile one app_id and one gatts_if, this array will store the gatts_if returned by ESP_GATTS_REG_EVT */ //设置每一个profile对应的事件处理函数 static struct gatts_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 */ }, }; static void gatts_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) { //GATT注册事件 if (param->reg.status == ESP_GATT_OK) { heart_rate_profile_tab[PROFILE_APP_IDX].gatts_if = gatts_if; //取得gatts接口,这个接口是服务端分配给每一个客户端的不同接口,底层协议返回给上层 } 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) { //当设置值没有连接或者取得gatt接口后调用每一个profile对应每一个的回调函数进一步处理事件 if (heart_rate_profile_tab[idx].gatts_cb) { heart_rate_profile_tab[idx].gatts_cb(event, gatts_if, param); } } } } while (0); }


1.3 gap_event_handler

static void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) { switch (event) { #ifdef CONFIG_SET_RAW_ADV_DATA //原始广播数据宏 case 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; #else case 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; #endif 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_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; } }

四、GATT服务端广播流程

1. 非定向广播的广播方

  • 主机的上层向下层的链路层发如下送命令
  • LE Set Advertising Parameters 设置广播参数
  • Read Advertising Channel Tx Power 读取广播通道的power
  • Set Advertising Data 设置广播数据
  • Set Scan Response Data 设置扫描应答数据
  • Set Advertising Enable  使能广播                                              
  • 链路成执行相应的命令后会返回Command Complete命令完成告知上层
  • 完成以上动作链路层开始广播

esp32 spiffs 分区表_运维_20

2. 非定向广播的被动扫描方

  • 主机的上层向下层的链路层发如下送命令
  • Set Scan Parameters 设置被动扫描的扫描参数
  • Set Scan Enable 设置扫描运行
  • 链路成执行相应的命令后会返回Command Complete命令完成告知上层
  • Advertising Report 上报设备B的广播数据到上层应用层

esp32 spiffs 分区表_esp32 spiffs 分区表_21

3. 非定向广播的主动扫描方

  • 主机的上层向下层的链路层发如下送命令
  • Set Scan Parameters 设置被动扫描的扫描参数
  • Set Scan Enable 设置扫描运行
  • 链路成执行相应的命令后会返回Command Complete命令完成告知上层
  • Advertising Report 上报设备B的广播数据到上层应用层
  • 链接层会向对端发送
  • SCAN_REQ 扫描请求命令
  • SCAN_RSP 对端广播设备返回扫描请求应答数据(广播数据携带最大31个字节,更多的信息如果在扫描的阶段知道就放在描请求应答数据中)

esp32 spiffs 分区表_服务器_22

4 .代码上的实现

  • 首先注册了来区分profile配置文件APP ID(ESP_APP_ID) 后触发ESP_GATTS_REG_EVT事件在gatts_event_handler中注册的回调函数中处理

esp32 spiffs 分区表_linux_23

注册的回调函数 ESP_GATTS_REG_EVT事件代码中做了如下事情

  • esp_ble_gap_set_device_name 设置设备名
  • esp_ble_gap_config_adv_data_raw 设置广播数据内容:设置完后会触发 GAP中的ESP_GAP_BLE_ADV_DATA_RAW_SET_COMPLETE_EVT事件,判断广播配置是否完成对应一个Command Complete,然后开始广播esp_ble_gap_start_advertising
  • esp_ble_gap_config_scan_rsp_data_raw 配置扫描应答数据内容:设置完后会触发GAP中的 ESP_GAP_BLE_SCAN_RSP_DATA_RAW_SET_COMPLETE_EVT事件,判断广播配置是否完成对应一个Command Complete,然后开始广播
  • esp_ble_gap_start_advertising 触发GAP中ESP_GAP_BLE_ADV_START_COMPLETE_EVT 对应流程图最后的Command Complete是链路层发送回来广播是否成功

esp32 spiffs 分区表_运维_24

图中的流程为首先注册了APP ID对应当前的profile,然后触发gatt中注册的profile中的回调函数中的注册事件执行配置广播和描应答数据数据,这两个事件会触发GAP中的ESP_GAP_BLE_ADV_DATA_RAW_SET_COMPLETE_EVT和ESP_GAP_BLE_SCAN_RSP_DATA_RAW_SET_COMPLETE_EVT 再等待链路层返回Command Complete后开始广播esp_ble_gap_start_advertising 这个函数又会触发ESP_GAP_BLE_ADV_START_COMPLETE_EVT来返回广播状态。

五、 GATT服务器广播数据

1. 广播数据数据以及格式

  • 有效数据部分用AD Structure形式,无效数据部分用0填充;
  • AD structure结构组成部分
  • 第一部分是1个字节用来表示后面数据的长度,包含这个structure后面所有字节的长度:Length
  • Data第二部分为数据:Data
  • 第一小部分为数据类型:AD Type
  •  第二小部分为实际数据内容:AD Data

esp32 spiffs 分区表_运维_25

2. 代码实现 

static uint8_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' }; static uint8_t raw_scan_rsp_data[] = { /* flags */ 0x02, 0x01, 0x06, /* tx power */ 0x02, 0x0a, 0xeb, /* service uuid */ 0x03, 0x03, 0xFF,0x00 };

  •  广播数据中设置的设备名/* device name */和程序在gatt_profile中设置的设备名不一样;后者是用来设备扫描后显示的名字,前者是用着广播数据中广播的名字;

esp32 spiffs 分区表_GAP_26

  • 修改gatt_profile中设置的名字

esp32 spiffs 分区表_运维_27

esp32 spiffs 分区表_linux_28

  • 修改广播数据中的名字

esp32 spiffs 分区表_linux_29

esp32 spiffs 分区表_linux_30

esp32 spiffs 分区表_esp32 spiffs 分区表_31

esp32 spiffs 分区表_运维_32

  • legacy advertisingPDUs广播数据最大31个字节,extended advertisingPDUs最大254个字节;

六、 GATT客户端工作流程

esp32 spiffs 分区表_服务器_33

  • 1

注册gatt_app ID时触发gatt中注册的profile中的回调函数中的ESP_GATTC_REG_EVT事件调用esp_ble_gap_set_scan_params设置扫描参数;


esp_ble_gap_set_scan_params再触发gap的cb的设置扫描参数完成事件ESP_GAP_BLE_SCAN_PARAM_SET_COMPLETE_EVT调用esp_ble_gap_start_scanning开始扫描;


esp_ble_gap_start_scanning再触发ESP_GAP_BLE_SCAN_START_COMPLETE_EVT扫描开始完成事件打印出开始扫描的结果是失败还是成功;


esp_ble_gap_start_scanning再触发ESP_GAP_BLE_SCAN_RESULT_EVT扫描结果事件,在这个事件中有两个子事件ESP_GAP_SEARCH_INQ_RES_EVT :用于扫描到每一个蓝牙服务器时在广播会触发该事件;ESP_GAP_SEARCH_INQ_CMPL_EVT:扫描完成所以的蓝牙主机事件;每一个扫描结果会通过ESP_GAP_SEARCH_INQ_RES_EVT上报到客户端这个事件中会调用esp_ble_resolve_adv_data解析每一个广播数据;如果扫描的设备是我们所需要的设备就会调用esp_ble_gap_stop_scanning停止扫描然后调用esp_ble_gap_gattc_open打开gatt的连接;


  • 2

esp_ble_gap_gattc_open再触发gatt中注册的profile中的回调函数中的ESP_GATTC_CONNECT_EVT以及ESP_GATTC_OPEN_EVT;ESP_GATTC_CONNECT_EVT会打印出连接和接口以及调用esp_ble_gattc_send_mtu_req再次设置MTU最大传输单元在初始化时已经设置过一次;ESP_GATT_OPEN_EVT主要是判断GATT打开是否失败还是成功并打印信息;


esp_ble_gattc_send_mtu_req再触发ESP_GATTC_DIS_SRVC_CMPL_EVT和ESP_GATTC_CFG_MTU_EVT;ESP_GATTC_DIS_SRVC_CMPL_EVT是发现服务是否成功寻找成功调用esp_ble_gattc_search_service去找服务器中的servic;ESP_GATTC_CFG_MTU_EVT是配置MTU是否成功;


esp_ble_gattc_search_service再触发ESP_GATTC_SEARCH_RES_EVT和ESP_GATT_SEARCH_CMPL_EVT;每找到一个service触发ESP_GATTC_SEARCH_RES_EVT找完所有service触发ESP_GATT_SEARCH_CMPL_EVT;ESP_GATTC_SEARCH_RES_EVT会保存每一个service的start_handle和end_handler;ESP_GATT_SEARCH_CMPL_EVT中调用esp_ble_gattc_get_attr_count去找属性的个数再用esp_ble_gattc_get_char_by_uuid通过UUID找属性如果这个属性有通知属性再用esp_ble_gattc_register_for_notify注册notify;


esp_ble_gattc_register_for_notify会触发ESP_GATTC_REG_FOR_NOTIFY_EVT调用esp_ble_gattc_write_char_descr写属性的描述符通过写这个值服务端才会主动发送数据到客户端;esp_ble_gattc_write_char_descr触发ESP_GATTC_WRITE_DESCR_EVT这个事件中调用esp_ble_gattc_write_char写一个特性值;


esp_ble_gattc_write_char触发ESP_GATTC_WRITE_CHAR_EVT中打印写是否成功失败; 在次之后如果服务端有发送通知来的数据就会触发ESP_GATTC_NOTIFY_EVT事件esp_log_buffer_hex把值打印出来;

七、 服务器端读取数据

  • 实例代码gatt_server_service_table
  • esp_gatts_attr_db_t gatt_db[HRS_IDX_NB]数组使用表格形式组织了蓝牙的数据;

esp32 spiffs 分区表_GAP_34

  • 通过ESP_GATTS_READ_EVT来处理读事件,但是代码中该事件不做任何操作原因见下;

esp32 spiffs 分区表_esp32 spiffs 分区表_35

  • 数组数据中的ESP_GATT_AUTO_RSP:表示ESP32会自动回复读写的操作
  • 客户端读取服务器的值时自动回复特征值11223344,在这个例子中客户端读取数据时协议栈会自动回复上面数据,因为在数据中设置的是ESP_GATT_AUTO_RSP;

esp32 spiffs 分区表_GAP_36

esp32 spiffs 分区表_服务器_37

  • 将数组中所有的ESP_GATT_AUTO_RSP替换为ESP_GATT_RSP_BY_APP(编程返回)其他不做修改会导致读取数据失败然后断开连接;

esp32 spiffs 分区表_服务器_38

  • 解决办法在ESP_GATTS_READ_EVT中添加返回数据给客户端 

case ESP_GATTS_READ_EVT: ESP_LOGI(GATTS_TABLE_TAG, "ESP_GATTS_READ_EVT"); esp_gatt_rsp_t rsp; //定义变量 memset(&rsp, 0, sizeof(esp_gatt_rsp_t)); //初始化变量 rsp.attr_value.handle = param->read.handle; //设置读写句柄 rsp.attr_value.len = 4; //数据长度 rsp.attr_value.value[0] = 0xde; //数据值 rsp.attr_value.value[1] = 0xed; rsp.attr_value.value[2] = 0xbe; rsp.attr_value.value[3] = 0xef; esp_ble_gatts_send_response(gatts_if, param->read.conn_id, param->read.trans_id, ESP_GATT_OK, &rsp); break;

八、 客户端读取数据处理

  • 实例代码gatt_client(实验需要两个开发板一个做服务端另外一个是客户端)
  • 基于GATT客户端的广播流程;先取消掉客户端对服务设置的Notify功能,然后添加读取指定服务器端特定特征值的功能;这里指定读取服务端FF02的特征值

esp_ble_gatts_read_char(gattc_if, //gattc 的接口 p_data->search_cmpl.conn_id, //连接的ID char_elem_result[0].char_handle, //特征值的句柄 ESP_GATT_AUTH_REQ_NONE); //授权模式

esp32 spiffs 分区表_linux_39

esp32 spiffs 分区表_esp32 spiffs 分区表_40

esp32 spiffs 分区表_服务器_41

  • 服务器上的特征配置:给FF02特征值配置的值是cha_value_b[4]

esp32 spiffs 分区表_linux_42

esp32 spiffs 分区表_GAP_43

  • 在esp_ble_gatts_read_char读取数据的时候ESP32客户端会触发读取特征值的事件处理;自己添加一个读取特征值的ESP_GATTC_READ_CHAR_EVT事件代码;即读取到特征值后蓝牙协议栈会触发ESP_GATTC_READ_CHAR_EVT事件;

case ESP_GATTC_READ_CHAR_EVT: ESP_LOGI(GATTC_TAG, "ESP_GATTC_READ_CHAE_EVT"); ESP_LOGI(GATTC_TAG, "p_data->read.value_len=%d", p_data->read.value_len); esp_log_buffer_hex(GATTC_TAG, p_data->read.value, p_data->read.value_len); break;

esp32 spiffs 分区表_linux_44

  • 上面是服务器,下面是客户端读取回服务端的数据;

esp32 spiffs 分区表_GAP_45

九、服务端写数据处理

  • 即服务端处理客户端写过来的数据
  • 对于服务端如果写入的数据小于或等于MTU-3时,服务端触发ESP_GATTS_WRITE_EVT在这个事件中接收处理数据;
  • 大于MTU-3会先把所有数据放入ESP_GATTS_WRITE_EVT事件在的prepare_buf接收完所以的数据后再触发ESP_GATTS_EXEC_WRITE_EVT,中处理prepare_buf数据;

esp32 spiffs 分区表_linux_46


  •  手机端向服务端写入数据会触发ESP_GATTS_WRITE_EVT事件
  • 在事件中
  • 首先判断是写入哪一个特征值的句柄这里指定是CHAR_VAL_A
  • 然后判断写入数据的长短是不是为1,写的数据为1开启LED,为2关闭LED;

case ESP_GATTS_WRITE_EVT: configure_led(); //配置IO //判断写入的是否为VAL_A特征值的特征值句柄 if(heart_rate_handle_table[IDX_CHAR_VAL_A] == param->write.handle) { //判读写的数据长度是否是1个bit和所写数据的内容 if(param->write.len == 1 && param->write.value[0] == 1) { //等于1打开LED blink_led(1); }else if (param->write.len == 1 && param->write.value[0] == 2) { //等于2关闭LED blink_led(0); } else { ESP_LOGI(GATTS_TABLE_TAG, "wrong led command!"); } }

esp32 spiffs 分区表_GAP_47

esp32 spiffs 分区表_linux_48

esp32 spiffs 分区表_GAP_49