skip to content
XSD-Blog

乐迪诺体脂秤 · 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
UUIDC7A7EA2F-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% 确认)

字节偏移长度内容解析实测示例
01帧头0xC0 = 芯海广播c0
11帧序号每次称重递增1415171f20
2-32体重大端 × 0.01 kg21 34 = 0x2134 → 85.00 kg
4-52阻抗大端 ÷ 10 Ω13 88 = 5000/10 → 500.0 Ω
6-72保留恒为 000 00
8-136固定信息多次称重纹丝不动,无解读价值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.jarcom.careforeyou.library.algorithm.CsAlgoBuilderjavap 反编译还原。与开源社区对芯海方案(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]

BMIW ÷ (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 × 100
clamp [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.4556
clamp [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.5367.691920.3625%20.4%
H=190, W=85, A=27, R=460.7369.498518.2371%18.2%
H=175, W=85, A=27, R=500(真实阻抗对照)56.943433.0077%证明未用真阻抗

估算阻抗公式(反推)R_est ≈ 3.8133 × H − 263.8

核心结论:京东健康的体脂完全没有使用你秤测的真实阻抗——客户端换算 bug 导致真实阻抗永远被判越界,服务端用身高拍估算阻抗替代。所以「改身高体脂立刻变」不是因为身高是正常的算法参数,而是因为 R 完全由身高决定京东健康的体脂数字物理上与你的秤脱钩。

3.5 三方数据验证

用户实测点

数据点体重身高BMI阻抗体脂来源
A 原身高85.0017527.8500.0Ω20.4%京东健康
B 改身高19085.0019023.5500.0Ω18.2%京东健康
医院金标准84.0017527.423.8%医院实测
广播对照 185.00500.0Ω21 34
广播对照 286.45500.0Ω21 c5
广播对照 324.55500.0Ω09 97
广播对照 485.1021 3e 单脚

CloudV3 复算 vs 京东健康 vs 医院

实测点BMICloudV3 体脂京东健康医院金标准
175cm / 85kg / 500Ω27.826.0%20.4%23.8%
190cm / 85kg / 500Ω23.520.2%18.2%
175cm / 84kg / 500Ω27.425.6%23.8%

可信度排序医院 > CloudV3(差 ±2)> 京东健康(差 −3.4,且没用真阻抗)

CloudV3 全套指标(男 27 / 175cm / 85kg / 500Ω)

指标CloudV3京东健康
体脂率26.0%20.4%
BMI27.827.8
脂肪量22.12 kg
瘦体重60.23 kg67.7 kg(肌肉量,口径不同)
内脏脂肪13.0 级6.0 级
躯干脂肪52.85%
基础代谢1827.9 kcal1782 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 机顶盒
SoCAmlogic S905L3A(G12A 家族)
系统Armbian 6.1.158-ophub(用户已刷好)
IP192.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 已加载,但无 hci0
  • lsusb 无蓝牙设备 → 判定为 UART 模组
  • 固件目录看到 fw_bcm43455c0_ag.bin → 误判 Broadcom BCM43455
  • btattach -B /dev/ttyAML1 -S 115200Device index 0 attached(hci0 注册成功)
  • hciconfig hci0 upConnection 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_deferredsdio-pwrseq: supplier wifi32k not ready
  • wifi32kpwm-clock(用 PWM 产生 32KHz),引用 pwm@19000
  • pwm@19000 在 dtb 里 status = "disabled" → 源头卡点
  • 完整卡住链:pwm@19000 disabled → wifi32k 时钟 not ready → sdio-pwrseq 上电驱动卡住 → mmc0(WiFi口) 不枚举 → 整颗芯片没上电

风险分叉:修复需改 dtb(disabled→okay)+ 重编译 + 重启。用户明确不接受变砖风险(改启动文件+重启)。尊重该决定,未执行写操作。

阶段 5:零风险试验(GPIOX_6 复位)

  • dtb 中 sdio-pwrseqreset-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_hciattach
    make # 产出 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 字段 0x049280021.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.bak
    cp /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: UART
    BD Address: XX:XX:XX:XX:XX:XX ← 真实 MAC!
    UP RUNNING ← 激活成功!
    Controller XX:XX:XX:XX:XX:XX armbian [default]

4.3 最终成功方案(完整可复现步骤)

前提:Armbian(ophub),已编译 rtk_hciattachrtl8761bt_config 可用。

Terminal window
# ① 确认蓝牙固件在(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.bak
sudo 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=0
nohup sudo gpioset --mode=signal 0 82=1 >/dev/null 2>&1 &
# ④ Realtek H5 协议挂载(注意不是博通 -B,是 rtk_h5;口是 ttyAML1)
cd ~/rtkbt-main/uart/rtk_hciattach
sudo ./rtk_hciattach -n -s 115200 /dev/ttyAML1 rtk_h5 >/tmp/rtk2.log 2>&1 &
sleep 6
# ⑤ 激活并确认
sudo hciconfig hci0 up
sudo hciconfig -a | head -10 # 期望 UP RUNNING + 真实 MAC
sudo bluetoothctl list # 期望 Controller XX:XX:XX:XX:XX:XX armbian

回滚(万一异常):

Terminal window
sudo cp /lib/firmware/rtlbt/rtl8761b_config.bak /lib/firmware/rtlbt/rtl8761b_config
sudo pkill rtk_hciattach

4.4 排障认知总结(为什么前面全失败)

阶段假设实际代价
前期Broadcom AP6255(看固件目录)Realtek RTL8761BTV所有 btattach -B 全白试
中期展锐 Unisoc(看 dtb wifi 节点)蓝牙独立走 UART,wifi 节点是展锐,但蓝牙是 RTL又绕一圈
卡点分析PWM 时钟 disabled → 芯片不上电方向对但非唯一卡点一度判定”板载无望”
转折issue #471 提供芯片真身 + 正确工具 + 波特率坑全对上一条命令解开

核心经验

  1. 先确认芯片型号再动手——固件目录的 brcm 文件具有误导性(那是 WiFi 的 SDIO 固件,蓝牙走 UART 是独立芯片)
  2. 协议不能猜——btattach-B(Broadcom)/rtk_h5(Realtek) 必须与芯片匹配
  3. 波特率是坑——Realtek 固件下载波特率 config 不对就超时
  4. 官方/社区 issue 是最快路径——比从零逆向 dtb 快得多

4.5 部署与运行状态(截至最后)

状态
rtk_hciattach 二进制~/rtkbt-main/uart/rtk_hciattach/(用户编译)
config 替换✅ 生效(115200 版),备份在 rtl8761b_config.bak
hci0UP 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. 取第 1 项=体重 / 第 2 项=体脂 / 第 3 项=BMI
    4. 记录健康样本:体重(kg)+ 体脂率(%)
    5. 返回主屏幕(跑完自动退出快捷指令 App,不再停留)
  • 踩过的坑(均已解决):
    1. shortcuts:// 传参格式:正确为 input=text&text=<数据>input 是类型标识、text 是内容),写成 input=<数据> 会导致快捷指令收到空输入(通知只显示”通知”两字)
    2. 快捷指令缺「记录健康样本」动作 → 数据通了但健康不写入
    3. 快捷指令运行完停留在 App 界面 → 末尾加「返回主屏幕」动作(iOS 无官方”运行后自动回主屏”设置,靠这个动作实现)
    4. ⚠️ 防垃圾数据建议:可在拆分后加「如果 体重介于30200 且 体脂介于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.serviceRestart=on-failure + RestartSec=5
  • 日志:/var/log/cm311-bt.log
  • 验证结果:systemctl is-enabledenabledis-activeactive;监听进程唯一;hci0 UP 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.pyCloudV3 算法 Python 参考实现
enable_bt_cm311.sh激活主脚本(博通版,已过时但 GPIO 逻辑可参考)参考
probe_bt_gpio.shGPIO 变体自动探测参考
collect_bt_diag.sh9 类诊断收集✅ 实战用过
check_dtb.shdtb 蓝牙节点自检参考
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.bakconfig 替换的备份(可回滚)
/tmp/rtk2.logRealtek 挂载日志

其他本地产物(tmp/chipsea/,备查)

文件说明
cloud_v3_lib1.0.4.jar芯海官方 SDK(反编译来源)
libchipsea_arm64.so / x86.so京东健康同款算法库实体
host.c.so 的 C 宿主(依赖 Docker,未跑通,已弃用)

六、完整命令速查

体脂秤广播测试(自检解析)

Terminal window
python3 /root/listen_scale.py --test
# 期望输出:85.00/500.0、86.45/500.0、24.55/500.0 全部 ✅

启动/停止监听

Terminal window
# 启动(后台常驻)
nohup python3 /root/listen_scale.py >/dev/null 2>&1 &
# 查看
ps aux | grep listen_scale
# 停止
pkill -f listen_scale.py

查看记录

Terminal window
cat /root/records.csv

iPhone 推送 + 写入 Apple Health(可选,联动模式)

  1. iPhone 装 Bark App → 获取 key
  2. iPhone 建快捷指令 RecordWeight(接收 input 体重|体脂|BMI,拆分后写健康样本:体重+体脂率)
  3. 编辑 /root/listen_scale.pyBARK_KEY = "..."SHORTCUT_NAME 保持与快捷指令一致)
  4. 重启监听:sudo systemctl restart bt-cm311

蓝牙重启后重新拉起(临时,直至做好自启)

Terminal window
sudo gpioset -s 1 -m time 0 82=0
nohup sudo gpioset --mode=signal 0 82=1 >/dev/null 2>&1 &
cd ~/rtkbt-main/uart/rtk_hciattach
sudo ./rtk_hciattach -n -s 115200 /dev/ttyAML1 rtk_h5 >/tmp/rtk2.log 2>&1 &
sleep 6
sudo hciconfig hci0 up

七、待办事项

#事项优先级说明
1站上秤 → 验证首条记录已完成2026-08-16 00:11 自动记录 85.55kg / 500.0Ω / 体脂26.3% / BMI 27.9 已落盘,闭环打通
2开机自启固化已完成bt-cm311.service enabled+active,重启自动拉起蓝牙+监听
3Bark + Apple Health 联动接通已完成2026-08-16 实测通过:站秤 → 通知 → 点通知 → 快捷指令 → 写入健康成功
4重启后验证🟡 中确认 config 替换重启后保留(若重启则重跑 4.3 步骤)
5京东健康 VFR 公式🟢 低仅学术兴趣,VFR 服务端公式未挖出,无实用必要

八、一句话总结

0.1 元的乐迪诺秤:广播里带真实体重 + 真实阻抗;京东健康的体脂是「没用真阻抗的身高估算」;芯海 CloudV3 公式用真阻抗算反而更接近医院。CM311-1A 的板载蓝牙折腾一整晚后,发现是 Realtek RTL8761BTV、换一份 115200 config 就活了。现在机顶盒正在后台旁听,上秤即自动记录——全程不需要京东健康。