Files
DLD110SV4B/docs/ble-protocol.md
T

234 lines
10 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.
# DLD110SV4 蓝牙小程序接口协议
> 依据源码整理:`BLE/DLD110S_Peripheral_28K/APP/dbn_ble_srv.c`(主逻辑)、`peripheral.c`/`Profile/gattprofile.c`BLE 服务)、`peripheral_main.c`(灵敏度/检测)、`include/storage.h`(配置结构)、`include/cmcng.h`(常量)
> 固件版本:V4.31cmcng.h `FIRMWARE_VER`| 硬件版本:V4.01 | 产品型号:DLD110SBLE 单线圈)
---
## ⚠ 与 DLD154Pro 协议的三处关键差异(先读这节)
| # | 差异 | DLD110SV4 | DLD154Pro |
|---|------|-----------|-----------|
| 1 | **灵敏度来源** | **硬件拨码 KEY_SA1/SA2 决定**`poll_sens_key`peripheral_main.c:263),协议 0x23 设置的 `sensitvity` **不参与检测** | BLE 配置(无拨码),sensitvity 1~4 直接生效 |
| 2 | 灵敏度档数 | **3 档** `{108,54,28}` / `{81,32,16}`sens_in/sens_out | 4 档 `{108,54,28,14}` / `{81,32,16,12}` |
| 3 | 命令 0x31 | **无**(无 RS485) | 有(读写波特率/地址) |
> **灵敏度拨码映射**`poll_sens_key`,每周期刷新 `g_freq_sens`,检测阈值 = `g_loop_sens_list.sens[g_freq_sens]`):
>
> | KEY_SA1 | KEY_SA2 | g_freq_sens | 档位 | 检测阈值 sens_in |
> |---------|---------|-------------|------|-----------------|
> | 1 | 1 | 0 | 最低 | 1080.165% Δf/f |
> | 0 | 1 | 1 | 中 | 540.082% |
> | 1 | 0 | 2 | 最高 | 280.043% |
> | 0 | 0 | — | 保持上次 | — |
>
> DEBUG 编译时固定 `g_freq_sens = 2`。
---
## 1. BLE 服务与特征
| 服务/特征 | UUID | 属性 | 长度 | 用途 |
|-----------|------|------|------|------|
| 服务 SIMPLEPROFILE | 0xFFE0 | — | — | 广播服务 UUID |
| Char1 | 0xFFE1 | 读写 (RW) | 64B | **小程序 → 芯片:命令下发** |
| Char2 | 0xFFE2 | 读 | 1B | 预留 |
| Char3 | 0xFFE3 | 写 | 8B | 预留 |
| Char4 | 0xFFE4 | **通知 (Notify)** | 32B | **芯片 → 小程序:命令响应 + 主动上报** |
| Char5 | 0xFFE5 | 读 | 5B | 预留 |
- 设备名:`DLD110S`(广播 + GATT Device Name
- **MTU**:默认 23,连接后协商(`ATT_MTU_UPDATED_EVENT`)。notify 单包 payload ≤ `peripheralMTU 3`
- **交互模型**:写 Char1 下发命令帧 → 芯片处理 → Char4 notify 返回响应帧;主动上报也走 Char4
---
## 2. 帧格式与校验
```
| Magic 1B | Header 1B | Len 1B | CMD 1B | Data N B | XOR 1B | SUM 1B |
```
| 字段 | 说明 |
|------|------|
| Magic | `0x8F` = 车检器默认协议;`0x7F` = 透传 |
| Header | 单包 = `0x00`;分包 = `(pkg_amount << 4) \| pkg_seq`seq 从 1 |
| Len | `1CMD+ NData 字节数)` |
| XOR | 从 Header 到 Data 末尾逐字节异或 |
| SUM | 从 Header 到 Data 末尾逐字节求和(低 8 位) |
校验算法(dbn_ble_srv.c `compute_ckb`/`check_pkg`)与 DLD154Pro 完全一致:最小帧长 6,XOR/SUM 任一不符丢弃。
---
## 3. 分包机制
与 DLD154Pro 相同(同一模板):
- **发送**`MAX_BLE_DAT_RESPONSE_LEN = 14`= BLE_BUFF_MAX_LEN4MTU=23 旧假设),多包 Header = `(amount<<4)|seq`
- **接收**`unpack_packs()` 支持重组(当前仅 0x1C/0x1D 使用,已注释)
- ⚠ 分包粒度写死 14B,MTU 协商更大后不重新分包
---
## 4. 命令总览
| CMD | 名称 | 方向 | 说明 |
|-----|------|------|------|
| 0x10 | GET_DEV_INFO | 小程序→芯片 | 读设备信息(MAC/版本/型号) |
| 0x1F | RESET_DEV | 小程序→芯片 | 复位设备(SYS_ResetExecute |
| 0x23 | SET_CJQ_PARAM | 小程序→芯片 | 设置车检器参数(**灵敏度不生效,见 §5.3**) |
| 0x24 | GET_CJQ_PARAM | 小程序→芯片 | 读车检器参数 |
| 0x8A | LOOP_SENS_LIST | 小程序→芯片 | 读写灵敏度表(sens_in/sens_out |
| 0x92 | SET_CJQ_FACTORY | 小程序→芯片 | 恢复出厂 |
| 0xC5 | SENS_ACS_ENABLE | 小程序→芯片 | 主动上报使能 |
| 0xC0 | SUB_SENS_REPORT | 芯片→小程序 | 主动上报线圈动态(不可下发) |
**不存在**0x31DLD154Pro 有,本产品无 RS485)。
**未实现(注释)**0x1C/0x1D/0x1E/0x20-0x22/0x6D/0x87/0x88/0x89。
---
## 5. 命令详述
### 5.1 0x10 读设备信息
无 Data。响应 Data
| 偏移 | 字节 | 内容 |
|------|------|------|
| 0-5 | 6 | MAC 地址 |
| 6-7 | 2 | 硬件版本 MAIN/SUBV4.01 → 04 01 |
| 8-9 | 2 | 固件版本 MAIN/SUBV4.31 → 04 1F |
| 10 | 1 | 产品型号长度 |
| 11.. | N | 产品型号 ASCII"DLD110S" |
| .. | 6 | 设备编号 |
| .. | 2 | sub_code(固定 0 |
| .. | 2 | 总线设备数(固定 0) |
### 5.2 0x1F 复位设备
无 Data。`SYS_ResetExecute()` 整机冷复位,**无响应帧**。
### 5.3 0x23 设置车检器参数
Data5B):
| 偏移 | 内容 |
|------|------|
| 0 | `sensitvity(低4位) \| loopFreq_Level(高4位)` |
| 1 | delay_time(离开延时) |
| 2 | `output_mode(低2位) \| loopSafe_Timeout(高5位)` |
| 3 | exist_mode(有限存在时长) |
| 4 | direction_mode(高 4 位 = g_function_mode |
**⚠ 灵敏度字段不生效**`g_loop_cng_unit.sensitvity` 仅写入 flash 存档,**检测阈值由拨码 `g_freq_sens` 决定**(见开头差异表)。小程序设置灵敏度无效,现场改灵敏度必须拨码。
内部换算(unpack_pkg_set_cjq_param):
```
g_function_mode = direction_mode >> 4
g_hold_time = exist_mode * 30 * 20 (600 tick ≈ 30s × exist_mode, @50ms tick)
```
处理:更新全局 → `write_cfg_to_store(4, tmp, 7)``para_store_init()`。响应:1B status=0。
### 5.4 0x24 读车检器参数
无 Data。响应 Data11B):
| 偏移 | 内容 |
|------|------|
| 0 | `(g_freq_sens + 1) \| (loopFreq_Level << 4)`(灵敏度回读 = **拨码档位 + 1**,值域 1~3 |
| 1 | delay_time |
| 2 | `output_mode \| (loopSafe_Timeout << 3)` |
| 3 | exist_mode |
| 4 | `sens_list.total << 4`= 3,高 4 位) |
| 5 | 线圈断开次数低字节(**上电后持续累计**,不截断) |
| 6 | 线圈断开次数高字节 |
| 7 | **direction_mode**(完整字节;高 4 位 = g_function_mode**V4.31 与 DLD154Pro 语义对齐** |
| 8-10 | **归一化基数 LOOP_MEASURE_BASE3B LE= 524288**V1.1 追加) |
> ⚠ **偏移 8-10 用途(协议 V1.12026-08-26 追加)**variation 换算 ΔL/L 的归一化基数。
> 第三方应用连接后读一次 0x24 缓存该值:`Δf/f ≈ variation / LOOP_MEASURE_BASE``ΔL/L ≈ -2 × variation / LOOP_MEASURE_BASE`。
> **背景**DLD110S 的 MEASUREMENT_BASE = 5242882^19 @60MHz),与 DLD154V4B 的 131072 不同——小程序曾照搬 131072 导致所有百分比放大 4 倍。固件显式上报后第三方无需猜测。
> 向后兼容:老小程序只读前 8B 不受影响;新小程序读 11B。
### 5.5 0x8A 读写灵敏度表
**读(pkg[4]=0**:响应 Data`[0x10] [total=3] [sens0_in LE2 | sens0_out LE2] [sens1...]`
**写(pkg[4]≠0**Data 从 pkg[5] 起:`[total] [表...]`。写 flash `Addr_Loop_Sens_List_Offset(0x20)` + `para_store_init()`。不直接更新 RAM,依赖重读生效。
出厂默认 3 档:`sens_in {108, 54, 28}` / `sens_out {81, 32, 16}`peripheral_main.c:187,滞回 ~75/59/57%)。
### 5.6 0x92 恢复出厂
`set_factory_param() + factory_dev()``para_store_init()`。响应:1B status=0。
### 5.7 0xC5 主动上报使能
Data`[enable] [interval] [timeout_min]`
- enable1=开启,0=关闭
- interval:间隔系数,实际 `interval × 0x7F`127
- timeout_min:超时分钟数(0=不限时)
处理:开启时 `timeout_counter = timeout_min × 60 × 100`**源码 TODO 标注 min→ms60×100=6000 疑应为 60000,待现场验证**)。关闭时 flag=0。
---
## 6. 主动上报 0xC0(芯片 → 小程序)
与 DLD154Pro 相同模板(type = 5 SENS_LOOP_DYNAMIC):
| 偏移 | 字节 | 内容 |
|------|------|------|
| 0 | 1 | `0x05`SENS_LOOP_DYNAMIC |
| 1 | 1 | `loop_num<<6 \| freq_level<<4 \| condition&0x0F` |
| 2 | 1 | `relay2_state<<6 \| relay2_cng<<4 \| relay1_state<<2 \| relay1_cng&0x3` |
| 3 | 1 | `loop1_FLAG_CUT<<7 \| car_state<<4 \| direction&0x0F` |
| 4-6 | 3 | 线圈断开次数(LE,**上电后持续累计**,不截断;上电未连时为 1) |
| 7-9 | 3 | 实际频率(LE`freq = sclk_freq/CAPVD×LPCNT + 360` 补偿) |
| 10-12 | 3 | variation**3B 有符号补码 LE**,正=车/金属进入方向) |
触发:事件(flag_event)立即上报;周期 `report_counter >= 120`120 × 50ms ≈ 6s);前提 0xC5 开启。
**variation 接收端注意**3B 有符号,负值需符号扩展(`if (v & 0x800000) v |= 0xFF000000`)。
**condition**:环境评估 0~15`g_env_activity/10`,>150 取 15;线圈断开强制 15)。
---
## 7. 实现细节与坑(对照源码)
1. **⚠ 灵敏度是拨码的不是协议的**:检测阈值 = `g_loop_sens_list.sens[g_freq_sens]`peripheral_main.c:609),g_freq_sens 由 `poll_sens_key()` 读 KEY_SA1/SA2 每周期刷新。0x23 的 sensitvity 只存档不生效,0x24 回读的是拨码档位+1
2. **3 档灵敏度**MAX_LOOP_SENS_AMOUNT=3),0x8A 写表 total 上限 3
3. **无 0x31**DLD110SV4 无 RS485,小程序不应下发 0x31
4. **0xC5 超时疑似 bug**`timeout_min × 60 × 100`(若意图 ms 应为 ×1000,待现场验证)
5. **0x8A 写表不直接更新 RAM**:依赖 `para_store_init()` 重读
6. **0xC0 frequent +360 补偿**:经验值,示波器标定用
7. **0x1F/0x92 无响应帧**:复位/出厂类命令执行后设备重启
8. **notify 超限静默丢弃**`len > (peripheralMTU-3)` 返回 FAIL 不重发,长响应必须分包
9. **0x24 偏移 7 = direction_mode 完整字节**V4.31 起;高 4 位 = g_function_mode,与 DLD154Pro 语义对齐)——解析别混:低 4 位是方向/触发模式
10. **g_hold_time 换算差异**`exist_mode × 600 tick`(≈30s/单位,DLD154Pro 为 ×100 tick ≈5s/单位)——产品差异,协议层语义一致
---
## 8. 交互示例
**读取设备信息**(无 DataLen=1):
```
下发: 8F 00 01 10 11 11
│ │ │ │ │ └ SUM = 0x00+0x01+0x10 = 0x11
│ │ │ │ └─ XOR = 0x00^0x01^0x10 = 0x11
│ │ │ └─ CMD = 0x10
│ │ └─ Len = 1 (CMD only)
│ └─ Header = 0 (单包)
└─ Magic = 0x8F
芯片 → Char4 notify: 8F 00 10 XX [MAC6][04 01][04 14][07 "DLD110S"][dev_no6][00 00][00 00] [XOR] [SUM]
```
**设置参数(如延时/输出,灵敏度不生效)**
```
下发: 8F 00 06 23 20 00 00 00 00 [XOR] [SUM]
Len=6: CMD(0x23)+Data5
Data[0]=0x20: sensitvity=2(仅存档) | freq_level=0
Data[1..4]=延时/输出/存在/方向模式
```