面向 Windows 10/11 x64 的 Xiaomi Watch S1 Pro 原生 WPF 实时健康客户端。
当前已完成并通过真实手表验证:
Bluetooth Classic / RFCOMM SPP
→ Xiaomi SPP V1 协商
→ Xiaomi Authentication
→ AES-CCM 加密 Protobuf
→ RealTimeStats
→ 实时 BPM
项目使用 C#、WPF、.NET 10、Google.Protobuf、System.Security.Cryptography.AesCcm
和 Windows 原生 Bluetooth/RFCOMM API,不依赖 Android 或 Gadgetbridge 运行时。
复制配置模板:
Copy-Item .\appsettings.example.json .\appsettings.json编辑本机的 appsettings.json:
{
"DeviceMac": "AA:BB:CC:DD:EE:FF",
"AuthKey": "",
"AttemptWindowsPairing": false,
"AutoReconnect": true,
"ReconnectIntervalSeconds": 5
}DeviceMac:手表的 48-bit Bluetooth Classic 地址。AuthKey:16 bytes,即 32 个十六进制字符;也接受可选的0x前缀。AttemptWindowsPairing:默认不主动发起 Windows 系统配对。AutoReconnect:连接丢失后是否重新建立完整 RFCOMM、SPP 和认证会话。ReconnectIntervalSeconds:重连间隔,范围 1~3600 秒。
appsettings.json、MiFitnessLogs/、运行日志、构建输出和临时协议参考目录均被
.gitignore 排除。程序不会输出 AuthKey、完整 session key 或认证帧中的
nonce/HMAC;认证帧日志只保留方向和字节长度。
项目不会把真实的 appsettings.json 自动复制到 bin/ 或发布目录,避免把
AuthKey 意外打包。dotnet run 会从当前项目目录读取本机配置;分发应用时,
请由最终用户在程序目录手动创建 appsettings.json,不要把开发机配置放入
安装包或压缩包。
dotnet build
dotnet run窗口启动后会读取 appsettings.json 并自动连接。主界面显示:
- 实时心率与最近 60 个样本的趋势;
- 今日步数、卡路里和站立小时;
- RFCOMM/SPP、协议版本、AES-CCM 和会话状态;
- 可滚动、可清空的十六进制协议日志。
仅在明确需要 Windows 系统配对时运行:
dotnet run -- --pair点击主窗口关闭按钮或按 Alt+F4 时,程序默认隐藏到 Windows 系统托盘, RFCOMM 与实时心率采集继续运行。双击托盘图标可恢复主界面;托盘菜单还可 显示/隐藏心率悬浮窗。只有选择托盘菜单中的“退出”,或点击主界面的“断开”, 程序才会尽可能发送 realtime stats stop 并关闭 RFCOMM socket。“连接”按钮 会从 RFCOMM 开始重新建立完整的新会话。
标准 Serial Port Profile UUID:
00001101-0000-1000-8000-00805F9B34FB
主连接路径:
BluetoothDevice.FromBluetoothAddressAsync(MAC)
→ GetRfcommServicesAsync(BluetoothCacheMode.Uncached)
→ 找到标准 SPP 的 RfcommDeviceService
→ StreamSocket.ConnectAsync(
ConnectionHostName,
ConnectionServiceName,
BluetoothEncryptionAllowNullAuthentication)
若 SDP 没有返回 SPP,程序不会立即退出,而会使用 Windows Winsock
Bluetooth RFCOMM,以 SOCKADDR_BTH.serviceClassId = SPP_UUID 直接连接。
版本请求与 Gadgetbridge 当前实现一致:
BA DC FE 00 C0 03 00 00 00 00 EF
真实手表响应:
BA DC FE 00 00 06 00 01 00 00 00 02 0E EF
响应 payload 为 00 02 0E。Gadgetbridge 当前依据 payload 首字节判断:
小于 2 使用 V1,大于等于 2 切换到 V2,因此该手表使用 SPP V1。
V1 Protobuf 发送使用 raw channel 2,接收使用 channel 1。认证阶段 data type 为 2;认证完成后的业务命令 data type 为 1。
认证流程:
type=1 subtype=26 + 16-byte phone nonce
→ watch nonce + watch HMAC
→ HMAC-SHA256 derivation context "miwear-auth"
→ encryption/decryption keys and nonce prefixes
→ constant-time watch HMAC verification
→ type=1 subtype=27
→ AES-CCM encrypted AuthDeviceInfo
→ authentication success
AES-CCM 使用 12-byte nonce 和 4-byte authentication tag。
认证成功后发送:
Command {
type = 8
subtype = 45
}
V1 上行加密包使用递增的 32-bit packet counter 构造 AES-CCM nonce,并在
密文前放置 counter 的低 16 位小端值。收到 type=8 subtype=47 后解析:
Command.health.realTimeStats.heartRate
输出示例:
[HR] 78 BPM
[HR] 79 BPM
[HR] 80 BPM
退出时发送:
Command {
type = 8
subtype = 46
}
2026-07-27 已使用真实 Xiaomi Watch S1 Pro 验证:
- RFCOMM SPP 连接成功。
- SPP V1 协商成功。
- Watch nonce 和 HMAC 校验成功。
- AES-CCM session 建立成功。
- realtime stats start 加密发送成功。
- 连续收到 subtype 47。
- 连续收到并解析到有效的实时 BPM 样本。
- WPF“断开”和托盘“退出”会执行 realtime stats stop 安全退出流程。
- 主窗口关闭默认隐藏到系统托盘,实时心率连接保持运行。
- WPF“连接”按钮可重新建立 RFCOMM、SPP、认证和实时统计会话。
dotnet build:0 个警告,0 个错误。
Windows 偶尔会在读取 pairing 元数据时返回 COMException 0x80004004,
但不影响后续 SDP 枚举、RFCOMM 连接和 Xiaomi 协议通信。
项目目前包含连接、SPP、认证、加密 Protobuf、实时心率以及 WPF 状态面板和 实时趋势。未实现 OBS、通知、天气、音乐或运动历史同步。