Migrate from Bazel
This commit is contained in:
commit
016dbd0814
59 changed files with 7044 additions and 0 deletions
436
content/posts/android-things-first-look.md
Normal file
436
content/posts/android-things-first-look.md
Normal file
|
|
@ -0,0 +1,436 @@
|
|||
+++
|
||||
template = "article.html"
|
||||
title = "Android Things: first look"
|
||||
date = 2017-01-06T10:55:08+01:00
|
||||
description = "An introduction to Android Things, Google's IoT platform that brings the Android ecosystem to embedded devices."
|
||||
|
||||
[taxonomies]
|
||||
tags = ["android", "iot"]
|
||||
+++
|
||||
|
||||
## What is Android Things?
|
||||
|
||||
Android Things is an alternative Android version, announced at Google I/O 2015,
|
||||
and released as a first developer preview in December 2016. Its purpose is to
|
||||
develop embedded IoT devices, with a known and widely documented Android
|
||||
ecosystem basis.
|
||||
|
||||
It's currently running on three different boards: the Intel Edison, the NXP Pico
|
||||
i.MX6UL, and the Raspberry Pi 3. Some higher-end boards are coming soon.
|
||||
|
||||
On the SDK side, Android Things comes with a specific support library to ease
|
||||
low-level hardware usage. It consists in two parts: the Peripheral I/O API,
|
||||
which supports GPIO, PWM, I2C, SPI and UART, and the User Driver API, which
|
||||
allows a developer to write a hardware-specific, high-level driver, to ease
|
||||
hardware reusability by injecting events into the Android framework. Other
|
||||
applications can in turn use those events without having to interact with the
|
||||
hardware directly.
|
||||
|
||||
There's a downside: the bundled Android is not as complete as the one you can
|
||||
find on a phone. Most of the standard applications aren't installed (Calendar,
|
||||
Phone…), and standard content providers are absent too (MediaProvider,
|
||||
Dictionary…).
|
||||
|
||||
Android Things supports displays, with the default Android UI toolkit. However,
|
||||
the display is a bit different from what you're used to seeing on an Android
|
||||
device: there's no notification bar, navigation bar or anything, the running
|
||||
application will use the full display. That is, if it uses it at all: displays
|
||||
are purely optional.
|
||||
|
||||
<!--more-->
|
||||
|
||||
## Installing Android Things
|
||||
|
||||
Installation depends on the device you're targeting. Up-to-date, device-specific
|
||||
instructions are available in [the official
|
||||
documentation](https://developer.android.com/things/hardware/developer-kits.html).
|
||||
|
||||
Note that there is no emulator available (yet?), you'll need to install Android
|
||||
Things on a real board.
|
||||
|
||||
The next steps of this post assume your local adb is connected to your device
|
||||
(that is, `adb devices` is listing it as attached).
|
||||
|
||||
## Creating a new application
|
||||
|
||||
Android Things uses the same Activity, Service… lifecycles you're used to
|
||||
seeing in any Android application. As so, creating an Android Things project is
|
||||
really close to creating an Android one:
|
||||
|
||||
- create a blank project on Android Studio (selecting a form factor is
|
||||
mandatory, and Android Studio doesn't support Things yet, keep Phone
|
||||
and Tablet selected), without any activities
|
||||
- in the `build.gradle`, remove all the dependencies and add the Things support
|
||||
library:
|
||||
|
||||
```groovy
|
||||
apply plugin: 'com.android.application'
|
||||
|
||||
android {
|
||||
compileSdkVersion 24
|
||||
buildToolsVersion "25.0.2"
|
||||
defaultConfig {
|
||||
applicationId "fr.enoent.mything"
|
||||
minSdkVersion 24
|
||||
targetSdkVersion 24
|
||||
versionCode 1
|
||||
versionName "1.0"
|
||||
}
|
||||
buildTypes {
|
||||
release {
|
||||
minifyEnabled false
|
||||
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
provided 'com.google.android.things:androidthings:0.1-devpreview'
|
||||
}
|
||||
```
|
||||
|
||||
- add a reference to the Things support library in the `AndroidManifest.xml`:
|
||||
|
||||
```xml
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="fr.enoent.mything">
|
||||
|
||||
<application
|
||||
android:allowBackup="true"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="@string/app_name">
|
||||
|
||||
<uses-library
|
||||
android:name="com.google.android.things"/>
|
||||
|
||||
</application>
|
||||
</manifest>
|
||||
```
|
||||
|
||||
- the last step is to create an Activity: create a new blank, without layout,
|
||||
non-support Activity from Android Studio. You'll also need to add a
|
||||
Things-specific intent-filter to this Activity in the Manifest, so it will
|
||||
start automatically on boot. Keep the Launcher intent-filter to make it easier
|
||||
to start this Activity from Android Studio:
|
||||
|
||||
```xml
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="fr.enoent.mything">
|
||||
|
||||
<application
|
||||
android:allowBackup="true"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="@string/app_name">
|
||||
|
||||
<uses-library
|
||||
android:name="com.google.android.things"/>
|
||||
|
||||
<activity android:name=".MainActivity">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN"/>
|
||||
|
||||
<category android:name="android.intent.category.LAUNCHER"/>
|
||||
</intent-filter>
|
||||
|
||||
<!-- Launch activity automatically on boot -->
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN"/>
|
||||
<category android:name="android.intent.category.IOT_LAUNCHER"/>
|
||||
<category android:name="android.intent.category.DEFAULT"/>
|
||||
</intent-filter>
|
||||
</activity>
|
||||
</application>
|
||||
</manifest>
|
||||
```
|
||||
|
||||
Now you have an Activity, which is supposed to start automatically on boot.
|
||||
Let's check that by adding a log in the `onCreate` method:
|
||||
|
||||
*MainActivity.java*
|
||||
|
||||
```java
|
||||
public class MainActivity extends Activity {
|
||||
@Override
|
||||
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
Log.d("MainActivity", "onCreate");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
As you can see, it's perfectly standard Android code.
|
||||
|
||||
You can run it from Android Studio, and you should see the `onCreate` mention in
|
||||
the logs.
|
||||
|
||||
## Lights, action!
|
||||
|
||||
I won't cover Android UI in this post, as it's something really standard you can
|
||||
already find all over the web. Let's do a much more fun UI: a blinking LED to
|
||||
indicate the application is running.
|
||||
|
||||
### Connecting the LED
|
||||
|
||||
I only have a Raspberry Pi 3, so I won't be able to cover the other boards for
|
||||
this part. You can find the Pi pinout details
|
||||
[here](https://developer.android.com/things/hardware/raspberrypi-io.html).
|
||||
|
||||
The circuit is dead simple: connect the LED's cathode to Pi's ground, a small
|
||||
resistor in series with your LED's anode (Pi's GPIOs are 3v3), and the other
|
||||
side of the resistor to the Pi's BCM6. You can use any pin labelled as GPIO in
|
||||
the diagram.
|
||||
|
||||
### Pimp your 'droid
|
||||
|
||||
Time to go back to Android Studio. First step: listing the available GPIOs on
|
||||
your board. There's a new class in the support library to access the GPIOs:
|
||||
`PeripheralManagerService`.
|
||||
|
||||
*MainActivity.java*
|
||||
|
||||
```java
|
||||
@Override
|
||||
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
PeripheralManagerService service = new PeripheralManagerService();
|
||||
Log.d("MainActivity", "Available GPIOs: " + service.getGpioList());
|
||||
}
|
||||
```
|
||||
|
||||
This code will list all the GPIOs we're allowed to use. On a Pi, hopefully
|
||||
you'll find the BCM6 pin you connected the LED to. Here's the output on a Pi 3:
|
||||
|
||||
```
|
||||
Available GPIOs: [BCM12, BCM13, BCM16, BCM17, BCM18, BCM19, BCM20, BCM21,
|
||||
BCM22, BCM23, BCM24, BCM25, BCM26, BCM27, BCM4, BCM5, BCM6]
|
||||
```
|
||||
|
||||
The next step is to initialize this GPIO. We'll use the
|
||||
`PeripheralManagerService` once more to get a reference to it, then set it up to
|
||||
`LOW` (0v) by default:
|
||||
|
||||
*MainActivity.java*
|
||||
|
||||
```java
|
||||
private static final String GPIO_PIN_NAME_LED = "BCM6";
|
||||
|
||||
@Override
|
||||
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
PeripheralManagerService service = new PeripheralManagerService();
|
||||
|
||||
try {
|
||||
Gpio ledGpio = service.openGpio(GPIO_PIN_NAME_LED);
|
||||
ledGpio.setDirection(Gpio.DIRECTION_OUT_INITIALLY_LOW);
|
||||
} catch (IOException e) {
|
||||
Log.e("MainActivity", "Error on PeripheralIO API", e);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Now, the only thing left to do is to toggle the GPIO value. This is a single
|
||||
call: `ledGpio.setValue(!ledGpio.getValue());`. Toggling it every second comes
|
||||
with a purely Android-oriented solution: a `Handler`, and a delayed
|
||||
`Runnable`.
|
||||
|
||||
*MainActivity.java*
|
||||
|
||||
```java
|
||||
public class MainActivity extends Activity {
|
||||
private static final int INTERVAL_BETWEEN_BLINKS_MS = 1000;
|
||||
private static final String GPIO_PIN_NAME_LED = "BCM6";
|
||||
|
||||
private Handler handler = new Handler();
|
||||
private Gpio ledGpio;
|
||||
|
||||
private Runnable blinkRunnable = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
// Exit if the GPIO is already closed
|
||||
if (ledGpio == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
ledGpio.setValue(!ledGpio.getValue());
|
||||
handler.postDelayed(blinkRunnable, INTERVAL_BETWEEN_BLINKS_MS);
|
||||
} catch (IOException e) {
|
||||
Log.e("MainActivity", "Error on PeripheralIO API", e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
PeripheralManagerService service = new PeripheralManagerService();
|
||||
|
||||
try {
|
||||
ledGpio = service.openGpio(GPIO_PIN_NAME_LED);
|
||||
ledGpio.setDirection(Gpio.DIRECTION_OUT_INITIALLY_LOW);
|
||||
|
||||
handler.post(blinkRunnable);
|
||||
} catch (IOException e) {
|
||||
Log.e("MainActivity", "Error on PeripheralIO API", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## User drivers
|
||||
|
||||
Google already ships some user-drivers, ready to use, in the form of Gradle
|
||||
libraries. Let's take the Button driver as an example.
|
||||
|
||||
### Installation
|
||||
|
||||
It's a simple Gradle dependency to add:
|
||||
`compile 'com.google.android.things.contrib:driver-button:0.1'`
|
||||
|
||||
### Usage
|
||||
|
||||
The button driver provides a simple class which you feed with the GPIO name to
|
||||
use, and a callback:
|
||||
|
||||
*MainActivity.java*
|
||||
|
||||
```java
|
||||
public class MainActivity extends Activity {
|
||||
private static final String GPIO_PIN_NAME_BUTTON = "BCM6";
|
||||
|
||||
Button button;
|
||||
|
||||
@Override
|
||||
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
Log.d("MainActivity", "onCreate");
|
||||
|
||||
try {
|
||||
button = new Button(GPIO_PIN_NAME_BUTTON,
|
||||
Button.LogicState.PRESSED_WHEN_HIGH
|
||||
);
|
||||
button.setOnButtonEventListener(new Button.OnButtonEventListener() {
|
||||
@Override
|
||||
public void onButtonEvent(Button button, boolean pressed) {
|
||||
Log.d("MainActivity", "Button has been pressed!");
|
||||
}
|
||||
});
|
||||
} catch (IOException e) {
|
||||
Log.e("MainActivity", "Unable to configure the button", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
super.onDestroy();
|
||||
|
||||
try {
|
||||
button.close();
|
||||
} catch (IOException e) {
|
||||
Log.e("MainActivity", "There's been an error while closing the button");
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Notice that you'll have to close the GPIO when you leave your Activity
|
||||
(`button.close()`).
|
||||
|
||||
And with those simple dozen lines of Java, you can use your hardware button
|
||||
to trigger things in your application. The button driver also provides a way to
|
||||
bind your hardware button to a software event, then any application can simply
|
||||
listen to the software key event. This is an example binding the button to the
|
||||
key `A`:
|
||||
|
||||
*MainActivity.java*
|
||||
|
||||
```java
|
||||
public class MainActivity extends Activity {
|
||||
private static final String GPIO_PIN_NAME_BUTTON = "BCM6";
|
||||
|
||||
ButtonInputDriver inputDriver;
|
||||
|
||||
@Override
|
||||
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
try {
|
||||
inputDriver = new ButtonInputDriver(GPIO_PIN_NAME_BUTTON,
|
||||
Button.LogicState.PRESSED_WHEN_HIGH,
|
||||
KeyEvent.KEYCODE_A // the keycode to send
|
||||
);
|
||||
inputDriver.register();
|
||||
} catch (IOException e) {
|
||||
Log.e("MainActivity", "Error while binding button", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
super.onDestroy();
|
||||
|
||||
inputDriver.unregister();
|
||||
try {
|
||||
inputDriver.close();
|
||||
} catch (IOException e) {
|
||||
Log.e("MainActivity", "Error while unregistering the button driver", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onKeyDown(int keyCode, KeyEvent event) {
|
||||
if (keyCode == KeyEvent.KEYCODE_A) {
|
||||
Log.d("MainActivity", "Button has been pressed");
|
||||
return true;
|
||||
}
|
||||
return super.onKeyDown(keyCode, event);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Conclusion
|
||||
|
||||
Even if it may be early to conclude anything from this preview, I have mixed
|
||||
feelings regarding the state of Android Things.
|
||||
|
||||
While having the whole UI
|
||||
toolkit available is great for industry-oriented hardware, I don't really see
|
||||
the point of it in consumer products. Most of the connected devices uses LEDs or
|
||||
a screen with minimal information. From my point of view, a connected device
|
||||
should be set up then forgot, and work without requesting anything from me past
|
||||
the initial configuration. I don't want to press a button to turn the lights on.
|
||||
I don't want to take my phone, unlock it, start the relevant application, then
|
||||
press a button to turn the lights on. I want the lights to turn on when I need
|
||||
it. Using a motion sensor, location tracking from my phone, whatever. As long as
|
||||
I have to interact with the lights, they are not smarter than my good old light
|
||||
bulbs with their big button I can use even in the dark with both hands full.
|
||||
|
||||
The first thing I expected from this preview was Weave integration. Which is
|
||||
completely absent (I guess it will come eventually). You can start making a
|
||||
connected device powered by Google technologies, but *you can't use the standard
|
||||
Google tries pushing forward to control it*. You'll have to write your own
|
||||
control interface (probably a REST API, which means integrating a web-server in
|
||||
your Android Things application).
|
||||
|
||||
Having the ability to work on the IDE I'm used to, with an SDK I already know,
|
||||
and being able to reuse the ton of existing Java libraries is really great
|
||||
though. It makes the entry barrier much lower than usual embedded development.
|
||||
That is, when you have someone else to do the hardware part for you.
|
||||
|
||||
I know computing power and storage comes nearly free nowadays, but being used to
|
||||
use AVRs, MSPs…, I can't help thinking a Raspberry Pi 3 is totally
|
||||
overkill for that kind of use. I just used a 1.2 GHz, quad-core SoC and 600 MB
|
||||
of storage *to blink an LED*. Most of those devices will only be
|
||||
remote-controlled and will send data for analysis anyway. An ESP8266 is much
|
||||
smaller, uses less power, comes with built-in Wi-Fi, for a couple bucks.
|
||||
|
||||
In the end, I think Google shouldn't have used Android as a basis for that kind
|
||||
of IoT platform. While it surely looks attractive, it comes with multiple
|
||||
drawbacks inherent to the idea itself. A Google-branded toolchain for something
|
||||
like the ESP8266 with built-in Weave support and first-class Firebase/Google
|
||||
Cloud client libraries would have been a much better approach.
|
||||
Loading…
Add table
Add a link
Reference in a new issue