Architecture & DesignHandling Android Things Peripheral Events

Handling Android Things Peripheral Events

Android Things apps have to communicate with an astounding array of peripheral devices, from buttons, temperature readers, and speakers, to handheld GPSes. Google’s Peripheral Driver Library simplifies the integration of these hardware components into your Android Things apps. Available on Github, it contains numerous drivers for popular peripherals for supported Android Things hardware. In this tutorial, we’ll use the Peripheral Driver Library to connect an LED that we set up in “Building an Android Things Application” via a button click.

About the Peripheral Driver Library

Google’s goal in creating the Peripheral Driver Library was to simplify the integration of various hardware components into an Android Things app. Drivers expose a high-level API that abstracts the communication logic and state management of each peripheral. This approach offers a few benefits, such as:

  • Portability: Application code can run on a variety of different boards and configurations without additional abstractions for the device driver implementation.
  • Reuse: You can pull existing Android code snippets and libraries into your application without the need to modify or fork them to handle your specific hardware implementation.
  • Integration: Android often combines data from various services together to enhance the information reported to apps or create new virtual data sets. User drivers (see below) can contribute to this process.

Drivers are categorized into two types: Peripheral I/O and User Drivers.

Peripheral I/O APIs communicate with sensors and actuators using industry standard protocols and interfaces. These include:

  • General Purpose Input/Output (GPIO): An API for simple sensors such as motion detectors, proximity detectors, and level switches that report their current state as a binary value—high or low.
  • Pulse Width Modulation (PWM): An API for servo motors, DC motors, and lights that require a proportional signal to provide fine-grained control over the output.
  • Serial Communication: A set of APIs to transfer larger payloads of data between two or more smart devices connected on the same local bus.

Android Things introduced the concept of a user driver to allow app developers to register new device drivers with the Android framework. User drivers are components registered within apps that extend existing Android framework services. They allow any application to inject hardware events into the framework that other apps can process using the standard Android APIs.

Drivers can be registered with the UserDriverManager with the registerMyCustomDriver() call:

private MyCustomDriver mDriver;
 
@Override
public void onCreate() {
   super.onCreate();
 
   mDriver = new MyCustomDriver();
     
   UserDriverManager manager =
      UserDriverManager.getManager();
   manager.registerMyCustomDriver( mDriver ); }

Working with the Button Driver

For this tutorial, we’re going to work with the Button Driver. Stored in the com.google.android.things.contrib.driver.button package, the button driver supports GPIO buttons. We’ll use it to listen for button clicks to toggle an LED on and off.

Toggling the LED State

Recall from the “Building an Android Things Application” tutorial that we saved a reference to the LED in the mLedGpio variable. We can use the setValue(Boolean) method on the GPIO to change a component’s state (in this case, between on and off). Add the following function to the MainActivity class:

// This function toggles the button on and off
private void setLedState(Boolean isOn) {
   try {
      mLedGpio.setValue(isOn);
   } catch (IOException e) {
      Log.e(TAG, "Error updating LED state", e);
   }
}

Initializing the Driver

Before we can use the button driver, we first must add the driver artifact dependency to our app-level build.gradle file:

compile 'com.google.android.things.contrib:driver-button:0.3'

Showing the driver button dependency
Figure 1: Showing the driver button dependency

We then need to declare a class level ButtonInputDriver object variable within the MainActivity class for future reference:

public class MainActivity extends Activity {
   private static final String TAG      = "AndroidThings";
   private static final String PIN_LED  = "BCM6";

   private Gpio              mLedGpio           = null;
   private ButtonInputDriver mButtonInputDriver = null;
   // ...

A good place to instantiate and register the ButtonInputDriver instance is within the try block inside the Activity’s onCreate() handler, immediately after the initialization of the LED pin:

// Initialize and register the InputDriver that will
// emit SPACE key events on GPIO state changes.
mButtonInputDriver = new ButtonInputDriver(
   BoardDefaults.getGPIOForButton(),
   Button.LogicState.PRESSED_WHEN_LOW,
   KeyEvent.KEYCODE_SPACE);

mButtonInputDriver.register();

Notice that we didn’t need to define a constant for the button GPIO as we did for the LED pin. Instead, we use BoardDefaults.getGPIOForButton() to fetch it for us.

The ButtonInputDriver will send button press events to the onKeyDown() and onKeyUp() methods with the key code that we specified at driver creation (we’re using the SPACE KEY), which can be overridden in our MainActivity to change the state of the LED:

@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
   if (keyCode == KeyEvent.KEYCODE_SPACE) {
      setLEDState(true);
      return true;
   }

   return super.onKeyDown(keyCode, event); }

@Override
public boolean onKeyUp(int keyCode, KeyEvent event) {
   if (keyCode == KeyEvent.KEYCODE_SPACE) {
      setLEDState(false);
      return true;
   }

   return super.onKeyUp(keyCode, event); }

Finally, we will need to unregister our ButtonInputDriver and set it to null in onDestroy(), much like we did with the LED in the last tutorial:

if (mButtonInputDriver != null) {
   mButtonInputDriver.unregister();
   try {
      mButtonInputDriver.close();
   } catch (IOException e) {
      Log.e(TAG, "Error closing Button driver", e);
   } finally{
      mButtonInputDriver = null;
   }
}

If all goes well, the LED should now light up when we press the button and turn off when we release it.

To show how everything fits together, here is the full source code for the MainActivity class:

package com.htmlgoodies.robgravelle.myfirstandroidthingsapp;

import android.app.Activity;
import android.os.Bundle;

import com.google.android.things.contrib.driver.button.Button;
import com.google.android.things.contrib.driver
   .button.ButtonInputDriver;
import com.google.android.things.pio.Gpio;
import com.google.android.things.pio.PeripheralManagerService;
import android.util.Log;
import android.view.KeyEvent;

import java.io.IOException;

public class MainActivity extends Activity {
   private static final String TAG        = "AndroidThings";
   private static final String PIN_LED    = "BCM6";

   private Gpio              mLedGpio;
   private ButtonInputDriver mButtonInputDriver;

   @Override
   protected void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);

      PeripheralManagerService service = new
         PeripheralManagerService();
      try {
         Log.i(TAG, "Configuring GPIO pins");
         mLedGpio = service.openGpio(PIN_LED);
         mLedGpio.setDirection(Gpio.DIRECTION_OUT_INITIALLY_LOW);

         Log.i(TAG, "Registering button driver");
         // Initialize and register the InputDriver that will
         // emit SPACE key events on GPIO state changes.
         mButtonInputDriver = new ButtonInputDriver(
            BoardDefaults.getGPIOForButton(),
            Button.LogicState.PRESSED_WHEN_LOW,
            KeyEvent.KEYCODE_SPACE);

          mButtonInputDriver.register();

      } catch (IOException e){
         Log.e(TAG, "Error configuring GPIO pins", e);
      }
   }

   @Override
   public boolean onKeyDown(int keyCode, KeyEvent event) {
      if (keyCode == KeyEvent.KEYCODE_SPACE) {
         setLEDState(true);
         return true;
      }

      return super.onKeyDown(keyCode, event);
   }

   @Override
   public boolean onKeyUp(int keyCode, KeyEvent event) {
      if (keyCode == KeyEvent.KEYCODE_SPACE) {
         setLEDState(false);
         return true;
      }

      return super.onKeyUp(keyCode, event);
   }

   @Override
   protected void onDestroy(){
      super.onDestroy();

      if (mLedGpio != null) {
         try {
            mLedGpio.close();
         } catch (IOException e) {
         } finally{
            mLedGpio = null;
         }
      }

      if (mButtonInputDriver != null) {
         mButtonInputDriver.unregister();
         try {
            mButtonInputDriver.close();
         } catch (IOException e) {
            Log.e(TAG, "Error closing Button driver", e);
         } finally{
            mButtonInputDriver = null;
         }
      }
   }
    
   // This function toggles the button on and off
   private void setLedState(boolean isOn) {
      try {
         mLedGpio.setValue(isOn);
      } catch (IOException e) {
         Log.e(TAG, "Error updating LED state", e);
      }
   }
}

Conclusion

Today’s tutorial provided just a taste of what the Android Things Peripheral Driver Library can do. In future articles, we’ll explore more complex examples.

Head shot

Rob Gravelle resides in Ottawa, Canada, and has built Web applications for numerous businesses and government agencies.

Rob’s alter-ego, “Blackjacques,” is an accomplished guitar player, and has released several CDs. His band, Ivory Knight, was rated as one of Canada’s top hard rock and metal groups by Brave Words magazine (issue #92).

Get the Free Newsletter!
Subscribe to Developer Insider for top news, trends & analysis
This email address is invalid.
Get the Free Newsletter!
Subscribe to Developer Insider for top news, trends & analysis
This email address is invalid.

Latest Posts

Related Stories