当前位置: 首页 > news >正文

【蓝牙】BLE 数据收发实战指南(手机 App ↔ 嵌入式 Linux/BlueZ)

BLE 数据收发实战指南(手机 App ↔ 嵌入式 Linux/BlueZ)

本文面向在手机 App(Android/iOS)与嵌入式 Linux(BlueZ)之间实现 BLE(Bluetooth Low Energy)数据收发的工程实践,涵盖基础概念、接口映射、两类典型拓扑的完整流程、命令与验证、常见问题与排查,以及上线建议。

1. 基础概念与方向

  • 角色与职责
    • Central(中心):负责扫描、连接、发现服务;常发起读写与订阅通知。
    • Peripheral(外设):暴露 GATT 服务/特征/描述符;常被读写并主动上报通知/指示。
  • GATT/ATT 数据通道
    • 订阅上报:notify/indicate 由外设主动上报;由 CCCD(0x2902)控制。
    • 主动交互:read/write 由中心主动发起;外设读写回调中处理。
  • 关键对象(BlueZ D-Bus)
    • org.bluez.GattService1org.bluez.GattCharacteristic1org.bluez.GattDescriptor1
    • 特征属性:UUID(s)Service(o)Value(ay)Notifying(b)Flags(as)
    • 特征方法:ReadValue(a{sv} → ay)WriteValue(ay, a{sv})StartNotify()StopNotify()
  • CCCD(Client Characteristic Configuration Descriptor)
    • 订阅通知:中心向 CCCD 写入 0x0001(notify)或 0x0002(indicate)。
    • 取消订阅:写入 0x0000

2. BlueZ 接口与信号格式(Linux 外设)

  • 特征 Value 类型:ay(array of bytes)。
  • 通知事件通过 PropertiesChanged 信号上报,签名:s a{sv} as
    • 正确结构示例:
      • string "org.bluez.GattCharacteristic1"
      • array [ dict entry( string "Value" variant array of bytes [ 12 34 56 78 ] ) ]
      • array [ ]
  • 重要实践:构造 a{sv} 时应直接以 ay 作为 "{sv}" 的值,由 D-Bus 框架封装成单层 variant。不要手动 g_variant_new_variant(ay) 再传入,否则会出现 variant-of-variant(双层封装),导致上位机解析异常。
    • 参考实现路径:vendor/linkric/acs-device-sdk/BluetoothImplementations/BlueZ/src/BlueZLEGattCharacteristic.cpp
    • 方法:emitPropertiesChanged()Valueay 直接放入 "{sv}"

2.1 从发现到数据收发的流程图(端到端)

在这里插入图片描述

详细步骤说明(基础通信过程)

  • 发现与连接

    • 中心启用控制器: btmgmt power onbtmgmt le on
    • 扫描:bluetoothctlscan on,观察目标设备的广播(名称/服务 UUID/厂商字段)。
    • 连接:bluetoothctl connect <MAC>,建立 LE 连接(可在 btmon 中看到 LE Connection Complete)。
  • 服务与特征发现

    • bluetoothctlmenu gattlist-attributes 查看服务与特征(GATT/ATT 句柄)。
    • 手机 App(Android/iOS)在连接后自动进行服务发现(BluetoothGatt#discoverServices / CBPeripheral#discoverServices),并定位目标特征与 CCCD(0x2902)。
  • 订阅通知 / 指示

    • 中心写入 CCCD:
      • Android:descriptor.setValue(ENABLE_NOTIFICATION_VALUE/ENABLE_INDICATION_VALUE)writeDescriptor
      • iOS:peripheral.setNotifyValue(true, for: characteristic)
    • 外设(Linux/BlueZ)在 StartNotify 设定 Notifying=true。随后调用 setValue(...) 会发出 PropertiesChanged,ATT 层表现为 Handle Value Notification/Indication。
  • 数据发送(外设→中心)

    • 外设侧将待发字节流放入特征的 Value(ay) 并触发 PropertiesChanged
      • 信号结构:s a{sv} as,其中 Value 必须是 variant(ay) 的单层封装。
      • 上层解析期望:array of bytes [...]
    • 中心在回调中接收:
      • Android:onCharacteristicChanged
      • iOS:didUpdateValueFor
  • 数据写入(中心→外设)

    • 中心构造要写入的 ay 字节流:
      • Android:writeCharacteristic
      • iOS:writeValue(data, type: .withResponse/.withoutResponse)
    • 外设在 WriteValue 中读取参数并执行业务处理;需要返回结果时,结合通知/指示上报:setValue(result_bytes)
  • 诊断与验证

    • D-Bus 信号:dbus-monitor --system "interface='org.freedesktop.DBus.Properties',member='PropertiesChanged'"
    • 低层抓包:sudo btmon,观察 ATT 读写与通知时序。
    • 常见问题:CCCD 写入失败、Notifying 未更新、variant-of-variant 导致解析异常(务必保证 Valueay 仅单层 variant)。

3. 案例 A:手机 App 为 Central,Linux/BlueZ 为 Peripheral

3.1 Linux 侧(外设)

  • 特征声明包含 Flagsnotifyindicate
  • 启用通知:在 StartNotify 中设置 Notifying=true;之后每次调用 setValue(...) 都会触发 PropertiesChanged 信号(通知)。
  • 验证信号:
    • dbus-monitor --system "interface='org.freedesktop.DBus.Properties',member='PropertiesChanged'" | grep -A3 /org/bluez/app/service0/chrc1
    • 低层抓包:sudo btmon 观察 ATT Handle Value Notification/Indication
  • 快速命令(bluetoothctl):
    • scan on → 发现并 connect <MAC>menu gatt → 选择特征后 notify on

3.2 手机 App 侧(中心)

  • Android(BluetoothGatt
    • 流程:连接→发现服务→查找特征与 CCCD
    • 订阅示例:
      • gatt.setCharacteristicNotification(characteristic, true)
      • 写入 CCCDdescriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE)gatt.writeDescriptor(descriptor)
    • 接收:onCharacteristicChanged 回调中读取字节数组。
  • iOS(CoreBluetooth
    • 流程:连接→discoverServices / discoverCharacteristics
    • 订阅:peripheral.setNotifyValue(true, for: characteristic)
    • 接收:peripheral(_:didUpdateValueFor:error:) 回调读取 characteristic.value

3.3 数据帧约定(建议)

  • 在字节流首部编码协议版本与命令字,便于扩展与兼容:
    • 示例:[magic=0x5AA5][ver=0x11][cmd=0x01][len=...][payload][CRC]

4. 案例 B:手机 App 写入命令(Central→Peripheral),Linux 回传结果

4.1 手机侧(写入)

  • Android:characteristic.setValue(bytes)gatt.writeCharacteristic(characteristic);是否需要 withResponse 视特征 Flags 而定。
  • iOS:peripheral.writeValue(data, for: characteristic, type: .withResponse/.withoutResponse)

4.2 Linux 侧(接收与反馈)

  • 在特征的 WriteValue 中读取 ay 参数,进行业务处理。
  • 需要反馈时:在处理完成后 setValue(result_bytes) 并确保已订阅通知(Notifying=true),中心即可收到结果通知。

4.3 验证

  • dbus-monitor 观察 PropertiesChangedbtmon 观察 ATT 写入与后续通知时序。

5. 案例 C:Linux 为 Central(BlueZ + 工具/bleak),手机/设备为 Peripheral

5.1 Linux 侧(中心)

  • bluetoothctl 快速验证:
    • scan onconnect <MAC>menu gattlist-attributes
    • 选择目标特征后:notify on 订阅、read/write <hex bytes> 主动交互。
  • Python bleak(如需自动化):
    • 订阅:await client.start_notify(char_uuid, callback)
    • 写入:await client.write_gatt_char(char_uuid, data)

5.2 手机 App(外设)

  • 暴露自定义 GATT 服务与特征,按需支持 notify/indicatewrite
  • 快速验证可用 nRF Connect/LightBlue 等通用 App。

6. 验证与诊断清单

  • 控制器启用:btmgmt power onbtmgmt le on
  • 扫描与连接:bluetoothctlscan onconnect <MAC>
  • 订阅通知:menu gatt → 选择特征 → notify on
  • 读写测试:readwrite <hex bytes>
  • D-Bus 信号监控:dbus-monitor --system "interface='org.freedesktop.DBus.Properties',member='PropertiesChanged'"
  • 低层抓包:sudo btmon

7. 常见问题与排查

  • 无法订阅通知
    • 特征 Flags 未包含 notify/indicate;CCCD 写入失败;外设未维护 Notifying 状态。
  • 收到空数据或长度异常
    • getValue() 返回空数组;写入处理未更新 m_value;两端编码约定不一致。
  • variant-of-variant 解析异常
    • 构造 a{sv} 时应直接传入 ay,让框架封装单层 variant;不要手动 g_variant_new_variant(ay) 再加入 "{sv}"
  • 连接稳定性与功耗
    • 广播与连接参数(interval/latency/supervision timeout)需与手机端适配;高频上报场景建议限流与降采样。

8. 上线建议

  • 建立“金路径”脚本:用 bluetoothctlbtmon 固化验证步骤,更新后快速回归。
  • 手机侧先用通用 App(nRF Connect/LightBlue)快速验证,再集成到自研 App。
  • 为数据帧定义清晰的协议文档与测试用例,确保两端编码一致。
http://www.dtcms.com/a/569567.html

相关文章:

  • 万象园网站建设与开发网站建设要架服务器
  • 【Cache缓存】cache的刷新
  • 水坑攻击的攻击原理和特点+案例和防御方法
  • Git 如何从某个 commit 新建分支
  • 做商业广告有什么网站好推销的无锡百姓网推广
  • 云南建个网站哪家便宜河北工程建设造价信息网
  • Spring Boot 集成 EMQ X 4.0 完整技术指南
  • git/github入门基操(终端版)
  • Spring Boot 集成 InfluxDB 2.x 完整技术指南
  • 汕头企业网站模板建站cm域名网站
  • 【Vue】Vue框架的基础知识强化
  • 计算机大类常见单词
  • 无头浏览器的效率救赎:Playwright连接复用与请求拦截技巧
  • dirname basename 命令
  • 邯郸网站制作基本流程网上销售网站建设
  • 如何优雅地找到二叉树的最近公共祖先?
  • 网站建设公司一般几个人成都设计公司装修
  • TOON 协议与 AIDotNet.Toon 实践指南
  • PPOCR 再编译
  • 网站关键词修改工作汇报总结怎么写
  • Vue 3模板如何通过编译三阶段实现从声明式语法到高效渲染的跨越
  • 做网站用的什么编程语言学编程的费用一般是多少
  • 智能家居技术发展与应用综述
  • linux udp广播数据包实际用例
  • 什么公司需要建立网站电商平台运营策略
  • 厦门怎么没有 网站备案wordpress网站的彻底清理
  • 一文讲全volatile关键字
  • 西安高校网站建设保定手机网站制作
  • 对称加密与非对称加密
  • Swift添加字体到项目中