In one of our projects we had to communicate with BLE (Bluetooth Low Energy) devices. After some early investigations we highlighted two issues:
GATT protocol did not seem to be supported by
$ mkdir /tmp/libbluetooth-dev && cd /tmp/libbluetooth-dev $ wget http://es.archive.ubuntu.com/ubuntu/pool/main/b/bluez/libbluetooth-dev_4.101-0ubuntu13_amd64.deb $ sudo dpkg -x libbluetooth-dev_4.101-0ubuntu13_amd64.deb $ grep -r gatt ./usr $
Our project being mainly built in C we were not really considering to rewrite it into another language to only use an existing GATT library.
The 'new' Bluez D-BUS API that enables any user space application to communicate with Bluetooth devices through D-BUS is only supported in the latest Bluez v5.x releases. The Bluez D-BUS API is still receiving many bug fixes - we recently contributed a fix for it.
Unfortunately, few LTS (Long Term Support) Linux distributions still use Bluez v4.x - including Ubuntu 14.04 LTS that only support Bluez v4.x. Ubuntu 14.04 LTS is still supported until 2019! Either we do not support a widely available Linux distribution (including my own development machine!) or we find the solution to our issue...
Funnily enough I discovered the day we started our investigation that the day after would have been released the new Ubuntu 16.04 LTS. So I could have potentially go to the beach for a day and upgrade my Ubuntu machine the day after with the new LTS and Bluez v5.x and its D-BUS API.
But looking at different discussion threads on Internet I realized that there was a real gap. We had the mission to fix it!
During my initial investigation, I found that the only way to use Bluez GATT support was to manually pickup the files used by BlueZ
gatttool and to call the GATT functions in the way
gatttool is doing.
Obviously as many Open Source project no documentation - the documentation is the source code...
After losing few hairs while trying to understand the almost non commented source code and thinking few times I would lose my time with this new project. I decided to start slowly and see if I could isolate the source files and build
gatttool into its own project directory.
The initial step was to retrieve the latest stable version of Bluez v4.x (ie: v4.101 - actually the same version used by Ubuntu 14.04).
From the Bluez
Makefile.tools, I could already see the required files and libraries used by
attrib_gatttool_SOURCES = attrib/gatttool.c attrib/att.c attrib/gatt.c \ attrib/gattrib.c btio/btio.c \ attrib/gatttool.h attrib/interactive.c \ attrib/utils.c src/log.c attrib_gatttool_LDADD = lib/libbluetooth-private.la @GLIB_LIBS@ @READLINE_LIBS@
I also wanted to see the Bluez build command lines to avoid missing any build arguments.
The following dependencies are required:
git clone git://git.kernel.org/pub/scm/bluetooth/bluez.git cd bluez git checkout 4.101 ./bootstrap-configure make V=1 attrib/gatttool
Finally, ensure the application runs
Isolate gatttool in its own project and create a CMake file to build the new project.
We should have this following tree:
├── bluez │ ├── attrib │ │ ├── att.c │ │ ├── att.h │ │ ├── gatt.c │ │ ├── gatt.h │ │ ├── gattrib.c │ │ ├── gattrib.h │ │ ├── gatttool.c │ │ ├── gatttool.h │ │ ├── interactive.c │ │ └── utils.c │ ├── btio │ │ ├── btio.c │ │ └── btio.h │ └── src │ ├── log.c │ └── log.h └── CMakeLists.txt
If we build this project:
mkdir build && cd build cmake .. && make
We have some expected linking issues because
gatttool depends on
Linking C executable gatttool /usr/bin/cmake -E cmake_link_script CMakeFiles/gatttool.dir/link.txt --verbose=1 /usr/bin/cc CMakeFiles/gatttool.dir/bluez/attrib/att.c.o CMakeFiles/gatttool.dir/bluez/attrib/gatt.c.o CMakeFiles/gatttool.dir/bluez/attrib/gattrib.c.o CMakeFiles/gatttool.dir/bluez/attrib/gatttool.c.o CMakeFiles/gatttool.dir/bluez/attrib/interactive.c.o CMakeFiles/gatttool.dir/bluez/attrib/utils.c.o CMakeFiles/gatttool.dir/bluez/btio/btio.c.o CMakeFiles/gatttool.dir/bluez/src/log.c.o -o gatttool -rdynamic -lglib-2.0 CMakeFiles/gatttool.dir/bluez/attrib/att.c.o: In function `att_get_uuid16': att.c:(.text+0x1e5): undefined reference to `bt_uuid16_create' CMakeFiles/gatttool.dir/bluez/attrib/att.c.o: In function `att_get_uuid128': att.c:(.text+0x263): undefined reference to `bt_uuid128_create' CMakeFiles/gatttool.dir/bluez/attrib/gatt.c.o: In function `sdp_get_service_classes': gatt.c:(.text+0x8d): undefined reference to `sdp_get_uuidseq_attr' CMakeFiles/gatttool.dir/bluez/attrib/gatt.c.o: In function `att_get_uuid16': gatt.c:(.text+0x140): undefined reference to `bt_uuid16_create' CMakeFiles/gatttool.dir/bluez/attrib/gatt.c.o: In function `att_get_uuid128': gatt.c:(.text+0x1be): undefined reference to `bt_uuid128_create' CMakeFiles/gatttool.dir/bluez/attrib/gatt.c.o: In function `encode_discover_primary': gatt.c:(.text+0x2bc): undefined reference to `bt_uuid16_create' CMakeFiles/gatttool.dir/bluez/attrib/gatt.c.o: In function `primary_all_cb':
lib/uuid.c (see Bluez
Makefile.am). Let's add these files to the project.
After adding this file, I managed to build
gatttool. The next step was to separate
gatttool from the GATT code to isolate what was
gatttool specific and what could be used as the core of the GATT library.
gatttool specific files
utils.c would live in their own directory
Thanks to CMake, I quickly had my first GATT shared library and
gatttool application binary.
The next challenge was to provide a new API that would make the use of GattLib really easy.
The main issue I had during this phase was to understand how
gatttool was communicating with Bluez stack.
I finally found all the interactions were made through GLib IO Channel. GLib IO Channel is a wrapper on top of files, pipes and sockets - Unix Domain Socket are used by Bluez. The IO Channel only works when a GLib Main Event Loop is running.
As I did not want to force the GattLib users to have to add a GLib Main Event loop to their project I added a thread to GattLib where the GattLib specific GLib Main Loop would run from.
Bluez GATT API is all about asynchronous callbacks. I wanted my GattLib API to keep this asynchronous feature but also to support synchronous functions.
After spending a couple of days on this new library, I ended up with a satisfactory result that was good enough for my project.
I wrote one of my project BLE use cases with my new GattLib library and the result was a code clear to read.
I also wrote a couple of examples to help users to use this new library.
After doing a bit of
CMake and its magic
CPack, I could quickly package a first release of
GattLib as a Debian/Ubuntu/... DEB package, a RPM package and a ZIP package.
$ dpkg-deb -I gattlib-0.1.deb new debian package, version 2.0. size 81510 bytes: control archive=440 bytes. 258 bytes, 10 lines control 175 bytes, 3 lines md5sums Package: gattlib Version: 0.1 Section: devel Priority: optional Architecture: amd64 Depends: libglib2.0-0 Installed-Size: 227 Maintainer: Olivier Martin <firstname.lastname@example.org> Description: Library to access GATT information from Bluetooth Low Power devices $ dpkg-deb -c gattlib-0.1.deb drwxrwxr-x root/root 0 2016-04-28 13:05 ./usr/ drwxrwxr-x root/root 0 2016-04-28 13:05 ./usr/lib/ drwxrwxr-x root/root 0 2016-04-28 13:05 ./usr/lib/pkgconfig/ -rw-r--r-- root/root 269 2016-04-28 13:04 ./usr/lib/pkgconfig/gattlib.pc -rw-r--r-- root/root 228334 2016-04-28 13:05 ./usr/lib/libgattlib.so drwxrwxr-x root/root 0 2016-04-28 13:05 ./usr/include/ -rw-r--r-- root/root 3421 2016-04-28 00:47 ./usr/include/gattlib.h
$ rpm -qpl gattlib-0.1.rpm /usr/include/gattlib.h /usr/lib/libgattlib.so /usr/lib/pkgconfig /usr/lib/pkgconfig/gattlib.pc
$ unzip -l gattlib-0.1.zip Archive: gattlib-0.1.zip Length Date Time Name --------- ---------- ----- ---- 269 2016-04-28 13:04 gattlib-0.1/lib/pkgconfig/gattlib.pc 228334 2016-04-28 13:05 gattlib-0.1/lib/libgattlib.so 3421 2016-04-28 00:47 gattlib-0.1/include/gattlib.h --------- ------- 232024 3 files
And finally a couple of days after starting the project, I can make it alive on Github: https://github.com/labapart/gattlib!