App Development

Driving an LCD display: a look into Android Things

blog-featured-image driving an LCD display

The Internet of Things (IoT) is poised to change the way we live and interact with technology, and Google’s latest Android variant, conveniently named Android Things, is positioning itself as a major competitor in the space. Android Things is a lightweight version of Android that allows you to make connected embedded devices using the same Android development tools and APIs that we are already familiar with, while providing access to lower level I/O and interfaces that are typically unavailable on mobile phones and tablets.

Low level access to hardware comes through the Android Things support library, which can be broken into the Peripheral I/O and User-space Driver APIs:

  • The Peripheral I/O apis allow you to communicate directly with sensors and other hardware peripherals through General purpose I/O (GPIO) or industry standard communication protocols such as I2C and SPI.

  • The User-space drivers (User drivers) API allow you to write drivers which can be registered with the Android framework. These drivers can then emit hardware events which can be handled and processed by other apps using the standard Android APIs. As of right now, User drivers are only limited to GPS drivers, Human Interface Devices (HID) for input such as keyboards, touch pads and game controllers, as well as arbitrary Sensors such as Accelerometers, Temperature etc…

User drivers provide an easy way to extend the system and allow you to decouple your application from the underlying hardware I/O implementation. A growing library of open source user drivers is already available on github.

Today, we’re going to explore Android things by driving an LCD display.

Setting up the hardware

To follow along with this guide, you’re going to need a few things:

  1. An Android Things compatible board. You can see which boards are supported here. I’ll be using a Raspberry Pi 3.
  2. A breadboard and some jumper wires.
  3. A 10k Ohm potentiometer.
  4. And a HD44780 compatible LCD display.

If you aren’t really familiar with hardware and general electronics concepts, I recommend checking out this great overview to get you up to speed.

Before we begin wiring up our LCD to our device we need to learn a bit about the pins we will be using starting with pin 1.

Pin 1 is the leftmost pin. It’s usually marked with a “1” on the back of the printed circuit board (PCB).

  1. Pin 1: Ground - this can be connected to any ground pin.
  2. Pin 2: 5V input - this is used to power the controller, it can be connected to any 5v output pin.
  3. Pin 3: Contrast Control - we can use this to adjust the contrast on the display. We will control this using the potentiometer.
  4. Pin 4: Register Select - this pin is used to tell the board whether we will be sending commands or data.
  5. Pin 5: Read/Write - this is used to tell the controller whether we will be writing or reading data. We won’t be using it so we can ground it.
  6. Pin 6: is Clock/Enable - this is how we notify the controller that we have given a command or passed some data.
  7. Pin 7 - 13: These are data pins known as D0-D7. These are used to send commands and data to the controller. We will be interfacing with the board in 4 bit mode, so we only need D4 - D7 (Pins 10 - 13).
  8. Pin 14: LED backlight anode - If your LCD has a backlight, this is its positive terminal.
  9. Pin 15: LED backlight cathode - If your LCD has a backlight, this is its negative terminal.

Wiring things up is very simple now that we have an idea of what each pin is supposed to do. The general idea is 5v to 5v, ground to ground, and the data to unoccupied general purpose I/O (GPIO) pins.

If you’re not sure of what each pin on the Raspberry Pi does, the Android Things documentation and Raspberry Pi datasheet both make handy pin layout reference maps available for your convenience.

Note that pin 1 on the Raspberry Pi 3 is labeled with “J8”.

Note that there are several ground and 5v pins available, you are free to use any of them. The GPIO pins are those labeled “BCM” followed by a number. You should note which pins you use as you will need to refer to them later.

Here’s a reference on how I’ve wired up my Pi to my LCD Display. android things lcd wiring

Putting the software together

Now you have to get your device ready for development, which means getting adb access so you can run your application. The Android Things documentation provides quick but detailed steps on how to connect to adb over Wifi here.

Perhaps the most awesome thing about Android things is the fact that Things projects are just plain old Android projects with the same tools and APIs. This means that the vast majority of the tools and libraries you know and love will probably work with your project. Since most Android Things compatible boards that are available right now support Wifi and/or Ethernet, your apps will have easy access to the Internet to connect to your favorite services.

We can kick things off by creating a new project in Android Studio (or importing the Android Things project template if you want to skip the setup). We’ll need to include the Android Things support library as a compile time only dependency in the application build.gradle file.

provided 'com.google.android.things:androidthings:0.1-devpreview'

Next we’ll need to add the Android Things library to the application’s manifest. We can do this using the uses-library tag.

  <uses-library android:name="com.google.android.things"/>

Android things apps should also include the IOT_LAUNCHER intent filter which tells the system that the app should be launched automatically on boot.

<!-- Launch activity automatically on boot -->
<intent-filter>
   <action android:name="android.intent.action.MAIN"/>
   <category android:name="android.intent.category.IOT_LAUNCHER"/>
   <category android:name="android.intent.category.DEFAULT"/>
</intent-filter>

At this point we are ready to begin writing the code to control our LCD display. We can access the pins which we wired up earlier through the peripheral manager API.

PeripheralManagerService service = new PeripheralManagerService();

We can use it to open connections to our devices GPIO pins. It identifies pins by name so we can refer to our pin out sheet to figure out the name of our pins.

Gpio rs = service.openGpio("BCM6");
Gpio e  = service.openGpio("BCM19");

Gpio d4 = service.openGpio("BCM26");
Gpio d5 = service.openGpio("BCM16");
Gpio d6 = service.openGpio("BCM20");
Gpio d7 = service.openGpio("BCM21");

Now that we have our references, we will need to set the pins directionality. This is how we let the system know whether a pin should be used for input or output or both. The Gpio class provides constants to help with this.

rs.setDirection(Gpio.DIRECTION_OUT_INITIALLY_LOW);
 e.setDirection(Gpio.DIRECTION_OUT_INITIALLY_LOW);

Now that we have references to our pins we can begin sending commands to the board. We need to let the board know that we are sending commands, not data, by setting the register selector pin to low.

rs.setValue(false);

To send a command, we have to do two things. First, convert the value that we intend to pass to the board into its unsigned binary form. Then set the pins to reflect this value and then notify the board that it should read and process the value by briefly toggling the enable pin. Since commands are typically 8 bit values, we can get around this in 4 bit mode by sending them 4 bits at a time.

private void write8(byte value) throws IOException {
    write4((byte) (value >> 4));
    write4(value);
}

private void write4(byte value) throws IOException {
    for (int i = 0; i < dataBus.size(); i++) {
        Gpio pin = dataBus.get(i);
        pin.setValue(((value >> i & 0x01) != 0));
    }
    pulseEnable();
    delay(1);
}

private void pulseEnable() throws IOException {
    enablePin.setValue(false);
    delay(1);
    enablePin.setValue(true);
    delay(1);
    enablePin.setValue(false);
    delay(1);
}

You might be wondering why there are a lot of delays in the code. This is because the display controller cannot accept input while it’s processing commands or data. This means that we need to actively wait before moving on with the next command.

private void delay(int ms) {
   try {
       Thread.sleep(ms);
   } catch (InterruptedException e) {
       Log.e(TAG, e.getMessage());
   }
}

This demonstrates that it’s not a good idea to interact with peripherals on your application’s main thread, especially if your application has to do several tasks or includes a traditional user interface.

Ok, now we nearly have everything we need to finally display some text. We now need to prep the controller to receive text. This is where the data sheet really comes in handy because it details all the commands that the board will accept as well as what they do. We can’t cover nearly half of them in detail, however we can go over the few that we need to get some text going.

// Let the controller know we are sending commands not data
rs.setValue(false);

// We want to send commands and data in 4 bit mode
write4(/*LCD_4_BIT_OPERATING_MODE*/ 0x02);

// Each value we send will be 8 bits in total
write8(/*LCD_8_BIT_FUNCTION*/ 0x28);

// Turn the LCD panel on
write8(/*LCD_DISPLAY_ON*/ 0x0F);

// Characters should come in from the left
// A new character should be to the right of the previous one
write8(/*LCD_SET_ENTRY_MODE_NO_SHIFT_DISPLAY*/ 0x06);

// Clear the display
write8(/*LCD_CLEAR_DISPLAY*/ 0x01);

Now we can finally write stuff to the display! All we need to do is convert characters to their ascii representation and write them as 8 bit values.

private void write(char c) throws IOException {
    write8((byte) c);
}

public void write(@NonNull String val) throws IOException {
    for (char b : val.toCharArray()) {
       write(b);
    }
}

Before we do that however we have to remember to let the board know we are sending data not commands by setting rs to true.

rs.setValue(true);
write("Hello Willowtree");

android things screen Here’s a link to a complete project that uses this peripheral driver.

Conclusion

Making awesome interactive devices with Android Things is really simple. It’s an exciting time to be an Android developer. The APIs are still a little green (which is to be expected from a preview SDK) and Java might not be the most fun for writing embedded drivers due to its lack of native unsigned values; however, it’s all a step in the right direction. The maker movement is coming to Android and it’s coming in a really big way. As a developer this means access to more devices than ever before, giving you the opportunity to create new and unique experiences that your users will absolutely love.

Quickstart-Guide-to-Kotlin-Multiplatform

A Quick Start Guide to Kotlin Multiplatform

Kotlin Multiplatform, though still experimental, is a great up-and-coming solution...

Read the article