10 KiB
墨水屏 Web 上位机 BLE 协议提取报告(原始证据)
来源:analysis/web/(ble.js 4408 行 / index.html 1309 行 / moban.js 943 行 / timedjs.js 714 行 / timedjs2.js 323 行 / switch2.js 54 行 / time.js 21 行 / hex.js 15 行) 设备:4.2寸 400x300 黑白红三色墨水屏,DA14585,设备名前缀
NRF-
1. GATT 结构
| 项 | 值 | 证据 |
|---|---|---|
| 设备名过滤 | namePrefix: 'NRF-' |
ble.js:4052-4054 |
| optionalServices | 0000221f-...34fb、00001f10-...34fb、13187b10-eba9-a3ba-044e-83d3217d9a38 |
ble.js:4054 |
| EPD Service | 13187b10-eba9-a3ba-044e-83d3217d9a38 |
ble.js:4163 |
| EPD Characteristic | 4b646063-6264-f3a7-8941-e65356ea82fe |
ble.js:4165 |
| RXTX Service | 00001f10-0000-1000-8000-00805f9b34fb |
ble.js:4101 |
| RXTX Characteristic | 00001f1f-0000-1000-8000-00805f9b34fb |
ble.js:4103 |
0000221f |
仅声明未使用 | ble.js:4054 |
- epdCharacteristic:图像数据块 + 刷屏命令(
00xx/01/AA),writeValueWithResponse(ble.js:38-44),订阅 notify(ble.js:4167-4205)。 - rxtxCharacteristic:全部控制命令(时间/模式/WiFi/激活/倒计时/模板槽位),writeValueWithResponse + 100ms 后 read(ble.js:50-71);另有 writeValueWithoutResponse(ble.js:84-90);notify 是设备ID 14 字节应答主通道(ble.js:4121-4137)。
- 连接顺序:preConnect()(ble.js:4045)→ connect()(ble.js:4158)先 EPD 后 RXTX(ble.js:4207);断线重连≤5次(ble.js:30-36)。
2. 命令协议
2.1 发送封装
| 函数 | 行为 | 证据 |
|---|---|---|
| sendCommand | EPD writeValueWithResponse | ble.js:38-44 |
| rxTxSendCommand | RXTX write+100ms read | ble.js:50-75 |
| rxTxSendCommand2 | RXTX writeWithoutResponse | ble.js:84-90 |
| triggerEpdCmd | hex→EPD | ble.js:3971-3974 |
| triggerRxTxCmd | hex→RXTX | ble.js:3963-3966 |
| triggerRxTxCmd2 | hex→RXTX(无响应) | ble.js:3967-3970 |
| setri | RXTX 发 cmd,300ms 后补 e2 |
ble.js:3956-3962 |
| clearScreen | EPD 发 00{cmd} 再 01 |
ble.js:45-49 |
| triggerEpdRed_lut | RXTX E6{cmd}(0 直接返回) |
ble.js:3975-3980 |
| triggerEpdJH | RXTX EF{cmd},500ms 后 e2 |
ble.js:3981-3987 |
| triggerEpdCHEPAI | 字符双写+EF 前缀,500ms 后 e2 |
ble.js:3988-3998 |
| triggerRxTxCmd_LED | cmd+note+color+HH+MM |
ble.js:3999-4009 |
| triggerRxTxCmd_XM | cmd+HH+MM |
ble.js:4010-4020 |
| triggerRxTxCmd_LB | e901+hex(n)+cmd |
ble.js:4022-4038 |
2.2 Opcode 一览
EPD 通道:
| Opcode | 含义 | 证据 |
|---|---|---|
00{xx} |
刷屏第一步 | ble.js:47 |
01 |
刷屏执行/整图结束刷新 | ble.js:48,160,230 |
AA |
日历小图传输结束 | ble.js:159 |
03{ff|00}{offset:2B BE}{data} |
整图数据块(≤240B/块) | ble.js:98,104 |
04{ff|00}{offset:2B BE}{data} |
日历小图数据块(hex 总长 7000/18000 时) | ble.js:99,104 |
RXTX 通道:
| Opcode | 含义 | 证据 |
|---|---|---|
e2 |
提交/强制刷新(多数命令收尾) | ble.js:256,3961 等 |
dd{unix:4B}{year:2B}{month:1B}{day:1B}{week:1B} |
设置时间 | ble.js:251-257 |
2300/2301 |
三色/黑白屏模式 | index.html:840-841 |
14 |
12/24 小时制切换 | index.html:845 |
15 |
时钟颜色切换 | index.html:881 |
e3 |
反色模式 | index.html:878 |
e4 |
时钟字体切换 | index.html:880 |
e5 |
旋转180° | index.html:886 |
e10000..e10003 |
图片模式 图一~图四 | index.html:868-873 |
e10103/e10104/e10105 |
日历模式一/二/三 | index.html:860-862 |
e10201/e10202/e10203 |
时钟模式一/二/三 | index.html:853-855 |
E6{lut} |
红/黑 LUT 校准:01–0f 设红、10–f0 设黑 | ble.js:3978; index.html:885-887 |
FB01{HH}{MM} / FB00{HH}{MM} |
开/关休眠时段 | index.html:897-898; ble.js:4017 |
e901{n:hex}{01|00} |
图片轮播间隔(01=分钟 00=小时) | ble.js:4035 |
EA0{slot} |
上传选槽位 EA00–EA03 | ble.js:213,237 |
EA000{n-1} |
轮播数量 1–4 | ble.js:241-249 |
EF{激活码hex} |
激活设备 | ble.js:3891,3983 |
EF{逐位双写号码} |
停车牌("12"→EF1122) |
ble.js:3990-3995 |
EFEF |
请求设备 ID(14B notify 应答) | ble.js:3924,4123 |
FA |
关闭倒计时 | timedjs2.js:142,308 |
ff00{year}{MM}{dd} |
时钟倒计时日期(year=4位十进制ASCII) | timedjs2.js:250-253 |
ffff{year}{MM}{dd} |
日历倒计时日期 | timedjs2.js:245-248 |
04{128B hex} + 0{charCount}{余hex} |
时钟倒计时前缀文字位图(>4字符拆两包) | timedjs2.js:206-217 |
0{charCount}{位图hex} |
同上 ≤4字符单包 | timedjs2.js:218-224 |
990{j}{72B hex} |
日历倒计时文字位图(每字符一包,24x24/8) | timedjs2.js:229-233,300-304 |
ASCII WIFI_SSID:..;WIFI_PASS:..;CITY:.. |
WiFi+天气城市配置(UTF-8 直发) | ble.js:3839-3863 |
{cmd}{note}{color}{HH}{MM} |
LED 变体(遗留) | ble.js:4000-4008 |
3. 图像传输协议(sendBufferData ble.js:92-107)
- 分块:
my_step=480hex 字符 = 240 数据字节/块(ble.js:9,100)。 - 帧格式(ble.js:104):
cod(1B: 03整图/04小图) +code(1B: ff=bw平面 00=红平面) + offset(2B 大端,=字节序号) + ≤240B 数据。块总长 244B。 - cod 判定(ble.js:98-99):hex 长度 7000(=3500B=280x100/8) 或 18000(=9000B=400x180/8) 用
04,否则03。 - 无应用层 ACK,顺序 writeWithResponse(ble.js:102-106)。
- 结束:delay(300) → 整图发
01;日历小图(宽280 或 高180)发AA(ble.js:158-160)。
上传函数差异:
| 函数 | 前置 | 打包 | 结束 |
|---|---|---|---|
| upload_image | 无 | 296宽→canvas2bytes_bw;250/212宽→bw;其他→canvas2bytes 双平面 | 01/AA |
| upload_image_moban(bat) | RXTX EA0{bat} |
296宽→bw,否则双平面 | 01 |
| upload_image_lb(cmd) | RXTX EA0{cmd} |
同 upload_image | 同 |
| upload_image_lb_num(cmd) | RXTX EA000{cmd-1} |
不传图 | 无 |
三色 = 两次发送:bw 平面(code=ff) + 红平面(code=00)(ble.js:154-155,219-222)。
4. 像素打包(timedjs.js)
| 函数 | 扫描 | 位序 | 用途 | 证据 |
|---|---|---|---|---|
| canvas2bytes | 行主序 y0→H,x0→W | MSB first,8px/B | 400x300/800x480/280x100/400x180 | timedjs.js:564-584 |
| canvas2bytes_bw | 列主序 x 从 W-1→0,y0→H | MSB first | 2.9寸296x128、2.13寸 | timedjs.js:586-611 |
| canvas2bytes_to | 转置 | MSB first | 未调用 | timedjs.js:612-632 |
位值:bw 平面 R,G,B 全>0→1(白) 否则 0(黑);bwr 平面 R>0,G=0,B=0→1(红)(timedjs.js:573-576,599-601)。
400x300 → 每平面 15000B。红色判定:r>g*1.5 && r>b*1.5 && r>threshold(timedjs.js:255 等)。
抖动算法(dithering() 分发 timedjs.js:513-563):
- 黑白:none/floydsteinberg(7,3,5,1 /16)/atkinson(1/8×6)/bayer(4x4 15,135,45,165],[195,75,225,105],[60,180,30,150],[240,120,210,90)/stucki(/42)/jarvis(/48)
- 黑白红:bwr_ 前缀同名 6 种,默认 bwr_atkinson
- threshold 默认 125;diffusion 0–100/100 误差缩放
- bwrPalette=黑白红(timedjs.js:504-508)
- 预处理 applyImageAdjustments:亮度/对比度/饱和度(timedjs.js:2-38),灰度 0.299/0.587/0.114
5. 时钟/日历/倒计时
setTime(ble.js:251-257)RXTX 连发 dd… + e2:
| 偏移 | 长度 | 含义 |
|---|---|---|
| 0 | 1B | dd |
| 1 | 4B | unix 秒(本地时区修正 Date.now()/1000 - tzOffset*60,ble.js:4222) |
| 5 | 2B | 年(大端) |
| 7 | 1B | 月 1–12 |
| 8 | 1B | 日 |
| 9 | 1B | 星期(getDay() |
倒计时:
- 时钟界面 gridSize=16:>4 字符(中文=1 英文=0.5)拆两包
04+128B /0{n}+余;≤4 单包0{n}(timedjs2.js:206-224) - 日历界面 gridSize=24:每字符一包
990{j}+72B,间隔1s(timedjs2.js:228-235) - 日期帧
ff00{year}{MM}{dd}(时钟)/ffff...(日历),year=4位十进制ASCII(timedjs2.js:244-254) - 收尾
E2;关闭FA+1s+E2(timedjs2.js:141-145) - upload_rili:
990*→FA→e10103→E2(timedjs2.js:308-312)
6. 模板系统(moban.js)
6 模板渲染到 #unifiedCanvas(#epdwh 选 400x300/800x480/296x128),无 BLE 代码;上传走 upload_image_moban(0)→EA00+图块+01(index.html:1243)。
| 模板 | data-template | 渲染 |
|---|---|---|
| 日程 schedule | renderPresetTemplate | moban.js:140-145,369-467 |
| 名片 businesscard | 同上 | moban.js:146-151 |
| 备忘录 memo | 同上 | moban.js:152-157 |
| 课程表 course | renderCourseTable(默认5天x4节,3–7天、2–9节) | moban.js:29-63,165-317 |
| 二维码 qrcode | renderQRCodeMode(qrcodejs CorrectLevel.M) | moban.js:81-113,501-570 |
| 自定义文本 customText | renderCustomMode(自动换行) | moban.js:572-693 |
红字语法 【…】(parseColoredText moban.js:470-498);反色模式 7 种(moban.js:122-137)。
7. 激活机制
- API:
http://4638e813e4.qicp.vip:8889/key_generator.php(ble.js:3866) - fullAutoActivate(ble.js:3932-3955):
- 读 #fixedId(用户固定ID,UI 隐藏 index.html:930-935)
- getDeviceIdFromScreen:RXTX 发
EFEF,8s 超时,notify 累计 14 字节(EXPECTED_ID_BYTES=14 ble.js:4123),可打印转 ASCII 否则 hex(ble.js:4121-4137) - requestActivationCode:GET
?id={fixedId}&key={deviceId}(ble.js:3879-3886) - extractActivationCode:正则
激活码1:([0-9A-F]{32,})/激活码:(…)/ 任意≥32hex(ble.js:3869-3876) - sendActivationToDevice:
EF+大写激活码,400ms 后e2(ble.js:3889-3902)
- 算法在服务端 key_generator.php,客户端无实现。自助站 qbsg.top,作者微信 2549294317。
- 固件下载同 host:
http://4638e813e4.qicp.vip:8889/GuJian/file_down.php(index.html:836),型号列表在服务端。
8. 高级命令
index.html:919-920:#cmdcmd 任意 hex → triggerRxTxCmd 直发 RXTX。 hex.js:2-15:hexToBytes/bytesToHex/intToHex(大端 pad)。
9. 变体与其他
- LED=灯板变体(遗留);XM=休眠(FB00/FB01);LB=轮播(e901/EA000);CHEPAI=停车牌;JH=激活;lut=LUT 校准 E6
- 设备名前缀仅
NRF-(ble.js:4053) - chinaCities 城市→天气代码表 ble.js:259 起约3580行,用于 WIFI 配置 CITY 字段
- 死代码:SendCommand(i 未初始化)、upload_image_lbs(未定义)