Android Things Tutorial: Getting Started
Did you ever want to tinker with all those little pins on hardware boards? Well in this tutorial, you’ll learn how to do just that, with AndroidThings! By Dean Djermanović.
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Contents
Android Things Tutorial: Getting Started
30 mins
- Developing for Android Things
- Getting Started
- Connect the Hardware
- Install Android Things
- Connect to the Internet
- Download and Explore the Starter Project
- Communication with Hardware
- Manage the Connection
- Read from an Input
- Listen for Input State Changes
- Write to an Output
- Connect with Firebase
- Capture the Ring Event
- Notify the User
- Handle Response
- Where to Go From Here?
Communication with Hardware
Android Things provides Peripheral I/O APIs to communicate with sensors and actuators. Peripheral I/O API is a part of the Things Support library which enables apps to use industry standard protocols and interfaces to connect to hardware peripherals.
Manage the Connection
You’ll use the PeripheralManager
class to list and open available peripherals. To access any of the PeripheralManager
APIs, you need to add USE_PERIPHERAL_IO
permission. Open AndroidManifest.xml
and add this line to the manifest tag:
<uses-permission android:name="com.google.android.things.permission.USE_PERIPHERAL_IO" />
Next, open the BoardManager
class, in the board
package. This is the class you’ll use to communicate with the board. Add the peripheralManager
field to the class:
private val peripheralManager by lazy { PeripheralManager.getInstance() }
Here, you use the lazy
delegate to initialize the field the first time it is accessed.
Next, add a function which will list available peripherals to the BoardManager
class, using the getGpioList()
method from PeripheralManager
class:
fun listPeripherals(): List = peripheralManager.gpioList
Now, go to the MainActivity
class and add the boardManager
field:
private lateinit var boardManager: BoardManager
Next, replace the initialize()
function with the following to create a BoardManager()
instance:
private fun initialize() {
boardManager = BoardManager()
}
Then, replace your onCreate
function with the following:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
initialize()
Log.d(TAG, "Available GPIO: " + boardManager.listPeripherals())
}
This calls the initialize()
function and uses the boardManager.listPeripherals()
call to list out the available peripherals in a log statement. Finally, build and run the app and look at the logcat. You’ll see your peripherals listed as below:
D/MainActivity: Available GPIO: [BCM10, BCM11, BCM12, BCM13, BCM14, BCM15, BCM16, BCM17, BCM18, BCM19, BCM2, BCM20, BCM21, BCM22, BCM23, BCM24, BCM25, BCM26, BCM27, BCM3, BCM4, BCM5, BCM6, BCM7, BCM8, BCM9]
Read from an Input
In the BoardManager
class, you’ll find uncommented, predefined, constants for Raspberry Pi board pin names. If you are using a Pico Pi, you will want to comment these and uncomment the NXP i.MX7D board
ones. The BUTTON_PIN_NAME
constant matches the A
button on your Rainbow HAT.
First off, add a field for the button to the BoardManager
class:
private lateinit var buttonGpio: Gpio
Second, paste the following initializeButton()
function to the same class:
private fun initializeButton() {
try {
//1
buttonGpio = peripheralManager.openGpio(BUTTON_PIN_NAME)
buttonGpio.apply {
//2
setDirection(Gpio.DIRECTION_IN)
//3
setEdgeTriggerType(Gpio.EDGE_BOTH)
//4
setActiveType(Gpio.ACTIVE_LOW)
}
} catch (exception: IOException) {
handleError(exception)
}
}
Going through this step by step:
- First, you open a connection with the GPIO pin wired to the button. You do that by calling
peripheralManager.openGpio(BUTTON_PIN_NAME)
, passing in the button pin name. -
General Purpose Input/Output (GPIO) pins provide a programmable interface to read the state of a binary input device (such as a push button switch) or control the on/off state of a binary output device (such as an LED). To configure GPIO as input, you call
setDirection(Gpio.DIRECTION_IN)
with theGpio.DIRECTION_IN
constant on thebuttonGpio
. - In electronics, a signal edge is a transition of a digital signal either from low to high (0 to 1) or from high to low (1 to 0). A rising edge is the transition from low to high. That transition happens when you press the button on board. A falling edge is the high to low transition. That transition happens when you release the button. You are going to be interested in both events, so you are calling the
setEdgeTriggerType(Gpio.EDGE_BOTH)
function with theGpio.EDGE_BOTH
constant. - Finally, you need to specify the active type. Since the button you are using defaults to a high voltage value (when the button is not pressed) you are setting this to
Gpio.ACTIVE_LOW
, so that this will return true when the input voltage is low (button is pressed).
Finally, add the initialize()
function to your BoardManager
class.
fun initialize() {
initializeButton()
}
You’ll rely on this to initialize the board before usage.
Listen for Input State Changes
To receive edge trigger events you need to configure an edge callback. Add a buttonCallback
field to your BoardManager
class:
private lateinit var buttonCallback: GpioCallback
Next paste the this initializeButtonCallback()
function to initialize buttonCallback
:
private fun initializeButtonCallback() {
buttonCallback = GpioCallback {
Log.i("BoardManager", "button callback value is " + it.value)
true
}
}
This callback triggers every time an input transition occurs that matches the configured edge trigger type. It returns true
to continue receiving future edge trigger events.
Now, replace your initializeButton()
function with the following:
private fun initializeButton() {
// 1
initializeButtonCallback()
try {
buttonGpio = peripheralManager.openGpio(BUTTON_PIN_NAME)
buttonGpio.apply {
setDirection(Gpio.DIRECTION_IN)
setEdgeTriggerType(Gpio.EDGE_BOTH)
setActiveType(Gpio.ACTIVE_LOW)
// 2
registerGpioCallback(buttonCallback)
}
} catch (exception: IOException) {
handleError(exception)
}
}
This calls the initializeButtonCallback()
method and registers the buttonCallback
it created.
Next, add the clear()
function to the BoardManager
class:
fun clear() {
buttonGpio.unregisterGpioCallback(buttonCallback)
try {
buttonGpio.close()
} catch (exception: IOException) {
handleError(exception)
}
}
Here, you unregister the callback and close the button GPIO connection when the application is done. Now, paste in the following into your MainActivity
‘s initialize()
function:
boardManager.initialize()
This calls your BoardManager
initialize method. Finally, add the following method to the same MainActivity
override fun onDestroy() {
super.onDestroy()
boardManager.clear()
}
This calls your boardManager
‘s clear
method to clean things up when the system destroys the activity. Build and run your app and press the A button. In your log file you will see it log true when you press the button and false when you release it.
Write to an Output
Next you will turn the Blue LED on the Rainbow HAT ON and OFF on button taps based on the state of your A button.
First open your BoardManager
class and add the following:
private lateinit var blueLedGpio: Gpio
This defines your GPIO field for the blue LED light. Next, paste in the following method:
private fun initializeLedLights() {
try {
//1
blueLedGpio = peripheralManager.openGpio(BLUE_LED_PIN_NAME)
//2
blueLedGpio.apply {
setDirection(Gpio.DIRECTION_OUT_INITIALLY_LOW)
}
} catch (exception: IOException) {
handleError(exception)
}
}
This adds a initializeLedLights()
function that does the following:
- You use the
PeripheralManager
to open a connection with the GPIO pin wired to the Blue LED. - You configure pin as an output using
setDirection(Gpio.DIRECTION_OUT_INITIALLY_LOW)
. This will also set the initial state of the pin to zero volts, which means LED light will be turned off initially.
Now, add a call to initializeLedLights
in your initialize()
function. Next, replace initializeButtonCallback()
with the following:
private fun initializeButtonCallback() {
buttonCallback = GpioCallback {
try {
//1
val buttonValue = it.value
//2
blueLedGpio.value = buttonValue
} catch (exception: IOException) {
handleError(exception)
}
true
}
}
This does the following:
- Reads the button state with the
it.value
property. Theit
field is the buttons GPIO instance. - Sets the LEDs value with the
blueLedGpio.value
and set it to on if you press the button, and off if you release it.
Finally, replace your clear()
function with the following:
fun clear() {
buttonGpio.unregisterGpioCallback(buttonCallback)
arrayOf(buttonGpio, blueLedGpio)
.forEach {
try {
it.close()
} catch (exception: IOException) {
handleError(exception)
}
}
}
This cleans up your resources. Instead of closing GPIO connections individually, you add the blue LED GPIO and button GPIO to an array that you iterate over.
That’s it. Build and run the app. When you press the blue button, the LED light will be turned on. When you release it, it will be turned off.
Great job so far! But turning LED light ON and OFF isn’t that exciting, unless you’re a fish, so let’s connect your app with Firebase! :]