Writing your first Android Things driver
Android Things is a lightweight version of the Android operating system that can be installed on IoT-focused devices. Android Things includes ‘user drivers’ functionality, enabling developers to easily control a wide range of peripherals (LEDs, switches, buttons, servo motors etc). Let’s take a look at what it takes to write a new peripherals driver for Android Things.
At the time of writing, Google hasn’t announced what the requirements will be for a piece of hardware to run Android Things but the hardware documentation states that they’re focusing on system on module (SoM) boards. While the only supported development boards for Android Things are the Raspberry Pi 3, the Intel Edison and the NXP Pico i.MX6UL, it would seem that almost any board will be able to run Android Things. The supported boards don’t offer many sensors, LEDs and buttons to play with, but once an Android Things device is up and running you can easily plug in peripherals.
One of the things that got me excited about working on an Android Things project was the idea of user drivers.
As shown in the above image, developers can use the Android Framework APIs directly and build their apps using user drivers. These are a thin layer that accesses and controls a peripheral piece of hardware from the Android Things app. This allows us to focus on what matters: writing an awesome app.
You can find a list of the available user drivers on Github. The cool thing about these drivers is that they’re like any other Android library, meaning you can add one line to the project’s
build.gradle dependencies and the driver will be imported.
What happens if you want to use a peripheral that doesn’t have an Android Things driver yet? You have 2 options:
- Pick a different peripheral and hope there’s a driver for that.
- Go the DIY-way and write the driver yourself.
If you take the second option, you can contribute back to the open source community by sharing the driver to let anyone (including you) reuse that code in future projects. To do this you’ll need the peripheral documentation that the vendor provides [¹].
First of all you have to understand which communication protocol the peripheral uses. Android Things has support for the following types of communication:
- General-purpose input/output (GPIO): This is the simplest way of communicating, you can use it to read from the peripheral (input) and write to the peripheral (output)[²]. Each physical pin represents either an input or an output (you can configure each pin mode from your app) and it can take only two values: up and down (or 1 and 0). Use GPIO with buttons, motion sensors, etc.
- Pulse Width Modulation (PWM): Similarly to GPIO, PWM uses one physical pin but it is output-only (meaning the Android Things board sends data, never reads). Using PWM lets you control more complex devices such as servo motors, dimmable lights, and other devices that can take a wider range of values, rather than just 1 or 0. See the documentation of PWM for further reading on this interface.
- Serial Peripheral Interface (SPI): If your peripheral is a bit more complex it will probably need SPI, which is used in a wide range of peripherals, from RGB LED strips to graphical displays. SPI allows a master (in this case the Android Things device) to communicate with one or more slaves.[³] It uses either one uni-directional data exchange pin or two bi-directional exchange pins for data transfer, one pin for clock and, in the case of multiple slaves, it needs extra Chip Select lines.
- Inter-Integrated Circuit (I2C): I2C is based on two pins since it needs the data connection and a clock. Similarly to SPI, I2C can be used with one or more slaves, but each slave has its own address that the master can specify. In this case, the master sends a byte of data at a time and the slave has to acknowledge if it has received it correctly. This acknowledgement is built-into I2C. This protocol is used in some sensors, LCD displays etc.
- Universal Asynchronous Receiver Transmitter (UART): While SPI and I2C are both synchronous interfaces (because they both need a clock line to synchronise), UART is asynchronous. A simple implementation of UART needs two pins: one data in and one data out. Unlike SPI and I2C, UART doesn’t support multiple slaves and the data is wrapped in data frames. A data frame includes a start bit, 5–9 data bits, possibly a parity bit and one or two end bits. UART is used with GPS devices, XBee radios, some printers etc.
Read the documentation from the peripheral vendor to find out what to use. If it uses one of the above methods then implementing a driver should be quite straight forward. Otherwise you’ll have to implement whichever protocol it uses with GPIO ports.
Investigating the driver protocol
I bought this RGB LED strip with the code WS2801 printed on both chips. This is the datasheet for the WS2801, which I found by searching for ‘WS2801 datasheet’. In the PDF, the features list mentions that the chip uses PWM, but don’t let that fool you. On page five there’s a list of input and output pins and you can see the chip has a clock input (CKI), a data input (SDI), a power supply (VCC) and ground (GND), which are the pins that you can also find in the LED strip.
Investigating which protocol to use, the main indicator here is the number of pins needed. While power and ground are generic for most peripherals, the need for a clock and data input as separate pins indicates that you have to use either I2C or SPI (not PWM as that only uses one line).
Another indicator is the clock speed: the I2C standard is 400kHz (but can go up to ~3MHz), while the datasheet says the WS2801 accepts a maximum clock frequency of 25MHz.
The third and last indicator is the data format: on page 12 it shows that the only data the chip needs to operate is 3 bytes (red, green and blue values) and it will leave the rest of the data for the next chip (ie next LED of the strip). There is no mention of acknowledgement or 2-way communication. This leaves out I2C and UART from the list so you can use SPI.
A peripheral datasheet will also specify important values such as minimum and maximum voltages, maximum clock speed and whether the data is read on the leading or trailing clock edge.
Once you know which form of communication you have to use (SPI in the above example) you can start thinking about connecting and coding.
Connecting the peripheral to the developer board
It’s time to find out which pins on the board need to be used in order to talk to the peripheral from the Android side. The easiest way is to find the pinout for your development board on the Android Things developer site. A pinout shows which pin can be used for each form of communication. Have a look at the Raspberry Pi 3 pinout for example:
In this case I used pin 2 (or 4) for power (VCC), pin 6 (or 9, 14, etc) for ground (GND), pin 23 for clock (CKI) and pin 19 for data (SDI). Note that there are two similar pins: 19 — MOSI, which stands for master out slave in and pin 21 — MISO or master in slave out. In this case I had to use MOSI because the master (Android Things device) will send out data and the slave (LED strip) will read it in.
One important thing to note is different boards have different output voltages and different peripherals need different voltages to operate, so be careful. The peripheral may not work and could burn out, so always check the specs before plugging a peripheral into a board and use a level converter if needed. For example, this article on Raspberry Pi 3 GPIO explains that pins can output either 0V(low) or 3.3V (high) and it also has two 5V pins. If the GPIO pins are configured as input then using any value higher than 3.3V will damage the board.
At this point you should have a peripheral device plugged in to your Android Things board and you know the protocol you’re going to use. So, it’s time to start writing some code.
Now let’s look at how to implement the peripheral driver using SPI. A few days ago I submitted a PR to the drivers repository on Github, adding the driver for WS2801. These are the main things you need to include when writing a driver.
Android Things driver creation
Start by creating a standard gradle module and in the
Remember, this is like any other Android library. You can use the
com.android.library plugin and for Android Things you'll need to use at least API 24 as minimum SDK version. You should also specify
com.google.android.things:android things as a
provided dependency. This means you need it to be able to compile the app, but you don't want to distribute this dependency with the driver once it’s released.
Next you need to add one line to your
Adding this line is the way to instruct the app to only run on Android Things devices rather than phones, tablets etc.
Now for the driver itself. Create a new class (I called it
Ws2801.java) and write the driver code. The first thing you’ll need to do is open the SPI port so you can start sending data to it, and then close the port. Here are the main parts:
Notice that first you need to create an instance of the
PeripheralManagerService. This is used to get information of the different input/output (I/O) interfaces the development board has, as well as open buses or GPIO pins. In the example code above, I called the
openSpiDevice(String spiBusName) method to open the SPI bus interface. Once you have a reference to the
SpiDevice you can configure the bus and start sending it data.
Note: the code has been simplified, see the full source code on Github.
And that’s all you need.[⁴] Now you can start using the driver.
So far we have looked at the different types of inputs and outputs Android Things boards support and how to identify which interface is needed to communicate with a peripheral. We’ve covered which pins to use for each different interface and how to implement a driver to control the peripheral using the Android Things SDK.
In my next blog post we’ll look at how to test the peripheral driver to improve your confidence when using or sharing your peripheral driver. If you’d like to discuss this post or give me any feedback on your own experiences for my next blog post please get in touch with me on Twitter or Google+ or leave a comment below.
[¹]: When you want to use any peripheral for the first time always check the manufacturer’s website for the datasheet, usually a PDF file. It may also be in the product description on the seller’s website. If you can’t find it on either of those websites then search for the keywords you see written on the chips or the board.
[²]: GPIO pins cannot be used as an output if they were previously enabled as an input with an edge trigger enabled since the last reboot.
[³]: A slave can be seen as an individual component on your hardware peripheral, e.g: each sensor in a rack of sensors.
Originally published at novoda.com.