


如题,买了小米手环之后一直很好奇这个运动心率广播怎么接收,都支持些什么设备。这几天终于兴趣来了,花了一点点时间研究,遂作此文章分享。


第一步肯定是找官方文档,但是小米已经算到了你这一步,所以压根就没有提供文档。嘿嘿♥
于是只能找资料和抓包,
网上居然没有一个能在Windows上跑的、能接受小米手环心率广播的Demo。而且大部分教程都是告诉你怎么点App上那个开关,但是一句话都不提这个广播要怎么接收。
最后还是要通过抓蓝牙数据包来试着找心率数据

在Windows上抓蓝牙包并不需要专门的硬件,只要你的电脑带有蓝牙功能即可。
下载微软提供的BTP工具:https://learn.microsoft.com/zh-cn/windows-hardware/drivers/bluetooth/testing-btp-setup-package
这个工具很坏地自动解压在了C盘根目录,然后你需要打开C:\BTP\v1.14.0\x86\btvs.exe,这个可执行文件会很坏地自动打开Wireshark并且开始抓包:

蓝牙工具
当然,你还需要点这个“Full Packet Logging”按钮。

让我们略去枯燥无味的寻找过程,直接贴上资料来源和结果。
https://github.com/custom-components/ble_monitor/issues/875

Wireshark界面
首先你需要应用以下过滤器,这样可以快速过滤掉其他不相关的设备:
btcommon.eir_ad.entry.company_id == 0x0157
然后随便点击一个抓到的包,按图中标记出的一个字节获取到的即是心率值。图中是十六进制的42,转换成十进制就是66。
查找信息不容易,如果帮到了大家,大家可以给这篇文章点一个小小的赞。

接下来就是编写程序,制作一个可以读取心率数据的 Demo。
选择正确的编程语言是关键,我对比了 Go 和 Rust 的 BLE 库,发现还是 Rust 这边轮子造的圆。我选择了一个叫“bluest”的 Crate,它兼容Windows、macOS/iOS和Linux平台,是一个非常好的库。
最后代码的实现非常简单,我甚至可以决定把它贴在这篇文章里面。这,就是Rust带给我的自信(?)
use std::error::Error;
use bluest::{Adapter, AdvertisingDevice};
use futures_lite::stream::StreamExt;
#[tokio::main]
async fn main() -> Result<(), Box<dyn Error>> {
let adapter = Adapter::default()
.await
.ok_or("Bluetooth adapter not found")?;
adapter.wait_available().await?;
println!("Starting scan");
let mut scan = adapter.scan(&[]).await?;
println!("Scan started");
while let Some(discovered_device) = scan.next().await {
handle_device(discovered_device)
}
Ok(())
}
fn handle_device(discovered_device: AdvertisingDevice) {
if let Some(manufacturer_data) = discovered_device.adv_data.manufacturer_data {
if manufacturer_data.company_id != 0x0157 {
return;
}
let name = discovered_device
.device
.name()
.unwrap_or(String::from("(unknown)"));
let rssi = discovered_device.rssi.unwrap_or_default();
let heart_rate = manufacturer_data.data[3];
println!("{name} ({rssi}dBm) Heart Rate: {heart_rate:?}",);
}
}
以下是运行截图:

Demo运行截图
值得注意的是,在测试时,最好让小米手环进入运动模式,比如“自由活动”。手环测量心率的频率将会提高,广播发送也会更快一些。
而如果你没有在App内开启“运动心率广播”,那么手环就不会发送心率数据,在数据包内则用0xFF填充,因此这个Demo会显示心率是255。
Tnze · 2023年10月19日20点43分