Getting Started with Bluetooth LE

Introduction

Over the past year I have worked on a number of projects but the one that is perhaps the most interesting to me is for a startup named Bashers & Bucklers. I don't want to give away Bashers & Bucklers business plan but it involves IoT technologies communicating with central coordinating authority over relatively short distances. Looking back, I remember thinking how simple the problem sounded. My first thought was that it is textbook client-server, just with low-power, embedded devices for clients. Today, Bashers & Bucklers is still working from the client-server model though our knowledge of Bluetooth LE which we choose as our underlying communications channel has been hard won, largely through trial and error. Therefore, I hope to document what we have learned so others hoping to communicate with IoT devices using Bluetooth LE can avoid some of the pitfalls I encountered.

Notes

Hardware

For my effort, I adopted PlatformIO Core as my development environment. PlatformIO allows me to build and flash devices from the command line which along with a syntax coloring text editor is my preferred development environment. Though shifting to the Adafruit Feather nRF52 Bluefruit LE in the near future, thus far, Bashers & Bucklers has employed Adafruit's Flora and Adafruit Flora Blufruit LE boards for our IoT prototyping [hereafter Flora and Bluefruit Flora respectively]. Therefore, in the Arduino code that follows, a Flora connected to a Bluefruit Flora is the assumed hardware platform. For our central coordinating authority or "server", I began using a low end PC I had laying around running a standard install of Ubuntu Linux though today we are targeting a Raspberry Pi 3 B+ running a standard install of Raspbian desktop.

Development Environment

I prefer a command line window, syntax coloring text editor such as TextMate and git for version control. I like this set up because the components are very basic and require little in the way of setup, they are flexible working for me not against me and each component is simple, comparatively speaking… git is easy to get started with but hard to master though it is the epitome of simplicity in comparison to some IDEs. I recognize that everyone is different and some will find my setup confusing and impossible to use. I expect that most people with a little experience coding will be able to read between the lines enough, however, that things will however make sense even if they must adapt some steps to fit their preferences.

Notation Convention

In command line examples I will use bold print for characters you might type into the command line and use sh/bash commands e.g. ls rather than the DOS equivalent e.g. dir. Because of the flexibility of my setup I will at times be leaving something up to you the reader which I will denote using bash variable notation and indicate in the surrounding text when that value is not completely arbitrary. For example:

$ cd ${YOUR_PROJECT_DIRECTORY}
$ ls
Green House Monitor
Simple Text Editor
Getting Started with Bluetooth LE
Occasionally, commands will output long runs of text that can safely be ignored, In those cases I will omit some of it marking the place of the omission with an ellipsis surrounded by square brackets.
$ ps -e
[…]
85860 ??        81:46.66 /Applications/TextMate.app/Contents/MacOS/TextMate
89130 ??         2:03.25 /Applications/Mail.app/Contents/MacOS/Mail
[…]

While, writing this piece I realized how confusing the term "firmware" is. Therefore, I have attempted to be very careful to write "application" when referring to a program written by you the reader or myself even when written into non-volatile memory (NVRAM) and run automatically when the device powers on thus reserving "firmware" for code that is provided by a hardware vendor and if present at power on time in the non-volatile memory of a device in enables a particular application programming interface (API) such as what our "application" might use. I understand full well that the code we write and compile with the Arduino IDE or platfomio must be and is automatically combined (linked) with a firmware to create the non-volatile memory images which we load onto our Arduino compatible devices blurring these distinctions.

Finally, I will be omitting details of how to install software such as git and platformio though I will provide links to the developers sites when I first mention a piece of software not part of default operating system installs. I am doing this because the developers have written great instructions for installing their software and they take the time to make sure those instructions are up to date as best they can for a wide variety of platforms, an effort I can not hope to duplicate.

Background

Bluetooth LE

Adafruit provides great example code 1 for their products to get innovators up and running and in the case of their Bluefruit line, they also provide free apps for iOS on the Apple App Store and Android on the Google Play store along with the source code for those apps on Github. However, Bashers & Bucklers goal is not to develop apps for a phone OS. Instead, Bashers & Bucklers wishes to take input from many IoT devices and show computed values on large displays (60+ inch diagonals), a task outside the capabilities of the current generation of mobile operating systems. Therefore, I began to search for example code and documentation to get me started working with Bluetooth LE on a desktop OS. I found code for macOS but did not like the idea of being locked into a particular hardware that platform. I found information on Windows such as how Microsoft added initial support for Bluetooth LE to Windows 8 and a full implementation in Windows 10 but found little API documentation and no example code for Windows. Somewhat expectedly I found lots of documentation and example code for Linux which was attractive because I knew that running on a Raspberry Pi would keep our hardware costs low while we prototype. Then, if we later decided that the Raspberry Pi did not have the muscle to support our needs, we could install the same Linux on more powerful hardware.

Sample code in hand I set about making sense of it. This required some understanding of Bluetooth. The Bluetooth Special Interest Group, a consortium of the companies which developed and guide the evolution of the Bluetooth standards maintains a website of documentation on Bluetooth. Bluetooth, however, is not a single thing but a family of protocols that happens to use the radio bands. It's a bit like having radio, television, IP (internet data), radio control toys, and more all sharing the same station. Furthermore, the Bluetooth has a history with a number of revisions and several additions where an unrelated standard was combined into the Bluetooth family of standards simply by rebranding. This creates more than a bit of confusion about what is "Bluetooth", muddies documentation and uneven adoption of standards. Therefore, careful attention must be paid to what the "Bluetooth" brand name and trademark is being used to describe at all times.

At its inception, Bluetooth was positioned as a competitor to USB meant to connect peripherals to PCs. Since the introduction of the smart phone and tablets, these devices have become the primary adopters of Bluetooth due to the lack ports to connect peripherals with wires. Today, Bluetooth dominates the wireless peripheral market connecting keyboards, printers, health monitoring devices and more to PCs and mobile computing devices.

In the last ten years a new market for wearable devices has emerged from the DIY scene. People have been incorporating computing devices into clothing and accessories for reasons both practical and not so practical In order to get by without bulky batteries designers have had to pay close attention to the power draw of the electronics and as a result most have foregone adding wireless connectivity to a phone or PC. However for those projects that have added wireless connectivity, namely health monitors, Bluetooth LE has been a natural fit.

Bluetooth Low Energy, abbreviated Bluetooth LE or simply BLE, started life as a project at Nokia and was originally debuted as Wibree before being folded into the Bluetooth specification in 2010. As such it differs significantly from Bluetooth Basic Rate/Enhanced Data Rate (BR/EDR) or Bluetooth "Classic" as the older portions of the Bluetooth standard have come to be known. Bluetooth Classic is largely a packetized stream protocol, like ethernet or wifi, sending data across the radio link as it is generated. However, this design means the wireless link to consumes significant amounts of energy passing data back and forth even if it is largely discarded. In order to limit this waste Bluetooth LE attempts to send only the data that will be used. It flips our traditional understanding of client-server making the energy conserving peripheral the server which sends data only if requested by a client. Bluetooth LE devices break the data they expose to remote devices into named pieces. These pieces known as Characteristics are assembled into groups called Services. Every Characteristic has a set of Permissions which control the direction that the value of the Characteristic can flow over the wireless link and a whether the data flows on change or only on request.

Bluetooth LE support in Linux

Linux is not really an operating system as most people understand it. Linux is strictly speaking one small part of an operating system that is combined with a number of separately developed components to create distributions such as Debian, Slackware, Fedora Core and many others. Because components are developed separately, distribution often pick and choose from alternatives thus each component must adhere to interfaces, a set of rules defining how components interact with each other. Combined with it's open source licensing which allows anyone to peer under the hood so to speak, this has made Linux the OS for those working on the cutting edge. Thus Linux often gains support for new technologies soon after they are released if not before they are released. Being on the cutting edge has drawbacks however. Sometimes the support for new technologies is incomplete, buggy or poorly conceived and often it is rewritten several times before stabilizing. Too often, there is a steep learning curve to get new features working with minimal or no documentation at all. This is the history of Bluetooth support in Linux.

Linux gained support for Bluetooth early on as developers working for corporations linked to the development of Bluetooth, working to speed the adoption of the standard, contributed the needed code to Linux. This code exists as the Bluez project. Under the Bluez project, support for Bluetooth Classic matured to a point where it is very stable and feature complete by 2005. However, when Bluetooth LE support was added it was a difficult fit.

Linux is heavily influenced by the Unix family of operating systems and the C programming language. Thus stream protocols such as TCP/IP and Bluetooth Classic are easy fits. Typically, a kernel level driver exposes a new socket type that applications could open, read write and close much like any other socket or file descriptor. However, Bluetooth LE does not work as a data stream, in order to save power by sending data only when necessary. This makes Bluetooth LE a difficult conceptual fit in an OS which attempts to cast everything (files, device input) as a stream. Thus when Bluetooth LE support was initially added, it was shoehorned in such that programmers could create a socket not for the remote device but for the PC's Bluetooth hardware and use that connection to issue commands to the Bluetooth hardware which in turn communicated with the remote device(s). This solution was far from ideal despite conforming to the everything is a stream philosophy of Unix.

While this early implementation worked, it was difficult to work with. Indeed Bluetooth LE adoption was hindered in no small part because the API for using Bluetooth LE devices was no better on Microsoft Windows or macOS. It was only when the vast majority of mobile devices gained support for Bluetooth LE that the standard started to see increased adoption. This in turn prompted the Linux community to improve support for Bluetooth LE and Bluetooth in general. In 2016 Bluetooth LE support in Linux was rebuilt around D-Bus and the new tool bluetoothctl replaced the original tools: hcitool and gatttool for connecting to and working with Bluetooth LE devices.

First steps

My first step when working with a new device or when unpacking a shipment of electronics is to power it on the hardware and use whatever software is provided by default to try to insure that the hardware works as advertised. When it comes to DIY electronics like Arduino compatible boards this means hooking up the board to any necessary components and loading a manufacturer provided example program on the board. So let's wire up our Bluefruit board to a Flora. The Bluefruit requires power via connections to its 3.3V and GND pads. It also needs also needs to be connected to a UART Serial sometimes referred to as hardware serial or hardware accelerated serial with the TX pad on the Bluefruit connected to the RX pad on the Flora and the RX pad on the Bluefruit to the TX pad on the Flora.

Wiring Diagram - Connect Adafruit Flora v3 to Adafruit Bluefruit Flora
Adafruit Flora v3 Connected to Adafruit Bluefruit Flora

We are now ready to load some software on the Flora. If you are using the Arduino IDE, and have not used it with the Adafruit Flora before, you need open the board manager and install or update the data for the board. Then if you create a new project and select the Flora as your board for the project, you will find a number of examples under the "Examples" menu. I prefer to use platformio and in particular platformio core instead of the Arduino IDE because it better fits my preferred programming environment. Therefore, my next step is to pull up a command line window, navigate to one of my test projects, and instruct platformio to load it onto my board. We will set up a platformio project from scratch in the next section but you may want to use the example project I have created for Bashers & Bucklers: bashersandbucklers/getting-started-with-bluetooth-le.git. You can clone a copy to your computer with:

$ git clone git@bitbucket.org:bashersandbucklers/getting-started-with-bluetooth-le.git ${DIRECTORY_FOR_PROJECT}
Cloning into '${DIRECTORY_FOR_PROJECT}'...
remote: Counting objects: 13, done.
remote: Compressing objects: 100% (9/9), done.
remote: Total 13 (delta 1), reused 0 (delta 0)
Receiving objects: 100% (13/13), 189.63 KiB | 1.31 MiB/s, done.
Resolving deltas: 100% (1/1), done.
You can then (assuming you have a working install of platformio core) build the project and push it to a Flora connected to your computer with a USB cable using:
$ cd ${DIRECTORY_FOR_PROJECT}
$ platformio lib install 177
Library Storage: ${DIRECTORY_FOR_PROJECT}/.piolibdeps
LibraryManager: Installing id=177
Downloading...
Unpacking  [####################################]  100%
Adafruit BluefruitLE nRF51 @ 1.9.5 has been successfully installed!
$ platformio run -t upload
Processing flora8 (platform: atmelavr; board: flora8; framework: arduino)
[...]
========================= [SUCCESS] Took 4.47 seconds =========================
Note: some authors use the pio command while others write platformio. While these command line tools are not identical, in the version I am using, 3.6.0 they end up calling the samecode and are thus interchangeable. With our device flashed with a known application and powered on; we can now switch to our Linux PC and connect to the Arduino device via Bluetooth.

On most Linux distributions, the Bluez and it's command line tools are included in the default installation. Some minimal Linux distributions however may require installing additional packages to get the bluetoothctl command. Therefore, we will first check that bluetoothctl is available and that the computer is equipped with Bluetooth LE compatible hardware. We can use the which command to find if bluetoothctl is installed:

$ which bluetoothctl
/usr/bin/bluetoothctl
and if you see a path to the command printed before the next command prompt, you know that it is installed. However if no path is reported and the very next line begins with a shell prompt, you likely do not have bluetoothctl. While it may be possible to install Bluez on your Linux PC, it is very likely that either your computer does not have supported Bluetooth hardware, your Linux install is extremely out of date and unable to support Bluez or you are reading this at a point in the future after which Bluez has undergone significant development and the rest of this piece will not be helpful.

Assuming however that your Linux PC does have bluetoothctl you can check if your computer is equipped with supported Bluetooth hardware by invoking bluetoothctl and issuing the list command at the "[bluetooth]#" prompt:

$ bluetoothctl
[NEW] 01:23:45:67:89:AB Linux-PC [default]
[NEW] Device 00:00:00:00:00:00 T.K.Egan's Phone
Agent registered
[bluetooth]# list
Controller 01:23:45:67:89:AB Linux-PC [default]
Assuming you see output including a line which begins with "Controller", your PC is equipped with supported Bluetooth hardware. Similarly, you can use the devices command to get the list of Bluetooth devices your is aware of… devices which have advertised their presence via a broadcast message while in range of your Linux PC while the Bluetooth hardware in your Linux PC was listening… and paired-devices to get the list of Bluetooth devices that your Linux PC will automatically attempt to connect with. As we are working with a device new to us it will not appear in the list of paired-devices and if your Linux PC has not been listening for so called device advertisements which is the default, then it will not appear in the devices list. To get the device to appear in the devices list we need to make sure the Bluetooth hardware is powered up, and set it to listen for advertisements which bluetoothctl calls scanning. Still at the "[bluetooth]#" prompt issue the command power on to power up the bluetooth hardware and scan on to cause the bluetooth hardware to listen for device advertisements:
[bluetooth]# power on
[CHG] Controller 01:23:45:67:89:AB Class: 0x001c010c
Changing power on succeeded
[CHG] Controller 01:23:45:67:89:AB Powered: yes
[bluetooth]# scan on
Discovery started
[CHG] Controller 01:23:45:67:89:AB Discovering: yes
[NEW] Device ${XX:XX:XX:XX:XX:XX} ${DEVICE_NAME}
Note, an alternative to the command line if using a desktop Linux distro such as Ubuntu Desktop or Fedora Core Workstation would be to open the bluetooth control panel and use the toggle switch to turn bluetooth on. The toggle switch has the same effect as issuing power on followed by scan on or scan off followed by power off at the bluetooth prompt. Importantly for us is at the command line we will see the hardware address of the device (${XX:XX:XX:XX:XX:XX} above) followed by its preferred display name (${DEVICE_NAME} above). If you loaded the Bashers & Bucklers example application on your device, the preferred display name of the device will be: "Bluefruit HRM". Hopefully, your device will show up after a few seconds. Devices broadcast advertisement messages frequently though not continuously to reduce radio congestion and of course to save power thus you may have to wait a minute or two before your device will be listed. For all the details of advertising refer to section 4.4 of the Bluetooth Protocol Specification (v5.0). Once your device has shown up make a note of the hardware address you will need it to connect to the device. To connect to the device you have two options. You can connect or you can pair. Connecting implies that once you have disconnected, the device will not automatically, reconnect if it is powered on in range of the PC while pairing implies that once if the connection drops, your PC and the device will attempt to reconnect if they discover each other powered on and within range of each other in the future. The various firmware releases from Adafruit for the nRF51 (up to v0.8.0 as of the time of writing) however do not appear to fully support pairing. Therefore we will simply use the connect command at the "[bluetooth]#" prompt followed by the device's hardware address to establish our connection:
[bluetooth]# connect ${XX:XX:XX:XX:XX:XX}
Attempting to connect to ${XX:XX:XX:XX:XX:XX}
[CHG] Device ${XX:XX:XX:XX:XX:XX} Connected: yes
Connection successful
[NEW] Primary Service
	/org/bluez/hci0/dev_XX_XX_XX_XX_XX_XX/service0008
	00001801-0000-1000-8000-00805f9b34fb
	Generic Attribute Profile
[NEW] Characteristic
	/org/bluez/hci0/dev_XX_XX_XX_XX_XX_XX/service0008/char0009
	00002a05-0000-1000-8000-00805f9b34fb
	Service Changed
[NEW] Descriptor
	/org/bluez/hci0/dev_XX_XX_XX_XX_XX_XX/service0008/char0009/desc000b
	00002902-0000-1000-8000-00805f9b34fb
	Client Characteristic Configuration
[…]
[NEW] Primary Service
	/org/bluez/hci0/dev_XX_XX_XX_XX_XX_XX/service0027
	0000180d-0000-1000-8000-00805f9b34fb
	Heart Rate
[NEW] Characteristic
	/org/bluez/hci0/dev_XX_XX_XX_XX_XX_XX/service0027/char0028
	00002a37-0000-1000-8000-00805f9b34fb
	Heart Rate Measurement
[NEW] Descriptor
	/org/bluez/hci0/dev_XX_XX_XX_XX_XX_XX/service0027/char0028/desc002a
	00002902-0000-1000-8000-00805f9b34fb
	Client Characteristic Configuration
[NEW] Characteristic
	/org/bluez/hci0/dev_XX_XX_XX_XX_XX_XX/service0027/char002b
	00002a38-0000-1000-8000-00805f9b34fb
	Body Sensor Location
[CHG] Device ${XX:XX:XX:XX:XX:XX} UUIDs: 00001530-1212-efde-1523-785feabcd123
[CHG] Device ${XX:XX:XX:XX:XX:XX} UUIDs: 00001800-0000-1000-8000-00805f9b34fb
[CHG] Device ${XX:XX:XX:XX:XX:XX} UUIDs: 00001801-0000-1000-8000-00805f9b34fb
[CHG] Device ${XX:XX:XX:XX:XX:XX} UUIDs: 0000180a-0000-1000-8000-00805f9b34fb
[CHG] Device ${XX:XX:XX:XX:XX:XX} UUIDs: 0000180d-0000-1000-8000-00805f9b34fb
[CHG] Device ${XX:XX:XX:XX:XX:XX} UUIDs: 6e400001-b5a3-f393-e0a9-e50e24dcca9e
[CHG] Device ${XX:XX:XX:XX:XX:XX} ServicesResolved: yes
[CHG] Device ${XX:XX:XX:XX:XX:XX} Name: Bluefruit HRM
[CHG] Device ${XX:XX:XX:XX:XX:XX} Alias: Bluefruit HRM
Once the PC and the device "connect" which is to say agree to talk to one another on a particular radio channel, your PC will ask the device to tell the PC about the device. With a Bluetooth LE devices this includes a listing of the Services, Characteristics and the Permissions for the Characteristics which bluetoothctl will report to the command line. For now we can ignore this lengthy output, we'll come back to it in the next section. We will finish up this section by finding a particular Characteristic, reading its value and asking to be notified of changes to the value. First, though, one note on device disconnections. Much like pairing, and these problems may be related, device disconnections are troublesome with the available firmwares. We'll look at this a bit more deeply in the next section but for now if the device looses its connection to the PC such as by being moved out of range, you will need to power cycle the device and instruct the PC to discard the connection, possibly even to forget about the device before you will be able to reconnect the device reliably. To disconnect the device use the disconnect command at the "[bluetooth]#" prompt followed by the device's hardware address and to have the PC forget about the device use the remove command at the "[bluetooth]#" prompt followed by the device's hardware address:
[bluetooth]# disconnect ${XX:XX:XX:XX:XX:XX}
Attempting to disconnect from C3:3F:A5:0F:E7:10
[CHG] Device ${XX:XX:XX:XX:XX:XX} ServicesResolved: no
Successful disconnected
[CHG] Device ${XX:XX:XX:XX:XX:XX} Connected: no
[bluetooth]# remove ${XX:XX:XX:XX:XX:XX}
[DEL] Descriptor
	/org/bluez/hci0/dev_XX_XX_XX_XX_XX_XX/service0008/char0009/desc000b
	00002902-0000-1000-8000-00805f9b34fb
	Client Characteristic Configuration
[DEL] Characteristic
	/org/bluez/hci0/dev_XX_XX_XX_XX_XX_XX/service0008/char0009
	00002a05-0000-1000-8000-00805f9b34fb
	Service Changed
[DEL] Primary Service
	/org/bluez/hci0/dev_XX_XX_XX_XX_XX_XX/service0008
	00001801-0000-1000-8000-00805f9b34fb
	Generic Attribute Profile
[…]
[DEL] Characteristic
	/org/bluez/hci0/dev_XX_XX_XX_XX_XX_XX/service0027/char0028
	00002a37-0000-1000-8000-00805f9b34fb
	Heart Rate Measurement
[DEL] Characteristic
	/org/bluez/hci0/dev_XX_XX_XX_XX_XX_XX/service0027/char002b
	00002a38-0000-1000-8000-00805f9b34fb
	Body Sensor Location
[DEL] Primary Service
	/org/bluez/hci0/dev_XX_XX_XX_XX_XX_XX/service0027
	0000180d-0000-1000-8000-00805f9b34fb
	Heart Rate
[DEL] Device ${XX:XX:XX:XX:XX:XX} Bluefruit HRM
Device has been removed

With the Bluetooth LE device connected we can now now explore the Services and Characteristics. Because bluetoothctl is intended to be useful for any and all Bluetooth connections, it has many features and commands we will not be using. In fact the commands we will be using next are not considered core features and are thus not available in the default "menu". You can see the commands available in the current menu at anytime with the help command in bluetoothctl. For now though use the command: menu gatt2 to activate the commands related to Bluetooth LE. The first command you should know of is list-attributes which can be used to see a listing of all of the Bluetooth LE Services and Characteristics exposed by a device. Note that we have already seen this output as the long sprawl of information that connect shows when connecting to a device.

[bluetooth]# menu gatt
[…]
[bluetooth]# list-attributes
Primary Service
	/org/bluez/hci0/dev_XX_XX_XX_XX_XX_XX/service0008
	00001801-0000-1000-8000-00805f9b34fb
	Generic Attribute Profile
Characteristic
	/org/bluez/hci0/dev_XX_XX_XX_XX_XX_XX/service0008/char0009
	00002a05-0000-1000-8000-00805f9b34fb
	Service Changed
Descriptor
	/org/bluez/hci0/dev_XX_XX_XX_XX_XX_XX/service0008/char0009/desc000b
	00002902-0000-1000-8000-00805f9b34fb
	Client Characteristic Configuration
[…]
Primary Service
	/org/bluez/hci0/dev_XX_XX_XX_XX_XX_XX/service0027
	0000180d-0000-1000-8000-00805f9b34fb
	Heart Rate
Characteristic
	/org/bluez/hci0/dev_XX_XX_XX_XX_XX_XX/service0027/char0028
	00002a37-0000-1000-8000-00805f9b34fb
	Heart Rate Measurement
Descriptor
	/org/bluez/hci0/dev_XX_XX_XX_XX_XX_XX/service0027/char0028/desc002a
	00002902-0000-1000-8000-00805f9b34fb
	Client Characteristic Configuration
Characteristic
	/org/bluez/hci0/dev_XX_XX_XX_XX_XX_XX/service0027/char002b
	00002a38-0000-1000-8000-00805f9b34fb
	Body Sensor Location
Notice that while we may think of characteristics belonging to services and even services belonging to other services (Secondary Service), bluetoothctl displays the list of attributes in a flat list. While the help text indicates we can use UUID to work with services and characteristics I have found that I have had to use the hierarchal style, file-path-like names to work with the services and characteristics. In any case the application we have loaded on to the device, if you loaded the Bashers & Bucklers example application on your device that is, adds a Service with the registered 16bit UUID 180D [which corresponds to the 128bit UUID: 0000180d-0000-1000-8000-00805f9b34fb 3] for a Heart Rate monitor along with the requisite characteristics and of particular interest it changes the value of the Heart Rate Measurement characteristic, corresponding to the characteristic with the registered 16bit UUID 2a37, to a random value every five seconds or so. According to the previous output the Heart Rate Measurement corresponds to the attribute: /org/bluez/hci0/dev_XX_XX_XX_XX_XX_XX/service0027/char0028. The number after the service and characteristic is not fixed and may vary from device to device and run to run thus it is important to pay attention that you are targeting the desired service or characteristic based on the UUID. To read or write the data of an attribute we must select it in bluetoothctl using the select-attribute command. Then provided that we have permission, we can read the attribute's data with the read command or write data to it with the write XX XX ... command where each XX is one byte of data.
[bluetooth]# select-attribute /org/bluez/hci0/dev_XX_XX_XX_XX_XX_XX/service0027/char0028
[Bluefruit HRM:/service0027/char0028]# read
Attempting to read /org/bluez/hci0/dev_XX_XX_XX_XX_XX_XX/service0027/char0028
[CHG] Attribute /org/bluez/hci0/dev_XX_XX_XX_XX_XX_XX/service0027/char0028 Value:
  00 45                                            .E              
  00 45                                            .E
Notice that the read command shows the raw data but is not necessarily capable of decoding it properly. In this case the data is a 16bit unsigned integer so the value shown 0x0045 corresponds to 69 beats per minute. Rather than polling the attribute we can also ask to receive a characteristic's data when it changes using the notify on|off command, again provided we have permission.
[Bluefruit HRM:/service0027/char0028]# notify on
[CHG] Attribute /org/bluez/hci0/dev_C3_3F_A5_0F_E7_10/service0027/char0028 Notifying: yes
Notify started
[CHG] Attribute /org/bluez/hci0/dev_XX_XX_XX_XX_XX_XX/service0027/char0028 Value:
  00 34                                            .4              
[CHG] Attribute /org/bluez/hci0/dev_XX_XX_XX_XX_XX_XX/service0027/char0028 Value:
  00 4c                                            .L              
[Bluefruit HRM:/service0027/char0028]# notify off

Coding for Arduino

Platformio Crash Course

Now that we have a bit of exposure to using Platformio form testing a new IoT device, let's dive a bit deeper and build a new application targeting a Flora connected to a Bluefruit. Platformio gives developers options for structuring their code. When programming for the Flora, I stick with the default which is to use the Arduino framework. This means I need to provide a C++ source file with at least a void setup() and a void loop() function. For those coming from the Arduino IDE, you know that the IDE creates a folder for your project containing a single file with the same name as the enclosing folder but ending with the extension ".ino". Platformio by contrast uses the more conventional project structure (for C/C++ developers anyways) with a project folder containing a "lib" folder for for dependancies you have created and a "src" folder where you should place your application's source code. It also one or two "hidden" directories where the library manager, platformio lib will place third party dependancies and "hidden" files for configuring integration with git and the travis continuous integration system. I encourage you to create a README or README.md file in the root of your project to tell others how they can build and use your project. I also encourage you to add a "docs" folder and to wiring diagrams for your project there. For example:

Folder Structure of a Typical Platformio Project

We'll start by making a project directory then initializing a new platformio project in it.

$ cd ${PROJECTS_FOLDER}
$ mkdir ${PROJECT NAME}
$ cd ${PROJECT NAME}
$ platformio init -b flora8
Note: Unlike with the Arduino IDE, you can use spaces in your project name with platformio. The final command creates the structure of the project folder and outputs some helpful information and tips on using platformio. The "-b" indicates that the next word on the command line indicates which board, IoT device, we are targeting, in this case "flora8" representing the Adafruit Flora. You can see the complete list of supported boards using the platformio boards command. If you planned on putting the project under version control you should do that now. I like to use git:
$ git init
$ git remote add origin ${URL_OF_YOUR_EMPTY_GIT_REPOSITORY}
Let's start coding with an empty Arduino application template and learn about the code as we go. Create a new file in the "src" folder, name it what you like so long as it ends with ".cpp", and paste in the code below:
/**
 *	${APPLICATION NAME}
 *
 *	@author ${NAME} <${EMAIL}>
 *	@license ${LICENSE}
 *
 *	@target ${PLATFORM}
 *	[@dependancy ${DEPENDANCY NAME} (${PLATFORMIO INSTALL COMMAND})]
 */

#include <Arduino.h>

/**
 *	setup is a special function defined by the Arduino platform
 *	that is called once after the core firmware initialization
 *	happens but before loop is called for the first time
 */
void setup() {
	
}

/**
 *	loop is a special function defined by the Arduino platform that is
 *	called continuously in an infinite loop once platform specific
 *	initialization and setup() have run.
 */
void loop() {
	
}
You could compile this project now though if you loaded it onto a Flora it would do nothing. Seasoned developers may recognize the gist of this template but some explanation may be helpful for those coming form different backgrounds. The template starts with a fairly typical multi-line comment meant to inform other developers about the code contained in the file including who wrote it, under what conditions they can use the code and what they need to compile it. Here is how I might fill it in:
/**
 *	Coding for Arduino Example
 *
 *	@author Tom Egan <tom@tomegan.tech>
 *	@license Creative Commons Attribution 4.0 International License.
 *
 *	@target Adafruit Flora v3 (flora8)
 *	@dependancy Adafruit NeoPixel (platformio lib install 28)
 *	@dependancy Adafruit Bluefruit (platformio lib install 177)
 */
Next is a "preprocessor" line: #include <Arduino.h> telling the complier that we want to pull in the Arduino framework. Finally we have to empty function definitions. Functions in C++ are lists of commands that a computer in to perform. It is because we have provided no instructions in these functions or any other functions that a Flora loaded with a compiled form of this code will do nothing. For more experienced programmers coming from other environments you may be asking where the void main() function is? The answer is that the Arduino framework takes care of that for us. I imagine the Arduino framework as having a main function akin to:
void main() {
	init();	// Arduino specific stuff that runs before our our code
	setup();
	while(1) {
		loop();
	}
}

Next, lets make use of the LED's built into the Flora. Adafruit calls this LED array a "NeoPixel". Therefore we will pull in the library for the Adafruit Neopixel. But first we need it's ID. I wish there was some sort of naming convention for code libraries… e.g. com.adafruit.bluefruit_nrf51 that would let us install libraries by name but for historical reasons there is not. Platformio attempts to make the messiness of managing C/C++ libraries possible by providing the ability to search for libraries:

$ platformio lib search Neopixel
platformio lib search Neopixel
Found 25 libraries:

NeoPixelBus
===========
#ID: 547
A library that makes controlling NeoPixels (WS2811, WS2812, WS2813 & SK6812)
and DotStars (APA102) easy.  Supports most Arduino platforms.  Support for RGBW
pixels.  Includes seperate RgbColor, RgbwColor, HslColor, and HsbColor objects.
Includes an...

Keywords: neopixel, ws2811, ws2812, ws2813, sk6812, dotstar, apa102, rgb, rgbw
Compatible frameworks: Arduino
Compatible platforms: Atmel AVR, Atmel SAM, Espressif 32, Espressif 8266,
Intel ARC32, Microchip PIC32, Nordic nRF51, Nordic nRF52, ST STM32, Teensy,
TI MSP430
Authors: Michael Miller

Adafruit NeoPixel
=================
#ID: 28
Arduino library for controlling single-wire-based LED pixels and strip.

Keywords: display
Compatible frameworks: Arduino
Compatible platforms: Atmel AVR, Atmel SAM, Espressif 32, Espressif 8266,
Intel ARC32, Microchip PIC32, Nordic nRF51, Nordic nRF52, ST STM32, Teensy,
TI MSP430
Authors: Adafruit

FastLED
=======
#ID: 126
FastLED is a library for programming addressable rgb led strips (APA102/Dotstar,
WS2812/Neopixel, LPD8806, and a dozen others) acting both as a driver and as a
library for color management and fast math.

Keywords: led, noise, rgb, math, fast
Compatible frameworks: Arduino
Compatible platforms: Atmel AVR, Atmel SAM, Espressif 32, Espressif 8266,
Freescale Kinetis, Nordic nRF51, NXP LPC, ST STM32, Teensy
Authors: Daniel Garcia, Mark Kriegsman
[…]
Twenty five results ends up being a long list however. It can be difficult to pick out the correct result. Fortunately, platformio provides several ways to narrow your search. Because I typically begin learning about new devices by looking at example code I often have some idea what header files are required to use a devices or feature. Thus I have had some success searching by header file:
$ platformio lib search "header:Adafruit_NeoPixel.h"
Found 4 libraries:

Adafruit NeoPixel
=================
#ID: 28
Arduino library for controlling single-wire-based LED pixels and strip.

Keywords: display
Compatible frameworks: Arduino
Compatible platforms: Atmel AVR, Atmel SAM, Espressif 32, Espressif 8266,
Intel ARC32, Microchip PIC32, Nordic nRF51, Nordic nRF52, ST STM32, Teensy,
TI MSP430
Authors: Adafruit

Gamebuino META
==============
#ID: 5152
Make your first game within hours.

Keywords: device, control
Compatible frameworks: Arduino
Compatible platforms: Atmel SAM
Authors: Sorunome, Aurélien Rodot et al., Rodot

RBL_nRF8001
===========
#ID: 682
An Arduino library for the nRF8001 products such as the BLE Shield and Blend.

Keywords: communication
Compatible frameworks: Arduino
Compatible platforms: Atmel AVR
Authors: Cheong

Pinduino
========
#ID: 1319
Infterface library for pinduino

Keywords: device, control
Compatible frameworks: Arduino
Compatible platforms: Atmel AVR
Authors: Eric Lyons
Four results is much more comprehensible and the library we need happens to be the first one in the list, #ID: 28. You can find more ways to search for libraries using the help flag for platformio lib search -h. Platformio installs libraries to the hidden .piolibdeps folder. These libraries are usually identical to the libraries you get if you were to install them in the Arduino IDE… including the examples.
$ ls .piolibdeps/Adafruit\ NeoPixel_ID28/examples/
RGBWstrandtest	StrandtestBLE	buttoncycler	simple		strandtest
Each of these folders is in fact an Arduino IDE project. You can even copy the contents of one of these folders, such as "simple", to your "src" directory and build your project with few to no changes. Let's however continue building an application piece by piece. To inform the compiler about the datatypes and functions provided by the library we just installed, we need to add #include <Adafruit_NeoPixel.h> on a new line just after #include <Arduino.h>. Next, I like to add constants for values that while documented may not be obvious as this will make our code more readable. Therefore you should add:
/******************************
	Onboard NeoPixel Support
******************************/
#define FLORA_ONBOARD_PIXEL_PIN 8
#define FLORA_ONBOARD_PIXEL_COUNT 1
#define FLORA_ONBOARD_PIXEL_ID 0
after the #include lines but before the functions. These constants indicate that attached to IO pin eight (8) of the Flora there is one (1) NeoPixel with ID zero (0). Now we need to do something that might offend experienced programmers, we need to put some data in the global scope. Adafruit's Neopixel library exposes a class that we can use as an interface to the NeoPixel hardware. We do not however want to create an instance of this class every time we pass though the loop function as instantiating a class can use a significant number of computations which would in turn slow down the execution of our loop function. We also want to use the same interface in our setup function to place the NeoPixel in a known state. So we are going to violate the maxim "Think globally, act only within the local scope". In general when it comes to embedded programming it is acceptable and even beneficial to place data in the global scope if: it models hardware that is always connected and it does not need to be reinitialized with every pass through the void loop() function or it is data that we need to track from one pass through the void loop() function to another. Now opinions do vary on whether one should simply declare the data in the global scope and instantiate it in the void setup() function or whether it is better to instantiate the data right away. Certainly, if you have multiple data structures in the global scope that must be instantiated in a particular order, you should place the instantiation in your void setup() function. Typically, though any data structures in the global scope will be independent of one another so calling the constructors in the global scope is not a problem. I try to err on the side of caution however and thus place calls to constructors in the void setup() function. Therefore, after the constants add: Adafruit_NeoPixel strip; on a new line and in your void setup() function add:
strip = Adafruit_NeoPixel(FLORA_ONBOARD_PIXEL_COUNT, FLORA_ONBOARD_PIXEL_PIN, NEO_GRB + NEO_KHZ800);
We can now use the Adafruit Neopixel by calling methods on the global variable strip in either the void setup() or the void loop() functions. For now let's have our LED turn on and appear green. To accomplish this we need to add:
strip.begin();
strip.setPixelColor(FLORA_ONBOARD_PIXEL_ID,0,255,0);
strip.setBrightness(7);
strip.show();
to the void setup() function. Walking through these lines, strip.begin() sets up communications to a NeoPixel connected to pin FLORA_ONBOARD_PIXEL_PIN (8) as we specified in the constructor. The next line sets the color of LED array FLORA_ONBOARD_PIXEL_ID(0) as red/green/blue components. Each component is an 8bit unsigned integer value making 0 equivalent to no light of a color and 255 equivalent to as much of a color as possible. The next line sets the brightness of the LED array, in essence a scaling factor that is applied to each color component. The NeoPixel can be a very bright light source. I find 15 to be intolerably bright and 7 bright enough to be noticeable from several meters away indoors. The last line, strip.show(), applies the changes to the color and brightness by sending commands to the LED instructing it to adopt the color and brightness we have just set. Experienced programmers might think of it as sort of sync or flush call pushing changes to the internal state model to the physical hardware. We can compile this code using:
$ platformio run
Hopefully it compiles without errors. If the compiler does report and error, well let's just admit some errors are quite cryptic for new developers. Fortunately, there is a large community working with Arduino on the internet and if you copy the error and paste it into the search engine of your choice, it is fairly likely that you will find relevant hints as to how to solve your error. You can also compare with my code push it to a Flora using:
$ platformio run -t upload

Success feels good though the simplicity of the achievement so far may leave you feeling underwhelmed. We are about to ramp up the complexity fairly quickly but it is hard to overstate how useful it can be to build projects bit by bit. Making the LED blink is fairly simple. In the loop() function add:

strip.setBrightness(15);
strip.show();

delay(100);

strip.setBrightness(7);
strip.show();

delay(900);
I have introduced setBrightness(uint8) and show() already so you should see that every time the Flora passes through the loop() function it will increase the brightness of the LED array then decrease it. There is just one new function here: delay(uint) which causes the Flora to not execute any more commands for the specified number of milliseconds. One big caveat here; the number of milliseconds is approximate. You should never rely on delay(uint) for millisecond accuracy timing. However, you should now see that the brightness of the LED array should increase for roughly 1/10th of a second then return to the initial level for 9/10th of a second making the LED array appear to pulse once a second.

Using the Bluefruit nRF51

We will keep building on the code in the previous section. If however you decided to skip everything we have done thus far you can catch up by creating a new platformio project targeting the Adafruit Flora (flora8) platformio init -b flora8, installing the Adafruit Neopixel library platformio lib install 28, and adding my code to your "src" directory.

As we did with the NeoPixel, we will start toward using the Bluefruit by installing the requisite library in our project using platformio platformio lib install 177. Next we will inform the compiler about the library by adding the necessary preprocessor lines. Because Bluefruit support is a bit more complex, Adafruit made the decision to break the library into multiple parts based on how the Bluefruit board might be connected to an Arduino compatible board. We only need the parts relevant for the Flora which are the core or shared component and the part implementing Bluefruit support when connected via a UART connection as this is what the Flora offers via pins 0 and 1 where we have connected the BlueFruit. You may have noticed that there are a number of wire protocols supported by Arduino that are sometimes called "serial". They are I2C, SPI and UART and none of them are compatible with the RS-232 Serial protocol you may remember from the late 1980's and early 1990's In an ideal world we would be ready to move on however, we need to include a library included with the Arduino framework but not included in the #include <Arduino.h> as it is not supported on all Arduino compatible boards, the Serial Peripheral Interface (SPI) library. We will not be using SPI but need to include it here due to an issue in version 1.9.5 of the Adafruit Bluefruit nRF51 library that assumes this library is always present. Together this means:

#include <Arduino.h>
#include <Adafruit_NeoPixel.h>
becomes:
#include <Arduino.h>
#include <SPI.h>

#include <Adafruit_NeoPixel.h>

#include <Adafruit_BLE.h>
#include <Adafruit_BluefruitLE_UART.h>
Just like with the NeoPixel we need to some global variables for data we will access in both the setup() and loop() functions.
/******************************
	Bluetooth Support
******************************/
// the connection to the Bluefruit
Adafruit_BluefruitLE_UART ble;

// the id of the BT LE Characteristic where we will be posting data
int32_t hrmMeasureCharId;
Adafruit_BluefruitLE_UART is a class provided by Adafruit which in the lingo "wraps" a UART "serial" connection for the purpose of hiding some of the technical details about communications with the Bluefruit while the int32_t is a 32 bit integer value which will be given to us by the Bluefruit and which we will use to name a BT LE Characteristic served to clients by the Bluefruit. In the setup() function, we initialize the Adafruit_BluefruitLE_UART object, passing in Serial1 as the UART to wrap.
ble = Adafruit_BluefruitLE_UART(Serial1);
Serial1 is defined in the board specification for the Flora provided by Adafruit as the UART using pins 0 and 1. The number and names of UART interfaces available on various Arduino compatible boards varies. Most if not all Arduino compatible boards have at least one UART defined as Serial in Arduino.h which is connected to via the USB port. On a fair number of boards this UART is the only UART and may also wired up to pin outs on the board. Always read the hardware specifications carefully when choosing an Arduino compatible board and if you are looking for a board that you can simultaneously connect to with USB and to a peripheral using UART make sure the board offers an adequate number of distinct UART interfaces. With the UART wrapped as an Adafruit_BluefruitLE_UART we can instruct the Arduino board to begin communicating with the Bluefruit using the begin() method.
ble.begin();
Unlike when using the Serial UART to communicate with a computer over the USB port we do not need to supply the baud rate when using the Adafruit_BluefruitLE_UART wrapped UART. This is the first of many technical details that Adafruit_BluefruitLE_UART takes care of for us. Another, technical detail that Adafruit_BluefruitLE_UART hides is performing a "factory reset" on the Bluefruit to clear out any BT LE Services and Characteristics that may have been added by "user" code i.e. code running on a connected Arduino, in the past.
ble.factoryReset();
Unlike with the NeoPixel where we send commands to the device and trust that it takes the desired action, with the Bluefruit we get a response back from most commands which the Adafruit_BluefruitLE_UART turns into a boolean value; true when the command succeeded and false otherwise. Therefore, we can report back over a USB connection when something went wrong by adding:
//init communications over the USB port
while(!Serial) {
	delay(1000);
}
Serial.begin(9600);
at the beginning of the setup() function and changing the method calls on ble to be:
//init communications with Bluefruit
bool success;

ble = Adafruit_BluefruitLE_UART(Serial1);
success = ble.begin();
if(!success) {
	Serial.println("Error: Unable to communicate with Bluefruit. Is it connected properly?");
}

success = ble.factoryReset();
if(!success) {
	Serial.println("Error: Unable to perform factory reset of Bluefruit.");
}
Of course these changes have the downside of causing the Arduino to loop endlessly in the setup() function if a computer is not connected and listening via the USB port and while the Arduino reports the errors it continues try to talk with the Bluefruit even if the begin command fails. Therefore you will find a function in many Arduino programs named something like: error(const char*) or fatalError(const char*) which print the error over Serial if a computer is connected then loop endlessly, in effect halting the board. We can add something similar though our function will take a const __FlashStringHelper* as it's parameter. This type is used with the F() marco which helps with memory management, specifically by keeping string constants out of RAM.
/**
 *	Should a fatal error occur this method will change the LED color to RED
 *	try to output a message over the (USB) serial port, and busy halt the
 *	device
 */
void fatalError(const __FlashStringHelper* err) {
	strip.setBrightness(7);
	strip.setPixelColor(FLORA_ONBOARD_PIXEL_ID, 255, 0, 0);
	strip.show();

	// Wait for serial
	while(!Serial) {
		delay(1000);
	}
	Serial.begin(9600);
	Serial.println(err);
	
	while(1) {
		delay(10000);
	}
}
And one last time we can rewrite our code in setup() for starting communications with the Bluefruit by removing the bit about initializing communications over the USB port and changing the rest to be:
//init communications with Bluefruit
bool success;

ble = Adafruit_BluefruitLE_UART(Serial1);
success = ble.begin();
if(!success) {
	fatalError(F("Error: Unable to communicate with Bluefruit. Is it connected properly?"));
}

success = ble.factoryReset();
if(!success) {
	fatalError(F("Error: Unable to perform factory reset of Bluefruit."));
}
We can build on this code and add a BT LE predefined Service (16bit UUID) such as the Heart Rate Measurement Service with just a little bit more code.
// this line is particularly required for Flora, but is a good idea
// anyways for the super long commands ahead!
ble.setInterCharWriteDelay(5); // 5 ms

// Change the device name to make it easier to find
success = ble.sendCommandCheckOK(F("AT+GAPDEVNAME=Bluefruit HRM"));
if(!success) {
	fatalError(F("Error: Could not set device name"));
}

// Add the Heart Rate Service
success = ble.sendCommandWithIntReply(F("AT+GATTADDSERVICE=UUID=0x180D"), &serviceId);
if(!success) {
	fatalError(F("Error: Could not add service"));
}

// Add the Heart Rate Measurement characteristic
success = ble.sendCommandWithIntReply(F("AT+GATTADDCHAR=UUID=0x2A37, PROPERTIES=0x10, MIN_LEN=2, MAX_LEN=3, VALUE=00-40"), &hrmMeasureCharId);
if(!success) {
	fatalError(F("Could not add Heart Rate Measurement characteristic"));
}

// Add the Body Sensor Location characteristic
success = ble.sendCommandWithIntReply(F("AT+GATTADDCHAR=UUID=0x2A38, PROPERTIES=0x02, MIN_LEN=1, VALUE=3"), &hrmLocationCharId);
if(!success) {
	fatalError(F("Could not add Body Sensor Location characteristic"));
}

// Add the Heart Rate Service to the advertising data (needed for Nordic apps to detect the service)
ble.sendCommandCheckOK(F("AT+GAPSETADVDATA=02-01-06-05-02-0d-18-0a-18"));

// Reset the device for the new service setting changes to take effect
ble.reset();
At this point we have done a lot of work in setup() but nothing in loop(). However, you should be able to compile the code so far, push it onto a Flora connected to a Bluefruit, and using the iOS or Android Bluefruit app connect to the Bluefruit with Bluetooth to see that the IoT hardware reports a heart rate and sensor location. If you have a compilation issue you can of course compare your code with my code.

Coding for Linux

Having written my first program for Linux nearly twenty years ago I feel that programing for Linux has never been easier… once you get started. There are so many languages and so many libraries available that it can be overwhelming to choose when starting a project. In the years since I have started more than one project only to throw it all away and start over in a different language or rip major parts out in order to use a library I thought would make things easier. For this reason I am going to build upon the python example code found in the Bluez project. If you clone the Bluez source code from https://git.kernel.org/pub/scm/bluetooth/bluez.git you will some interesting sample code in the ${BLUEZ_PROJECT}/test/ directory. Specifically, take a look at example-gatt-client which happens to be a python script.