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?
Download and Explore the Starter Project
Before you start building apps for Android Things make sure you have SDK tools version 27.0.1, or higher, and SDK with Android 8.1 (API 27), or higher, otherwise update them accordingly.
Download the materials for the tutorial by using the Download Materials button at the top or bottom of the page, and import the starter project into Android Studio.
Explore the project and see what makes the Android Things project different from a traditional phone and tablet project.
Open the app-level build.gradle
file.
In the dependencies block, you’ll see this line:
compileOnly 'com.google.android.things:androidthings:' + project.ext.androidThingsVersion
The Things support library is a new library which Google is developing to handle the communication with peripherals and drivers. This is a completely new library not present in the Android SDK, and this library is one of the most important features. It exposes a set of Java Interfaces and classes (APIs) that you can use to connect and exchange data with external devices such as sensors, actuators and so on. This library hides the inner communication details, supporting several industry standard protocols (GPIO, I2C, UART, etc.).
Next, open AndroidManifest.xml
. You’ll see this line under the Application tag:
<uses-library android:name="com.google.android.things" />
The uses-library
element makes this prebuilt library available to the app’s classpath at runtime.
Android Things does not include a user-facing launcher app. Any app intending to run on an embedded device must launch an Activity
automatically on boot. In order to do that, it must attach an intent-filter
containing the HOME category to the Activity
that it’s going to launch. Since you’re not writing a production app, a simple intent-filter with the action MAIN and category LAUNCHER is enough to set which Activity
you’ll launch from Android Studio by default.
Build and run the app. Currently, not much has changed, so let’s start communicating with the board.
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.