Files
qianbian/docs/protocol.md
T

6.6 KiB
Raw Blame History

BLE 协议规范(4.2寸墨水屏设备 · 实现标准)

状态:单一事实来源。实现 src/ppclock 以本文件为准。 证据来源:analysis/web/protocol-raw.md(Web JS),APK/固件互证结果以"互证"标注;未互证项以 ⚠ 标注。

1. 设备与 GATT

项 值
广播名前缀 NRF-
EPD Service 13187b10-eba9-a3ba-044e-83d3217d9a38
EPD Characteristic(图像+刷屏,write+notify) 4b646063-6264-f3a7-8941-e65356ea82fe
RXTX Service 00001f10-0000-1000-8000-00805f9b34fb
RXTX Characteristic(命令,write+read+notify) 00001f1f-0000-1000-8000-00805f9b34fb
0000221f-... 预留,未使用

连接顺序:先 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 通道)

  1. 请求 ID:发 EFEF → notify 收 14 字节(可打印即 ASCII,否则 hex 表示)。
  2. 激活码:原流程 GET http://4638e813e4.qicp.vip:8889/key_generator.php?id={用户ID}&key={设备ID} → 返回 ≥32 hex(正则 激活码1:([0-9A-F]{32,}))。⚠算法在服务端,本地化方案见 §7。
  3. 下发: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. 标准会话流程

  1. 扫描(namePrefix NRF-)→ 连接 → EPD service → RXTX service → 订阅两通道 notify。
  2. (可选)EFEF 取设备 ID。
  3. 对时:dd… + e2。
  4. 操作:模式切换(+e2)/ 传图(EA0{slot} → 数据块×N → 01/AA)/ 倒计时 / WiFi。
  5. 断线自动重连 ≤5 次。

7. 本地化(去云)清单

云依赖 本地化方案
key_generator.php 激活码 分析固件校验逻辑(见 docs/firmware-analysis.md),本地实现算法或固件补丁绕过 ⚠待固件分析
file_down.php 固件下载 固件已本地存档 app/
qbsg.top 自助激活 同上激活方案
天气(CITY 代码) 本地化=不依赖天气;CLI 提供 wifi-config 透传
qrcodejs CDN(模板用) CLI 用本地 qrcode 库

8. 测试向量(实现前必须写入 tests/)

来源:analysis/web/protocol-raw.md 各节;标 ⚠ 的待 APK/固件互证确认。

  1. setTime 布局(§3.3)⚠
  2. 图像块帧头:400×300 bw 平面首块 → 03 ff 0000 + 240B ⚠
  3. 小图判定:3500B → cod=04;15000B → cod=03
  4. 停车牌 "12" → EF1122
  5. 轮播 5 分钟 → e9010501
  6. 槽位 3 上传前置 → EA02;轮播数量 4 → EA0003
  7. 停车牌/激活/取 ID 的 EF 前缀区分:EFEF=取ID、双写数字=车牌、≥32hex=激活