BlueZ5.45 D-Bus总线 GATT API 分析

2023-11-08

        笔者目前做linux系统下bluez蓝牙项目开发,发现网上关于bluez开发的资料很少。对于刚开始接触bluez蓝牙的开发人员来说是非常痛苦的。通过调试bluez源码自带的应用例子和文档说明,对BlueZ5.45 D-Bus总线 GATT API有了一些感悟。笔者不才,愿意将所了解到的一些知识分享给大家,希望能带给大家一些帮助。

一、首先看一下gatt-api说明文档,路径如下:Bluez-5.45/doc/gatt-api.txt

1、Service hierarchy

外部应用首先要用GattManager1中的注册方法注册该服务才能使本地服务生效,而且使这些方法和属性生效,还要在GattService1 接口声明,以下是gatt-api.txt文档中关于Service hierarchy说明

Service        org.bluez

Interface    org.bluez.GattService1

Object path    [variable prefix]/{hci0,hci1,...}/dev_XX_XX_XX_XX_XX_XX/serviceXX
Properties    string UUID [read-only]
            128-bit service UUID.
        boolean Primary [read-only]
            Indicates whether or not this GATT service is a
            primary service. If false, the service is secondary.
        object Device [read-only, optional]
            Object path of the Bluetooth device the service
            belongs to. Only present on services from remote
            devices.
        array{object} Includes [read-only]: Not implemented
            Array of object paths representing the included
            services of this service.

为了便于理解以gatt-service.c文档为例说明:

程序开始定义了以下接口:

#define GATT_MGR_IFACE            "org.bluez.GattManager1"
#define GATT_SERVICE_IFACE        "org.bluez.GattService1"
#define GATT_CHR_IFACE            "org.bluez.GattCharacteristic1"
#define GATT_DESCRIPTOR_IFACE        "org.bluez.GattDescriptor1"

其中GATT_SERVICE_IFACE为org.bluez.GattService1接口

然后定义了服务uuid和对应的特征值uuid

/* Immediate Alert Service UUID */
#define IAS_UUID            "ea816b5a-129c-4941-8a20-1c2ae3239f02"
#define ALERT_LEVEL_CHR_UUID        "7df357bb-4f53-49c0-861e-84aa2be57f65"
#define UPDATE_CHR_UUID        "b5635b6e-02cb-4fe8-a4a2-3c0279f53755"

其中#define IAS_UUID            "ea816b5a-129c-4941-8a20-1c2ae3239f02"为服务uuid

然后创建和注册该服务:

create_services()->service_path = register_service(IAS_UUID)->g_dbus_register_interface(connection, path, GATT_SERVICE_IFACE,
                                                                                                                                                                         NULL, NULL, service_properties,
                                                                                                                                                                         g_strdup(uuid), g_free)

为该服务注册两个特征值:

/* Add Alert Level Characteristic to Immediate Alert Service */
    if (!register_characteristic(ALERT_LEVEL_CHR_UUID,
                        &level, sizeof(level),
                        ias_alert_level_props,
                        READ_WRITE_DESCRIPTOR_UUID,
                        desc_props,
                        service_path)) {
        printf("Couldn't register Alert Level characteristic (IAS)\n");
        g_dbus_unregister_interface(connection, service_path,
                            GATT_SERVICE_IFACE);
        g_free(service_path);
        return;
    }

    /* Add update Characteristic to return data */
    if (!register_characteristic(UPDATE_CHR_UUID,
                        &level, sizeof(level),
                        update_props,
                        READ_WRITE_DESCRIPTOR_UUID,
                        desc_props,
                        service_path)) {
        printf("Couldn't register Alert Level characteristic (IAS)\n");
        g_dbus_unregister_interface(connection, service_path,
                            GATT_SERVICE_IFACE);
        g_free(service_path);
        return;
    }

其中ALERT_LEVEL_CHR_UUID,UPDATE_CHR_UUID为特征值uuid,READ_WRITE_DESCRIPTOR_UUID,READ_WRITE_DESCRIPTOR_UUID为定义的描述符uuid

在这里面定义了特征值的属性和描述符的属性

static const char *ias_alert_level_props[] = { "read","write","write-without-response","notify", NULL };

static const char *update_props[] = { "read","write","write-without-response","notify", NULL };
static const char *desc_props[] = { "read", "write", NULL };

2、Characteristic hierarchy

Service        org.bluez

Interface    org.bluez.GattCharacteristic1

Object path    [variable prefix]/{hci0,hci1,...}/dev_XX_XX_XX_XX_XX_XX/serviceXX/charYYYY
Methods        array{byte} ReadValue(dict options)
            Issues a request to read the value of the
            characteristic and returns the value if the
            operation was successful.
            Possible options: "offset": uint16 offset "device": Object Device (Server only)
            Possible Errors: org.bluez.Error.Failed org.bluez.Error.InProgress
                     org.bluez.Error.NotPermitted
                     org.bluez.Error.NotAuthorized
                     org.bluez.Error.NotSupported
        void WriteValue(array{byte} value, dict options)
            Issues a request to write the value of the
            characteristic.
            Possible options: "offset": Start offset "device": Device path (Server only)
            Possible Errors: org.bluez.Error.Failed org.bluez.Error.InProgress
                     org.bluez.Error.NotPermitted
                     org.bluez.Error.InvalidValueLength
                     org.bluez.Error.NotAuthorized
                     org.bluez.Error.NotSupported
        void StartNotify()
            Starts a notification session from this characteristic
            if it supports value notifications or indications.
            Possible Errors: org.bluez.Error.Failed org.bluez.Error.InProgress
                     org.bluez.Error.NotSupported
        void StopNotify()
            This method will cancel any previous StartNotify
            transaction. Note that notifications from a
            characteristic are shared between sessions thus
            calling StopNotify will release a single session.
            Possible Errors: org.bluez.Error.Failed
Properties    string UUID [read-only]
            128-bit characteristic UUID.
        object Service [read-only]
            Object path of the GATT service the characteristic
            belongs to.
        array{byte} Value [read-only, optional]
            The cached value of the characteristic. This property
            gets updated only after a successful read request and
            when a notification or indication is received, upon
            which a PropertiesChanged signal will be emitted.
        boolean Notifying [read-only, optional]
            True, if notifications or indications on this
            characteristic are currently enabled.
        array{string} Flags [read-only]
            Defines how the characteristic value can be used. See
            Core spec "Table 3.5: Characteristic Properties bit
            field", and "Table 3.8: Characteristic Extended
            Properties bit field". Allowed values:
                "broadcast"
                "read"
                "write-without-response"
                "write"
                "notify"
                "indicate"
                "authenticated-signed-writes"
                "reliable-write"
                "writable-auxiliaries"
                "encrypt-read"
                "encrypt-write"
                "encrypt-authenticated-read"
                "encrypt-authenticated-write"
                "secure-read" (Server only)
                "secure-write" (Server only)

从上面一段说明可知Characteristic特征值dbus总线接口是org.bluez.GattCharacteristic1,对应的操作函数有ReadValue,WriteValue,StartNotify(),StopNotify()

属性中包括:

string UUID [read-only]

object Service [read-only]

array{byte} Value [read-only, optional]

boolean Notifying [read-only, optional]

array{string} Flags [read-only]

对特征值可以进行以下几种操作:

                "broadcast" "read"
                "write-without-response"
                "write"
                "notify"
                "indicate"
                "authenticated-signed-writes"
                "reliable-write"
                "writable-auxiliaries"
                "encrypt-read"
                "encrypt-write"
                "encrypt-authenticated-read"
                "encrypt-authenticated-write"
                "secure-read" (Server only)
                "secure-write" (Server only)


然后我们看一下register_characteristic()函数:

static gboolean register_characteristic(const char *chr_uuid,
                        const uint8_t *value, int vlen,
                        const char **props,
                        const char *desc_uuid,
                        const char **desc_props,
                        const char *service_path)
{
    struct characteristic *chr;
    struct descriptor *desc;
    static int id = 1;

    chr = g_new0(struct characteristic, 1);
    chr->uuid = g_strdup(chr_uuid);
    chr->value = g_memdup(value, vlen);
    chr->vlen = vlen;
    chr->props = props;
    chr->service = g_strdup(service_path);
    chr->path = g_strdup_printf("%s/characteristic%d", service_path, id++);

    if (!g_dbus_register_interface(connection, chr->path, GATT_CHR_IFACE,
                    chr_methods, NULL, chr_properties,
                    chr, chr_iface_destroy)) {
        printf("Couldn't register characteristic interface\n");
        chr_iface_destroy(chr);
        return FALSE;
    }

    if (!desc_uuid)
        return TRUE;

    desc = g_new0(struct descriptor, 1);
    desc->uuid = g_strdup(desc_uuid);
    desc->chr = chr;
    desc->props = desc_props;
    desc->path = g_strdup_printf("%s/descriptor%d", chr->path, id++);

    if (!g_dbus_register_interface(connection, desc->path,
                    GATT_DESCRIPTOR_IFACE,
                    desc_methods, NULL, desc_properties,
                    desc, desc_iface_destroy)) {
        printf("Couldn't register descriptor interface\n");
        g_dbus_unregister_interface(connection, chr->path,
                            GATT_CHR_IFACE);

        desc_iface_destroy(desc);
        return FALSE;
    }
    
    if (strcmp(chr_uuid,UPDATE_CHR_UUID) == 0)
    {
        chr_update = chr;
        printf("chr_update is OK");
    }

    return TRUE;
}

该段代码首先填充了struct characteristic *chr,然后调用g_dbus_register_interface方法将该特征值注册到GATT_CHR_IFACE,/ "org.bluez.GattCharacteristic1"接口。

参数chr_methods定义了可以对特征值的所做的操作,分别是读操作、写操作、开始通知操作和停止通知操作:

static const GDBusMethodTable chr_methods[] = {
    { GDBUS_ASYNC_METHOD("ReadValue", GDBUS_ARGS({ "options", "a{sv}" }),
                    GDBUS_ARGS({ "value", "ay" }),
                    chr_read_value) },
    { GDBUS_ASYNC_METHOD("WriteValue", GDBUS_ARGS({ "value", "ay" },
                        { "options", "a{sv}" }),
                    NULL, chr_write_value) },
    { GDBUS_ASYNC_METHOD("StartNotify", NULL, NULL, chr_start_notify) },
    { GDBUS_METHOD("StopNotify", NULL, NULL, chr_stop_notify) },
    { }
};

参数chr_properties表示该特征值所具备的属性:

static const GDBusPropertyTable chr_properties[] = {
    { "UUID",    "s", chr_get_uuid },
    { "Service",    "o", chr_get_service },
    { "Value",    "ay", chr_get_value, chr_set_value, NULL },
    { "Flags",    "as", chr_get_props, NULL, NULL },
    { }
};

描述符注册过程跟特征值的注册过程类似,大家可以自己看一下。

做完这些操作以后就可以对注册的特征值进行操作了,具体操作方法就是注册特征值时候注册的方法这里是chr_read_value,chr_write_value,chr_start_notify,chr_stop_notify。

描述符的操作方法跟特征值类似。

这里需要注意的是,对特征值的读操作,写操作是针对特征值的,不区分客户端还是服务端。也就是说服务端,客户端对特征值的读写都会调用chr_read_value,chr_write_value方法,服务端可以调用chr_write/chr_read方法对自己定义的特征值进行读写操作。当特征值定义为通知属性时,服务端如果对该特征值写入数据,就会自动发出通知消息。如果客户端注册了该特征值通知,则会接收到该通知消息。



本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

BlueZ5.45 D-Bus总线 GATT API 分析 的相关文章

  • 如何从 C 文件更改终端中的目录

    如何从 C 程序更改将在终端上生效的目录 实际上不要告诉 system 函数或 chdir 函数 这些仅适用于 C 中的进程或子 shell 假设我正在从 bash shell 执行一个 C 程序 其进程 ID 为 10223 那么 我可以
  • 从 gitlab docker runner 启动声纳扫描仪

    我有一个 CI 工作流程 集成了 linting 作业和代码质量作业 我的 Linting 工作是一个 docker runner 从应用程序代码启动我的 eslint 脚本 然后我的代码质量工作应该启动声纳扫描仪泊坞窗实例 检查我的代码并
  • 使用 linux perf 工具测量应用程序的 FLOP

    我想使用 perf Linux 性能计数器子系统的新命令行接口命令 来测量某些应用程序执行的浮点和算术运算的数量 出于测试目的 我使用了我创建的一个简单的虚拟应用程序 请参见下文 因为我找不到任何为测量 FP 和整数运算而定义的 perf
  • 操作系统崩溃的常见原因[关闭]

    Closed 这个问题需要多问focused help closed questions 目前不接受答案 我有兴趣了解 操作系统崩溃 不限于Windows崩溃 最常见的技术原因 从操作系统编程的角度 有哪些 我正在寻找一个不像 打开太多应用
  • 如何使用 tmuxinator 在 tmux 中拆分水平窗格内的两个垂直窗格

    目前我的 tmuxinator yml 文件中有这个 windows editor layout main horizontal panes vim server rails s 这给了我两个窗口 一个用于编辑器 另一个用于服务器 在编辑器
  • 在单个命令中使用前缀重命名文件夹中的所有文件

    重命名带有前缀的文件夹中的所有文件 Unix 假设一个文件夹有两个文件 a txt b pdf 那么它们都应该从一个命令重命名为 Unix a txt Unix b pdf 如果您的文件名包含没有空格并且你没有任何子目录 你可以使用一个简单
  • 在64位操作系统上以32位模式和64位模式编译ioctl函数的执行有什么不同?

    我有 64 位 Enterprise SuSE 11 我有一个应用程序 它打开 HIDRAW 设备并在其上操作 ioctl 函数以从该设备获取原始信息 如下所示 struct hidraw devinfo devinfo int fd op
  • 在键盘热插拔上加载模块

    我正在尝试学习如何为 Linux 系统编写模块和驱动程序 类似于this https unix stackexchange com questions 120839 usb kernel module does not load on de
  • 如何删除树莓派的相机预览

    我在我的 raspberryPi 上安装了 SimpleCv 并安装了用于使用相机板的驱动程序 uv4l 驱动程序 现在我想使用它 当我在 simpleCV shell Camera 0 getImage save foo jpg 上键入时
  • 为什么使用Python的os模块方法而不是直接执行shell命令?

    我试图了解使用Python的库函数执行特定于操作系统的任务 例如创建文件 目录 更改文件属性等 背后的动机是什么 而不是仅仅通过执行这些命令os system or subprocess call 例如 我为什么要使用os chmod而不是
  • 无法为 Python 3.4 创建工作虚拟环境

    I 安装Python 3 4 2 https docs python org 3 using unix html building python和我的 Linux Mint 17 1 中的 Virtualenv 12 0 5 然后我尝试创建
  • ARM 系统调用的接口是什么?它在 Linux 内核中的何处定义?

    我读过有关 Linux 中的系统调用的内容 并且到处都给出了有关 x86 架构的描述 0x80中断和SYSENTER 但我无法追踪 ARM 架构中系统调用的文件和进程 任何人都可以帮忙吗 我知道的几个相关文件是 arch arm kerne
  • /proc/PID 文件格式

    我想从中检索一些流程信息 proc目录 我的问题如下 中的文件是否有标准格式 proc PID 例如 有这个proc PID status文件与Name t ProcName在第一行 我可以在其他地方用空格代替这个文件吗 t或者类似的东西
  • 是否有可能通过 mmap 匿名内存“打孔”?

    考虑一个使用大量大致页面大小的内存区域 例如 64 kB 左右 的程序 每个内存区域的寿命都相当短暂 在我的特定情况下 这些是绿色线程的替代堆栈 如何最好地分配这些区域 以便一旦该区域不再使用 它 们的页面可以返回到内核 天真的解决方案显然
  • Xenomai 中的周期性线程实时失败

    我正在创建一个周期性线程 它在模拟输出上输出方波信号 我正在使用 Xenomai API 中的 Posix Skin 和 Analogy 我使用示波器测试了代码的实时性能 并查看了方波信号 频率为 1kHz 的延迟 我应该实现 250us
  • Windows 与 Linux 文本文件读取

    问题是 我最近从 Windows 切换到 Ubuntu 我的一些用于分析数据文件的 python 脚本给了我错误 我不确定如何正确解决 我当前仪器的数据文件输出如下 Header 有关仪器等的各种信息 Data 状态 代码 温度 字段等 0
  • 使用命令行将 MediaWiki 维基文本格式转换为 HTML

    我倾向于编写大量文档 因此 MediaWiki 格式对我来说很容易理解 而且比编写传统 HTML 节省了我很多时间 然而 我也写了一篇博客 发现一直从键盘切换到鼠标来输入正确的 HTML 标签会增加很多时间 我希望能够使用 Mediawik
  • Linux 中的电源管理通知

    在基于 Linux 的系统中 我们可以使用哪些方法 最简单的方法 来获取电源状态更改的通知 例如 当计算机进入睡眠 休眠状态等时 我需要这个主要是为了在睡眠前保留某些状态 当然 在计算机唤醒后恢复该状态 您只需配置即可获得所有这些事件acp
  • R 未获取用户库

    我有一个带 R 3 6 0 的 Fedora 30 系统 用户库设置在Renviron就像这个 R LIBS USER R LIBS USER R x86 64 redhat linux gnu library 3 6 事实上 它出现在交互
  • 如何通过代理将套接字连接到http服务器?

    最近 我使用 C 语言编写了一个程序 用于连接到本地运行的 HTTP 服务器 从而向该服务器发出请求 这对我来说效果很好 之后 我尝试使用相同的代码连接到网络上的另一台服务器 例如 www google com 但我无法连接并从网络中的代理

随机推荐

  • 浏览器的默认样式及去除

    目录 前言 一 reset 二 normalize 总结 前言 通常情况 浏览器都会为元素设置一些默认样式 默认样式的存在会影响到页面的布局 通常情况下编写网页时必须要去除浏览器的默认样式 PC端的页面 一 reset reset css
  • 【MATLAB第68期】基于MATLAB的LSTM长短期记忆网络多变量时间序列数据多步预测含预测未来(非单步预测)

    MATLAB第68期 基于MATLAB的LSTM长短期记忆网络多变量时间序列数据多步预测含预测未来 非单步预测 输入前25个时间 输出后5个时间 一 数据转换 1 原始数据 5列时间序列数据 70行样本 70 5 数据矩阵结构 2 数据转换
  • web前端一定会用到的三种技术,小白必看!

    Web前端是目前高薪岗位之一 是从最开始的页面开发不断发展优化而产生的一个特定的岗位 现在web前端开发一定会用到的三门技术 HTML CSS JavaScript 无论是多么炫酷的页面都是这三门技术的组合而形成的 网页开发离不开的三要素
  • Cisco 启动帧模式MPLS的命令

    两步骤 1 配置CEF 2 在接口上配置MPLS 在接口上开启标签交换 开启分发协议 LDP 或者 TDP 可选步骤 配置MTU大小 一 配置CEF 非常简单 在配置模式下输入ip cef distributed Router config
  • 前缀树算法模板秒杀 5 道算法题

    后台回复进群一起刷力扣 点击卡片可搜索关键词 读完本文 可以去力扣解决如下题目 208 实现 Trie 前缀树 Medium 1804 实现 Trie 前缀树 II Medium 648 单词替换 Medium 211 添加与搜索单词 Me
  • AttributeError: module 'random' has no attribute 'rand

    问题 在跟着 机器学习实战 这本书练习的时候 遇到AttributeError module random has no attribute rand的问题 1 出现如下的报错 2 原因 后来发现当时为了方便能够知道自己 py文件是主要练习
  • 【华为OD机试】数字序列比大小【2023 B卷

    华为OD机试 真题 点这里 华为OD机试 真题考点分类 点这里 题目描述 A B两个人玩一个数字比大小的游戏 在游戏前 两个人会拿到相同长度的两个数字序列 两个数字序列不相同的 且其中的数字是随机的 A B各自从数字序列中挑选出一个数字进行
  • 进军微信第一步:接入微信JS-SDK

    前言 某天 接到这么一个需求 自定义微信网页分享出来的标题 描述和图标 以前没玩过这个 感觉应该很简单 动手了之后 躺过各种坑才知道并没那么容易 完全独立研究排错 感受颇多 分享出来给大家铺一铺路 一 需求来源 开发了一个移动端H5活动页面
  • LaTeX技巧189:LaTeX括号总结

    begin longtable l 1 你好啊 2 吃了没 end longtable 功能 语法 显示 不好看 frac 1 2 好一点 left frac 1 2 right 您可以使用 left和 right来显示不同的括号 功能 语
  • 在WPF中获取程序的专用工作集内存 PerformanceCounter

    使用 PerformanceCounter 获取程序的专用工作集内存并不难 但是就是得找一下属性 通过 CategoryName 遍历 InsanceName 再通过它们遍历 CounterName 之后通过这三个属性得到我们想要的内存 p
  • 数字电路设计之Leon系列处理器结构

    LEON处理器核心是一个与SPARCV8兼容的整数处理单元IU Integer Unit LEON2 是 5 级流水线 LEON3 是 7 级流水线 LEON 包含整数硬件乘法和除法单元 双协处理器 接口 FPU 浮点处理单元和Co pro
  • Python Web:绝对路径和相对路径

    Python Web篇学习汇总 Python Web 操作系统与虚拟机软件 Python Web 了解Ubuntu操作系统 Python Web Linux查看 切换目录命令 努力为大家更新Python web部分的内容 想看持续更新的记得
  • 电脑中常用的“扇区”、“簇”、“块”、“页”等概念

    先看百度百科对于磁盘簇的解释 扇区是磁盘最小的物理存储单元 但由于操作系统无法对数目众多的扇区进行寻址 所以操作系统就将相邻的扇区组合在一起 形成一个簇 然后再对簇进行管理 每个簇可以包括2 4 8 16 32或64个扇区 显然 簇是操作系
  • svg -> text文本水平、垂直居中。文本垂直对齐方式

  • Systemd 入门教程:实战篇

    本文转载至 http www ruanyifeng com blog 2016 03 systemd tutorial part two html 作者 阮一峰 日期 2016年3月 8日 上一篇文章 我介绍了 Systemd 的主要命令
  • 企业知识分享系统的设计与实现

    摘 要 随着信息技术和网络技术的飞速发展 人类已进入全新信息化时代 传统管理技术已无法高效 便捷地管理信息 为了迎合时代需求 优化管理效率 各种各样的管理系统应运而生 各行各业相继进入信息管理时代 企业知识分享系统就是信息时代变革中的产物之
  • 基于AI的4G/5G基站节能的智能解决方案

    随着移动通信网络建设规模逐年增加 通信设备对能源的需求与日俱增 移动通信网络的能耗在运营商的运营成本 OPEX Operating Expense 占比已高于15 经过5G试商用网络的测试验证 5G单站功耗是4G单站功耗的3 4倍 运营商面
  • 提示OpenGL版本过低怎么办

    OpenGL是一个可以加速2D和3D图形的图形库 在计算机显示技术中广泛使用 常用于游戏制作 建筑设计 医疗成像 科学数据可视化等领域 然而 当你尝试运行使用OpenGL的软件或游戏时 你可能会收到一个消息 OpenGL版本过低 请升级驱动
  • 易语言升级版火山软件开发平台现在很庞大了

    中文编程的魅力很吸引人 易语言时代就经常用它编编小程序 易语言最后是输出成vc6编译出来的效果一样 小而精 vc6毕竟是比较古老的技术 现在升级版火山软件开发平台已经能够比较耐用了 一个ide可以开发安卓和windows这2种应用 wind
  • BlueZ5.45 D-Bus总线 GATT API 分析

    笔者目前做linux系统下bluez蓝牙项目开发 发现网上关于bluez开发的资料很少 对于刚开始接触bluez蓝牙的开发人员来说是非常痛苦的 通过调试bluez源码自带的应用例子和文档说明 对BlueZ5 45 D Bus总线 GATT