Baji_Rtc_Toy/BLE_JSON_通讯模块开发计划.md

490 lines
14 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# BLE JSON 通讯模块开发计划
> 新增功能,保留原有 BluFi 配网等全部功能不变。
---
## 一、功能定位
在现有 BluFi 蓝牙配网基础上,**新增**一个自定义 BLE GATT Service使用 JSON 格式进行设备与 App 之间的双向通讯。
- BluFi 配网模块(`bluetooth_provisioning.*`**完全保留,不做任何修改**
- 新 BLE JSON 通讯模块(`ble_service.*`):独立注册 GATTS App与 BluFi 共存
### 共存原理
```
Bluedroid GATTS 栈(已配置 max_profiles=8
├── App 0: BluFi Service (UUID: 0xFFFF) ← 已有,不动
└── App 1: JSON Service (UUID: 0xAB00) ← 新增
```
两个 App 独立回调、独立 handle共享同一个 Bluedroid 栈和 BLE 连接。
---
## 二、底层传输参数约束
| 参数 | 值 | 说明 |
|------|-----|------|
| 默认 MTU | 23 bytes | BLE 标准默认值 |
| 协商目标 MTU | **512 bytes** | `esp_ble_gatt_set_local_mtu(512)` |
| ATT 协议头开销 | 3 bytes | 固定开销 |
| **单包最大有效载荷** | **509 bytes** | 512 - 3 |
| 广播包最大长度 | 31 + 31 bytes | ADV + SCAN RSP |
| BLE 协议版本 | 4.2 | BLE 5.0 已关闭BluFi 兼容) |
| 最大连接数 | 4 (CONFIG_BT_ACL_CONNECTIONS) | 当前 BluFi 占 1 |
---
## 三、GATT Service 设计
```
Custom JSON Communication Service
├── Service UUID: 0xAB00 (Primary Service)
├── Characteristic 1: JSON_WRITE (App → 设备)
│ ├── UUID: 0xAB01
│ ├── Properties: WRITE
│ ├── Permissions: ESP_GATT_PERM_WRITE
│ └── Max Value: 512 bytes
├── Characteristic 2: JSON_NOTIFY (设备 → App)
│ ├── UUID: 0xAB02
│ ├── Properties: NOTIFY | READ
│ ├── Permissions: ESP_GATT_PERM_READ
│ ├── Max Value: 512 bytes
│ └── Descriptor: CCCD (0x2902, 2 bytes, 用于开启/关闭 NOTIFY)
└── Characteristic 3: JSON_STATUS (设备状态被动读取,可选)
├── UUID: 0xAB03
├── Properties: READ
├── Permissions: ESP_GATT_PERM_READ
└── Max Value: 512 bytes
```
---
## 四、JSON 消息格式
### 4.1 公共格式
**请求App → 设备):**
```json
{"cmd":"xxx","id":1,"data":{...}}
```
**响应(设备 → App**
```json
{"cmd":"xxx","id":1,"code":0,"data":{...}}
```
**主动推送(设备 → App无 id**
```json
{"cmd":"event","data":{"type":"xxx",...}}
```
### 4.2 固定开销
| 字段 | 占用 | 说明 |
|------|------|------|
| `{"cmd":""}` | 10 bytes | 命令名空壳 |
| `,"id":1` | 7 bytes | 消息 ID1~999 |
| `,"data":{}` | 10 bytes | 数据域空壳 |
| `,"code":0` | 9 bytes | 响应码(仅响应) |
| **请求固定开销** | **~27 bytes** | 留给 data 约 482 bytes |
| **响应固定开销** | **~36 bytes** | 留给 data 约 473 bytes |
---
## 五、逐条命令参数与大小计算
### 5.1 set_wifi — WiFi 配置
**方向:** App → 设备
**请求:**
```json
{"cmd":"set_wifi","id":1,"data":{"ssid":"MyHomeWiFi_5G","pass":"myP@ssw0rd123"}}
```
| 字段 | 类型 | 最大长度 | 来源 |
|------|------|---------|------|
| ssid | string | 32 bytes | IEEE 802.11 标准 |
| pass | string | 64 bytes | WPA2 标准 |
**最大请求大小:** 55(框架) + 32(ssid) + 64(pass) = **151 bytes** → 单包 ✅
**响应:**
```json
{"cmd":"set_wifi","id":1,"code":0,"data":{"status":"connecting"}}
```
**大小:** ~60 bytes → 单包 ✅
---
### 5.2 wifi_list — 获取 WiFi 列表
**方向:** App → 设备
**请求:**
```json
{"cmd":"wifi_list","id":2}
```
**大小:** 24 bytes → 单包 ✅
**响应:**
```json
{"cmd":"wifi_list","id":2,"code":0,"data":{"list":[{"s":"MyWiFi","r":-40},{"s":"Office","r":-55}]}}
```
| 字段 | 类型 | 最大长度 | 说明 |
|------|------|---------|------|
| s | string | 32 bytes | SSID 名称 |
| r | number | 4 bytes | RSSI 值 (-100~0) |
**单条记录最大:** ~46 bytesSSID 32字符时
**容量计算(可用空间 = 509 - 58 = 451 bytes**
| 场景 | 每条大小 | 单包可容纳 |
|------|---------|-----------|
| 典型SSID ~10字符 | ~25 bytes | ~17 条 |
| 最坏SSID 32字符 | ~46 bytes | ~9 条 |
| **设计限制** | — | **最多返回 8 条** |
**最大响应大小:** 58(框架) + 8 × 46 = **426 bytes** → 单包 ✅
---
### 5.3 dev_info — 获取设备信息
**方向:** App → 设备
**请求:**
```json
{"cmd":"dev_info","id":3}
```
**大小:** 23 bytes → 单包 ✅
**响应:**
```json
{"cmd":"dev_info","id":3,"code":0,"data":{"model":"Kapi_Rtc","fw":"1.0.0","mac":"AA:BB:CC:DD:EE:FF","board":"movecall-moji-esp32s3","uuid":"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"}}
```
| 字段 | 类型 | 最大长度 | 说明 |
|------|------|---------|------|
| model | string | 20 bytes | 产品型号 |
| fw | string | 12 bytes | 固件版本 x.x.x |
| mac | string | 17 bytes | MAC 地址 AA:BB:CC:DD:EE:FF |
| board | string | 30 bytes | 板型名称 |
| uuid | string | 36 bytes | 设备 UUID |
**最大响应大小:** ~180 bytes → 单包 ✅
---
### 5.4 status — 获取设备运行状态
**方向:** App → 设备
**请求:**
```json
{"cmd":"status","id":4}
```
**大小:** 21 bytes → 单包 ✅
**响应:**
```json
{"cmd":"status","id":4,"code":0,"data":{"state":"idle","bat":85,"chg":false,"rssi":-45,"vol":70}}
```
| 字段 | 类型 | 最大长度 | 说明 |
|------|------|---------|------|
| state | string | 11 bytes | 设备状态(见状态枚举表) |
| bat | number | 3 bytes | 电池电量 0~100 |
| chg | boolean | 5 bytes | 是否充电中 |
| rssi | number | 4 bytes | WiFi 信号 -100~0 |
| vol | number | 3 bytes | 音量 0~100 |
**设备状态枚举对照:**
| DeviceState 枚举 | JSON 值 | 字节数 |
|------------------|---------|--------|
| kDeviceStateUnknown | `"unknown"` | 7 |
| kDeviceStateStarting | `"starting"` | 8 |
| kDeviceStateWifiConfiguring | `"wifi_config"` | 11 |
| kDeviceStateIdle | `"idle"` | 4 |
| kDeviceStateConnecting | `"connecting"` | 10 |
| kDeviceStateListening | `"listening"` | 9 |
| kDeviceStateSpeaking | `"speaking"` | 8 |
| kDeviceStateDialog | `"dialog"` | 6 |
| kDeviceStateUpgrading | `"upgrading"` | 9 |
| kDeviceStateActivating | `"activating"` | 10 |
| kDeviceStateFatalError | `"error"` | 5 |
**最大响应大小:** ~105 bytes → 单包 ✅
---
### 5.5 set_vol — 设置音量
**方向:** App → 设备
**请求:**
```json
{"cmd":"set_vol","id":5,"data":{"vol":80}}
```
| 字段 | 类型 | 范围 | 说明 |
|------|------|------|------|
| vol | number | 0~100 | 音量百分比 |
**最大请求大小:** 37 bytes → 单包 ✅
**响应:**
```json
{"cmd":"set_vol","id":5,"code":0}
```
**大小:** 30 bytes → 单包 ✅
---
### 5.6 iot — 控制 IoT 设备属性
**方向:** App → 设备
**请求:**
```json
{"cmd":"iot","id":6,"data":{"thing":"lamp","prop":"brightness","val":50}}
```
| 字段 | 类型 | 最大长度 | 说明 |
|------|------|---------|------|
| thing | string | 20 bytes | IoT 设备名 (speaker/lamp/screen 等) |
| prop | string | 20 bytes | 属性名 |
| val | number/string/bool | 20 bytes | 属性值 |
**最大请求大小:** ~95 bytes → 单包 ✅
**响应:**
```json
{"cmd":"iot","id":6,"code":0}
```
**大小:** 27 bytes → 单包 ✅
---
### 5.7 reboot — 重启设备
**方向:** App → 设备
**请求:**
```json
{"cmd":"reboot","id":7}
```
**大小:** 22 bytes → 单包 ✅
**响应:**
```json
{"cmd":"reboot","id":7,"code":0}
```
**大小:** 29 bytes → 单包 ✅
---
### 5.8 ota — 检查固件更新
**方向:** App → 设备
**请求:**
```json
{"cmd":"ota","id":8}
```
**大小:** 19 bytes → 单包 ✅
**响应:**
```json
{"cmd":"ota","id":8,"code":0,"data":{"cur":"1.0.0","new":"1.1.0","has_update":true}}
```
| 字段 | 类型 | 最大长度 | 说明 |
|------|------|---------|------|
| cur | string | 12 bytes | 当前版本 |
| new | string | 12 bytes | 最新版本 |
| has_update | boolean | 5 bytes | 是否有更新 |
**最大响应大小:** ~78 bytes → 单包 ✅
---
### 5.9 ping — 心跳保活
**方向:** 双向
**请求:**
```json
{"cmd":"ping","id":9}
```
**大小:** 20 bytes → 单包 ✅
**响应:**
```json
{"cmd":"ping","id":9,"code":0}
```
**大小:** 28 bytes → 单包 ✅
---
### 5.10 event — 设备主动推送
**方向:** 设备 → App通过 NOTIFY无 id
**WiFi 连接成功:**
```json
{"cmd":"event","data":{"type":"wifi_connected","ssid":"Home","ip":"192.168.1.100","rssi":-40}}
```
**最大大小:** ~125 bytes → 单包 ✅
**WiFi 断开:**
```json
{"cmd":"event","data":{"type":"wifi_disconnected","reason":201}}
```
**大小:** ~58 bytes → 单包 ✅
**电池低电量:**
```json
{"cmd":"event","data":{"type":"low_battery","bat":10}}
```
**大小:** ~51 bytes → 单包 ✅
**设备状态变化:**
```json
{"cmd":"event","data":{"type":"state_changed","state":"listening"}}
```
**大小:** ~62 bytes → 单包 ✅
---
## 六、总览表
| # | 命令 | 方向 | 请求最大 | 响应最大 | 单包? |
|---|------|------|---------|---------|-------|
| 1 | `set_wifi` | App→设备 | **151 B** | 60 B | ✅ |
| 2 | `wifi_list` | App→设备 | 24 B | **426 B** (限8条) | ✅ |
| 3 | `dev_info` | App→设备 | 23 B | **180 B** | ✅ |
| 4 | `status` | App→设备 | 21 B | **105 B** | ✅ |
| 5 | `set_vol` | App→设备 | 37 B | 30 B | ✅ |
| 6 | `iot` | App→设备 | 95 B | 27 B | ✅ |
| 7 | `reboot` | App→设备 | 22 B | 29 B | ✅ |
| 8 | `ota` | App→设备 | 19 B | 78 B | ✅ |
| 9 | `ping` | 双向 | 20 B | 28 B | ✅ |
| 10 | `event` | 设备→App | — | **≤125 B** | ✅ |
**结论MTU=512 时,所有命令均可单包传输,不需要分包机制。**
---
## 七、错误码定义
| code | 含义 | 示例场景 |
|------|------|---------|
| 0 | 成功 | 所有正常响应 |
| 1 | 参数错误 | JSON 格式错误 / 缺少必要字段 |
| 2 | 命令不支持 | 未知的 cmd |
| 3 | 设备忙 | 正在 OTA / 正在配网 |
| 4 | WiFi 连接失败 | SSID 不存在 / 密码错误 |
| 5 | 超时 | 操作超时 |
| 6 | 内部错误 | 设备内部异常 |
**错误响应示例:**
```json
{"cmd":"set_wifi","id":1,"code":4,"msg":"wrong password"}
```
---
## 八、通讯时序示例
### 场景App 配置 WiFi
```
App 设备
│ │
│──── BLE Connect ──────────────────────────>│
│<─── MTU Exchange (512) ───────────────────>│
│──── Enable NOTIFY (write CCCD=0x0001) ───>│
│ │
│──── WRITE: {"cmd":"status","id":1} │
│<─── NOTIFY: {"cmd":"status","id":1, │
│ "code":0,"data":{"state":"idle", │
│ "bat":85,"chg":false,"rssi":0, │
│ "vol":70}} │
│ │
│──── WRITE: {"cmd":"wifi_list","id":2} │
│ [设备扫描WiFi]
│<─── NOTIFY: {"cmd":"wifi_list","id":2, │
│ "code":0,"data":{"list":[ │
│ {"s":"Home","r":-40}, │
│ {"s":"Office","r":-55}]}} │
│ │
│──── WRITE: {"cmd":"set_wifi","id":3, │
│ "data":{"ssid":"Home", │
│ "pass":"123456"}} │
│<─── NOTIFY: {"cmd":"set_wifi","id":3, │
│ "code":0,"data":{ │
│ "status":"connecting"}} │
│ [设备连接WiFi]
│<─── NOTIFY: {"cmd":"event","data": │
│ {"type":"wifi_connected", │
│ "ssid":"Home", │
│ "ip":"192.168.1.100","rssi":-40}} │
│ │
```
---
## 九、实现文件清单
| 文件 | 动作 | 说明 |
|------|------|------|
| `main/ble_service.h` | **新增** | BleJsonService 类定义 |
| `main/ble_service.cc` | **新增** | GATT Server 实现 + JSON 收发 |
| `main/ble_service_config.h` | **新增** | UUID / MTU / 超时等配置宏 |
| `main/application.cc` | **修改** | 集成 BleJsonService注册命令处理回调 |
| `main/application.h` | **修改** | 添加 BleJsonService 成员指针 |
| `main/CMakeLists.txt` | **修改** | 添加 ble_service.cc 到编译列表 |
| `sdkconfig.defaults` | **可能修改** | 若需调整 GATT profile 数量 |
| `bluetooth_provisioning.*` | **不动** | BluFi 配网保持原样 |
| `bluetooth_provisioning_config.h` | **不动** | BluFi 配置保持原样 |
---
## 十、备用分包协议(当前不需要实现)
若未来某条消息超过 509 字节,可启用以下分包协议:
```
分包头 (2 bytes):
Byte 0: [7:4] 总包数(1~15), [3:0] 当前包号(0~14)
Byte 1: 0x00=中间包, 0x01=最后一包
Byte 2~N: JSON 片段
单包判断: 首字节为 '{' (0x7B) → 完整 JSON无分包头
首字节非 '{' → 分包数据,需重组
```
当前设计下 WiFi 列表限制 8 条,所有消息均 ≤509 字节,**无需实现分包**。
---
## 十一、依赖与约束
- **JSON 库:** cJSON项目已有无需引入新依赖
- **BLE 栈:** Bluedroid已启用与 BluFi 共用)
- **GATT 资源:** max_profiles=8BluFi 占 1新模块占 1富余 6 个)
- **内存:** 8MB PSRAM + 320KB DRAMJSON 解析开销可忽略)
- **输出格式:** 使用 `cJSON_PrintUnformatted()` 紧凑输出,无空格无换行
- **BLE 回调线程安全:** GATTS 回调中不直接解析 JSON通过 FreeRTOS 队列转发到应用任务处理