如何在Linux系统下使用C程序实现与BLE设备的交互?
基于BlueZ的BLE C代码交互指南(心率监测仪场景)
BlueZ是Linux官方默认的蓝牙协议栈,你可以通过bluetoothctl正常读取心率数据,说明系统BlueZ服务运行状态正常。C代码实现设备交互当前有两种可选实现路径:
- 用BlueZ的DBus API(现在官方推荐的方式,不需要链接BlueZ内部私有库,兼容所有5.40以上版本的BlueZ)
- 用旧版hci、gattlib类第三方库(不推荐,新版本BlueZ已经弃用绝大多数hci相关接口,兼容性极差)
前置依赖确认
- 编译依赖:提前安装
libdbus-1-dev、glib2.0-dev包,示例代码采用GDBus(GLib封装的DBus绑定)实现 - 已知参数:提前通过
bluetoothctl获取目标设备MAC地址,心率服务标准UUID为0000180d-0000-1000-8000-00805f9b34fb,心率测量特征值标准UUID为00002a37-0000-1000-8000-00805f9b34fb,绝大多数合规心率监测设备都采用蓝牙联盟规定的通用UUID - 运行要求:BlueZ版本≥5.40,bluetoothd系统服务正常启动
核心操作流程
流程和你用bluetoothctl操作的逻辑完全对应:
- 扫描设备(你已经实现该功能,扫描完成后可以拿到目标设备的DBus对象路径,格式一般为
/org/bluez/hci0/dev_XX_XX_XX_XX_XX_XX,其中XX为MAC地址的十六进制值,冒号替换为下划线) - 发起设备连接,等待BlueZ自动完成GATT服务发现
- 遍历GATT特征列表,匹配到心率测量对应的特征对象
- 开启特征值通知,订阅属性变更信号,设备上报心率时会触发回调
- 按协议解析特征值拿到心率数据
关键代码片段示例
设备连接代码
// 入参conn为已初始化的GDBusConnection对象,obj_path为目标设备的DBus对象路径 GError *error = NULL; g_dbus_connection_call_sync( conn, "org.bluez", obj_path, "org.bluez.Device1", "Connect", NULL, NULL, G_DBUS_CALL_FLAGS_NONE, -1, NULL, &error ); if (error != NULL) { g_printerr("连接失败: %s\n", error->message); g_error_free(error); return 1; }
开启特征通知代码
// 入参char_path为心率测量特征的DBus对象路径 g_dbus_connection_call_sync( conn, "org.bluez", char_path, "org.bluez.GattCharacteristic1", "StartNotify", NULL, NULL, G_DBUS_CALL_FLAGS_NONE, -1, NULL, &error ); // 订阅特征值变更信号 g_dbus_connection_signal_subscribe( conn, "org.bluez", "org.freedesktop.DBus.Properties", "PropertiesChanged", char_path, NULL, G_DBUS_SIGNAL_FLAGS_NONE, on_heart_rate_update, // 自定义回调函数,处理收到的心率数据 NULL, NULL );
心率数据回调函数
static void on_heart_rate_update(GDBusConnection *connection, const gchar *sender_name, const gchar *object_path, const gchar *interface_name, const gchar *signal_name, GVariant *parameters, gpointer user_data) { const gchar *iface; GVariant *props; g_variant_get(parameters, "(&s@a{sv}as)", &iface, &props, NULL); if (g_strcmp0(iface, "org.bluez.GattCharacteristic1") != 0) { g_variant_unref(props); return; } GVariant *value = g_variant_lookup_value(props, "Value", G_VARIANT_TYPE_BYTESTRING); if (value != NULL) { gsize len; const guint8 *data = g_variant_get_data(value, &len); if (len >= 2) { guint8 flags = data[0]; // 标志位第0位为1时心率是16位值,否则为8位值 guint16 heart_rate = (flags & 0x01) ? GUINT16_FROM_LE(*(guint16*)(data+1)) : data[1]; g_print("当前心率: %d bpm\n", heart_rate); } g_variant_unref(value); } g_variant_unref(props); }
BlueZ API参考入口
所有接口定义的官方文档都在你本地系统的BlueZ安装目录下,路径为/usr/share/doc/bluez/api/:
device-api.txt:设备连接、配对、属性查询相关的接口定义gatt-api.txt:GATT服务、特征、描述符的读写、通知相关的接口定义
所有方法、参数、信号的规则都有明确说明。
注意事项
- 程序运行需要root权限,或者给编译后的二进制文件加
cap_net_raw,cap_net_admincapabilities权限 - 连接设备前确保没有其他进程(比如
bluetoothctl)占用目标设备 - 如果需要主动读取特征值,调用
org.bluez.GattCharacteristic1.ReadValue方法,参数传入空字典即可
内容的提问来源于stack exchange,提问作者Maneesh N
相关产品推荐
相关产品推荐

