List items
Items from the current list are shown below.
Blog
All items from September 2017
On the Pico project we've recently been moving from Bluetooth Classic to BLE. We have multiple motivations for this, and not just the low energy promise. In addition, BLE provides RSSI values, which means we can control proximity detection better, and frankly, Bluetooth has been causing us a lot of reliability problems that we've had to work around. We're hoping BLE will work better. Finally, we're developing an iPhone client. iOS, it seems doesn't properly support Bluetooth Classic, so BLE is our best option for cross-platform compatibility.
One of the challenges developing anything that uses a protocol built on top of some transport is that typically both ends of the protocol have to be developed simultaneously. This slow things down, especially when we're trying to distribute tasks across several developers. So we were hoping to use gatttool
, part of bluez on Linux, as an intermediate step, to allow us to check the initial iOS BLE code worked before moving on to the Pico protocol proper.
So, here's a quick summary of how we used gatttool
to write characteristics to the iPhone.
One point to note is that things weren't smooth for us. In retrospect we had the iPhone correctly running as a BLE peripheral, but we had real trouble connecting. I'll explain how we fixed this too.
Writing to a BLE peripheral using bluez is a four step process:
- Scan for the device using hcitool.
- Having got the MAC from the scan, connect to it using gatttool.
- Find the handle of the characteristic you want to write to.
- Perform the write.
The first step, scanning for a device, can be done using the following command.
sudo hcitool -i hci0 lescan
This commend is using hcitool
to perform a BLE scan (lescan
) using the local device (-i hci0
). If you have more than one Bluetooth adaptor, you may want to specify the use of something other than hci0
.
When we first tried this, we kept on getting input/output errors, even when run as root. I don't know why this was, but eventually we found a solution:
sudo hciconfig hci0 down sudo hciconfig hci0 up
Not very elegant, but it seemed to work. After this, the scan started throwing up results.
flypig@delphinus:~sudo hcitool -i hci0 lescan
LE Scan ...
58:C4:C5:1F:C7:70 (unknown)
58:C4:C5:1F:C7:70 Pico's iPhone
CB:A5:42:40:F8:68 (unknown)
58:C4:C5:1F:C7:70 (unknown)
58:C4:C5:1F:C7:70 Pico's iPhone
Note the repeated entries. The device I was interested in was "Pico's iPhone", where we were running our test app. On other occasions when I've performed the scan, the iPhone MAC address came up, but without the name (marked as "unknown"). Again, I don't know why this is, but just trying the MACs eventually got me connected to the correct device.
Having got the MAC, now it's time to connect (step 2).
sudo gatttool -t random -b 58:C4:C5:1F:C7:70 -I
What's this all about? Here we're using gatttool
to connect to the remote device using its Bluetooth address (-b 58:C4:C5:1F:C7:70
). Obviously if you're doing this at home you should use the correct MAC which is likely to be different from this. Our iPhone is using a random address type, so we have to specify this too (-t random
). Finally, we set it to interactive mode with -I
. This will open gatttool's own command console so we can do other stuff.
If everything goes well, the console prompt will change to include the MAC address.
[58:C4:C5:1F:C7:70][LE]>
So far we've only set things up and not actually connected. So we should connect.
[58:C4:C5:1F:C7:70][LE]> connect
Attempting to connect to 58:C4:C5:1F:C7:70
Connection successful
Great! Now there's a time problem. The iPhone will throw us off this connection after only a few seconds. If it does, enter 'connect
' again to re-establish the connection. There's another catch though, so be careful: the iPhone will also periodically change it's MAC address. If it does, you'll need to exit the gatttool
console (Ctrl-D), rescan and then reconnect to the device as above.
Having connected we want to know what characteristics are available, which we do be entering 'characteristics
' at the console.
[58:C4:C5:1F:C7:70][LE]> characteristics handle: 0x0002, char properties: 0x02, char value handle: 0x0003, uuid: 00002a00-0000-1000-8000-00805f9b34fb handle: 0x0004, char properties: 0x02, char value handle: 0x0005, uuid: 00002a01-0000-1000-8000-00805f9b34fb handle: 0x0007, char properties: 0x20, char value handle: 0x0008, uuid: 00002a05-0000-1000-8000-00805f9b34fb handle: 0x000b, char properties: 0x98, char value handle: 0x000c, uuid: 8667556c-9a37-4c91-84ed-54ee27d90049 handle: 0x0010, char properties: 0x98, char value handle: 0x0011, uuid: af0badb1-5b99-43cd-917a-a77bc549e3cc handle: 0x0034, char properties: 0x12, char value handle: 0x0035, uuid: 00002a19-0000-1000-8000-00805f9b34fb handle: 0x0038, char properties: 0x12, char value handle: 0x0039, uuid: 00002a2b-0000-1000-8000-00805f9b34fb handle: 0x003b, char properties: 0x02, char value handle: 0x003c, uuid: 00002a0f-0000-1000-8000-00805f9b34fb handle: 0x003e, char properties: 0x02, char value handle: 0x003f, uuid: 00002a29-0000-1000-8000-00805f9b34fb handle: 0x0040, char properties: 0x02, char value handle: 0x0041, uuid: 00002a24-0000-1000-8000-00805f9b34fb handle: 0x0043, char properties: 0x88, char value handle: 0x0044, uuid: 69d1d8f3-45e1-49a8-9821-9bbdfdaad9d9 handle: 0x0046, char properties: 0x10, char value handle: 0x0047, uuid: 9fbf120d-6301-42d9-8c58-25e699a21dbd handle: 0x0049, char properties: 0x10, char value handle: 0x004a, uuid: 22eac6e9-24d6-4bb5-be44-b36ace7c7bfb handle: 0x004d, char properties: 0x98, char value handle: 0x004e, uuid: 9b3c81d8-57b1-4a8a-b8df-0e56f7ca51c2 handle: 0x0051, char properties: 0x98, char value handle: 0x0052, uuid: 2f7cabce-808d-411f-9a0c-bb92ba96c102 handle: 0x0055, char properties: 0x8a, char value handle: 0x0056, uuid: c6b2f38c-23ab-46d8-a6ab-a3a870bbd5d7 handle: 0x0059, char properties: 0x88, char value handle: 0x005a, uuid: eb6727c4-f184-497a-a656-76b0cdac633b
In this case, there are many characteristics, but the one we're interested in is the last one, with UUID 'eb6727c4-f184-497a-a656-76b0cdac633b
'. We know this is the one we're interested in, because this was the UUID we used in our iPhone app. We set this up to be a writable characteristic, so we can also write to it.
[58:C4:C5:1F:C7:70][LE]> char-write-req 0x005a 5069636f205069636f205069636f205069636f205069636f205069636f205069636f20 Characteristic value was written successfully
Success! On the iPhone side, we set it up to output the characteristic to the log if it was written to. So we see the following.
2017-09-29 20:09:21.206744+0100 Pico[241:25455] QRCodeReader:start() 2017-09-29 20:09:21.802875+0100 Pico[241:25455] BLEPeripheral: State Changed 2017-09-29 20:09:21.803002+0100 Pico[241:25455] BLEPeripheral: Powered On 2017-09-29 20:09:22.801024+0100 Pico[241:25455] BLEPeripheral:start() 2017-09-29 20:10:01.122027+0100 Pico[241:25455] BLE received: Pico Pico Pico Pico Pico Pico Pico
Where did all those 'Pico's come from? That's the value we wrote in, but in hexadecimal ASCII:
50 69 63 6f 20 50 69 63 6f 20 50 69 63 6f 20 50 69 63 6f 20 50 69 63 6f 20 50 69 63 6f 20 50 69 63 6f 20 P i c o P i c o P i c o P i c o P i c o P i c o P i c o
So, to recap, the following is the command sequence we used.
sudo hciconfig hci0 down sudo hciconfig hci0 up sudo hcitool -i hci0 lescan sudo gatttool -t random -b 58:C4:C5:1F:C7:70 -I [LE]> connect [LE]> characteristics [LE]> char-write-req 0x005a 5069636f205069636f205069636f205069636f205069636f205069636f205069636f20
When it's working, my experience is that gatttool
works well. But BLE is a peculiar paradigm, very different from general networking and offers lots of opportunity for confusion.