In this article, we cover how to develop an Android IoT app that uses Android Things. In more detail, this Android IoT app will send data acquired from sensors to the cloud. This IoT app helps us to introduce some important aspects about programming Android Things apps that use sensors.

As you may already know, Android Things is the new IoT OS developed by Google that brings the power of Android to IoT. Using Android Things, we can reuse the Android knowledge to build Android Things apps using almost the same APIs. For this purpose, in this tutorial, we will explore how to send the data acquired by sensors directly to the cloud using an IoT cloud platform: Ubidots.

Moreover, this Android IoT app will log all the data acquired by the sensors to the cloud. At the end of this tutorial, you will be able to develop an Android Things app with cloud support. This is a common scenario in an IoT project, so it is useful to investigate. Moreover, you can further improve this Android IoT app to support other kinds of sensors or to send data to a different cloud platform. This is a general purpose project that can be customized according to your need and your cloud platform specifications. If you want to try other IoT cloud platforms, you can read this article listing all the free IoT platforms.

To build this project we will use:

A temperature, pressure, and humidity sensor: BMP280

A Raspberry Pi 3 to run the Android Things OS

Moreover, the Android IoT app uses:

Connecting Android Things to the BMP280 Sensor

In this step, we will cover how to connect the BMP280 to Android Things. The BMP280 is an I2C sensor, which means we need four different wires to connect it:

Vcc (+3v)

GND

CLK (Clock)

SDA (data)

In this tutorial, as said previously, we'll use a Raspberry Pi 3 board. The board pinout connections should look like the picture shown below:

Once the wires are connected correctly, we can focus our attention on app development. If this is the first time you've used Android Things, it is important for you to read this tutorial about how to get started with Android Things. Anyway, as the first step, you have to clone the template repository from GitHub. This is an empty project made for Android Studio that we will use to build our Android IoT app. Let's start.

The first step is adding the Android Things driver that the Android Things app uses to exchange data with the sensor. For this purpose, open gradle.build (app level) and add the following line:

dependencies { .... compile 'com.google.android.things.contrib:driver-bmx280:0.4' ... }





The purpose of this app is acquiring data continuously from the sensor. Then, we need to implement the same approach used in Android when the app monitors a sensor.

Acquiring Data: SensorManager, Listeners, and Drivers

Android Things provides an elegant way to monitor the status of a sensor — using listeners. In MainActivity, add the following lines:

private Bmx280SensorDriver sensor; private SensorManager manager; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); Log.d(TAG, "onCreate"); try { manager = (SensorManager) getSystemService(Context.SENSOR_SERVICE); registerSensors(); sensor = new Bmx280SensorDriver(pin); sensor.registerTemperatureSensor(); sensor.registerPressureSensor(); } catch (IOException ioe) { ioe.printStackTrace(); } }





The code above is quite simple:

The app gets a reference of SensorManager. The app registers sensor listeners to get notified when the sensors read new values. It initializes the BMP280 driver. It registers the dynamic sensor callback (temperature and pressure).

Let us skip step 2 for a while and focus our attention on steps 3 and 4. In step 3, the Android IoT app creates a new instance of the Bmx280Sensor driver using the pin name where the sensor is connected. For a Raspberry Pi 3, the pin is I2C1.

Before registering the listeners to get the new values from sensors, it is necessary to register the two different listeners, which are notified when the sensor is connected to Android Things. This BMP280 sensor reads two physical properties, so it is considered as two sensors. Finally, here the code to register the value listeners:

private void registerSensors() { manager.registerDynamicSensorCallback(new SensorManager.DynamicSensorCallback() { @Override public void onDynamicSensorConnected(Sensor sensor) { if (sensor.getType() == Sensor.TYPE_AMBIENT_TEMPERATURE) { manager.registerListener(tempListener, sensor, SensorManager.SENSOR_DELAY_NORMAL); } else if (sensor.getType() == Sensor.TYPE_PRESSURE) { manager.registerListener(pressListener, sensor, SensorManager.SENSOR_DELAY_NORMAL); } } }); }





The method onDynamicSensorConnected is invoked as soon as the sensor is connected to the board. In this moment, the app can register the two listeners that will be notified when the sensor reads new values.

The last step is reading data from the sensor:

private SensorEventListener tempListener = new SensorEventListener() { @Override public void onSensorChanged(SensorEvent event) { // new value read } @Override public void onAccuracyChanged(Sensor sensor, int accuracy) {} }; private SensorEventListener pressListener = new SensorEventListener() { @Override public void onSensorChanged(SensorEvent event) { // new value read } @Override public void onAccuracyChanged(Sensor sensor, int accuracy) {} };





As you may already guess, in the method onSensorChanged , the app will send the new value to the cloud. In this phase, you can test the Android IoT app and check if it reads the data from the sensor. Anyway, before using the app, it is necessary to add these permissions in the Manifest.xml :

<uses-permission android:name="com.google.android.things.permission.MANAGE_INPUT_DRIVERS" /> <uses-permission android:name="com.google.android.things.permission.MANAGE_SENSOR_DRIVERS" />





Configure Ubidots to Get Data From the Android IoT App

This step describes how to configure Ubidots to accept data coming from the Android Things app. Before starting, it is necessary to have a free Ubidots account. Then, we have to configure the device that represents our device. For more information, you can referer to this article: how to configure Ubidots for an IoT project:

The next step is adding the variables that will hold the values sent from the Android app:

That's all! All the next steps necessary to configure Ubidots are completed. We can focus on the last step of sending data to the cloud.

Sending Data From Android Things to the Cloud

In this last step of this project, the Android IoT app sends data to the cloud. For this purpose, the app uses libraries developed for Android apps that simplify programming.

Ubidots exposes a set of APIs that the app can use to send data. These are JSON APIs. In this context, the Retrofit library and Gson are very useful.

Let us add the dependencies in the build.gradle file:

dependencies { ... compile 'com.squareup.retrofit2:retrofit:2.3.0' compile 'com.google.code.gson:gson:2.8.2' compile 'com.squareup.retrofit2:converter-gson:2.3.0' }





The Ubidots API the app calls is /api/v1.6/collections/values , which accepts a JSON array holding:

variable id

variable value

Where the variable id is the unique variable identifier provided by Ubidots and the value is the value the app sends. To develop an Android IoT app that invokes this API, we use Retrofit. Briefly, this library simplifies the process of invoking the JSON API. To this purpose, the first step is developing a Java interface that represents the API invoked:

public interface UbiAPI { @POST("/api/v1.6/collections/values") public Call<ResponseBody> sendValue( @Body ArrayList<Data> dataList, @Query("token") String token); }





The annotation @POST declares the API context, while the method defined in the interface represents the method the app invokes to send the data. This method accepts an array of Data as the body and a token that identifies our app. The Data class is:

import com.google.gson.annotations.Expose; import com.google.gson.annotations.SerializedName; public class Data { @SerializedName("variable") @Expose private String variable; @SerializedName("value") @Expose private Double value; public String getVariable() { return variable; } public void setVariable(String variable) { this.variable = variable; } public Double getValue() { return value; } public void setValue(Double value) { this.value = value; } }





This class holds the variable id and the value and describes how to convert these values to JSON format. Finally, we have to define the client that handles the communication to Ubidots:

public class UbiClient { private static final String TAG = UbiClient.class.getSimpleName(); private static final String UBI_BASE_URL = "http://things.ubidots.com/"; private static UbiClient client; private UbiAPI api; private Retrofit retroClient; private UbiClient() { retroClient = new Retrofit.Builder() .baseUrl(UBI_BASE_URL) .addConverterFactory(GsonConverterFactory.create()) .build(); } public static final UbiClient getClient() { if (client != null) return client; client = new UbiClient(); return client; } private UbiAPI getUbiClient() { return retroClient.create(UbiAPI.class); } public void sendData(ArrayList<Data> dList, String token) { api = client.getUbiClient(); Call c = api.sendValue(dList, token); c.enqueue(new Callback() { @Override public void onResponse(Call call, Response response) { Log.d(TAG, "onResponse"); Log.d(TAG, "Result:" + response.isSuccessful()); } @Override public void onFailure(Call call, Throwable t) { t.printStackTrace(); } }); } }





That's all. We have our client that connects to Ubidots and sends data. The last step is modifying the code shown above, where the app reads the sensor values, and add the following code to send the data to the cloud:

private SensorEventListener tempListener = new SensorEventListener() { @Override public void onSensorChanged(SensorEvent event) { final Data dTemp = new Data(); dTemp.setValue( (double) event.values[0]); dTemp.setVariable("59edbdc7c03f9721cc571662"); UbiClient.getClient().sendData(new ArrayList<Data>() {{ add(dTemp); }} , token); } @Override public void onAccuracyChanged(Sensor sensor, int accuracy) { } }; private SensorEventListener pressListener = new SensorEventListener() { @Override public void onSensorChanged(SensorEvent event) { final Data dPress = new Data(); dPress.setVariable("59edbdcec03f97212ff872c6"); dPress.setValue( (double) event.values[0]); UbiClient.getClient().sendData(new ArrayList<Data>() {{ add(dPress); }} , token); } @Override public void onAccuracyChanged(Sensor sensor, int accuracy) { } };





The interesting piece of code is the part that sends data — where the app builds the information to send to the cloud:

final Data dPress = new Data(); // Variable id dPress.setVariable("59edbdcec03f97212ff872c6"); dPress.setValue((double) event.values[0]); UbiClient.getClient().sendData(new ArrayList < Data > () { { add(dPress); } }, token);





Do not forget to add the permission to connect to the internet in the Manifest.xml

<uses-permission android:name="android.permission.INTERNET" />





Running the app and accessing to the Ubidots dashboard the result is:

Summary

At the end of this article, you, hopefully, gained the knowledge to develop an Android IoT app that uses Android Things to send data to the cloud. Moreover, you gained the knowledge to use an I2C sensor with Android Things.