Bluetooth Low Energy – BLE – Bluetooth 4.0 is an industry-standard wireless protocol built for the Internet of Things – IoT, it is designed to provide connectivity for devices operating from low capacity power sources such as coin cell batteries.

In this introduction to BLE I’ll be configuring a Raspberry Pi2 computer to talk to a smart watch. We will be installing the latest version of BlueZ from source, enabling BLE support. This is not a tutorial on decoding the data from the watch I am just using it as an example, although I may write about decoding it in a future posting.
I am using a ASUS USB-BT400 Bluetooth 4.0 Dongle on a Raspberry Pi2 but this will work on any computer with a Debian based distribution. Your dongle must be BLE/Bluetooth 4.0 capable otherwise this won’t work. I am using an ID107HR activity tracker with pedometer and heart rate monitor, randomly chosen from the list of cheap ones available on Amazon. While using the Pi to talk to the the watch make sure Bluetooth on the phone is off as it can only connect to one device at a time.
The current distribution of Raspbian – jessie on the Raspberry Pi comes with version 5.23 of the BlueZ Bluetooth stack that’s rather old, dating from September 2014 which lacks many of the features we will be needing. The current version 5.44 of the BlueZ has many changes in the package with many familiar components such as hcitool and gatttool being depreciated, so I will be ignoring those and using the available commands, bluetoothctl, on the terminal.
Installing BlueZ
With Raspbian – jessie installed we will need to update the Pi make sure some packages are installed and then installing the latest version of BlueZ. But first, remove the installed version 5.23 of BlueZ:
1 2 |
$ sudo apt-get --purge remove bluez $ sudo apt-get autoremove |
Next, perform the traditional housekeeping updates then install the build tools and USB libraries. Those parts that are installed already will be automatically skipped.
1 2 3 4 |
$ sudo apt-get update $ sudo apt-get upgrade $ sudo apt-get install build-essential $ sudo apt-get install -y libusb-dev libdbus-1-dev libglib2.0-dev libudev-dev libical-dev libreadline-dev |
Now, download the source code, at time of writing the current stable release is version 5.44, check the BlueZ site for the latest version.
1 2 3 4 |
$ cd ~ $ wget http://www.kernel.org/pub/linux/bluetooth/bluez-5.44.tar.xz $ tar xf bluez-5.44.tar.xz $ cd bluez-5.44/ |
Inside the BlueZ directory, configure, make (this takes a while), and install. The experimental option adds BLE support and enabling the library allows for python use later on:
1 2 3 |
$ ./configure --enable-experimental --enable-library $ make $ sudo make install |
Configuring and Starting BlueZ
At this stage we will need to check that the installation worked and that we can see your bluetooth dongle. With your bluetooth dongle in a USB port you should see it on your list of USB devices, here you see mine as device ID: 0b05:17cb ASUSTek Computer, Inc.:
1 2 3 4 5 6 |
$ lsusb Bus 001 Device 006: ID 0b05:17cb ASUSTek Computer, Inc. Bus 001 Device 004: ID 046d:c52e Logitech, Inc. Bus 001 Device 003: ID 0424:ec00 Standard Microsystems Corp. SMSC9512/9514 Fast Ethernet Adapter Bus 001 Device 002: ID 0424:9514 Standard Microsystems Corp. Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub |
You will also need to enable the experimental services, edit the file /lib/systemd/system/bluetooth.service and in the [Service] section change the ExecStart line to end with –experimental:
1 |
ExecStart=/usr/local/libexec/bluetooth/bluetoothd --experimental |
Start the bluetooth service, and while we are at it enable it to load on boot:
1 2 |
$ sudo systemctl start bluetooth $ sudo systemctl enable bluetooth |
Once started, check the status of the bluetooth daemon with:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
$ sudo systemctl status bluetooth ● bluetooth.service - Bluetooth service Loaded: loaded (/lib/systemd/system/bluetooth.service; enabled) Active: active (running) since Sat 2017-05-06 19:54:28 BST; 54s ago Docs: man:bluetoothd(8) Main PID: 10407 (bluetoothd) Status: "Running" CGroup: /system.slice/bluetooth.service └─10407 /usr/local/libexec/bluetooth/bluetoothd --experimental May 06 19:54:27 raspberrypi bluetoothd[10407]: Bluetooth daemon 5.44 May 06 19:54:28 raspberrypi systemd[1]: Started Bluetooth service. May 06 19:54:28 raspberrypi bluetoothd[10407]: Starting SDP server May 06 19:54:28 raspberrypi bluetoothd[10407]: Bluetooth management interface 1.14 initialized |
Should you need to, the service can be stopped and prevented from loading on boot with:
1 2 |
$ sudo systemctl stop bluetooth $ sudo systemctl disable bluetooth |
Finally, you may want to enable auto-power on for the device, to do so create this bluetooth config file:
1 2 |
$ sudo mkdir /etc/bluetooth $ sudo nano /etc/bluetooth/main.conf |
and add these two lines:
1 2 |
[Policy] AutoEnable=true |
You should restart the Pi at this point and check that the daemon has loaded properly with sudo systemctl status bluetooth
Testing BlueZ
Start the bluetooth controller, you should see your dongles MAC address and alias:
1 2 |
$ sudo bluetoothctl [NEW] Controller 5C:F3:70:80:8A:A6 raspberrypi [default] |
for first time use, try scanning to find your watch, if it doesn’t appear it is out of range, its battery is flat, or your dongle does not support BLE, here you can see it as ID107 HR:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
[bluetooth]# power on Changing power on succeeded [CHG] Controller 5C:F3:70:80:8E:FB Powered: yes [bluetooth]# scan on Discovery started [CHG] Controller 5C:F3:70:80:8E:FB Discovering: yes [NEW] Device C5:E8:FB:BF:F2:6C ID107 HR [CHG] Device C5:E8:FB:BF:F2:6C RSSI: -63 [CHG] Device C5:E8:FB:BF:F2:6C ManufacturerData Key: 0x001c [CHG] Device C5:E8:FB:BF:F2:6C ManufacturerData Value: 0x02 [CHG] Device C5:E8:FB:BF:F2:6C ManufacturerData Value: 0x00 [CHG] Device C5:E8:FB:BF:F2:6C ManufacturerData Value: 0xc5 [CHG] Device C5:E8:FB:BF:F2:6C ManufacturerData Value: 0xe8 [CHG] Device C5:E8:FB:BF:F2:6C ManufacturerData Value: 0xfb [CHG] Device C5:E8:FB:BF:F2:6C ManufacturerData Value: 0xbf [CHG] Device C5:E8:FB:BF:F2:6C ManufacturerData Value: 0xf2 [CHG] Device C5:E8:FB:BF:F2:6C ManufacturerData Value: 0x6c [CHG] Device C5:E8:FB:BF:F2:6C ManufacturerData Value: 0xe3 [CHG] Device C5:E8:FB:BF:F2:6C AdvertisingFlags: 0x00 ...etc... [bluetooth]# scan off |
bluetoothctl remembers your devices, so when you next use the program the watch appears on the list at the start. The controller has a number of options, these can be seen with help command. You can use show to view the status of your dongle:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
[bluetooth]# show Controller 5C:F3:70:80:8A:A6 Name: raspberrypi Alias: raspberrypi Class: 0x000000 Powered: no Discoverable: no Pairable: yes UUID: Generic Attribute Profile (00001801-0000-1000-8000-00805f9b34fb) UUID: A/V Remote Control (0000110e-0000-1000-8000-00805f9b34fb) UUID: PnP Information (00001200-0000-1000-8000-00805f9b34fb) UUID: Generic Access Profile (00001800-0000-1000-8000-00805f9b34fb) UUID: A/V Remote Control Target (0000110c-0000-1000-8000-00805f9b34fb) Modalias: usb:v1D6Bp0246d052C Discovering: no |
The list of UUID’s show the services supported by the Dongle. Now we can power the dongle on, set the agent – this manages the connection, and then connect to the watch on which the bluetooth symbol will appear. Once connected there will be a pause then you will see a list of attributes supported by the watch, it is advertising the services available:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 |
[bluetooth]# power on Changing power on succeeded [CHG] Controller 5C:F3:70:80:8A:A6 Powered: yes [bluetooth]# agent on Agent registered [bluetooth]# default-agent Default agent request successful [bluetooth]# connect C5:E8:FB:BF:F2:6C Attempting to connect to C5:E8:FB:BF:F2:6C [CHG] Device C5:E8:FB:BF:F2:6C Connected: yes Connection successful [NEW] Primary Service /org/bluez/hci0/dev_C5_E8_FB_BF_F2_6C/service0008 00001801-0000-1000-8000-00805f9b34fb Generic Attribute Profile [NEW] Characteristic /org/bluez/hci0/dev_C5_E8_FB_BF_F2_6C/service0008/char0009 00002a05-0000-1000-8000-00805f9b34fb Service Changed [NEW] Descriptor /org/bluez/hci0/dev_C5_E8_FB_BF_F2_6C/service0008/char0009/desc000b 00002902-0000-1000-8000-00805f9b34fb Client Characteristic Configuration [NEW] Primary Service /org/bluez/hci0/dev_C5_E8_FB_BF_F2_6C/service000c 00000af0-0000-1000-8000-00805f9b34fb Unknown [NEW] Characteristic /org/bluez/hci0/dev_C5_E8_FB_BF_F2_6C/service000c/char000d 00000af6-0000-1000-8000-00805f9b34fb Unknown [NEW] Characteristic /org/bluez/hci0/dev_C5_E8_FB_BF_F2_6C/service000c/char000f 00000af7-0000-1000-8000-00805f9b34fb Unknown [NEW] Descriptor /org/bluez/hci0/dev_C5_E8_FB_BF_F2_6C/service000c/char000f/desc0011 00002902-0000-1000-8000-00805f9b34fb Client Characteristic Configuration [NEW] Characteristic /org/bluez/hci0/dev_C5_E8_FB_BF_F2_6C/service000c/char0012 00000af2-0000-1000-8000-00805f9b34fb Unknown [NEW] Descriptor /org/bluez/hci0/dev_C5_E8_FB_BF_F2_6C/service000c/char0012/desc0014 00002902-0000-1000-8000-00805f9b34fb Client Characteristic Configuration [NEW] Characteristic /org/bluez/hci0/dev_C5_E8_FB_BF_F2_6C/service000c/char0015 00000af1-0000-1000-8000-00805f9b34fb Unknown [CHG] Device C5:E8:FB:BF:F2:6C UUIDs: 00000af0-0000-1000-8000-00805f9b34fb [CHG] Device C5:E8:FB:BF:F2:6C UUIDs: 00001800-0000-1000-8000-00805f9b34fb [CHG] Device C5:E8:FB:BF:F2:6C UUIDs: 00001801-0000-1000-8000-00805f9b34fb [CHG] Device C5:E8:FB:BF:F2:6C ServicesResolved: yes [ID107 HR]# |
and now that we have connected we can ask for some info:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
[ID107 HR]# info C5:E8:FB:BF:F2:6C Device C5:E8:FB:BF:F2:6C Name: ID107 HR Alias: ID107 HR Appearance: 0x0341 Paired: no Trusted: no Blocked: no Connected: yes LegacyPairing: no UUID: Unknown (00000af0-0000-1000-8000-00805f9b34fb) UUID: Generic Access Profile (00001800-0000-1000-8000-00805f9b34fb) UUID: Generic Attribute Profile (00001801-0000-1000-8000-00805f9b34fb) ManufacturerData Key: 0x001c ManufacturerData Value: 0x02 ManufacturerData Value: 0x00 ManufacturerData Value: 0xc5 ManufacturerData Value: 0xe8 ManufacturerData Value: 0xfb ManufacturerData Value: 0xbf ManufacturerData Value: 0xf2 ManufacturerData Value: 0x6c ManufacturerData Value: 0xe3 [ID107 HR]# |
These UUID’s are used to describe the sevices available on the device, some are pre-defined and can be found in the a href=”https://www.bluetooth.com/specifications/gatt/characteristics” target=”_blank” rel=”noopener noreferrer”>GATT schema, others are vendor specific and unless they publicly release these, decoding can become rather difficult. There are four types of attribute:
- Services – collections of characteristics and relationships to other services that encapsulate the behavior of part of a device
- Characteristics – attribute types that contain a single logical value
- Descriptors – defined attributes that describe a characteristic value
- Declarations – defined GATT profile attribute types
Each attribute is identified by a 128 bit ID, for example, one of the characteristics from the list above: 00002902-0000-1000-8000-00805f9b34fb, the first eight bits are used as an unique identifier: 00002902 and are shown as UUID’s: 0x2902. Data is contained in services, each service has a number of characteristics that may contain further descriptions depending on the requirement of the characteristic. You can see how the data is mapped out in this chart:

A spreadsheet with the watch data reformatted and tastefully coloured to illustrates this. Observe the Service URL column, it looks a lot like a directory structure:
Here we see two services /service0008 and /service000c looking further into the second service: /service000c we see that it has four characteristics, and to of those have descriptors. We can interrogate the characteristics and descriptors to glean further information by selecting the attribute and reading, like so:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
[ID107 HR]# select-attribute /org/bluez/hci0/dev_C5_E8_FB_BF_F2_6C/service000c/char000f [ID107 HR:/service000c/char000f]# read Attempting to read /org/bluez/hci0/dev_C5_E8_FB_BF_F2_6C/service000c/char000f [CHG] Attribute /org/bluez/hci0/dev_C5_E8_FB_BF_F2_6C/service000c/char000f Value: 0x02 [CHG] Attribute /org/bluez/hci0/dev_C5_E8_FB_BF_F2_6C/service000c/char000f Value: 0xa0 [CHG] Attribute /org/bluez/hci0/dev_C5_E8_FB_BF_F2_6C/service000c/char000f Value: 0x23 [CHG] Attribute /org/bluez/hci0/dev_C5_E8_FB_BF_F2_6C/service000c/char000f Value: 0x00 [CHG] Attribute /org/bluez/hci0/dev_C5_E8_FB_BF_F2_6C/service000c/char000f Value: 0x00 [CHG] Attribute /org/bluez/hci0/dev_C5_E8_FB_BF_F2_6C/service000c/char000f Value: 0x00 [CHG] Attribute /org/bluez/hci0/dev_C5_E8_FB_BF_F2_6C/service000c/char000f Value: 0x01 [CHG] Attribute /org/bluez/hci0/dev_C5_E8_FB_BF_F2_6C/service000c/char000f Value: 0x00 [CHG] Attribute /org/bluez/hci0/dev_C5_E8_FB_BF_F2_6C/service000c/char000f Value: 0x00 [CHG] Attribute /org/bluez/hci0/dev_C5_E8_FB_BF_F2_6C/service000c/char000f Value: 0x00 [CHG] Attribute /org/bluez/hci0/dev_C5_E8_FB_BF_F2_6C/service000c/char000f Value: 0x19 [CHG] Attribute /org/bluez/hci0/dev_C5_E8_FB_BF_F2_6C/service000c/char000f Value: 0x00 [CHG] Attribute /org/bluez/hci0/dev_C5_E8_FB_BF_F2_6C/service000c/char000f Value: 0x00 [CHG] Attribute /org/bluez/hci0/dev_C5_E8_FB_BF_F2_6C/service000c/char000f Value: 0x00 [CHG] Attribute /org/bluez/hci0/dev_C5_E8_FB_BF_F2_6C/service000c/char000f Value: 0x92 [CHG] Attribute /org/bluez/hci0/dev_C5_E8_FB_BF_F2_6C/service000c/char000f Value: 0x00 [CHG] Attribute /org/bluez/hci0/dev_C5_E8_FB_BF_F2_6C/service000c/char000f Value: 0x00 [CHG] Attribute /org/bluez/hci0/dev_C5_E8_FB_BF_F2_6C/service000c/char000f Value: 0x00 [CHG] Attribute /org/bluez/hci0/dev_C5_E8_FB_BF_F2_6C/service000c/char000f Value: 0x00 02 a0 23 00 00 00 01 00 00 00 19 00 00 00 92 00 ..#............. 00 00 00 ... |
Which is all very nice, but not particularly helpful as the manufacturer has chosen to use custom, proprietary, UUID’s for the watch. We don’t know the instructions to send to have the watch realease its data.
Those Scripting BlueZ
Inevitably, you’ll be wanting to automate connections. This becomes easy with the automation scripting language expect. Install, then make a script file:
1 2 3 4 |
$ sudo apt-get install expect $ cd ~ $ nano bttest $ chmod +x bttest |
In this example the script forgets the watch, finds the watch, connects to the watch, gets some info and then disconnects:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 |
#!/usr/bin/expect -f set timeout 10 set prompt ".*#" set usrpasswd "" set address "C5:E8:FB:BF:F2:6C" ## execute blutetoothctl spawn sudo bluetoothctl expect -re ".*password.*" send "$usrpasswd\r" ## forget about the device - if connected previously expect -re $prompt send "remove $address\r" expect -re $prompt sleep 3 ## switch on the dongle send "power on\r" expect "Changing power on succeeded" expect -re $prompt ## scan for devices send "scan on\r" expect -re $prompt sleep 5 send "scan off\r" expect -re $prompt ## set the agent send "agent on\r" expect "Agent registered" send "default-agent\r" expect -re $prompt sleep 2 ## connect to watch send "connect $address\r" expect -re $prompt ## get some info send "info $address\r" expect -re $prompt ## disconnect send "disconnect $address\r" sleep 2 expect -re "\[bluetooth\]#" ## bye send "exit\r" |
in the script, send sends a command, don’t forget to add the carriage return – \r and expect is used to wait for a response within the timeout period, here it is set to 10 seconds. expect -re is using regex when looking for a reply, otherwise it uses a literal string. So much more can be done with expect and there are many tutorials, such as this one written by FluidBank.
More Bluetooth Data
For analysing bluetooth data a couple of very useful tools are available, Wireshark and Android data logging. I will go through the installation but not look at the data in any detail, this posting is getting a bit long. This Section is in two parts, installing Wireshark and Android Debug Bridge.
Sniffing with the Shark
Wireshark is a network and bluetooth packet sniffer, it shows you network and bluetooth traffic occurring on your Pi. Here is a quick installation method for a reasonably new version of Wireshark (v2.2.4) from the backports, answer yes to the question “Should non-superusers be able to capture packets?”:
1 2 3 4 5 |
$ sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 7638D0442B90D010 $ sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 8B48AD6246925553 $ echo 'deb http://httpredir.debian.org/debian jessie-backports main contrib non-free' | sudo tee -a /etc/apt/sources.list.d/jessie-backports.list $ sudo apt-get update $ sudo sudo apt-get -t jessie-backports install wireshark wireshark-dev libwireshark-dev |
and if you get a message about permissions, reconfigure the package and answer yes:
1 |
$ sudo dpkg-reconfigure wireshark-common |
Start Wireshark and double click your bluetooth device on the list, in my case bluetooth0. There is not much to see as Wireshark will only see traffic between the watch and the Pi:

Android Debug Bridge – ADB
For Anroid 4.2.2 and above, activate developer mode on the phone, go to Settings, tap About Phone and at the bottom of the list tap Build Number three times. Back in the main settings page Developer Options has appeared, tap developer and turn USB debugging On. With the phone plugged into a USB port a little Android head should appear in the information bar at the top-left of the screen. To begin we will need to install some udev rules written by Nicolas Bernaerts:
1 2 3 |
$ sudo wget -O /etc/udev/rules.d/51-android.rules https://raw.githubusercontent.com/NicolasBernaerts/ubuntu-scripts/master/android/51-android.rules $ sudo chmod a+r /etc/udev/rules.d/51-android.rules $ sudo service udev restart |
Install the android tools, confirm that you have at least version 1.0.31, and start ADB
1 2 3 4 5 6 7 8 |
$ sudo apt-get install android-tools-adb android-tools-fastboot $ adb version Android Debug Bridge version 1.0.31 $ adb devices * daemon not running. starting it now on port 5037 * * daemon started successfully * List of devices attached 064be417008eef9f offline |
At this point on the phone an allow USB debugging dialog will appear, give permission and always trust to authorise it. ADB will now show the device as a device:
1 2 3 |
$ adb devices List of devices attached 064be417008eef9f device |
If the device list is empty, with everything plugged in good and proper and the phone setup in developer mode, start your diagnosis by checking udev; open another terminal window and view logging with udevadm monitor –environment and reload with sudo udevadm control –reload I’m not entirely sure what I did to get it from ‘not working’ to ‘working’. If all else fails elevate yourself to root.
Data Capture
With ADB now setup we can capture the Bluetooth data being exchanged. With bluetooth off, in the Developer Settings find Enable Bluetooth HCI snoop log and turn it On. In the smartwatch app synchronise with your watch, once complete turn Bluetooth off manually – this is to minimise the amount of captured data. Don’t forget to turn logging off on the phone when done. To find where the log file has been stored and copy the file from the phone to the Pi use:
1 2 3 |
$ adb shell "cat /etc/bluetooth/bt_stack.conf | grep FileName" BtSnoopFileName=/sdcard/btsnoop_hci.log $ adb pull /sdcard/btsnoop_hci.log ~/ |
We can now use Wireshark to read the log file…

This wasn’t quite the posting I originally had in mind, I wanted to decode the data from the watch for my own use, making something more useful, impressive graphs and charts, than that provided by the Android App VeryFit 2.0 but as the manufacturer has chosen to use proprietary GATT codes it makes the job that much harder. It may be much simpler to just buy an expensive FitBit and download the data from them. But with writing this I now know a few things that were previously unknown, and I hope that this has provided some light to your BlueZ (a pun!, right at the end!).
Links and Sources
- Bluetooth Low Energy Description
- ID107HR activity tracker review
- BlueZ – Official Linux Bluetooth protocol stack
- List of GATT Services
- Raspbian for the Raspberry Pi
- Adafuit: Install bluez on the Raspberry Pi
- ArchLinux Wiki: Bluetooth
- Bluetooth audio paired but not connect? – Very useful BlueZ guide
- Getting Started with Bluetooth Low Energy: Tools and Techniques for Low-Power Networking – ISBN: 1491949511
Dear Karl
It is very instructive! Thanks a lot!
I have the SDK package for ID 107 if you need it!
Hello, thank you very much for your post, it has been really helpful.
I was trying to get more information about that watch, the ID107HR, especially about how to retrieve the data using bluetooth.
Have you had any look at the commands used to retrieve the data? Do you have any source where i can gain such informations?
Thank you very much and thanks for your help.
Awesome directions! Thank you!
I tried your version above, 5.44, and the latest 5.50, and seem to be stuck. All looks good until I reboot. Then I see the following:
systemctl status bluetooth.service
● bluetooth.service – Bluetooth service
Loaded: loaded (/lib/systemd/system/bluetooth.service; enabled)
Active: inactive (dead)
Docs: man:bluetoothd(8)
Oct 12 12:35:59 raspberrypi systemd[1]: Started Bluetooth service.
Also trying to start bluetoothctl just hangs at this point.
Any ideas? Thanks in advance for any help!!
Hmm, another update after playing with this for a few more hours. For whatever reason, my builtin bluetooth controller on rPi no longer is visible. I am able to get new and old versions of bluez to build/install/run, but not sure it is listed in lsusb anymore and going back to the default 5.23 version can’t seem to find the built in bluetooth either. Darn! Rebooted many times just in case. Perhaps the local install of bluetooth left something behind that wouldn’t clean up? Is there a make argument to uninstall? I removed several things from /usr/local/… but didn’t seem to help. Last test was to plug in an external bluetooth usb device. It works with the newer versions and is found (yay!) but would still like to use the internal one too if I can.