智能手表与 App 蓝牙低功耗(BLE)实战指南

DemoApplication --- 智能手表与 App 蓝牙低功耗(BLE)实战指南

文档主题

智能手表与手机 App 之间的通信常采用蓝牙低功耗(BLE) 。相比经典蓝牙,BLE 更省电、适合周期性小数据同步(心率、步数、通知、固件升级进度等),是穿戴设备的主流方案。大文件(如 MP3 音频)若必须走 BLE,需按分包与断点续传设计,见下文第四章第 6 节。

本仓库当前为 Jetpack Compose 示例工程(minSdk 24 / compileSdk 34),可作为在此基础上接入 BLE 的起点。


一、BLE 在手表场景中的常见用法

能力 说明
GATT 客户端 手机 App 通常作为 Central(中心设备) ,手表作为 Peripheral(外设),App 连接后读写特征值(Characteristic)。
通知 / 指示 手表主动上报数据:使用 NotifyIndicate(Indicate 带应用层确认)。
写入指令 App 向手表下发控制或配置:对可写特征执行 Write / Write Without Response
MTU 与分包 数据量大时需协商 MTU,或在上层做分包与粘包协议。
配对与绑定 涉及敏感数据或防重放时,可能依赖系统配对;纯明文广播+连接则未必每次配对。

常见协议形态:厂商自定义 GATT Service/Characteristic UUID ,或基于 标准服务(如 Heart Rate、Battery 等)再扩展私有特征。


二、开发环境与本项目运行

  • Android Studio:建议 Hedgehog 及以上,自带 JDK 与 Android SDK。
  • 设备:真机(推荐)用于 BLE;模拟器对蓝牙支持有限。
  • 运行 :用 Android Studio 打开工程根目录,同步 Gradle 后选择设备,运行 app 模块。

当前入口:MainActivity 使用 Compose;已实现 BLE Demo (权限门闸、扫描列表、连接、服务枚举、requestMtu、尝试读标准电量、WatchProtocol 本地分包演示),见 bleui/BleDemoScreen 包。


三、Android 端实战步骤(推荐顺序)

1. 声明权限(随 Android 版本组合使用)

  • 大致规律 :Android 12(API 31)起位置权限与蓝牙权限拆分;扫描场景常需 精确位置 或新蓝牙权限组合,具体以目标 targetSdk 与官方文档为准。
  • 清单中常见项 (名称以你最终 targetSdk 为准核对官方表):
    • BLUETOOTH / BLUETOOTH_ADMIN(低版本)
    • BLUETOOTH_SCANBLUETOOTH_CONNECT(API 31+)
    • 若扫描需满足「旧策略」:ACCESS_FINE_LOCATION
    • BLUETOOTH_ADVERTISE(若 App 需作为外设广播,一般手表 App 较少)

实战要点 :在 运行时 请求危险权限;连接前检查 BluetoothAdapter 是否可用、蓝牙是否开启。

2. 扫描附近外设

  • 使用 BluetoothLeScannerstartScan / stopScan)。
  • 过滤 :按厂商提供的 Service UUID 或设备名前缀过滤,减少列表噪音。
  • 节流 :避免长时间全量扫描耗电;找到目标后及时 stopScan

3. 建立 GATT 连接

  • device.connectGatt(context, false, callback),在 BluetoothGattCallback 中处理:
    • onConnectionStateChange:已连接 / 断开
    • onServicesDiscovered:发现服务后枚举 Service → Characteristic → Descriptor
    • onCharacteristicRead / onCharacteristicWrite / onCharacteristicChanged(Notify 数据)

4. 打开 Notify / Indicate

  • 对 CCCD(Client Characteristic Configuration Descriptor)写入 ENABLE_NOTIFICATION_VALUEENABLE_INDICATION_VALUE
  • onDescriptorWrite 中确认成功后再认为「订阅就绪」。

5. 读写业务数据

  • readCharacteristic(注意队列:部分机型不宜连续无等待地堆叠操作)。
  • :根据固件约定选择 Write With ResponseWithout Response
  • 协议:与手表固件约定好字节序、命令字、长度、校验(CRC 等)及错误码。

6. 断开与释放

  • disconnect()close(),避免泄漏;页面销毁或退后台策略要与产品一致(部分场景需保持连接)。

7. 前台服务(可选但常见)

  • 长时间同步或 OTA 时,使用 Foreground Service + 类型合适的 foregroundServiceType,避免被系统强杀并符合后台限制。

四、大量数据(如走路步数)怎么传

BLE 单次 ATT 载荷受 MTU 限制,步数若按「每分钟/每小时一条」累积成几天历史,很容易超过单包长度,必须在应用层协议产品形态上一起设计。

1. 能少传就少传:摘要优先

  • 实时:连接期间只推「当前会话增量」或「今日累计」,字节很少。
  • 历史 :不要传原始传感器流;用 按天/按小时聚合 的结构(时间戳 + 步数 + 可选距离/卡路里),必要时再支持「拉取某一天明细」。
  • 二进制紧凑:定长记录或小端整数数组,避免 JSON/XML 占满 MTU。

2. 必须传大包时:MTU + 分包 + 确认

  • 协商 MTU :连接建立后调用 requestMtu()(具体上限因手机与手表协议栈而异,常见在约 185~247 字节有效 ATT 载荷量级,以实测为准)。
  • 自定义分包帧 :例如「命令字 + 总长度 + 分片序号/总分片数 + 载荷 + CRC」。手表按序发 Notify(或 App 读长数据),App 侧 重组缓冲区;丢包则重传某片。
  • 流控:每发 N 包等一层 ACK(或 Indicate),避免手表 RAM 与控制器队列溢出导致断连。
  • Indicate vs Notify :大批量若要求可靠,可对关键片用 Indicate(有确认),或仍用 Notify 但在应用层发 ACK 包。

3. 传输路径选择

  • Notify 推流 :手表主动推,适合「同步历史」会话;注意 Android 端 串行化 GATT 操作(不要无脑并发写)。
  • Read 分块 :固件把历史放在「逻辑块」里,App 发「读第 k 块」命令,再 readCharacteristic 或走私有「块特征」;便于断点续传。
  • Prepare Write / Long Write :标准上适合较长写入;穿戴里更常见仍是 厂商自定义分包,实现简单、两端一致即可。

4. 吞吐与体验

  • 连接间隔:主要由系统与对端协商,App 能控制的有限;大批量同步时保持屏幕常亮、前台服务,减少被限速或杀后台。
  • PHY :若双方支持 2M PHY,在可接受距离内有利于提高速率(仍远低于经典蓝牙)。
  • 失败重试:超时、CRC 错误、只收到部分分片时,从最后成功序号续传。

5. 与「经典蓝牙」的取舍

若手表同时支持 经典蓝牙(SPP 等) 且产品允许配对两套栈,大批量文件类同步可走经典通道;纯 BLE 手表则按上文分包与聚合设计即可。

6. MP3 / 音频文件传输

MP3 属于已压缩的二进制大文件 (常见数 MB),与步数不同:不能靠「聚合摘要」缩小,只能整文件按字节搬运 ,技术本质与 固件 OTA 分包 相同,只是落盘路径与格式校验不同。

能力预期

  • BLE 实际吞吐受连接间隔、PHY、对端实现影响,常见在 每秒数十 KB 量级 (以双端实测为准)。一首 5MB 的 MP3 纯 BLE 可能要 数分钟甚至更久,且耗电、占连接,需有明确 UI(进度、取消、后台策略)。
  • 不要再压一层「通用压缩」:MP3 本身已压缩,gzip 等收益很小,徒增 CPU。

协议层面(与上文「分包 + 流控」一致)

  • 定义 文件会话fileId(或路径 token)、总长度分片大小 (建议与协商 MTU 对齐,留出帧头/CRC 空间)、分片序号 、载荷、CRC32/SHA256(整文件校验)。
  • 断点续传:持久化「已确认收到的最大连续偏移」,重连后从该偏移继续,避免用户反复全量传。
  • 方向 :通常是 手机 → 手表 (下发铃声、离线播客片段);若手表回传录音 MP3,思路相同但注意手表侧 存储空间与写闪存寿命
  • 存储与格式 :固件约定写入路径(如「音乐分区」)、单文件上限;可选先写到临时文件再 rename 做原子提交,避免半截文件被播放器打开。

产品形态上的更优解(优先评估)

  • 手表带 WiFi / 配套手机热点:大文件走 HTTP(S) 或厂商私有 WiFi 通道,体验远好于纯 BLE。
  • 经典蓝牙(A2DP/SPP 或厂商大通道):适合「传歌到表」类场景,若硬件支持应优先考虑。
  • 只做短音频 :闹钟、提示音可用 几十~数百 KB 的短片段(甚至降级为低码率或专用提示音格式),显著缩短 BLE 传输时间。

Android 实现注意

  • 读本地 MP3 用 顺序流式读取FileInputStream / MappedByteBuffer 分块),避免一次性 readBytes() 整文件进内存。
  • 传输会话放在 前台服务 中执行,并处理 系统杀进程、蓝牙断开 后的恢复与重试。

五、与手表固件协作的检查清单

  1. UUID 表:主服务 UUID、各特征 UUID、属性(读/写/Notify)、字节布局文档。
  2. 连接参数:是否要求指定 PHY、连接间隔偏好(由固件与主机协商,但需知预期)。
  3. 安全:是否加密、是否必须配对、密钥与证书流程。
  4. OTA:分包大小、断点续传、校验与回滚策略。
  5. 调试工具 :手机端可用 nRF Connect 等 App 对真实手表 GATT 做探查,与 Android 日志对照。

六、常见问题与排查

现象 可能原因
扫描不到设备 权限未授予、蓝牙关闭、手表未处于可发现模式、过滤条件过严
连接立刻断开 UUID 不匹配、固件只允许单连接、RSSI 过弱、固件侧拒绝
Notify 无数据 CCCD 未写成功、订阅错特征、固件未推数据
写入无响应 特征不可写、需 Write Without Response、MTU/长度超限

调试时打开 HCI snoop 或使用厂商抓包工具,可快速区分是 App 层还是协议/固件层问题。


七、在本项目中落地的建议结构(Kotlin)

便于维护的拆分方式(示例思路,非强制目录名):

  • BlePermissions:统一请求与解释权限文案
  • BleScanner:扫描生命周期与回调转换
  • GattClient:封装 BluetoothGatt、队列化读写、重连策略
  • WatchProtocol:纯 Kotlin 的组包/解包,与 UI 解耦
  • UI 层(Compose):只观察状态(已连接 / 扫描列表 / 最新心率等),不直接操作 GATT 细节

依赖上可逐步引入 Kotlin 协程 + MutableStateFlow 向界面层推送状态;若后续需要蓝牙相关 Jetpack API,再按官方文档添加对应依赖版本。


点击跳转代码链接

https://gitcode.com/qq_33495943/watchesBLEAPP

相关推荐
开开心心就好11 天前
吾爱大佬原创的文件时间修改工具
安全·智能手机·pdf·电脑·智能音箱·智能手表·1024程序员节
华清远见IT开放实验室24 天前
智能手表完整项目实现,比赛求职双向加分,基于嵌入式大赛推荐开发板(STM32U5)
stm32·单片机·嵌入式硬件·学习·智能手表·嵌入式大赛
LCG元25 天前
STM32实战:基于FreeRTOS的LVGL嵌入式GUI移植(智能手表界面)
stm32·嵌入式硬件·智能手表
Wave8451 个月前
STM32+ESP8266 智能手表实战:天气获取与阿里云时钟同步
stm32·阿里云·智能手表
Wave8451 个月前
从裸机到 FreeRTOS:STM32 智能手表重构之路
stm32·重构·智能手表
Wave8451 个月前
基于 STM32 的模块化多功能手表系统:从架构设计到低功耗深度实践
stm32·嵌入式硬件·智能手表
数据猿视觉2 个月前
2026 智能手表怎么选?这款 AI 旗舰闭眼冲
人工智能·智能手表
HwJack202 个月前
HarmonyOS Wear Engine Kit:让智能手表应用“活”起来的魔法工具箱
华为·harmonyos·智能手表
开开心心就好2 个月前
进程启动瞬间暂停工具,适合调试多开
linux·运维·安全·pdf·智能音箱·智能手表·1024程序员节