Interact with peripherals

The power of Android Things is realized when developers begin connecting hardware peripherals directly to apps.

In this lesson, you will learn how to use the basic Peripheral I/O APIs to discover and communicate with General Purpose Input Output (GPIO) ports.

List available peripherals

The system service responsible for managing peripheral connections is PeripheralManager. You can use this service to list the available ports for all known peripheral types.

The following code writes the list of available GPIO ports to logcat:

Kotlin

import com.google.android.things.pio.PeripheralManager

class HomeActivity : Activity() {
  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    val manager = PeripheralManager.getInstance()
    Log.d(TAG, "Available GPIO: " + manager.getGpioList())
  }
  companion object {
    private val TAG = "HomeActivity"
  }
}

Java

import com.google.android.things.pio.PeripheralManager;
...

public class HomeActivity extends Activity {
    private static final String TAG = "HomeActivity";

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

        PeripheralManager manager = PeripheralManager.getInstance();
        Log.d(TAG, "Available GPIO: " + manager.getGpioList());
    }
}

Handle button events

To receive events when a button connected to GPIO is pressed:

  1. Use PeripheralManager to open a connection with the GPIO port wired to the button.
  2. Configure the port with DIRECTION_IN.
  3. Configure which state transitions will generate callback events with setEdgeTriggerType().
  4. Register a GpioCallback to receive edge trigger events.
  5. Return true within onGpioEdge() to continue receiving future edge trigger events.
  6. When the application no longer needs the GPIO connection, close the Gpio resource.

Kotlin

import com.google.android.things.pio.PeripheralManager
import com.google.android.things.pio.Gpio
import com.google.android.things.pio.GpioCallback

...

class ButtonActivity : Activity() {
  // GPIO port wired to the button
  private lateinit var buttonGpio: Gpio
  // Step 4. Register an event callback.
  private val callback = object : GpioCallback() {
    fun onGpioEdge(gpio: Gpio): Boolean {
      Log.i(TAG, "GPIO changed, button pressed")
      // Step 5. Return true to keep callback active.
      return true
    }
  }

  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    val manager = PeripheralManager.getInstance()
    try {
      // Step 1. Create GPIO connection.
      buttonGpio = manager.openGpio(BUTTON_PIN_NAME)
      // Step 2. Configure as an input.
      buttonGpio.setDirection(Gpio.DIRECTION_IN)
      // Step 3. Enable edge trigger events.
      buttonGpio.setEdgeTriggerType(Gpio.EDGE_FALLING)
      // Step 4. Register an event callback.
      buttonGpio.registerGpioCallback(mCallback)
    } catch (e: IOException) {
      Log.e(TAG, "Error on PeripheralIO API", e)
    }
  }

  override fun onDestroy() {
    super.onDestroy()
    // Step 6. Close the resource
    if (buttonGpio != null)
    {
      buttonGpio.unregisterGpioCallback(mCallback)
      try {
        buttonGpio.close()
      } catch (e: IOException) {
        Log.e(TAG, "Error on PeripheralIO API", e)
      }
    }
  }

  companion object {
    private val TAG = "ButtonActivity"
    private val BUTTON_PIN_NAME: String
  }
}

Java

import com.google.android.things.pio.PeripheralManager;
import com.google.android.things.pio.Gpio;
import com.google.android.things.pio.GpioCallback;

...

public class ButtonActivity extends Activity {
    private static final String TAG = "ButtonActivity";
    private static final String BUTTON_PIN_NAME = ...; // GPIO port wired to the button

    private Gpio buttonGpio;

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

        PeripheralManager manager = PeripheralManager.getInstance();
        try {
            // Step 1. Create GPIO connection.
            buttonGpio = manager.openGpio(BUTTON_PIN_NAME);
            // Step 2. Configure as an input.
            buttonGpio.setDirection(Gpio.DIRECTION_IN);
            // Step 3. Enable edge trigger events.
            buttonGpio.setEdgeTriggerType(Gpio.EDGE_FALLING);
            // Step 4. Register an event callback.
            buttonGpio.registerGpioCallback(mCallback);
        } catch (IOException e) {
            Log.e(TAG, "Error on PeripheralIO API", e);
        }
    }

    // Step 4. Register an event callback.
    private GpioCallback mCallback = new GpioCallback() {
        @Override
        public boolean onGpioEdge(Gpio gpio) {
            Log.i(TAG, "GPIO changed, button pressed");

            // Step 5. Return true to keep callback active.
            return true;
        }
    };

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

        // Step 6. Close the resource
        if (buttonGpio != null) {
            buttonGpio.unregisterGpioCallback(mCallback);
            try {
                buttonGpio.close();
            } catch (IOException e) {
                Log.e(TAG, "Error on PeripheralIO API", e);
            }
        }
    }
}

To execute a blinking pattern on an LED connected to GPIO:

  1. Use PeripheralManager to open a connection with the GPIO port wired to the LED.
  2. Configure the port with DIRECTION_OUT_INITIALLY_LOW.
  3. Toggle the state of the LED by passing the inverse of getValue() to the setValue() method.
  4. Use a Handler to schedule an event to toggle the GPIO again after a short delay.
  5. When the application no longer needs the GPIO connection, close the Gpio resource.

Kotlin

import com.google.android.things.pio.PeripheralManager
import com.google.android.things.pio.Gpio

...

class BlinkActivity : Activity() {
  // GPIO port wired to the LED
  private val handler = Handler()
  private lateinit var ledGpio: Gpio
  private val blinkRunnable = object : Runnable {
    public override fun run() {
      // Exit if the GPIO is already closed
      if (ledGpio == null) {
        return
      }

      try {
        // Step 3. Toggle the LED state
        ledGpio.setValue(!ledGpio.getValue())
        // Step 4. Schedule another event after delay.
        handler.postDelayed(blinkRunnable, INTERVAL_BETWEEN_BLINKS_MS)
      } catch (e:IOException) {
        Log.e(TAG, "Error on PeripheralIO API", e)
      }
    }
  }

  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    // Step 1. Create GPIO connection.
    val manager = PeripheralManager.getInstance()
    try
    {
      ledGpio = manager.openGpio(LED_PIN_NAME)
      // Step 2. Configure as an output.
      ledGpio.setDirection(Gpio.DIRECTION_OUT_INITIALLY_LOW)
      // Step 4. Repeat using a handler.
      handler.post(blinkRunnable)
    }
    catch (e: IOException) {
      Log.e(TAG, "Error on PeripheralIO API", e)
    }
  }

  override fun onDestroy() {
    super.onDestroy()
    // Step 4. Remove handler events on close.
    handler.removeCallbacks(blinkRunnable)
    // Step 5. Close the resource.
    if (ledGpio != null)
    {
      try {
        ledGpio.close()
      } catch (e: IOException) {
        Log.e(TAG, "Error on PeripheralIO API", e)
      }
    }
  }

  companion object {
    private val TAG = "BlinkActivity"
    private val INTERVAL_BETWEEN_BLINKS_MS = 1000
    private val LED_PIN_NAME: String
  }
}

Java

import com.google.android.things.pio.PeripheralManager;
import com.google.android.things.pio.Gpio;

...

public class BlinkActivity extends Activity {
    private static final String TAG = "BlinkActivity";
    private static final int INTERVAL_BETWEEN_BLINKS_MS = 1000;
    private static final String LED_PIN_NAME = ...; // GPIO port wired to the LED

    private Handler handler = new Handler();

    private Gpio ledGpio;

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

        // Step 1. Create GPIO connection.
        PeripheralManager manager = PeripheralManager.getInstance();
        try {
            ledGpio = manager.openGpio(LED_PIN_NAME);
            // Step 2. Configure as an output.
            ledGpio.setDirection(Gpio.DIRECTION_OUT_INITIALLY_LOW);

            // Step 4. Repeat using a handler.
            handler.post(blinkRunnable);
        } catch (IOException e) {
            Log.e(TAG, "Error on PeripheralIO API", e);
        }
    }

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

        // Step 4. Remove handler events on close.
        handler.removeCallbacks(blinkRunnable);

        // Step 5. Close the resource.
        if (ledGpio != null) {
            try {
                ledGpio.close();
            } catch (IOException e) {
                Log.e(TAG, "Error on PeripheralIO API", e);
            }
        }
    }

    private Runnable blinkRunnable = new Runnable() {
        @Override
        public void run() {
            // Exit if the GPIO is already closed
            if (ledGpio == null) {
                return;
            }

            try {
                // Step 3. Toggle the LED state
                ledGpio.setValue(!ledGpio.getValue());

                // Step 4. Schedule another event after delay.
                handler.postDelayed(blinkRunnable, INTERVAL_BETWEEN_BLINKS_MS);
            } catch (IOException e) {
                Log.e(TAG, "Error on PeripheralIO API", e);
            }
        }
    };
}