Tuesday, August 13, 2013

Experiments with Bluetooth Low Energy (4.0) under Linux

With iPhônes liberated from the apple authentication chip by it, and Android also having added support in the latest Android 4.3, Bluetooth Low Energy (4.0) is starting to look more interesting.

In this blog post I'm going to use a bluegiga BT111 based USB bluetooth dongle on Linux as a client and a TI CC2541 SensorTag as server.

USB dongle in computer
The SensorTag is a nice little bluetooth 4 example device provided by Texas Instruments.


First step is put the device in the computer. As my laptop already has an existing bluetooth device build-in, the new one will enumerate as "hci1". This can be found out also by using hcitool.

$ hcitool dev
        hci1    00:07:80:60:CE:4D
        hci0    EC:55:F9:F4:A0:xx

In this case the bluetooth mac address of the BT111 is 00:07:80:60:CE:4D.

Scanning for a device

Normal bluetooth scanning is done with hcitool scan. Low Energy scanning is done with hcitool lescan.
Make sure to press the button on the side of the SensorTag first to make it discoverable. Scanning is continuous to stop it with Ctrl-C. Low Energy devices require root access hence the sudo. I would expect being in the bluetooth group to be enough but for some reason it isn't.

$ sudo hcitool -i hci1 lescan
LE Scan ...
BC:6A:29:AC:2E:B4 (unknown)
BC:6A:29:AC:2E:B4 SensorTag
BC:6A:29:AC:2E:B4 (unknown)
BC:6A:29:AC:2E:B4 SensorTag
BC:6A:29:AC:2E:B4 (unknown)

Using gatttool to access the device

Now we know the address of the SensorTag: BC:6A:29:AC:2E:B4. In contrast with regular Bluetooth where there are a whole range of protocols, with Bluetooth Low Energy there is only one protocol at the top and it is GATT (Generic Attribute).

The actual functionality of a device is implemented by means of attributes which can be read, written to or notification/indication enabled for, depending on the attribute.

gatttool is a simple Linux tool that can be used to manipulate these attributes with a Bluetooth Low Energy device. It can be used as a simple command line tool but I find it easier to use it in it's interactive mode.

Let's enter the interactive mode.

$ sudo gatttool -i hci1 -b BC:6A:29:AC:2E:B4 -I
[   ][BC:6A:29:AC:2E:B4][LE]>

It returns us a prompt. Let's connect to the device.

[   ][BC:6A:29:AC:2E:B4][LE]> connect

The CON indication tells us that the connection is established.
Now the primary command can be used to find the primary services of the device.

[CON][BC:6A:29:AC:2E:B4][LE]> primary
attr handle: 0x0001, end grp handle: 0x000b uuid: 00001800-0000-1000-8000-00805f9b34fb
attr handle: 0x000c, end grp handle: 0x000f uuid: 00001801-0000-1000-8000-00805f9b34fb
attr handle: 0x0010, end grp handle: 0x0022 uuid: 0000180a-0000-1000-8000-00805f9b34fb
attr handle: 0x0023, end grp handle: 0x002a uuid: f000aa00-0451-4000-b000-000000000000
attr handle: 0x002b, end grp handle: 0x0035 uuid: f000aa10-0451-4000-b000-000000000000
attr handle: 0x0036, end grp handle: 0x003d uuid: f000aa20-0451-4000-b000-000000000000
attr handle: 0x003e, end grp handle: 0x0048 uuid: f000aa30-0451-4000-b000-000000000000
attr handle: 0x0049, end grp handle: 0x0054 uuid: f000aa40-0451-4000-b000-000000000000
attr handle: 0x0055, end grp handle: 0x005c uuid: f000aa50-0451-4000-b000-000000000000
attr handle: 0x005d, end grp handle: 0x0061 uuid: 0000ffe0-0000-1000-8000-00805f9b34fb
attr handle: 0x0062, end grp handle: 0x0068 uuid: f000aa60-0451-4000-b000-000000000000
attr handle: 0x0069, end grp handle: 0xffff uuid: f000ffc0-0451-4000-b000-000000000000

Attributes in GATT have uuids and handles. UUIDs (unique identifiers) define a certain type of entry, handles can be used to access a value. To make things more complicated there is also nesting involved. Certain UUIDs are defined in the standard and always have to be provided. Others can just be vendor specific and provide the actual data for the service. It's beyond the scope of this blog post to further look into how all that fits together.

SensorTag attributes

Luckily for us, the meaning of the sensortag attributes are defined on the user guide at http://processors.wiki.ti.com/index.php/SensorTag_User_Guide.

For this blog I'm looking at the humidity sensor as it is the easiest and the values shown in the document are actually correct :) http://processors.wiki.ti.com/index.php/SensorTag_User_Guide#Humidity_Sensor_2

As you can see on that page, there are 3 important handles for the humidity sensor, Data (0x38), DataNotification (0x39) and Config (0x3C).

Let's try reading from the Data handle:

[CON][BC:6A:29:AC:2E:B4][LE]> char-read-hnd 38
Characteristic value/descriptor: 00 00 00 00

So far so good, but no useful data. We first need to enable the sensor. This is done by writing 1 in the Config handle.

[CON][BC:6A:29:AC:2E:B4][LE]> char-write-req 3c 01
[CON][BC:6A:29:AC:2E:B4][LE]> Characteristic value was written successfully

Let's retry reading the value now.

[CON][BC:6A:29:AC:2E:B4][LE]> char-read-hnd 38
Characteristic value/descriptor: 68 68 4e 72

That's more like it. Using the math provided on the website and a python script, this gives the temperature and the humidity.

>>> -46.85 + 175.72/65536 * 0x6868
>>> -6.0 + 125.0/65536 * (0x72e4 & ~3)

So 25 Celsius and 50% humidity. That agrees with my local weather sensor. Nice :)

Instead of reading values it is also possible to use notifications.

[CON][BC:6A:29:AC:2E:B4][LE]> char-write-req 39 0100
[CON][BC:6A:29:AC:2E:B4][LE]> Characteristic value was written successfully

Notification handle = 0x0038 value: 68 68 f6 71 
This will provide the temperature and the humidity every second automatically. Let's disable it again it is spamming me :)

[CON][BC:6A:29:AC:2E:B4][LE]> char-write-req 39 0000
[CON][BC:6A:29:AC:2E:B4][LE]> Characteristic value was written successfully

Finally let's disable the sensor and disconnect.

[CON][BC:6A:29:AC:2E:B4][LE]> char-write-req 3c 00
[CON][BC:6A:29:AC:2E:B4][LE]> Characteristic value was written successfully
[CON][BC:6A:29:AC:2E:B4][LE]> disconnect 
[   ][BC:6A:29:AC:2E:B4][LE]>


Bluetooth Low Energy might look a bit different/strange at first but it is really quite nice! I'm certainly going to explore it further!

Shameless plug!

Don't forget to check out the very nice BT111 based USB dongle available for sale at my tindie store!


James said...

Thanks very much. Just worked through your entire post with the TI Keyfob CC2450 Dev Kit. For my next trick I'm going to attempt to find/write a python routine to poll the sensor.

Ron Hinchley said...

Thanks, hard to find BLE on linux. Can't imagine why Broadcom doesn't support Linux. So what are the feed throughs for on your Tindie dongle?

Unknown said...

@Ron: What do you mean by feed throughs? There's an extra IC for ESD protection of the USB lines on the board.