Hello ReadWrite
读写交互 — BLE GATT Read/Write Request
前置阅读:Hello BLE、Hello Notify
学习目标
- 理解 BLE 中 Notify(Peripheral 主动推)和 Read/Write(Central 主动读/写)的本质区别
- 掌握 Peripheral 端处理 Central 发来的 Read/Write Request
- 掌握 Central 端
gattc_read/write的使用 - 理解 Write With Response 和 Write Without Response 的区别
- 理解 Characteristic Properties 对读写操作的实际约束
规格与功能
在 hello-notify 的基础上,Central 主动读一个 Characteristic(设备名称),写一个 Characteristic(配置参数)。
| 规格项 | 说明 |
|---|---|
| Characteristic 1 | UUID 0x2A00, Property: READ, 值 = "BLE Device" |
| Characteristic 2 | UUID 0x2A01, Property: WRITE, 值 = 设备配置参数 |
| 读操作 | Central Read → Peripheral 返回设备名称 |
| 写操作 | Central Write → Peripheral 校验 → 更新本地配置 → 确认 |
基本概念
三种交互模式
| 模式 | 方向 | 发起方 | Peripheral 角色 | Central 角色 |
|---|---|---|---|---|
| Notify | Peripheral → Central | Peripheral | 推送数据 | notify_cb 接收 |
| Read | Peripheral → Central | Central | 返回数据 | 发起读 + 接收结果 |
| Write | Central → Peripheral | Central | 接收数据 + 确认 | 发起写 + 接收确认 |
SLE 中也有 Read/Write,但 Write 不区分 With Response / Without Response。这是 BLE 特有概念。
Write With Response vs Without Response
| With Response | Without Response | |
|---|---|---|
| 可靠性 | Central 收到 Peripheral 确认 | 不确认 |
| 速度 | 较慢(等确认往返) | 较快 |
| 适用 | 配置参数(必须确认写入成功) | 高频数据流(丢一帧无所谓) |
| Characteristic Property | CHAR_PROPERTY_WRITE |
CHAR_PROPERTY_WRITE_NO_RESPONSE |
通信流程
sequenceDiagram
participant C as Central
participant P as Peripheral
Note over C,P: 已连接、已发现服务
C->>P: Read Request (char_uuid = 0x2A00)
P-->>C: Read Response: "BLE Device"
C->>P: Write Request (char_uuid = 0x2A01, timeout = 5000)
P->>P: 校验数据合法性
P-->>C: Write Response: SUCCESS
涉及 API
| API | 谁调用 | 用途 |
|---|---|---|
gattc_read() |
Central | 发起读请求 |
gattc_write() |
Central | 发起写请求 |
gatts_register_callbacks() |
Peripheral | 注册回调(含 read/write handler) |
案例说明
Central 连接后,先读取设备名称(Read),再修改配置参数(Write)。
案例操作指导
Central 串口输出:
[ble central] read device name: BLE Device
[ble central] write config: interval=2000ms
[ble central] write confirmed: SUCCESS
关键配置
- Characteristic 1:
CHAR_PROPERTY_READ(设备名称,只读) - Characteristic 2:
CHAR_PROPERTY_WRITE(配置参数,可写) - Write 类型:
WRITE_TYPE_DEFAULT(带响应——配置参数需要确认)
代码详解
Peripheral 端 Read Handler
static void read_request_cb(uint16_t conn_id, uint16_t char_handle)
{
if (char_handle == g_device_name_handle) {
// 返回设备名称
char *device_name = "BLE Device";
gatts_send_response(conn_id, char_handle,
(uint8_t *)device_name, strlen(device_name));
}
}
Peripheral 端 Write Handler
static void write_request_cb(uint16_t conn_id, uint16_t char_handle,
uint8_t *data, uint16_t len)
{
if (char_handle == g_config_handle) {
uint16_t new_value = *(uint16_t *)data;
if (new_value < 100 || new_value > 60000) {
// 校验失败 → 返回错误
gatts_send_error(conn_id, char_handle, ERR_INVALID_VALUE);
return;
}
g_config_value = new_value;
gatts_send_response(conn_id, char_handle, NULL, 0);
printf("[ble peripheral] config updated: %d\n", new_value);
}
}
Central 端发起读写
// 读设备名称
gattc_read(g_conn_id, g_device_name_handle);
// 读结果回调
static void read_result_cb(uint16_t conn_id, uint16_t char_handle,
uint8_t *data, uint16_t len, uint8_t status)
{
if (status == GATT_SUCCESS) {
printf("[ble central] device name: %.*s\n", len, data);
}
}
// 写配置参数
uint16_t new_interval = 2000;
gattc_write(g_conn_id, g_config_handle,
(uint8_t *)&new_interval, sizeof(new_interval),
WRITE_TYPE_DEFAULT); // 带响应
// 写确认回调
static void write_confirm_cb(uint16_t conn_id, uint16_t char_handle, uint8_t status)
{
if (status == GATT_SUCCESS) {
printf("[ble central] write confirmed: SUCCESS\n");
}
}