1/3
2/3
3/3
编写一个小米手环“运动心率广播”接收Demo
Tnze
编辑于 2023年10月20日 04:45
收录于文集
共4篇

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

cut-off

第一步肯定是找官方文档,但是小米已经算到了你这一步,所以压根就没有提供文档。嘿嘿♥

于是只能找资料和抓包,

网上居然没有一个能在Windows上跑的、能接受小米手环心率广播的Demo。而且大部分教程都是告诉你怎么点App上那个开关,但是一句话都不提这个广播要怎么接收。

最后还是要通过抓蓝牙数据包来试着找心率数据

cut-off

在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”按钮。

cut-off

让我们略去枯燥无味的寻找过程,直接贴上资料来源和结果。

https://github.com/custom-components/ble_monitor/issues/875

Wireshark界面

首先你需要应用以下过滤器,这样可以快速过滤掉其他不相关的设备:

btcommon.eir_ad.entry.company_id == 0x0157

然后随便点击一个抓到的包,按图中标记出的一个字节获取到的即是心率值。图中是十六进制的42,转换成十进制就是66。

查找信息不容易,如果帮到了大家,大家可以给这篇文章点一个小小的赞。

cut-off

接下来就是编写程序,制作一个可以读取心率数据的 Demo。

选择正确的编程语言是关键,我对比了 Go 和 Rust 的 BLE 库,发现还是 Rust 这边轮子造的圆。我选择了一个叫“bluest”的 Crate,它兼容Windows、macOS/iOS和Linux平台,是一个非常好的库。

最后代码的实现非常简单,我甚至可以决定把它贴在这篇文章里面。这,就是Rust带给我的自信(?)

代码块
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分