General Purpose Input/Output (GPIO) pins provide a programmable interface to read the state of a binary input device (such as a pushbutton switch) or control the on/off state of a binary output device (such as an LED).
You can configure GPIO pins as an input or output with either a high or low state. As an input, an external source determines the state, and your app can read the current value or react to changes in state. As an output, your app configures the state of the pin.
Adding the required permission
Add the required permission for this API to your app's manifest file:
<uses-permission android:name="com.google.android.things.permission.USE_PERIPHERAL_IO" />
Managing the connection
In order to open a connection to a GPIO port, you need to know the unique port name. During the initial stages of development, or when porting an app to new hardware, it's helpful to discover all the available port names from PeripheralManager using getGpioList():
Kotlin
val manager = PeripheralManager.getInstance() val portList: List<String> = manager.gpioList if (portList.isEmpty()) { Log.i(TAG, "No GPIO port available on this device.") } else { Log.i(TAG, "List of available ports: $portList") }
Java
PeripheralManager manager = PeripheralManager.getInstance(); List<String> portList = manager.getGpioList(); if (portList.isEmpty()) { Log.i(TAG, "No GPIO port available on this device."); } else { Log.i(TAG, "List of available ports: " + portList); }
Once you know the target name, use openGpio() to connect to that port. When you're done communicating with the GPIO port, close the connection to free up resources. Additionally, you cannot open a new connection to the same port until the existing connection is closed. To close the connection, use the port's close() method.
Kotlin
// GPIO Pin Name private const val GPIO_NAME: String = ... class HomeActivity : Activity() { private var gpio: Gpio? = null override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) // Attempt to access the GPIO gpio = try { PeripheralManager.getInstance() .openGpio(GPIO_NAME) } catch (e: IOException) { Log.w(TAG, "Unable to access GPIO", e) null } } override fun onDestroy() { super.onDestroy() try { gpio?.close() gpio = null } catch (e: IOException) { Log.w(TAG, "Unable to close GPIO", e) } } }
Java
public class HomeActivity extends Activity { // GPIO Pin Name private static final String GPIO_NAME = ...; private Gpio gpio; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // Attempt to access the GPIO try { PeripheralManager manager = PeripheralManager.getInstance(); gpio = manager.openGpio(GPIO_NAME); } catch (IOException e) { Log.w(TAG, "Unable to access GPIO", e); } } @Override protected void onDestroy() { super.onDestroy(); if (gpio != null) { try { gpio.close(); gpio = null; } catch (IOException e) { Log.w(TAG, "Unable to close GPIO", e); } } } }
Reading from an input
To read a GPIO port as an input:
- Configure it as an input using setDirection() with the mode DIRECTION_IN.
Optional: Configure the pin voltage represented by an input value of
true
(active) with setActiveType():- ACTIVE_HIGH: High voltage (near IOREF) is active. This is the default value.
- ACTIVE_LOW: Low voltage (near zero) is active.
Access the current state with the getValue() method.
The following code shows you how to set up an input with an active state associated with a high voltage level:
Kotlin
@Throws(IOException::class) fun configureInput(gpio: Gpio) { gpio.apply { // Initialize the pin as an input setDirection(Gpio.DIRECTION_IN) // High voltage is considered active setActiveType(Gpio.ACTIVE_HIGH) ... // Read the active high pin state if (value) { // Pin is HIGH } else { // Pin is LOW } } }
Java
public void configureInput(Gpio gpio) throws IOException { // Initialize the pin as an input gpio.setDirection(Gpio.DIRECTION_IN); // High voltage is considered active gpio.setActiveType(Gpio.ACTIVE_HIGH); ... // Read the active high pin state if (gpio.getValue()) { // Pin is HIGH } else { // Pin is LOW } }
Listening for input state changes
A GPIO port configured as an input can notify your app when its value changes. To register for these change events:
- Attach a GpioCallback to the active port connection.
Declare the state changes that trigger an interrupt event using the setEdgeTriggerType() method. The edge trigger supports the following four types:
- EDGE_NONE: No interrupt events. This is the default value.
- EDGE_RISING:
Interrupt on a value transition from
false
totrue
- EDGE_FALLING:
Interrupt on a value transition from
true
tofalse
- EDGE_BOTH: Interrupt on all transitions
Return
true
from within onGpioEdge() to indicate that the listener should continue receiving events for each port state change.The following code registers an interrupt listener for all state changes on the given input port:
Kotlin
@Throws(IOException::class) fun configureInput(gpio: Gpio) { gpio.apply { // Initialize the pin as an input setDirection(Gpio.DIRECTION_IN) // Low voltage is considered active setActiveType(Gpio.ACTIVE_LOW) // Register for all state changes setEdgeTriggerType(Gpio.EDGE_BOTH) registerGpioCallback(gpioCallback) } } private val gpioCallback = object : GpioCallback { override fun onGpioEdge(gpio: Gpio): Boolean { // Read the active low pin state if (gpio.value) { // Pin is LOW } else { // Pin is HIGH } // Continue listening for more interrupts return true } override fun onGpioError(gpio: Gpio, error: Int) { Log.w(TAG, "$gpio: Error event $error") } }
Java
public void configureInput(Gpio gpio) throws IOException { // Initialize the pin as an input gpio.setDirection(Gpio.DIRECTION_IN); // High voltage is considered active gpio.setActiveType(Gpio.ACTIVE_HIGH); // Register for all state changes gpio.setEdgeTriggerType(Gpio.EDGE_BOTH); gpio.registerGpioCallback(gpioCallback); } private GpioCallback gpioCallback = new GpioCallback() { @Override public boolean onGpioEdge(Gpio gpio) { // Read the active low pin state if (gpio.getValue()) { // Pin is HIGH } else { // Pin is LOW } // Continue listening for more interrupts return true; } @Override public void onGpioError(Gpio gpio, int error) { Log.w(TAG, gpio + ": Error event " + error); } };
Unregister any interrupt handlers when your app is no longer listening for incoming events:
Kotlin
class HomeActivity : Activity() { private var gpio: Gpio? = null ... override fun onStart() { super.onStart() // Begin listening for interrupt events gpio?.registerGpioCallback(gpioCallback) } override fun onStop() { super.onStop() // Interrupt events no longer necessary gpio?.unregisterGpioCallback(gpioCallback) } }
Java
public class HomeActivity extends Activity { private Gpio gpio; ... @Override protected void onStart() { super.onStart(); // Begin listening for interrupt events gpio.registerGpioCallback(gpioCallback); } @Override protected void onStop() { super.onStop(); // Interrupt events no longer necessary gpio.unregisterGpioCallback(gpioCallback); } }
Writing to an output
To programmatically control the state of a GPIO port:
Configure it as an output using setDirection() with appropriate initial value:
- DIRECTION_OUT_INITIALLY_HIGH: Set the initial output value to high voltage (near IOREF).
- DIRECTION_OUT_INITIALLY_LOW: Set the initial output value to low voltage (near zero).
Optional: Configure the pin voltage represented by an output value of
true
(active) with setActiveType():- ACTIVE_HIGH: High voltage (near IOREF) is active. This is the default value.
- ACTIVE_LOW: Low voltage (near zero) is active.
Set the current state with the setValue() method.
The following code shows you how to set up an output to initially be high, then toggle its state to low using the setValue() method:
Kotlin
@Throws(IOException::class) fun configureOutput(gpio: Gpio) { gpio.apply { // Initialize the pin as a high output setDirection(Gpio.DIRECTION_OUT_INITIALLY_HIGH) // Low voltage is considered active setActiveType(Gpio.ACTIVE_LOW) ... // Toggle the value to be LOW value = true } }
Java
public void configureOutput(Gpio gpio) throws IOException { // Initialize the pin as a high output gpio.setDirection(Gpio.DIRECTION_OUT_INITIALLY_HIGH); // Low voltage is considered active gpio.setActiveType(Gpio.ACTIVE_LOW); ... // Toggle the value to be LOW gpio.setValue(true); }