9.0 KiB
BLE 协议规范(4.2寸墨水屏设备 · 实现标准)
状态:单一事实来源。实现
src/ppclock以本文件为准。 证据来源:analysis/web/protocol-raw.md(Web JS)+analysis/apk/apk-analysis.md(APK 反编译互证)。 互证结论:GATT UUID、NRF- 前缀、240B 分块、ff/00 平面码、dd 时间帧、e2 收尾、e100/e101/e102 模式族、倒计时 04/0n/990/ff00/ffff 帧、6 种抖动(Bayer 矩阵逐值一致)、红色判定、灰度系数 —— 三线一致。 Web 独有(APK 未实现):WiFi 配置、EFEF 取设备 ID、2300/2301、14/15、e3/e4/e5、E6、FB、e901/EA000、EA0x —— 本规范以 Web 证据为准。 APK 独有:$IMG(0,0)、8000/8001 图像起始帧(属扫地/轻舞固件家族分叉,本设备不适用)。
1. 设备与 GATT
| 项 | 值 |
|---|---|
| 广播名前缀 | NRF- |
| EPD Service | 13187b10-eba9-a3ba-044e-83d3217d9a38 |
| EPD Characteristic(图像+刷屏,write+notify) | 4b646063-6264-f3a7-8941-e65356ea82fe(h=48) |
| RXTX Service | 00001f10-0000-1000-8000-00805f9b34fb |
| RXTX 命令通道(实机) | 0000331f-0000-1000-8000-00805f9b34fb(h=57,notify+write+read,svc 0x221f) |
00001f1f-...(h=53,仅 write) |
⚠ 实机证据:写入仅 ATT-ACK 不执行命令(2026-07-28);Web/App 文档中的"RXTX 特征",本设备弃用 |
| SUOTA Service | 0000fef5-...(h=28-45,含自定义特征,见 §9) |
实机 GATT 全表(2026-07-28 bridge 诊断,设备 NRF-5DBF28): GAP/GATT/DIS(2A24/2A26/2A28/2A23/2A50)/BAS(2A19 notify) + fef5 SUOTA 六特征 + EPD + 1f1f(write-only) + 331f(notify+write+read)。 notify 订阅:设备 ID 应答案测经 331f 到达(EFEF 同样须写 331f);EPD/2A19 亦可订阅。
连接顺序:先 EPD service → 再 RXTX;写均用 write-with-response(triggerRxTxCmd2 例外,without-response)。
2. 屏幕参数
| 参数 | 值 |
|---|---|
| 分辨率 | 400 × 300 |
| 色彩 | 黑/白/红 三色(亦可切黑白模式) |
| 像素打包 | 1bpp,MSB first,行主序(y:0→300, x:0→400) |
| 平面 | bw 平面 15000B + 红平面 15000B(红仅三色模式) |
| bw 位值 | R,G,B 任一>0 → 1(白),全 0 → 0(黑)⚠即"非黑即白" |
| 红平面位值 | R>0,G=0,B=0 → 1(红) |
| 日历小图 | 280×100(3500B)或 400×180(9000B),同 1bpp |
3. 帧格式
3.1 图像数据块(EPD 通道)
[cod:1B][code:1B][offset:2B BE][payload:≤240B]
| 字段 | 值 |
|---|---|
| cod | 03=整图;04=日历小图(数据总长 3500B 或 9000B 时) |
| code | ff=bw 平面;00=红平面 |
| offset | 本块在平面内的字节偏移,大端 |
| payload | ≤240 字节;按 offset 顺序连发,块间无应用层 ACK |
结束:全部块发完延时 ≥300ms → 整图发 01;日历小图发 AA。
3.2 刷屏(EPD 通道)
00{param} 后接 01。
3.3 时间设置(RXTX 通道)
dd [unix:4B BE][year:2B BE][month:1B][day:1B][week:1B] → 随后发 e2
- unix = 本地时区修正秒(
now_utc + tz);week:周一=1 … 周日=7。 - 测试向量:2026-07-24 15:30:00 (UTC+8) 周五 →
dd 6982a7f0 07ea 07 18 05(unix=1785475800=0x6982A7D8 ⚠按实现时实际值校验,此处仅示意格式)→ 待真机/互证校准。
3.4 倒计时(RXTX 通道)
- 日期:
ff00{YYYY}{MM}{DD}(时钟界面)/ffff{YYYY}{MM}{DD}(日历界面);YYYY=4 位十进制 ASCII。 - 前缀文字位图(时钟界面,16px 字格):≤4 字符(中文=1,英文=0.5)单包
0{charCount}{位图hex};>4 拆两包:04{前128B}然后0{charCount}{剩余}。 - 前缀文字位图(日历界面,24px 字格):每字符一包
990{索引}{72B},包间隔 1s。 - 位图打包:1bpp MSB first 行主序。
- 收尾
E2;关闭倒计时FA→ 1s →E2。 - 日历中文字设置:
990*→FA→e10103→E2。
3.5 激活与设备 ID(RXTX 通道)
- 请求 ID:发
EFEF→ notify 收 14 字节(可打印即 ASCII,否则 hex 表示)。 - 激活码:原流程
GET http://4638e813e4.qicp.vip:8889/key_generator.php?id={用户ID}&key={设备ID}→ 返回 ≥32 hex(正则激活码1:([0-9A-F]{32,}))。⚠算法在服务端,本地化方案见 §7。 - 下发:
EF{激活码hex大写}→ 400ms →e2。
3.6 WiFi 配置(RXTX 通道)
ASCII UTF-8 直发:WIFI_SSID:{ssid};WIFI_PASS:{pass};CITY:{城市代码}(城市代码为天气代码,如 101010100)。
4. 命令 opcode 总表(RXTX 通道,除注明外)
| 命令 | 含义 |
|---|---|
e2 |
提交/刷新(多数命令收尾) |
dd… |
设置时间(§3.3) |
2300 / 2301 |
三色模式 / 黑白模式 |
14 |
12/24 小时制切换 |
15 |
时钟颜色切换 |
e3 |
反色模式 |
e4 |
时钟字体切换 |
e5 |
旋转 180° |
e10000–e10003 |
图片模式:显示图一~图四 |
e10103 / e10104 / e10105 |
日历模式一/二/三 |
e10201 / e10202 / e10203 |
时钟模式一/二/三 |
E6{01-0f} / E6{10-f0} |
红 / 黑 LUT 校准(越大越深;0 无效) |
FB01{HH}{MM} / FB00{HH}{MM} |
开启 / 关闭休眠时段 |
e901{n}{01|00} |
轮播间隔 n(01=分钟,00=小时) |
EA0{0-3} |
上传目标槽位(图一~图四) |
EA000{0-3} |
轮播数量 1–4(值=n-1) |
EF{hex} |
激活码下发 |
EF{数字逐位双写} |
停车牌号码("12"→EF1122) |
EFEF |
请求设备 ID |
FA |
关闭倒计时 |
ff00… / ffff… / 990… / 04… / 0… |
倒计时(§3.4) |
00{xx}+01(EPD) |
刷屏 |
01(EPD) |
整图传输结束刷新 |
AA(EPD) |
小图传输结束 |
模式切换命令(e1xx/23xx/14/15/e3-e5)发后 300ms 补 e2(setri 模式)。
5. 抖动算法参数(图像管线)
| 算法 | 参数 |
|---|---|
| none | 阈值化,threshold 默认 125 |
| floydsteinberg | 误差核 7/16,3/16,5/16,1/16 |
| atkinson | 6 邻域 ×1/8 |
| bayer | 4×4 矩阵 15,135,45,165],[195,75,225,105],[60,180,30,150],[240,120,210,90 |
| stucki | /42 核 |
| jarvis | /48 核 |
| bwr_* | 同上 6 种三色版;红色判定 r>g*1.5 && r>b*1.5 && r>threshold |
预处理:亮度 ±255·v/100;对比度 (c+100)/100(128 中点);饱和度=灰度插值(0.299/0.587/0.114);diffusion 0–100/100 误差缩放。
6. 标准会话流程
- 扫描(namePrefix
NRF-)→ 连接 → EPD service → RXTX service → 订阅两通道 notify。 - (可选)
EFEF取设备 ID。 - 对时:
dd…+e2。 - 操作:模式切换(+
e2)/ 传图(EA0{slot}→ 数据块×N →01/AA)/ 倒计时 / WiFi。 - 断线自动重连 ≤5 次。
7. 本地化(去云)清单 —— 已全部解决
| 云依赖 | 本地化方案 | 状态 |
|---|---|---|
| key_generator.php 激活码 | 本地 keygen:code[i]=ror2(((MAC[i]<<1)&0xFF)^0xEF)%101(firmware-analysis §4.2);ppclock activate --auto 经 EFEF 反解 MAC 自动完成;且固件侧 EF 不门禁任何命令 |
✅ 已实现 |
| file_down.php 固件下载 | 固件本地存档 app/;ppclock ota 本地升级(SUOTA 经 0x221F 服务,firmware-analysis §5) |
✅ 已实现 |
| qbsg.top 自助激活 | 同 keygen | ✅ |
| 天气(CITY 代码) | 原厂云功能,本地化范围外;wifi 命令仅透传。注意:本固件 WIFI 命令为 no-op(firmware-analysis §3.3) |
⚠ 固件未实现 |
| qrcodejs CDN(模板用) | CLI 本地 qrcode 库 | ✅ |
固件侧 no-op 命令(Web 端发送但本固件版本忽略,CLI 保留前向兼容):E9xx 轮播间隔、EA0x 槽位/EA000x 轮播数量、WIFI 配置(firmware-analysis §3.3 分发器跳表)。
9. SUOTA 固件升级(证据:APK §e)
| 项 | 值 |
|---|---|
| Service | 0000fef5-0000-1000-8000-00805f9b34fb |
| SERV_STATUS(notify) | 64b4e8b5-0de5-401b-a21d-acc8db3b913a |
| MEM_DEV | 8082caa8-41a6-4021-91c6-56f9b954cc34 |
| GPIO_MAP | 724249f0-5ec3-4b5f-8804-42345af08651 |
| PATCH_DATA(自定义映射) | 457871e8-d516-4ca1-9116-57d0b17b9cb2 |
| PATCH_LEN(自定义映射) | 9d84b9a3-000c-49d8-9183-855b673fda31 |
流程:MEM_DEV←{00,00,00,13}(SPI Flash)→ GPIO_MAP←{00,00,06,05} →
镜像+尾 1B XOR 校验 → 240B 块(PATCH_LEN 2B LE + 20B 无响应写块 + 等 SERV_STATUS==2)→
MEM_DEV←{00,00,00,FE} → 等 ==2 → {00,00,00,FD} 重启。
状态码:2=成功,3-9/17-22/0xFFFF/0xFFFE=各类错误(见 ota.py STATUS_ERRORS)。
8. 测试向量(实现前必须写入 tests/)
来源:analysis/web/protocol-raw.md 各节;标 ⚠ 的待 APK/固件互证确认。
- setTime 布局(§3.3)⚠
- 图像块帧头:400×300 bw 平面首块 →
03 ff 0000+ 240B ⚠ - 小图判定:3500B → cod=
04;15000B → cod=03 - 停车牌 "12" →
EF1122 - 轮播 5 分钟 →
e9010501 - 槽位 3 上传前置 →
EA02;轮播数量 4 →EA0003 - 停车牌/激活/取 ID 的
EF前缀区分:EFEF=取ID、双写数字=车牌、≥32hex=激活