Monday, December 27, 2010

GATT related dbus API

After trying gatttool, I am trying how to use this new attribute dbus api in bluez. I find there is an "attrib/example.c" and it would create some service records to demo GATT. When we create a device to bluez, it would detect the device type. If it's a LE device, it would run to "attrib_client_register", then register Primary and Characteristics services. We can use d-feet to see these interfaces. Also, bluez will write some data files to "/var/lib/bluetooth/". When we restart bluez, it can create these interfaces without discovering the remote device again.


Primary Service
We can know what kind of services this remote BT device has from 'UUIDs' value. For example, "0x1801" is GENERIC_ATTRIB_SVCLASS_ID and "0x1800" is GENERIC_ACCESS_PROFILE_ID. 'Services' value is the object path of all primary services.

erin@sundays:~$ dbus-send --system --type=method_call --print-reply --dest=org.bluez /org/bluez/5444/hci0/dev_78_DD_08_A3_A7_52 org.bluez.Device.GetProperties


method return sender=:1.93 -> dest=:1.96 reply_serial=2
array [
dict entry(
string "Address"
variant string "78:DD:08:A3:A7:52"
)
dict entry(
string "Name"
variant string ""
)
dict entry(
string "Alias"
variant string "78-DD-08-A3-A7-52"
)
dict entry(
string "Paired"
variant boolean true
)
dict entry(
string "Trusted"
variant boolean false
)
dict entry(
string "Blocked"
variant boolean false
)
dict entry(
string "Connected"
variant boolean false
)
dict entry(
string "UUIDs"
variant array [
string "00001105-0000-1000-8000-00805f9b34fb"
string "00001106-0000-1000-8000-00805f9b34fb"
string "0000110a-0000-1000-8000-00805f9b34fb"
string "0000110c-0000-1000-8000-00805f9b34fb"
string "0000110e-0000-1000-8000-00805f9b34fb"
string "00001112-0000-1000-8000-00805f9b34fb"
string "0000111f-0000-1000-8000-00805f9b34fb"
string "00001800-0000-1000-8000-00805f9b34fb"
string "00001801-0000-1000-8000-00805f9b34fb"
string "0000a002-0000-1000-8000-00805f9b34fb"
string "0000a004-0000-1000-8000-00805f9b34fb"
string "4f0ac096-35d4-4911-9631-dea8dc74eefe"
]
)
dict entry(
string "Services"
variant array [
object path "/org/bluez/5444/hci0/dev_78_DD_08_A3_A7_52/service0001"
object path "/org/bluez/5444/hci0/dev_78_DD_08_A3_A7_52/service0010"
object path "/org/bluez/5444/hci0/dev_78_DD_08_A3_A7_52/service0100"
object path "/org/bluez/5444/hci0/dev_78_DD_08_A3_A7_52/service0200"
object path "/org/bluez/5444/hci0/dev_78_DD_08_A3_A7_52/service0680"
]
)
dict entry(
string "Adapter"
variant object path "/org/bluez/5444/hci0"
)
]


Characteristics Service
We can also discover what kind of characteristics service this BT device has. It would like use gatttool to run "--characteristics" and it would register related interfaces like below:

erin@sundays:~$ dbus-send --system --type=method_call --print-reply --dest=org.bluez /org/bluez/5612/hci0/dev_78_DD_08_A3_A7_52/service0200 org.bluez.Characteristic.Discover
method return sender=:1.107 -> dest=:1.112 reply_serial=2


erin@sundays:~$ dbus-send --system --type=method_call --print-reply --dest=org.bluez /org/bluez/5612/hci0/dev_78_DD_08_A3_A7_52/service0200 org.bluez.Characteristic.GetProperties


method return sender=:1.107 -> dest=:1.117 reply_serial=2
array [
dict entry(
string "Characteristics"
variant array [
object path "/org/bluez/5612/hci0/dev_78_DD_08_A3_A7_52/service0200/characteristic0204"
object path "/org/bluez/5612/hci0/dev_78_DD_08_A3_A7_52/service0200/characteristic0212"
]
)
]

Attribute Value - READ
From example.c, we know "0xA006" is TEMPERATURE_UUID. We can call below dbus method to retrieve its properties. This "Value" property is an array of bytes and it could have format, value and representation to describe the exact value meaning.


erin@sundays:~$ dbus-send --system --type=method_call --print-reply --dest=org.bluez /org/bluez/5612/hci0/dev_78_DD_08_A3_A7_52/service0200/characteristic0204 org.bluez.Characteristic.GetProperties


method return sender=:1.107 -> dest=:1.118 reply_serial=2
array [
dict entry(
string "UUID"
variant string "0000a006-0000-1000-8000-00805f9b34fb"
)
dict entry(
string "Name"
variant string ""
)
dict entry(
string "Description"
variant string "Outside Temperature"
)
dict entry(
string "Value"
variant array of bytes [
8a 02
]
)
]


Bluez Data Files
GATT related files in /var/lib/bluetooth

erin@sundays:~$ cat /var/lib/bluetooth/00\:1B\:DC\:02\:86\:C5/primary
78:DD:08:A3:A7:52 0001#0006#00001800-0000-1000-8000-00805f9b34fb 0010#0012#00001801-0000-1000-8000-00805f9b34fb 0100#0111#0000a002-0000-1000-8000-00805f9b34fb 0200#0214#0000a004-0000-1000-8000-00805f9b34fb 0680#0685#4f0ac096-35d4-4911-9631-dea8dc74eefe

erin@sundays:~$ cat /var/lib/bluetooth/00\:1B\:DC\:02\:86\:C5/characteristic
78:DD:08:A3:A7:52#0001 0006#02#0006#00002a00-0000-1000-8000-00805f9b34fb 0006#02#0006#00002a00-0000-1000-8000-00805f9b34fb
78:DD:08:A3:A7:52#0010 0012#02#0012#0000a001-0000-1000-8000-00805f9b34fb
78:DD:08:A3:A7:52#0200 0204#02#0210#0000a006-0000-1000-8000-00805f9b34fb 0212#02#0214#0000a009-0000-1000-8000-00805f9b34fb

erin@sundays:~$ cat /var/lib/bluetooth/00\:1B\:DC\:02\:86\:C5/attributes
78:DD:08:A3:A7:52#0205 00002904-0000-1000-8000-00805f9b34fb#0EFE07A00108A0
78:DD:08:A3:A7:52#0206 00002901-0000-1000-8000-00805f9b34fb#4F7574736964652054656D706572617475726500
78:DD:08:A3:A7:52#0213 00002904-0000-1000-8000-00805f9b34fb#04000AA00BA008
78:DD:08:A3:A7:52#0214 00002901-0000-1000-8000-00805f9b34fb#4F7574736964652052656C61746976652048756D696400

erin@sundays:~$ cat /var/lib/bluetooth/00\:1B\:DC\:02\:86\:C5/profiles
78:DD:08:A3:A7:52 00001105-0000-1000-8000-00805f9b34fb 00001106-0000-1000-8000-00805f9b34fb 0000110a-0000-1000-8000-00805f9b34fb 0000110c-0000-1000-8000-00805f9b34fb 0000110e-0000-1000-8000-00805f9b34fb 00001112-0000-1000-8000-00805f9b34fb 0000111f-0000-1000-8000-00805f9b34fb 00001801-0000-1000-8000-00805f9b34fb


Attribute Value - WRITE
How to change a characteristic value? I really don't know how to send a value like "variant array of bytes [8a 02]". Then, I use ipython to send this dbus method, then I can rewrite its value.


In [1]: import dbus
In [2]: bus_name="org.bluez"
In [3]: obj_path="/org/bluez/5612/hci0/dev_78_DD_08_A3_A7_52/service0200/characteristic0204"
In [4]: bus = dbus.SystemBus()
In [5]: proxy = bus.get_object(bus_name,obj_path)
In [6]: char = dbus.Interface(proxy, dbus_interface="org.bluez.Characteristic")
In [7]: properties = char.GetProperties()
In [8]: print properties
dbus.Dictionary({dbus.String(u'Description'): dbus.String(u'Outside Temperature', variant_level=1), dbus.String(u'UUID'): dbus.String(u'0000a006-0000-1000-8000-00805f9b34fb', variant_level=1), dbus.String(u'Value'): dbus.Array([dbus.Byte(128), dbus.Byte(110)], signature=dbus.Signature('y'), variant_level=1), dbus.String(u'Name'): dbus.String(u'', variant_level=1)}, signature=dbus.Signature('sv'))
In [9]: result = char.SetProperty("Value", dbus.Array([dbus.Byte(77), dbus.Byte(22)]))
In [10]: properties = char.GetProperties()
In [11]: print properties
dbus.Dictionary({dbus.String(u'Description'): dbus.String(u'Outside Temperature', variant_level=1), dbus.String(u'UUID'): dbus.String(u'0000a006-0000-1000-8000-00805f9b34fb', variant_level=1), dbus.String(u'Value'): dbus.Array([dbus.Byte(77), dbus.Byte(22)], signature=dbus.Signature('y'), variant_level=1), dbus.String(u'Name'): dbus.String(u'', variant_level=1)}, signature=dbus.Signature('sv'))

Tuesday, December 21, 2010

gatttool in bluez over BR/EDR

Recently, I am studying Bluetooth 4.0 (Low Energy) core spec and checking the latest bluez code about LE stuff. Here is my working notes and how I use 'gatttool' to verify Generic Attribute Profile (GATT) features. We can check this feature over BR/EDR first, not use real LE device yet. My working machine is Ubuntu 10.10.

Set up Bluez part

retrieve latest bluez code, configure it with enable-attrib, and compile it
# git clone git://git.kernel.org/pub/scm/bluetooth/bluez.git
# cd bluez
# autoreconf -vifs
# ./configure --prefix=/usr --mandir=/usr/share/man \
--sysconfdir=/etc --localstatedir=/var --libexecdir=/lib --enable-attrib
# make && sudo make install

modify main.conf, enable LE and attribute-server

diff --git a/src/main.conf b/src/main.conf
index c03f135..121ee9b 100644
--- a/src/main.conf
+++ b/src/main.conf
@@ -58,9 +58,9 @@ DebugKeys = false

# Enable Low Energy support if the dongle supports. Default is false.
# Enable/Disable interleave discovery and attribute server over LE.
-EnableLE = false
+EnableLE = true

# Enable the GATT Attribute Server. Default is false, because it is only
# useful for testing. Attribute server is not enabled over LE if EnableLE
# is false.
-AttributeServer = false
+AttributeServer = true

start bluetooth daemon in foreground with debug information

# sudo /usr/sbin/bluetoothd -n -d

Play with gatttool

Since we configured Bluez with enable-attrib, we will start an attribute-server from running bluetooth daemon. It would add some attribute records for GATT service, and it's created from attrib/example.c file. Also, we will add a GAP service like below.


erin@sundays:~/project/bluez$ sdptool browse local
Browsing FF:FF:FF:00:00:00 ...
Service Name: Generic Attribute Profile
Service Provider: BlueZ
Service RecHandle: 0x10000
Service Class ID List:
"Generic Attribute" (0x1801)
Protocol Descriptor List:
"L2CAP" (0x0100)
PSM: 31
"ATT" (0x1801)
uint16: 0x1
uint16: 0xffff
Profile Descriptor List:
"Generic Attribute" (0x1801)
Version: 0x0100

Below GATT features are from BT 4.0 core spec Volume 3 Part G.

Primary Service Discovery
This procedure is used by a client to discover primary services on a server. Once the primary services are discovered, additional information about the primary services can be accessed using other procedures, including characteristic discovery and relationship discovery to find other related primary and secondary services.

a. Discover all primary services ==> "Read By Group Type Request"
b. Discover primary service by service UUID ==> "Find By Type Value Request"



gatt_discover_primary(attrib, opt_start, opt_end, opt_uuid, primary_by_uuid_cb, attrib);

erin@sundays:~$ sudo gatttool -b 78:DD:08:A3:A7:52 --primary --psm=31 --mtu=48
attr handle = 0x0001, end grp handle = 0x0006, attr value (UUID) = 1800
attr handle = 0x0010, end grp handle = 0x0012, attr value (UUID) = 1801
attr handle = 0x0100, end grp handle = 0x0111, attr value (UUID) = a002
attr handle = 0x0200, end grp handle = 0x0214, attr value (UUID) = a004
attr handle = 0x0680, end grp handle = 0x0685, attr value (UUID) = 4f0ac096-35d4-4911-9631-dea8dc74eefe

erin@autumn:~/project/bluez$ gatttool -b 78:DD:08:A3:A7:52 --primary --uuid=1801
Starting handle: 0010 Ending handle: 0012

erin@autumn:~/project/bluez$ gatttool -b 78:DD:08:A3:A7:52 --primary --uuid=1800
Starting handle: 0001 Ending handle: 0006


Characteristic Discovery
This procedure is used by a client to discover service characteristics on a server. Once the characteristics are discovered additional information about the characteristics can be discovered or accessed using other procedures.

a. Discover all characteristics of a Service ==> "Read By Type Request"
b. Discover characteristics by UUID ==> "Read By Type Request"



gatt_discover_char(attrib, opt_start, opt_end, char_discovered_cb, char_data);
gatt_read_char_by_uuid(attrib, start, end, &uuid, func, user_data);

erin@sundays:~$ sudo gatttool -b 78:DD:08:A3:A7:52 --characteristics --psm=31 --mtu=48
handle = 0x0004, char properties = 0x02, char value handle = 0x0006, uuid = 2a00
handle = 0x0011, char properties = 0x02, char value handle = 0x0012, uuid = a001
handle = 0x0106, char properties = 0x02, char value handle = 0x0110, uuid = a003
handle = 0x0203, char properties = 0x02, char value handle = 0x0204, uuid = a006
handle = 0x0210, char properties = 0x02, char value handle = 0x0212, uuid = a009
handle = 0x0501, char properties = 0x02, char value handle = 0x0502, uuid = a00c
handle = 0x0503, char properties = 0x02, char value handle = 0x0504, uuid = a00d
handle = 0x0506, char properties = 0x02, char value handle = 0x0507, uuid = a00c
handle = 0x0508, char properties = 0x02, char value handle = 0x0509, uuid = a00d
handle = 0x0560, char properties = 0x02, char value handle = 0x0568, uuid = a00f
handle = 0x0682, char properties = 0x02, char value handle = 0x0683, uuid = 8088f218-902c-450b-b6c4-62891e8c25e9

erin@autumn:~/project/bluez$ gatttool -b 78:DD:08:A3:A7:52 --characteristics --start=0x0001 --end=0x0100
handle = 0x0004, char properties = 0x02, char value handle = 0x0006, uuid = 2a00
handle = 0x0011, char properties = 0x02, char value handle = 0x0012, uuid = a001


Characteristic Descriptor Discovery
This procedure is used by a client to discover characteristic descriptors of a characteristic. Once the characteristic descriptors are discovered additional information about the characteristic descriptors can be accessed using other procedures.

a. Discover All Characteristic Descriptors ==> "Find Information Request"



gatt_find_info(attrib, opt_start, opt_end, char_desc_cb, NULL);
erin@sundays:~$ sudo gatttool -b 78:DD:08:A3:A7:52 --char-desc --psm=31
handle = 0x0001, uuid = 2800
handle = 0x0004, uuid = 2803
handle = 0x0006, uuid = 2a00
handle = 0x0010, uuid = 2800
handle = 0x0011, uuid = 2803

erin@autumn:~/project/bluez$ gatttool -b 78:DD:08:A3:A7:52 --char-desc --start=0x0001 --end=0x0010
handle = 0x0001, uuid = 2800
handle = 0x0004, uuid = 2803
handle = 0x0006, uuid = 2a00
handle = 0x0010, uuid = 2800
erin@autumn:~/project/bluez$ gatttool -b 78:DD:08:A3:A7:52 --char-desc --start=0x0001 --end=0x0005
handle = 0x0001, uuid = 2800
handle = 0x0004, uuid = 2803


Characteristic Value Read
This procedure is used to read a Characteristic Value from a server.

a. Read characteristic value
b. Read using characteristic UUID
c. Read long characteristic values (not implement)
d. Read multiple characteristic values (not implement)



gatt_read_char(attrib, opt_handle, char_read_cb, attrib);
==> g_attrib_send(attrib, ATT_OP_READ_REQ, pdu, plen, func, user_data, NULL);

gatt_read_char_by_uuid(attrib, opt_start, opt_end, opt_uuid, char_read_by_uuid_cb, char_data);
==> g_attrib_send(attrib, ATT_OP_READ_BY_TYPE_REQ, pdu, plen, func, user_data, NULL);

erin@sundays:~$ sudo gatttool -b 78:DD:08:A3:A7:52 --char-read --uuid=2800
handle: 0x0001 value: 00 18
handle: 0x0010 value: 01 18
handle: 0x0100 value: 02 a0
handle: 0x0200 value: 04 a0
handle: 0x0680 value: 4f 0a c0 96 35 d4 49 11 96 31 de a8 dc 74 ee fe

erin@sundays:~$ sudo gatttool -b 78:DD:08:A3:A7:52 --char-read --handle=0x0001
Characteristic value/descriptor: 00 18

erin@autumn:~/project/bluez$ gatttool -b 78:DD:08:A3:A7:52 --char-read --uuid=a00d
handle: 0x0504 value: 32 33 37 34 39 35 2d 33 32 38 32 2d 41
handle: 0x0509 value: 31 31 32 36 37 2d 32 33 32 37 41 30 30 32 33 39


Characteristic value write
This procedure is used to write a Characteristic Value to a server.

a. Write without response
b. Signed write without response (not check yet)
c. Write characteristic value
d. Write long charcharacteristic values (not check yet)
e. Reliable writes (not check yet)



erin@sundays:~$ gatttool -b 78:DD:08:A3:A7:52 --char-write --psm=31 --mtu=48 --handle=0x0001 --value=0x0011
erin@sundays:~$ gatttool -b 78:DD:08:A3:A7:52 --char-read --psm=31 --mtu=48 --handle=0x0001
Characteristic value/descriptor: 00 00 11