笔者目前做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方法对自己定义的特征值进行读写操作。当特征值定义为通知属性时,服务端如果对该特征值写入数据,就会自动发出通知消息。如果客户端注册了该特征值通知,则会接收到该通知消息。