Recording Remote Temperatures With AndroidThings, Arduino And Firebase

I have had much fun with AndroidThings so far. However I have been on the lookout for a different way to apply the use of AndroidThings and learn a bit more.

We have a data centre which has hot aisles and cold aisles, and we like to measure as much as we can to help make decisions and measure their effects. Having quick access to temperature data helps with the process of tweaking process coolers and fans to improve power efficiency, while maintaining a stable and reliable environment.

Sensors

There are a few options with temperature sensors, however, for my case, I wanted to be able to locate my sensors a long distance from where they were plugged in. Reading through a number of threads online lead me to experimenting with the DS18B20. This was later confirmed by Marcos Placona. Thanks Marcos!

It turns out that the DS18B20s would not work directly with Android due to the GPIO speed available being too low for the sensor.

So… I’d need to use an Arduino to get my temperature values. I finally ended up using an Arduino Nano but I started using an Eleven (from this kit). How hard could it be……?

Arduino Development

Initially I used a desktop Arduino IDE, however, I eventually ended up using the web based IDE which I found to be a little slower compiling, but much more convenient for loading libraries and sharing code.

The Arduino Setup

As an Arduino beginner I found a number of libraries to help with all sorts of hardware and software. Getting temperatures from a DS18B20 and wiring it up was pretty simple. What’s more, I found that these could be run in a bus arrangement whereby only one digital input was required.

The circuit can be found in a number of places online. It contains a 4k7 (4700Ω) pull up resistor which you can see on the right.

Getting a reading from a DS18B20 sensor with an Arduino Uno “Eleven”

With the two libraries OneWire and DallasTemperature, the code is pretty straight forward too. OneWire allows communicating with a number of devices over a single data line, which can also be used to provide power to the slave devices. Thanks to the community which made these so easy to use.

#include <OneWire.h>
#include <DallasTemperature.h>
OneWire temp2(2); // pin 2
DallasTemperature sensors(&temp2);
void setup()
{
// set up the serial port
Serial.begin(38400);

// initialise sensors
sensors.begin();
}
void loop()
{
// get temperatures
sensors.requestTemperatures();
  // print out a temperature
Serial.print("Temperature 0: ");
Serial.print(sensors.getTempCByIndex(0));
Serial.println(" deg C");
delay(1000);
}

This was great for grabbing one temperature, but I’d require a number more. To distinguish between the sensors and all the temperatures returned each of the sensors has an address that looks something like this:

0x28, 0xFF, 0x2F, 0xB4, 0x70, 0x16, 0x04, 0x88

The addresses can be found by plugging them in one at a time and running the sketch that is described here or downloaded here.

Finally to get a temperature I simply called the following:

sensors.getTempC(deviceAddress);

where deviceAddress relates to a sensor. I ended up labelling all my sensors with numbers and stored a table relating addresses to the labels.

Android to Arduino

I decided to use the UART ports on my Raspberry Pi 3 Model B to communicate with the Arduino’s RX/TX ports. This seemed simple enough and in hindsight is, however, there are a couple of things that I learnt and wish I’d known:

  1. I/O through the TX/RX ports on the Arduino device doesn’t work when connected to the Arduino device through USB. (Thanks Marcos for confirming my suspicion on this after many hours)
  2. The Arduino ports may work on a different voltage from the Raspberry Pi (5V vs 3.3V).

Looking at diagrams online, the Arduino Nano didn’t seem to require a level converter and I only needed one port so I decided to go with that.

I got the version which has an FTDI chip. Note that I was unable to get this to communicate with my MacBook(!) and ended up using my single purpose Windows computer to program the Nano through the web IDE. I tried using the Sierra built in FTDI driver as well as a downloaded one to no avail.

Here is a diagram of the circuit with a couple of sensors:

The Nano is being powered by the Raspberry Pi and it has its TX/RX connected to the RX/TX pins of the Raspberry Pi. In addition the Raspberry Pi’s 5V and ground pins are connected the Nano’s VIN and ground.

In my circuit, I also added an LED to act as a signal that a temperature request has been received from the Raspberry Pi (pin D12). It’s pretty handy to know that the Android and Arduino code is working when it’s all set up.

Receiving a Temperature Request

The plan was to have the Uno retrieve temperatures from the sensors only when requested by the Raspberry Pi. To do this, the Uno monitors the Serial input regularly (every ~250ms) in its loop() function. When the Raspberry Pi sends a “T” request, it is received by the Uno, which in turn gets all the temperatures and sends them back to the Raspberry Pi:

  // turn OFF the receivedRequest LED
digitalWrite(receivedRequest, LOW);
raspberryPiCommand = ""; // clear out the read data

// read data
if( Serial.available() ) { // if data is available to read
// read it and store it in 'raspberryPiCommand'
raspberryPiCommand = Serial.readString();
raspberryPiCommand.trim(); // trim it
}
  // check read data
if( raspberryPiCommand == "T" ) { // if "T" was received
// turn ON the receivedRequest LED
digitalWrite(receivedRequest, HIGH);

// read the temperatures
    // send the temperatures back to the Raspberry Pi
  }
delay(250);

Note that at the beginning of the loop() I turn off an LED and I illuminate it when a “T” request is received for the duration of a loop (~250ms).

Returning a Temperature Result

When communicating over the RX/TX ports on the Uno, returning a result is as simple as writing to the Serial interface using Serial.print() methods. However, once I realised I needed more than just a list of temperatures I decided to look for a JSON library, and sure enough, there is a great one, ArduinoJson, which worked very well. It comes with a handy website to assist with calculating the JSON buffer size.

const size_t bufferSize = JSON_ARRAY_SIZE(SENSORS_COUNT) + JSON_OBJECT_SIZE(1) + SENSORS_COUNT*JSON_OBJECT_SIZE(2) + 100;
StaticJsonBuffer<bufferSize> jsonBuffer;
JsonObject& root = jsonBuffer.createObject();
JsonArray& data = root.createNestedArray("sensors");
    // get temperatures
sensors.requestTemperatures();
    for (int i=0; i <= SENSORS_COUNT; i++){
JsonObject& item = jsonBuffer.createObject();
item["id"] = i;
item["value"] = sensors.getTempC(SENSORS[i]);
data.add(item);
}

I am creating something a bit like this:

{
"sensors": [
{"id": 1,"value":19.8123}
]
}

Handling Temperature Results on AndroidThings

It took a little while to get here and this is probably the easy part for many.

The AndroidThings github space has a uart loopback example which was not exactly what I was looking for but it was close. It demonstrates how to receive data through a uart port and then transmit it back. I was looking to regularly send one thing and receive something else back with a simple Handler / Runnable.

private Runnable mTransferUartRunnable = new Runnable() {
@Override public void run() {
transferUartData();
mOutputHandler.postDelayed(mTransferUartRunnable, REQUESTS_DELAY);
}
};

To connect using UART, I needed to make sure that the Serial connection was using the same settings on the Raspberry Pi as had been set in the Nano:

mLoopbackDevice.setBaudrate(baudRate);
mLoopbackDevice.setDataSize(DATA_BITS);
mLoopbackDevice.setParity(UartDevice.PARITY_NONE);
mLoopbackDevice.setStopBits(STOP_BITS)

My modifications are pretty straight forward, simply parsing the data into an Object from the JSON using Moshi and then pushing the data to Firebase:

while ((mLoopbackDevice.read(buffer, buffer.length)) > 0) {
String receivedData = new String(buffer, Charset.forName("UTF-8")).trim();
Log.i(TAG, "rcvd: "+ receivedData);

// create object from data
Moshi moshi = new Moshi.Builder().build();
JsonAdapter<LoggedData> jsonAdapter = moshi.adapter(LoggedData.class);
LoggedData data = jsonAdapter.fromJson(receivedData);
if(data != null) {
data.setTimestamp(System.currentTimeMillis() / 1000);

// send data to firebase
storeSensorData(data);
}
}

I have noticed that very occasionally the data received is not complete or doesn’t come through in one stream. This means that parsing will fail and some data will not be collected. For my purposes this isn’t an issue; however, it might need some handling for other projects with more important data.

It seems almost unreal how easy it is to store the data with Firebase:

mDatabase.getReference().child(LOGGING_TABLE)
.child(String.valueOf(data.getTimestamp())).setValue(data);

I used the timestamp for the key to the sensor readings so fetching the last N records would be as simple as using the limitToLast method with a Firebase DatabaseReference.

Visualising the data

Collecting logs is great, however, the real fun is in visualising the temperatures. To do this I built a simple charting application which connects to Firebase and pulls down a number of hours of logs.

MPAndroidChart is such an amazing project which is perfect for charting in Android. The documentation is great so I won’t go into any implementation details.

Here is a screenshot from the application using the library. It is displaying 11 hours of logs and a process cooler just turned on:

This is displaying Firebase data, logged from the hot aisle near the rack where I am testing 4 sensors

Next Steps

There are a number of further pieces that need to be explored for this project to move it on from a proof of concept:

  1. A companion app for AndroidThings configuration (labelling the device data, adding sensors, authenticating with Firebase, Wi-Fi networking).
  2. Determine if the AndroidThings application can be used to update the Arduino code.
  3. Authenticate with Firebase as the authentication library is not available in AndroidThings.
  4. Experiment with other options to communicate between Arduino and AndroidThings (such as I²C or USB).
  5. Update the OneWire network design to support long runs of multiple sensors.
  6. Get a board made for the circuit.

Conclusion

Combined with Arduino, AndroidThings seems like a pretty compelling combination to build measurement hardware / software solutions. The libraries and APIs available in Android allow for quite rapid development, especially managing the collected data.

Like what you read? Give Julius Spencer a round of applause.

From a quick cheer to a standing ovation, clap to show how much you enjoyed this story.