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_CFG_A] == 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"); 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); }else if (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; } // if want to change the value in server database, call: // esp_ble_gatts_set_attr_value(heart_rate_handle_table[IDX_CHAR_VAL_A], sizeof(indicate_data), indicate_data); //the size of indicate_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(indicate_data), indicate_data, true); } else if (descr_value == 0x0000){ ESP_LOGI(GATTS_TABLE_TAG, "notify/indicate disable "); }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;分析这段代码
时间: 2025-06-21 18:22:58 浏览: 14
<think>好的,我现在要分析用户提供的这段关于ESP_GATTS_WRITE_EVT事件的代码。首先,我需要理解这段代码的整体结构和功能。看起来这是一个处理GATT服务器写事件的回调函数,属于ESP-IDF框架下的蓝牙应用开发。用户希望了解这段代码的作用和工作原理。
首先,我应该回忆一下BLE中的GATT服务器相关概念。当客户端(比如手机)向服务器写入数据时,服务器会收到WRITE_EVT事件。这个事件有两种类型:普通写和准备写(需要响应和不需要响应)。代码中的`param->write.is_prep`判断是否是准备写操作,如果不是,就处理普通写事件。
接下来,代码中首先记录日志,打印出写操作的句柄、数据长度和内容。这里用到了`ESP_LOGI`和`ESP_LOG_BUFFER_HEX`,应该是用于调试目的。然后检查特定的特征句柄(`heart_rate_handle_table[IDX_CHAR_CFG_A]`),这里可能对应一个客户端特性配置描述符(CCCD),因为当客户端启用通知或指示时,会向CCCD写入0x0001或0x0002。
然后,根据写入的值判断是启用通知、指示还是禁用。如果启用了通知(0x0001),代码构造一个通知数据数组,并通过`esp_ble_gatts_send_indicate`发送通知,最后一个参数是false,表示不需要确认。如果是指示(0x0002),则发送指示数据,参数为true,需要客户端确认。这里还提到,如果需要更新服务器数据库中的属性值,可以调用`esp_ble_gatts_set_attr_value`,但当前代码中注释掉了这部分,实际发送的是生成的数据。
当写入的值为0x0000时,表示禁用通知或指示。其他值则记录错误。处理完成后,如果需要响应(`param->write.need_rsp`为真),则发送响应给客户端。
如果事件是准备写(即is_prep为真),则调用`example_prepare_write_event_env`处理,这里可能涉及长数据的写入,需要分多次处理,但用户提供的代码中没有展示具体实现。
接下来,我需要确认各个部分的细节是否正确。比如,CCCD的描述符值是否正确处理了大小端,这里`descr_value`的构造是将第一个字节作为低位,第二个作为高位,因为蓝牙属性通常是小端格式,所以是正确的。
另外,发送通知和指示时,数据长度是否确实小于MTU大小?代码中的注释提到需要小于MTU,但实际代码里数组是固定15字节,这可能没问题,但实际应用中需要注意MTU大小,可能需要动态获取。
还有,在启用指示的情况下,注释提到可以更新服务器属性值,但代码中并没有这么做,而是直接发送数据。这可能是因为示例代码仅演示发送,而实际应用中可能需要根据需求决定是否更新属性值。
总结一下,这段代码处理了客户端对CCCD的写入操作,根据配置启用或禁用通知和指示,并发送相应的数据。同时处理了普通写请求和准备写请求,确保正确响应客户端。
现在需要考虑用户可能的疑问点,比如CCCD的作用,通知和指示的区别,数据长度的限制,以及是否需要更新属性值等。在解释时需要简明扼要,结合代码中的具体处理步骤,分点说明每个部分的功能,帮助用户理解整个事件处理的流程。</think>以下是对代码的逐步分析:
1. **事件类型判断**
```c
case ESP_GATTS_WRITE_EVT:
```
表示处理来自客户端的写操作事件,这是BLE通信中客户端向服务端写入数据的核心事件。
2. **区分普通写与准备写**
```c
if (!param->write.is_prep)
```
- `is_prep=false`:普通写入操作(立即执行)
- `is_prep=true`:准备写入操作(需要调用`example_prepare_write_event_env`处理长数据)
3. **普通写处理流程**
```c
ESP_LOGI(...) // 打印写入的句柄、数据长度
ESP_LOG_BUFFER_HEX(...) // 以HEX格式输出写入值
```
4. **CCCD配置处理(核心逻辑)**
```c
if (heart_rate_handle_table[IDX_CHAR_CFG_A] == param->write.handle && param->write.len == 2)
```
- **判断条件**:操作对象是特征配置描述符(CCCD)且数据长度为2字节
- **字节序处理**:`descr_value = param->write.value[1]<<8 | param->write.value[0]` 按小端格式解析
5. **通知/指示控制**
| 值 | 操作 | 协议区别 |
|-----------|-----------------------------|----------------|
| 0x0001 | 启用通知(`notify enable`) | 无需客户端确认 |
| 0x0002 | 启用指示(`indicate enable`) | 需要客户端确认 |
| 0x0000 | 禁用通知/指示 | |
6. **数据发送机制**
```c
// 发送通知(无确认)
esp_ble_gatts_send_indicate(..., false);
// 发送指示(需要确认)
esp_ble_gatts_send_indicate(..., true);
```
- **MTU限制**:注释强调数据长度需小于MTU大小(代码中使用固定15字节需注意实际MTU协商值)
- **数据生成**:示例使用循环生成测试数据`i % 0xff`
7. **属性值更新说明**
```c
// esp_ble_gatts_set_attr_value(...)
```
被注释的代码显示:若需要更新服务端数据库中的特征值,需显式调用此API(当前示例仅发送数据不更新存储)
8. **响应机制**
```c
if (param->write.need_rsp) {
esp_ble_gatts_send_response(...)
}
```
根据协议要求,当客户端设置`need_rsp`标志时,服务端必须发送响应包
9. **错误处理**
```c
else {
ESP_LOGE(...) // 记录未知描述符值
ESP_LOG_BUFFER_HEX(...) // 输出错误数据
}
```
**关键设计要点**:
1. CCCD配置采用标准蓝牙规范处理流程
2. 区分通知(indication)与通知(notification)的确认机制
3. 演示了BLE数据传输的基本模式
4. 体现服务端对客户端写操作的完整响应流程
**实际应用注意事项**:
1. 应动态获取MTU大小而非使用固定长度
2. 生产环境需要增加数据有效性校验
3. 长数据写入需配合准备写入机制实现
4. 特征值更新与服务端数据存储需要同步处理
阅读全文
相关推荐


















