In my last experiment I used the Android Things developer console to create a factory image for my bus stop project. I then created an OTA update to update the bus stop apk ‘in the field.’ I concluded that experiment wondering how I was going to get my network credentials onto the device (without using adb). If your device doesn’t have a touchscreen or keyboard how are you going to get it onto your end user’s network. One possible solution to this problem is to use a companion app on the user’s phone to collect configuration information. With a companion app you get all of the UI niceness provided by that platform for ‘free.’ You still need to get the configuration from the phone to your Android Things device. This is where Bluetooth Low Energy comes in.

Bluetooth Low Energy

Bluetooth Low Energy is exactly the kind of short range wireless technology that can help out here. It’s relatively easy to implement using Android APIs and there’s no need for the device to be on a network to configure it. I can start with the BLE sample for Android Things that has almost everything I’m going to need.

For my bus stop project I have two things to configure that require end user input. I need to configure which stop number I want to display bus data for. I also need to get it onto a network to get real-time bus schedule information from a web API.

I’m going to make my bus stop a BLE peripheral device. This means creating a Bluetooth GATT server and adding some characteristics and descriptors to it. If none of what I just said makes any sense take a few minutes to read this BLE tutorial by adafruit.

I’m going to use nRF connect by Nordic Semiconductor for development to get the Android Things side of the equation going. I can deffer development of my own phone app.

Profile, service, characteristic, descriptor

I need to create two services; one for configuring the bus stop, and one for configuring the network. Each service contains characteristics that enable configuration of the device.

The configuration service has a single characteristic; the bus stop number. This is the id of the bus stop as defined by Metlink. Most stops are 4 digit numbers but a few contain letters, e.g., “WELL” for the main train station. I’m going to transfer a string of text via this characteristic.

The network service has a three characteristics; SSID, password, and status. The status characteristic has a descriptor that I can use to subscribe to notifications. The arrangement of services, characteristics and descriptors make up the bus stop’s Generic ATTribute profile (GATT) shown in the image below.

Android BLE API

Using the Android BLE API to make a peripheral means that I need to create a GATT server containing the services and the handlers for the read and write requests from a client. To do this I need to:

  1. get a handle to the system BluetoothAdapter (a representation of the device’s Bluetooth radio)
  2. configure an advertising packet that will be broadcast by the device.
  3. create a GATT server on the device to handle client connections
  4. create the logic in a GATT server callback to handle interaction between the device characteristics and descriptor and the client.

Getting the Bluetooth adapter

The first step to performing any Bluetooth operations with Android is to get hold of the Bluetooth adapter. This means asking the system for a reference to the Bluetooth manager, then asking the manager for the adapter, then enabling the adapter.

In the code above I’m registering a Broadcast Receiver to respond to changes in Bluetooth state. This will tell me when the adapter is enabled and disabled. Because enabling the adapter can take a while, and I can’t block the main thread waiting for this to happen. If the adapter is disabled I enable it, deferring the adapter configuration until the Broadcast Receiver is invoked. If the adapter is already enabled I can start advertising and start the BLE server immediately.

Start advertising

I tell the Bluetooth adapter to start advertising whenever its state changes to STATE_ON. To start advertising I get a BluetoothAdvertiser from the BluetoothAdapter and configure it with AdvertisingSettings and AdvertisingData. I also change the name of the adapter to “Bus Stop.” This is the name I’ll see in nrfConnect. The advertising data packet size is limited to 31 bytes of data, so I’m only adding the UUID for one of my two services. It doesn’t matter that only one of the services is advertised, both services will still be part of the profile.

The advertiseCallback is defined as:

I’m not doing anything with the callback except logging the result. The next task required to get the system to work is to start a GATT server.

Start the server

The openGattServer method of the BluetoothManager takes a BluetoothGattServerCallback, I’ll define this a little later. Once I have an instance of the GATT server I add my two services to it. This is where the profile of the device is defined as services, characteristics and descriptors.

Create the services

To create the services I need to add information about the characteristics and descriptor to a BluetoothGattService.

Here CONFIG_SERVICE and STOP_NUMBER are UUIDs that I’ve defined for my device. I generated the UUIDs with uuidgen. The network service is similar except that I also add a descriptor to one of the status characteristic.

Implement the callback

With all of the above the system will nearly work. All I need to do now is define what will happen when a client interacts with my server. This is done by implementing the BluetoothGattServerCallback that I passed to the Bluetooth manager earlier. the callback is invoked whenever a read or write request occurs on the characteristics and descriptor.

Using nRF Connect

nRF Connect is a great little application made by Nordic Semiconductor. It’s an app that’s invaluable  when you’re experimenting with BLE. I used it a lot, back when it was called Master Control Panel, during the development of the firmware for Spike.  With nRF Connect you can scan for BLE devices, and read and write characteristics and descriptors. With my bus stop application running on my development board, and no stop configured the display shows the imaginative “No Stop Configured” message.

Display with imaginative message

No Stop Configured (yet)

When I scan with nRF Connect, the bus stop shows up as Bus Stop.

Bus Stop Scan

Connecting to it reads the services, characteristics and descriptors. I can expand the services to see the bus stop characteristic. All services, characteristics and descriptors show up as “unknown” because they are the random UUIDs I generated. If I had used UUIDs defined by The Bluetooth SIG, they would have names in the list, but there are no characteristics defined for setting a stop number in a bus stop display. This doesn’t matter because my companion app will know that each of the characteristic UUIDs mean.

Bus Stop Services

Bus Stop Number Characteristic

I can write to the bus stop number characteristic (84ba0101-d691-4bf5-8f5a-093f328cf182″) from the stop number service (84ba0100-d691-4bf5-8f5a-093f328cf182). I’ve saved my local stop number in nRF Connect, so that I don’t need to remember it or type it in.

Writing My Stop

Once it’s written to the characteristic, the bus stop display is updated with departures from the new stop.

Showing Departures for Configured Stop

I do a similar thing for network SSID and Password.

Security

This solution, as it stands, is terribly insecure. There’s nothing stopping anybody from scanning the device, changing the configuration options, and vandalising the bus stop display. If someone knew what they were doing they could change the stop number, so that the display shows busses for another stop. To make this production ready I’d need to implement some kind of authentication mechanism to ensure that only trusted devices are changing the configuration.

Conclusion

Presented here is a way to enable the configuration of an Android Things device without it having any physical input method on the device, and without it already being connected to a network. This was done using Android APIs to turn the device into a BLE peripheral. There are a few steps involved, but nothing difficult about the process. The hardest part was to define what services, characteristics, and descriptors made up the device’s profile. That involved doing a little research to understand what those terms meant. To bring this idea to market I’m going to have to consider security to implement some sort of authentication between the device and the end user’s phone. I’ll also need to write the companion app, (maybe for the MVP I can convince people to use nrf Connect – just kidding).