乐迪诺体脂秤 · CM311-1A 自动记录 · 完整总结
/ 27 min read
Table of Contents
一、项目目标(一句话)
不打开京东健康 App、不碰手机,实现「上秤自动记录体重 + 阻抗 + 体脂」。
- 数据源:0.1 元购得的乐迪诺(LEDINUO)白牌体脂秤
- 网关:闲置的中国移动魔百和 CM311-1A 机顶盒(已刷 Armbian,24h 常驻)
- 输出:上秤后自动写入
records.csv,可推送到 iPhone(Bark)
二、最终成果总览
| 环节 | 状态 | 说明 |
|---|---|---|
| 广播协议破解 | ✅ 100% | 体重(×100)、阻抗(÷10)字节位确认,多次实测命中 |
| 算法逆向 | ✅ 完成 | 芯海 CloudV3 全套公式(BFR/BMI/BMR/VFR/SLM/FM/身体年龄/标准体重) |
| 京东健康真相 | ✅ 破译 | 客户端换算 bug → 服务端用身高估算阻抗兜底,真实阻抗没用上 |
| CM311-1A 板载蓝牙 | ✅ 救活 | 芯片实为 Realtek RTL8761BTV(非博通),换 115200 config 后 hci0 UP RUNNING |
| 旁听脚本 | ✅ 已部署运行 | /root/listen_scale.py,由 systemd 托管 |
| 首条记录验证 | ✅ 已验证(2026-08-16 00:11 ) | 85.55 kg / 500.0Ω / 体脂 26.3% / BMI 27.9 已自动写入 /root/records.csv |
| 开机自启固化 | ✅ 已完成(2026-08-16 00:29 ) | bt-cm311.service enabled+active,重启后自动:GPIO82复位→rtk_hciattach→hci0 up→拉起监听 |
两大关键外部信息源(对最终成功起决定性作用):
| 来源 | 决定性贡献 |
|---|---|
| GitHub:ophub issue #471(用户提供) | 揭示 CM311-1A 板载蓝牙实为 Realtek RTL8761BTV(非博通/展锐),给出 GPIO 82 与 115200 config 线索 → 蓝牙一举救活(详见 4.2 阶段 6) |
| Codex 分析截图(用户提供) | 破译京东健康真实链路:客户端未除 10 → 阻抗越界 → 服务端身高估算兜底 + FFM 公式;本报告独立验证两个实测点全部命中(详见 3.4) |
三、体脂秤部分(详细)
3.1 设备信息
| 项目 | 值 |
|---|---|
| 品牌/型号 | 乐迪诺 LEDINUO(京东自营 OEM) |
| 购买成本 | 0.1 元(促销) |
| 连接方式 | BLE 广播型(Connectable: No,无需连接) |
| 广播名 | Yoda0 |
| UUID | C7A7EA2F-FFA6-EB21-373F-580A24317430 |
| 芯片方案 | 芯海科技(Chipsea)——国内体脂秤 BIA 方案龙头 |
| 传感器 | 体重称重传感器 + BIA 四电极阻抗(真实测量,实验证实) |
3.2 广播协议逆向
nRF Connect 抓到的原始帧(ManufacturerData):
<0x14c0> 21 34 13 88 00 00 25 e0 d3 8e 45 06 38 ← 85.00 kg / 500.0 Ω(基线)<0x15c0> 21 c5 13 88 00 00 25 e0 d3 8e 45 06 38 ← 86.45 kg / 500.0 Ω(抱重物)<0x20c0> 21 3e 00 00 00 00 25 e0 d3 8e 45 06 38 ← 85.10 kg / 0.0 Ω(单脚站立)<0x1fc0> 09 97 13 88 00 00 25 e0 d3 8e 45 06 38 ← 24.55 kg / 500.0 Ω(双手压桌)帧头 0xC0 = 芯海方案标识(CloudV3 SDK 的 ChipseaBroadcastFrame 走 0xC0 分支)。
字节结构(已 100% 确认):
| 字节偏移 | 长度 | 内容 | 解析 | 实测示例 |
|---|---|---|---|---|
| 0 | 1 | 帧头 | 0xC0 = 芯海广播 | c0 |
| 1 | 1 | 帧序号 | 每次称重递增 | 14→15→17→1f→20 |
| 2-3 | 2 | 体重 | 大端 × 0.01 kg | 21 34 = 0x2134 → 85.00 kg |
| 4-5 | 2 | 阻抗 | 大端 ÷ 10 Ω | 13 88 = 5000/10 → 500.0 Ω |
| 6-7 | 2 | 保留 | 恒为 0 | 00 00 |
| 8-13 | 6 | 固定信息 | 多次称重纹丝不动,无解读价值 | 25 e0 d3 8e 45 06 38 |
CloudV3 SDK 反编译确认的解析代码:
// ChipseaBroadcastFrame.a(byte[], String) —— 0xC0 帧头分支weight = WeightUnitUtil.parser(scaleProperty, data[2], data[3], false).kgWeight;r1 = BytesUtil.bytesToInt(BytesUtil.subBytes(data, 4, 2)) / 10.0f; // → 500.0 Ω⚠️ 重要坑(部署时发现):bleak(Python BLE 库)拿到的
manufacturer_data已剥离 Company ID 前两字节,即 nRF 里的<0x14c0>中的c0 14不会传给脚本,payload 直接以21 34...开头。listen_scale.py已兼容两种形态(见脚本parse_payload)。
3.3 算法逆向(芯海 CloudV3)
来源:本地 SDK cloud_v3_lib1.0.4.jar → com.careforeyou.library.algorithm.CsAlgoBuilder,javap 反编译还原。与开源社区对芯海方案(OKOK App)的逆向结果系数完全一致。
构造参数:身高 H cm / 体重 W kg / 性别 / 年龄 / 阻抗 Z Ω
体脂率 BFR(%):
男:BFR = [ −0.3315×H + 0.6216×W + 0.0183×年龄 + 0.0085×Z + 22.554 ] ÷ W × 100女:BFR = [ −0.3332×H + 0.7509×W + 0.0196×年龄 + 0.0072×Z + 22.7193 ] ÷ W × 100输出 clamp 到 [5, 45]BMI:W ÷ (H/100)²
基础代谢 BMR(kcal):
男:BMR = 7.5037×H + 13.1523×W − 4.3376×年龄 − 0.3486×Z − 311.7751女:BMR = 7.5432×H + 9.9474×W − 3.4382×年龄 − 0.309×Z − 288.2821内脏脂肪 VFR(级,年龄>17 生效):
男:VFR = −0.2675×H + 0.42×W + 0.1462×年龄 + 0.0123×Z + 13.9871 → 四舍五入,clamp [1,59]女:VFR = −0.1651×H + 0.2628×W + 0.0649×年龄 + 0.0024×Z + 12.3445躯干脂肪 TFR(%,年龄>17 生效):
男:TFR = ( 0.0939×H + 0.3758×W − 0.0032×年龄 − 0.006925×Z + 0.097 ) ÷ W × 100女:TFR = ( 0.0877×H + 0.2973×W + 0.0128×年龄 − 0.00603×Z + 0.5175 ) ÷ W × 100clamp [20, 85],保留 2 位瘦体重 SLM(kg):
若 BFR > 45:SLM = W − 0.45W − 4若 BFR < 5:SLM = W − 0.05W − 1男:SLM = 0.2867×H + 0.3894×W − 0.0408×年龄 − 0.01235×Z − 15.7665女:SLM = 0.3186×H + 0.1934×W − 0.0206×年龄 − 0.0132×Z − 16.4556clamp [7, 141.5]脂肪量 FM(kg) = BFR × W ÷ 100
身体年龄(岁,年龄>17 生效):
男:−0.7471×H + 0.9161×W + 0.4184×年龄 + 0.0517×Z + 54.2267女:−1.1165×H + 1.5784×W + 0.4615×年龄 + 0.0415×Z + 83.2548取整 → 限制在 实际年龄±10 → clamp [18, 80]标准体重 BW(kg):男 (H−80)×0.7,女 (H−70)×0.6
3.4 京东健康链路真相(重大发现)
来源:用户提供 Codex 分析截图(链路图 + FFM/体脂公式)。本报告对其两个实测点做了独立验证(H=175→20.4%、H=190→18.2%)全部精确命中,并据此反推估算阻抗公式
R_est ≈ 3.8133×H − 263.8,确认结论成立。
客户端/服务端资产:
- 京东健康 Android 9.2.2 APK 含
libchipsea_bias_v235.so+com.jd.health.bodyweight模块 .so与第三方 App HY-Fit 反编译出现的同名同版本 → JNI 调用语义一致- 函数签名:
cs_bias_v235(mode, sex, age, height, weight_x10, impedance_raw, vcode, &out),输出 15 项指标 - 参数合法区间:impedance_raw = 2000 ~ 15000
真实链路(已破译):
你的秤广播真实阻抗 500.0Ω(原始值 = 5000) ↓京东健康客户端拿到 5000,但【没有除以 10】 ↓直接传给服务端算法接口 → 被判【阻抗越界】 ↓服务端用身高估算阻抗兜底: 175 cm → 403.53 Ω 190 cm → 460.73 Ω ↓用估算阻抗 R 代入 FFM 公式 → 算体脂服务端体成分公式(已破译):
FFM = 0.73404512·H²/R + 0.11600760·W − 0.07402987·A − 0.09651946·F + 4.12132247体脂率 = 100 × (1 − FFM/W)变量:H=身高 cm,W=体重 kg,A=年龄,R=算法实际使用的阻抗 Ω,F=性别(男 0 / 女 1)
验证(两个实测点精确命中):
| 参数 | FFM | 体脂率 | 京东健康显示 | 匹配 |
|---|---|---|---|---|
| H=175, W=85, A=27, R=403.53 | 67.6919 | 20.3625% | 20.4% | ✅ |
| H=190, W=85, A=27, R=460.73 | 69.4985 | 18.2371% | 18.2% | ✅ |
| H=175, W=85, A=27, R=500(真实阻抗对照) | 56.9434 | 33.0077% | — | 证明未用真阻抗 |
估算阻抗公式(反推):R_est ≈ 3.8133 × H − 263.8
核心结论:京东健康的体脂完全没有使用你秤测的真实阻抗——客户端换算 bug 导致真实阻抗永远被判越界,服务端用身高拍估算阻抗替代。所以「改身高体脂立刻变」不是因为身高是正常的算法参数,而是因为 R 完全由身高决定。京东健康的体脂数字物理上与你的秤脱钩。
3.5 三方数据验证
用户实测点:
| 数据点 | 体重 | 身高 | BMI | 阻抗 | 体脂 | 来源 |
|---|---|---|---|---|---|---|
| A 原身高 | 85.00 | 175 | 27.8 | 500.0Ω | 20.4% | 京东健康 |
| B 改身高190 | 85.00 | 190 | 23.5 | 500.0Ω | 18.2% | 京东健康 |
| 医院金标准 | 84.00 | 175 | 27.4 | — | 23.8% | 医院实测 |
| 广播对照 1 | 85.00 | — | — | 500.0Ω | — | 21 34 |
| 广播对照 2 | 86.45 | — | — | 500.0Ω | — | 21 c5 |
| 广播对照 3 | 24.55 | — | — | 500.0Ω | — | 09 97 |
| 广播对照 4 | 85.10 | — | — | 0Ω | — | 21 3e 单脚 |
CloudV3 复算 vs 京东健康 vs 医院:
| 实测点 | BMI | CloudV3 体脂 | 京东健康 | 医院金标准 |
|---|---|---|---|---|
| 175cm / 85kg / 500Ω | 27.8 ✅ | 26.0% | 20.4% | 23.8% |
| 190cm / 85kg / 500Ω | 23.5 ✅ | 20.2% | 18.2% | — |
| 175cm / 84kg / 500Ω | 27.4 | 25.6% | — | 23.8% |
可信度排序:医院 > CloudV3(差 ±2)> 京东健康(差 −3.4,且没用真阻抗)
CloudV3 全套指标(男 27 / 175cm / 85kg / 500Ω):
| 指标 | CloudV3 | 京东健康 |
|---|---|---|
| 体脂率 | 26.0% | 20.4% |
| BMI | 27.8 | 27.8 |
| 脂肪量 | 22.12 kg | — |
| 瘦体重 | 60.23 kg | 67.7 kg(肌肉量,口径不同) |
| 内脏脂肪 | 13.0 级 | 6.0 级 |
| 躯干脂肪 | 52.85% | — |
| 基础代谢 | 1827.9 kcal | 1782 kcal |
| 身体年龄 | 37 岁 | 27 岁 |
| 标准体重 | 66.5 kg | — |
内脏脂肪 13 vs 6 差异大 → 京东健康体成分系数体系与 CloudV3 不同(其 FFM 公式已破译,VFR 公式未挖出)。
3.6 关键实验(证据链)
| 实验 | 目的 | 结果 |
|---|---|---|
| 单脚站立 | 验证阻抗字段是否真实测量 | 阻抗归零(脚-脚电流路径断)→ 字段是活的测量,非写死常量 |
| 双手压桌 | 验证手是否参与测量 | 阻抗不变(手不参与脚-脚测量,符合四电极原理) |
| 改身高 175→190 | 判断体脂是否由广播决定 | 广播字节不变、App 体脂 20.4→18.2 → 身高是 App 算法参数 |
| 医院实测 | 金标准校准 | 23.8%,判定京东健康偏低 3.4、CloudV3 接近 ±2 |
四、CM311-1A 蓝牙激活(更详细)
这一部分是整个项目最曲折、最终成功的部分。核心教训:方向错了,再努力也白费;找到正确的芯片型号和协议,一条命令就通了。
4.1 硬件与初始判断
| 项目 | 值 |
|---|---|
| 设备 | 中国移动魔百和 CM311-1A 机顶盒 |
| SoC | Amlogic S905L3A(G12A 家族) |
| 系统 | Armbian 6.1.158-ophub(用户已刷好) |
| IP | 192.168.x.x(已脱敏) |
| 蓝牙芯片 | Realtek RTL8761BTV(最终确认,走 UART) |
初始误判:因 /lib/firmware/ 里有大量 brcm(Broadcom)固件(fw_bcm43455c0_ag.bin 等),整个排障前期都按 Broadcom AP6255 + UART 方向进行——这是绕的最大弯路。
4.2 排障全历程(时间线)
阶段 0:环境准备
- 用户报
bluetooth.service不存在 →apt install bluez - 装好后
bluetoothctl list仍为空 → 内核没识别到控制器
阶段 1:误以为博通方案(全白费)
dmesg显示Bluetooth: Core ver 2.22已加载,但无 hci0lsusb无蓝牙设备 → 判定为 UART 模组- 固件目录看到
fw_bcm43455c0_ag.bin→ 误判 Broadcom BCM43455 btattach -B /dev/ttyAML1 -S 115200→Device index 0 attached(hci0 注册成功)- 但
hciconfig hci0 up→Connection timed out (110),BD 地址全 0 - 换 3M 波特率 / 换 ttyAML0 → 全部失败
- 为什么白费:芯片实际是 Realtek,
-B是博通协议,喂错对象;且 GPIO 没上电/时钟链路没通。
阶段 2:多轮脚本准备(本地侧)
产出工具集(全部在 ledino-scale/,后续 4.5 列出):
enable_bt_cm311.sh:一键激活主脚本(装 gpiod → 放固件 → 拉 GPIOX_17 → 2M 挂 HCI)probe_bt_gpio.sh:自动探测 5 个候选使能脚collect_bt_diag.sh:9 类诊断信息一键收集check_dtb.sh:dtb 蓝牙节点自检- 开机自启三件套:
cm311-bt-up.sh/bt-cm311.service/install_bt_autostart.sh - 期间修复
listen_scale.py的致命 bug(bleak 剥离 Company ID → 解析永远失败)
在盒子上实际执行结果:
enable_bt_cm311.sh:GPIOX_17(82) 拉高 → 2M attach → 仍超时probe_bt_gpio.sh:GPIOX_17/18、GPIOH_7/8、GPIOX_16 全无效collect_bt_diag.sh:收集到关键线索(当时没意识到是 Realtek 的线索都在里面)
阶段 3:诊断深挖(dtb 反编译)
collect_bt_diag.sh 关键输出:
- 固件目录实含
rtl8761b_fw/rtl8761bt_config(当时被 brcm 固件淹没没注意) - dtb 列表含
meson-g12a-s905l3a-cm311.dtb(当前使用)与meson-g12a-s905l3a-e900v22c.dtb等 console=ttyAML0,115200n8→ ttyAML0 被内核 console 占用(Device or resource busy)
用户反馈「串口是今天救命的通道」→ 决定不动 console,改用只读检查确认接线。
阶段 4:确定芯片不上电(WiFi/蓝牙同芯)
ip link→ 无 wlan0(WiFi 也没工作)→ 蓝牙和 WiFi 同一芯片(AP6255 或同类型),芯片整个没上电- 反编译 dtb →
wifi@1 { compatible = "sprd,unisoc-wifi" }→ 芯片是展锐 Unisoc?!(又一个方向,但也推翻了博通) devices_deferred→sdio-pwrseq: supplier wifi32k not readywifi32k是pwm-clock(用 PWM 产生 32KHz),引用pwm@19000pwm@19000在 dtb 里status = "disabled"→ 源头卡点- 完整卡住链:
pwm@19000 disabled → wifi32k 时钟 not ready → sdio-pwrseq 上电驱动卡住 → mmc0(WiFi口) 不枚举 → 整颗芯片没上电
风险分叉:修复需改 dtb(disabled→okay)+ 重编译 + 重启。用户明确不接受变砖风险(改启动文件+重启)。尊重该决定,未执行写操作。
阶段 5:零风险试验(GPIOX_6 复位)
- dtb 中
sdio-pwrseq的reset-gpios = <0x2f 0x47 0x01>→ gpiochip0 line 71 = GPIOX_6 - 运行时拉低 GPIOX_6(解除芯片复位,不写盘不重启)→ 重新 attach → 芯片仍无响应
- 结论:解除复位不够,还需要被禁用的 PWM 时钟链路 → 零风险前提下板载确认无望(此结论随后被推翻)
阶段 6:用户提供 ophub issue #471(决定性转折 🎯)
用户发来:https://github.com/ophub/amlogic-s9xxx-armbian/issues/471
Issue 关键内容(本项目的”圣杯”):
- CM311-1A 板载蓝牙芯片是 Realtek RTL8761BTV(不是博通,不是展锐)
- 走 UART 串口
- 建议使用 e900v22c.dtb(非 cm311.dtb)
- GPIO 重置脚 = 82(GPIOX_17),顺序先 0 后 1
- 提到 radxa 的
rtkbt工具 + 波特率坑(1.5M 有时不稳定,建议 115200/230400)
只读验证全部对上:
- 当前 cm311.dtb 的 uart_A(serial@24000,对应 ttyAML1)
status = "okay"+ 流控引脚已配 → 串口已就绪,连换 dtb 都省了 /lib/firmware/rtlbt/里rtl8761b_fw/rtl8761bt_config一直都在(ophub 自带)- dmesg 确认
ttyAML1已注册
结论:之前全部失败的唯一原因是协议喂错了——用博通 -B 去喂 Realtek 芯片。
阶段 7:Realtek 挂载测试(接近成功)
-
用户克隆 radxa
rtkbt并在盒子上编译出rtk_hciattach(aarch64):cd ~/rtkbt-main/uart/rtk_hciattachmake # 产出 rtk_hciattach 二进制 -
执行:GPIO 82 先 0 后 1 +
rtk_hciattach -n -s 115200 /dev/ttyAML1 rtk_h5 & -
日志确认:芯片识别 RTL8761BTV,H5 协议握手成功,固件也读到了
-
但固件下载在切换波特率后失败:
Config baudrate → Final speed 1500000(1.5M)Patch pkt trans timeout, re-trans ×4 → ERROR: h5_download_patch: Retransmission exhausts -
正中 issue 说的坑:radxa 自带 config 是 1.5M 速率,有的机器不稳定
阶段 8:换 115200 config(最终成功 🎉)
-
比对两份 config:
- 当前生效
rtl8761b_config(25 字节):baudrate 字段0x04928002→ 1.5M - 备用
rtl8761bt_config(81 字节,RTL8761BTV 专用):baudrate 字段0x50000050→ 不在源码映射表 → 回退 115200 - 源码波特率表:
{0x04928002, 1500000}, {0x0252C014, 115200}, {0x0252C00A, 230400}
- 当前生效
-
执行替换(可回滚):
Terminal window cp /lib/firmware/rtlbt/rtl8761b_config /lib/firmware/rtlbt/rtl8761b_config.bakcp /lib/firmware/rtlbt/rtl8761bt_config /lib/firmware/rtlbt/rtl8761b_config -
重新走 GPIO 82(先 0 后 1)+
rtk_hciattach -n -s 115200 /dev/ttyAML1 rtk_h5 & -
成功:
hci0: Type: Primary Bus: UARTBD Address: XX:XX:XX:XX:XX:XX ← 真实 MAC!UP RUNNING ← 激活成功!Controller XX:XX:XX:XX:XX:XX armbian [default]
4.3 最终成功方案(完整可复现步骤)
前提:Armbian(ophub),已编译
rtk_hciattach,rtl8761bt_config可用。
# ① 确认蓝牙固件在(ophub 自带,无需下载)ls /lib/firmware/rtlbt/ # 应见 rtl8761b_fw + rtl8761bt_config
# ② 替换 config 为 115200 版(关键!原 1.5M 版固件下载会超时)sudo cp /lib/firmware/rtlbt/rtl8761b_config /lib/firmware/rtlbt/rtl8761b_config.baksudo cp /lib/firmware/rtlbt/rtl8761bt_config /lib/firmware/rtlbt/rtl8761b_config
# ③ GPIO 82(GPIOX_17)重置:先 0 后 1(顺序重要)sudo gpioset -s 1 -m time 0 82=0nohup sudo gpioset --mode=signal 0 82=1 >/dev/null 2>&1 &
# ④ Realtek H5 协议挂载(注意不是博通 -B,是 rtk_h5;口是 ttyAML1)cd ~/rtkbt-main/uart/rtk_hciattachsudo ./rtk_hciattach -n -s 115200 /dev/ttyAML1 rtk_h5 >/tmp/rtk2.log 2>&1 &sleep 6
# ⑤ 激活并确认sudo hciconfig hci0 upsudo hciconfig -a | head -10 # 期望 UP RUNNING + 真实 MACsudo bluetoothctl list # 期望 Controller XX:XX:XX:XX:XX:XX armbian回滚(万一异常):
sudo cp /lib/firmware/rtlbt/rtl8761b_config.bak /lib/firmware/rtlbt/rtl8761b_configsudo pkill rtk_hciattach4.4 排障认知总结(为什么前面全失败)
| 阶段 | 假设 | 实际 | 代价 |
|---|---|---|---|
| 前期 | Broadcom AP6255(看固件目录) | Realtek RTL8761BTV | 所有 btattach -B 全白试 |
| 中期 | 展锐 Unisoc(看 dtb wifi 节点) | 蓝牙独立走 UART,wifi 节点是展锐,但蓝牙是 RTL | 又绕一圈 |
| 卡点分析 | PWM 时钟 disabled → 芯片不上电 | 方向对但非唯一卡点 | 一度判定”板载无望” |
| 转折 | issue #471 提供芯片真身 + 正确工具 + 波特率坑 | 全对上 | 一条命令解开 |
核心经验:
- 先确认芯片型号再动手——固件目录的 brcm 文件具有误导性(那是 WiFi 的 SDIO 固件,蓝牙走 UART 是独立芯片)
- 协议不能猜——
btattach的-B(Broadcom)/rtk_h5(Realtek) 必须与芯片匹配 - 波特率是坑——Realtek 固件下载波特率 config 不对就超时
- 官方/社区 issue 是最快路径——比从零逆向 dtb 快得多
4.5 部署与运行状态(截至最后)
| 项 | 状态 |
|---|---|
rtk_hciattach 二进制 | ✅ ~/rtkbt-main/uart/rtk_hciattach/(用户编译) |
| config 替换 | ✅ 生效(115200 版),备份在 rtl8761b_config.bak |
| hci0 | ✅ UP RUNNING,MAC XX:XX:XX:XX:XX:XX |
| 蓝牙扫描验证 | ✅ 能扫到附近设备(I+7(:A;-DB&&,(&) 等) |
listen_scale.py | ✅ 已拷到 /root/(修复 import bug 后版本,6654 字节) |
| bleak 依赖 | ✅ 已装(bleak 3.0.2 + dbus-fast),装法是 python3 -m pip install --break-system-packages bleak |
| 监听进程 | ✅ pid 22103 后台运行中 |
/root/records.csv | ✅ 已生成并有首条记录:2026-08-16 00:11:47, 85.55, 500.0, 26.3, 27.9(体重85.55kg / 阻抗500.0Ω / 体脂26.3% / BMI 27.9) |
4.6 完整链路图
体脂秤 Yoda0 --BLE广播 13B--> CM311-1A(Armbian) hci0(RTL8761BTV) └─ 体重×0.01 / 阻抗÷10 解析 └─ CloudV3 公式 → 体脂/BMI └─ records.csv 落盘(/root/) └─ Bark 推送 → iPhone 通知(点击才运行)→ 快捷指令 → Apple Health(可选,见 4.7)4.7 Bark + 快捷指令联动写入 Apple Health(✅ 已接通并验证)
流程:脚本检测到有效测量(阻抗>0)→ Bark 推送通知(带体重/体脂)→ 用户点通知 → 运行快捷指令 RecordWeight → 写入 Apple Health 体重+体脂率。
- 「不点不写入」:快捷指令只有点击通知才运行,别人称的数据最多出现在通知/CSV,不会进健康。
- 2026-08-16 实测通过:站上秤 → 通知实时到达(
⚖️ 85.70kg · 体脂 26.3% · 阻抗 500Ω · BMI 28.0)→ 点通知 → 快捷指令运行 → Apple Health 写入成功。 - 脚本侧(
listen_scale.py):BARK_KEY:Bark 推送 key(留空不推送)SHORTCUT_NAME = "RecordWeight":点通知运行的快捷指令名(须与 iPhone 上一致)- 推送 payload:
shortcuts://run-shortcut?name=RecordWeight&input=text&text=体重%7C体脂%7CBMI(已 URL 编码,%7C=|) - 仅当
阻抗 > 0才推送(避免单脚站立等异常数据进健康)
- iPhone 侧快捷指令
RecordWeight(用户已建好,最终版):- 从输入获取文本
- 按
|拆分文本 - 取第 1 项=体重 / 第 2 项=体脂 / 第 3 项=BMI
- 记录健康样本:体重(kg)+ 体脂率(%)
- 返回主屏幕(跑完自动退出快捷指令 App,不再停留)
- 踩过的坑(均已解决):
shortcuts://传参格式:正确为input=text&text=<数据>(input是类型标识、text是内容),写成input=<数据>会导致快捷指令收到空输入(通知只显示”通知”两字)- 快捷指令缺「记录健康样本」动作 → 数据通了但健康不写入
- 快捷指令运行完停留在 App 界面 → 末尾加「返回主屏幕」动作(iOS 无官方”运行后自动回主屏”设置,靠这个动作实现)
- ⚠️ 防垃圾数据建议:可在拆分后加「如果 体重介于30
200 且 体脂介于360 → 写入;否则提示异常不写入」,作为第二道闸(第一道是”点通知才写入”)
4.8 开机自启(✅ 已固化)
2026-08-16 00:29
完成,bt-cm311.service 已 enabled + active,盒子重启后自动拉起全链路。
Realtek 版自启脚本(ledino-scale/cm311-bt-up.sh)职责:
等待串口就绪(ttyAML1) → 清理残留gpioset → GPIO82 先0后1(复位+上电)→ rtk_hciattach -n -s 115200 /dev/ttyAML1 rtk_h5 (挂载Realtek H5)→ hciconfig hci0 up → bluetoothctl power on→ 若无监听实例则 nohup 拉起 /root/listen_scale.py→ wait 保持前台(systemd 维持 active)- systemd 单元:
/etc/systemd/system/bt-cm311.service(Restart=on-failure+RestartSec=5) - 日志:
/var/log/cm311-bt.log - 验证结果:
systemctl is-enabled→ enabled;is-active→ active;监听进程唯一;hci0UP RUNNING
⚠️ 修复过的坑:早期版本漏了「清理残留 gpioset」——手动跑的
gpioset --mode=signal 82=1会一直占用 line 82,脚本再设置 GPIO 报Device or resource busy→ 芯片不上电 → H5 同步超时 → 服务重启循环。已在脚本第 1 步加pkill -f gpioset解决。
五、全部脚本与文件清单
本地工作区
| 文件 | 说明 | 状态 |
|---|---|---|
README.md | 技术报告(九章,偏算法/逆向) | ✅ |
完整总结.md | 本文档(全流程) | ✅ |
listen_scale.py | 旁听+解析+CloudV3+CSV+Bark 推送 | ✅ 已部署盒子 |
chipsea_cloudv3.py | CloudV3 算法 Python 参考实现 | ✅ |
enable_bt_cm311.sh | 激活主脚本(博通版,已过时但 GPIO 逻辑可参考) | 参考 |
probe_bt_gpio.sh | GPIO 变体自动探测 | 参考 |
collect_bt_diag.sh | 9 类诊断收集 | ✅ 实战用过 |
check_dtb.sh | dtb 蓝牙节点自检 | 参考 |
cm311-bt-up.sh / bt-cm311.service / install_bt_autostart.sh | 开机自启三件套(Realtek 版,已部署盒子) | ✅ 已部署 |
盒子上 /root/
| 文件/目录 | 说明 |
|---|---|
listen_scale.py | 旁听脚本(运行中,pid 22103) |
rtkbt-main/ | radxa rtkbt 源码 + 编译出的 rtk_hciattach |
records.csv | 记录文件(首次测量后生成) |
/lib/firmware/rtlbt/rtl8761b_config.bak | config 替换的备份(可回滚) |
/tmp/rtk2.log | Realtek 挂载日志 |
其他本地产物(tmp/chipsea/,备查)
| 文件 | 说明 |
|---|---|
cloud_v3_lib1.0.4.jar | 芯海官方 SDK(反编译来源) |
libchipsea_arm64.so / x86.so | 京东健康同款算法库实体 |
host.c | 调 .so 的 C 宿主(依赖 Docker,未跑通,已弃用) |
六、完整命令速查
体脂秤广播测试(自检解析)
python3 /root/listen_scale.py --test# 期望输出:85.00/500.0、86.45/500.0、24.55/500.0 全部 ✅启动/停止监听
# 启动(后台常驻)nohup python3 /root/listen_scale.py >/dev/null 2>&1 &# 查看ps aux | grep listen_scale# 停止pkill -f listen_scale.py查看记录
cat /root/records.csviPhone 推送 + 写入 Apple Health(可选,联动模式)
- iPhone 装 Bark App → 获取 key
- iPhone 建快捷指令
RecordWeight(接收 input体重|体脂|BMI,拆分后写健康样本:体重+体脂率) - 编辑
/root/listen_scale.py的BARK_KEY = "..."(SHORTCUT_NAME保持与快捷指令一致) - 重启监听:
sudo systemctl restart bt-cm311
蓝牙重启后重新拉起(临时,直至做好自启)
sudo gpioset -s 1 -m time 0 82=0nohup sudo gpioset --mode=signal 0 82=1 >/dev/null 2>&1 &cd ~/rtkbt-main/uart/rtk_hciattachsudo ./rtk_hciattach -n -s 115200 /dev/ttyAML1 rtk_h5 >/tmp/rtk2.log 2>&1 &sleep 6sudo hciconfig hci0 up七、待办事项
| # | 事项 | 优先级 | 说明 |
|---|---|---|---|
| 1 | ✅ 已完成 | 2026-08-16 00:11
自动记录 85.55kg / 500.0Ω / 体脂26.3% / BMI 27.9 已落盘,闭环打通 | |
| 2 | ✅ 已完成 | bt-cm311.service enabled+active,重启自动拉起蓝牙+监听 | |
| 3 | ✅ 已完成 | 2026-08-16 实测通过:站秤 → 通知 → 点通知 → 快捷指令 → 写入健康成功 | |
| 4 | 重启后验证 | 🟡 中 | 确认 config 替换重启后保留(若重启则重跑 4.3 步骤) |
| 5 | 京东健康 VFR 公式 | 🟢 低 | 仅学术兴趣,VFR 服务端公式未挖出,无实用必要 |
八、一句话总结
0.1 元的乐迪诺秤:广播里带真实体重 + 真实阻抗;京东健康的体脂是「没用真阻抗的身高估算」;芯海 CloudV3 公式用真阻抗算反而更接近医院。CM311-1A 的板载蓝牙折腾一整晚后,发现是 Realtek RTL8761BTV、换一份 115200 config 就活了。现在机顶盒正在后台旁听,上秤即自动记录——全程不需要京东健康。