product & design

Porting a python library to Android .. Things - The InkypHat

Likes to Android with the aim of making that million dollar app one day; retiring to a beach of his choice. For now writes blog posts to hopefully enlighten others & gain some feeling of self worth.

Android Things allows you to create driver libraries around your IoT peripherals. This is a powerful platform feature allowing you to use peripherals without needing to know the low level details. In this blog post, I’ll show you how such a driver was created for the Inky pHAT e-paper display.

The Inky pHAT e-ink display is a 212 x 104 pixel three-colour display. You can draw in black, white or red. As it uses e-ink, the screen is perfectly readable in bright sunlight. You can also disconnect the power and it will continue to display the last image drawn. Ideal for badges or battery-powered IoT devices.

Whenever starting with a new peripheral, first you should check an Android Things driver library has not already been created. After that, reading the datasheet or the documentation that is sometimes supplied on the vendor’s website will suffice. I bought the Inky pHAT from Pimoroni and their website links directly to the Python library.

Reading the Python source code in order to create an Android Things driver library, we need to break down our problem and understand a few things to proceed.

Once we know these three definitions, we can go about starting to write some Android code.

The Python repository is made up of a couple of folders including a sample use folder and the library itself. The code for driving the Inky pHAT is mostly within Inky214x104.py, so we’ll concentrate on this file.

Which Peripheral IO APIs does it use (GPIO, SPI, I2C etc)?

First, look at the imports and the __init__ method (equivalent to a Java constructor). We can see the class is importing SPI and GPIO. It seems there are 3 GPIO pins used and 1 SPI bus. That’s good for us, we have GPIO & SPI in the AndroidThings Peripheral IO APIs. Converting the peripheral IO use from Python to Java will look something like this:

Python

       GPIO.setup(self.dc_pin, GPIO.OUT, initial=GPIO.LOW)
       GPIO.setup(self.reset_pin, GPIO.OUT, initial=GPIO.HIGH)
       GPIO.setup(self.busy_pin, GPIO.IN)


       self._spi = spidev.SpiDev()
       self._spi.open(0, self.cs_pin)

Java

   spiBus.setMode(SpiDevice.MODE0);
   chipCommandPin.setDirection(Gpio.DIRECTION_OUT_INITIALLY_LOW);
   chipCommandPin.setActiveType(Gpio.ACTIVE_HIGH);
   chipResetPin.setDirection(Gpio.DIRECTION_OUT_INITIALLY_HIGH);
   chipResetPin.setActiveType(Gpio.ACTIVE_HIGH);
   chipBusyPin.setDirection(Gpio.DIRECTION_IN);
   chipBusyPin.setActiveType(Gpio.ACTIVE_HIGH);

What is the public API exposed in order to use the peripheral?

The public API can tell us how the original author expects the peripheral to be communicated with. It can give hints to features available but also explain timings or execution order necessary for correct use.

Looking through the examples. It sets a border, sets an image on the Inky pHAT, then calls show. Diving into the show() method we have set_pixel and update. This makes sense, we can set any pixel we want on the display. After we’ve configured what we want to show, we call update to refresh the display itself.

Whilst this isn’t the full API, it's enough to get us started and to use the display. 1

Python

def set_pixel(self, x, y, v):  
def update(self):  

Java

void setPixel(int x, int y, int color);  
void refresh();  

Which features does the device support?

The examples are a great place to understand feature support. We already know we can set any pixel to black, white or red. Looking at a further example, it seems partial updates of the screen are possible. 2

Looking into the update(self) mechanism for the Inky pHAT. It seems with every call to update:

Diving into the black pixel data transmission it looks like this:

Python

        # start black data transmission
        self._send_command(_DATA_START_TRANSMISSION_1)
        self._send_data(buf_black)

Which is easily translated to Android:

Java

sendCommand(DATA_START_TRANSMISSION_1, pixelBuffer.getDisplayPixelsForColor(Palette.BLACK));  

Understanding the send_command method and the send_data method is getting into the knowledge of the Peripheral IO APIs. Once we have converted these we will be able to communicate with the Inky pHAT from our Android driver library.

Python

    def _send_command(self, command, data = []):
        #print("Command {0:02x}".format(command))
        self._spi_write(_SPI_COMMAND, [command])
        if len(data) > 0:
            self._spi_write(_SPI_DATA, data)

    def _spi_write(self, dc, values):
        GPIO.output(self.dc_pin, dc)
        self._spi.xfer(values)

Here _send_command first writes to the command GPIO pin (dc_pin). It sets this pin to true3 which tells the Inky pHAT that the next chunk of bytes it receives over SPI is a command. Then it sets the same command pin to false, meaning the next chunk of bytes is a data array. I think this is conveyed a little better when moving to Android.

Java

private void sendCommand(byte command, byte[] data) throws IOException {  
   sendCommand(command);
   sendData(data);
}

private void sendCommand(byte command) throws IOException {  
   chipCommandPin.setValue(SPI_COMMAND);
   byte[] buffer = new byte[]{command};
   spiBus.write(buffer, buffer.length);
}

private void sendData(byte[] data) throws IOException {  
   chipCommandPin.setValue(SPI_DATA);
   spiBus.write(data, data.length);
}

The beauty of porting the Python library in this way, means after understanding these points, the rest of the code can be moved from Python to Android without detailed knowledge of how it actually works. Here’s an example:

Python

    def _display_fini(self):
        self._busy_wait()
        self._send_command(_VCOM_DATA_INTERVAL_SETTING, [0x00])
        self._send_command(_POWER_SETTING, [0x02, 0x00, 0x00, 0x00])
        self._send_command(_POWER_OFF)

Java

private void turnDisplayOff() throws IOException {  
   busyWait();

   sendCommand(VCOM_DATA_INTERVAL_SETTING, new byte[]{0x00});
   sendCommand(POWER_SETTING, new byte[]{0x02, 0x00, 0x00, 0x00});
   sendCommand(POWER_OFF);
}

We can then turn the display off by adjusting some on-device settings. The four bytes 0x02, 0x00, 0x00, 0x00 must command it to reduce the power. However we don’t need to know more than that to port a working AndroidThings driver library from Python.

The AndroidThings Pimoroni Inky pHAT driver library is now awaiting merge on the contrib-drivers GitHub repository, and will soon be available for use by anyone.

All the end user will need to do is something like this:

Java

InkyPhat inkyPhat = InkyPhat.Factory.create(INKY_PHAT_DISPLAY, BUSY_PIN, RESET_PIN, COMMAND_PIN, Orientation.LANDSCAPE);  
Bitmap bitmap = BitmapFactory.decodeResource(resources, R.drawable.ic_logo);  
inkyPhat.setBorder(InkyPhat.Palette.RED);  
inkyPhat.setImage(0, 0, bitmap, InkyPhat.Scale.FIT_X_OR_Y);  
inkyPhat.refresh();  

This knowledge is transferable to porting other IoT peripheral libraries from multiple languages to Android with AndroidThings. I’m looking forward to the future of IoT with AndroidThings and I hope after having read this you will be inspired yourself to contribute and perhaps make a driver of your own for others to use and benefit from.

If you do, or if there is a peripheral you want that is missing a driver library :-) then let me know on Twitter @Blundell_apps.


  1. I recommend just getting a high level understanding before diving into the coding, rather than attempting to grasp everything upfront. I think you’ll get to the fun stuff faster and more easily that way.

  2. While it’s an interesting feature, I don’t have a use case for this right now, so I’ll save some time and not address partial updates here.

  3. _SPI_COMMAND is a constant for true

Enjoyed this article? There's more...

We send out a small, valuable newsletter with the best stories, app design & development resources every month.

No spam, no giving your data away, unsubscribe anytime.

About Novoda

We plan, design, and develop the world’s most desirable software products. Our team’s expertise helps brands like Sony, Motorola, Tesco, Channel4, BBC, and News Corp build fully customized Android devices or simply make their mobile experiences the best on the market. Since 2008, our full in-house teams work from London, Liverpool, Berlin, Barcelona, and NYC.

Let’s get in contact