Introduction: Arduino Yún With DHT22

I bought an Aruino Yún board back in 2014 or so, to have an IoT-enabled evaluation board to play with. Actually it's a nice package of an Atmel ATmega32u4 AVR microcontroller and an Atheros AR9331 SOC running a modified version of OpenWrt Linux called OpenWrt-Yun. These two main components form the heart of the Yún and are connected with an on-board serial line. The Atheros chip comes with Ethernet- and WiFi connectivity, a microSD card slot and with a USB-host port.

There is a library -called Bridge- which connects these two chips over the on-board serial line. This library has many features, in this project I'm going to use only some of them.

After this intro you might ask what's this project about? Well, it's a project built around the DHT22 temperature- and humidity sensor. It has two uses. One of them is to have a momentary display of the current temperature and humidity readings using analogue gauges in a web page on my home network. The other use is to have a historical record by uploading the readings together with a timestamp every hour to my Google Sheet where I can further process the data.

The Yún can be used for both, at the same time. If you're interested, read on!

Step 1: Sketch for the AVR Microcontroller Side

The Bridge library for the Yún -which is available in the Arduino IDE-, makes it possible to publish a REST API using the webserver on the Linux side. The REST API is a good thing in general, because it enables us to build a loosely-coulped architecture.

That means we write the back-end once -on the AVR microcontroller side- and we can use it from a variety of front-ends, no matter what it is, as long as it fulfills the "contract".

In this case both the web page and the Google Sheet uploader will use the same REST API.

So what should we put on the microcontroller?

First make sure you have the DHT sensor library version 1.2.3 from Adafruit installed in your Arduino IDE.

You can configure your REST API to require or not to require a password. The default is to require a password.

You can download and extract the attached zip, it contains the same sketch as shown below. (You most probably want to extract it in /Users/Your User Name/Documents/Arduino if you use a Mac.)

#include <Bridge.h>
#include <BridgeServer.h>
#include <BridgeClient.h>

#include <DHT.h>

// Listen to the default port 5555, the Yún webserver
// will forward there all the HTTP requests you send
BridgeServer server;

// DHT22 is connected to Pin 2
DHT dht(2, DHT22);

void setup() {
  // Bridge startup
  pinMode(13, OUTPUT);
  digitalWrite(13, LOW);
  Bridge.begin();
  digitalWrite(13, HIGH); 

  // Listen for incoming connection only from localhost
  // (no one from the external network could connect)
  server.listenOnLocalhost();
  server.begin();

  dht.begin();
}

void loop() {
  // Get clients coming from server
  BridgeClient client = server.accept();

  // There is a new client
  if (client) {
    // Process the request
    process(client);

    // Close connection and free resources.
    client.stop();
  }

  delay(50); // Poll every 50ms
}

void process(BridgeClient client) {
  
  // read the command
  String command = client.readStringUntil('\r');

  if (command == "readsensor") {

    float celsius = dht.readTemperature();
    float humidity = dht.readHumidity();
    
    boolean success = !isnan(celsius) &&
                      !isnan(humidity);

    // set HTTP headers
    client.println(F("Status: 200"));
    client.println(F("Content-Type: application/json"));
    client.println();

    // set JSON body
    String data = "{\"success\":" + String(success ? "true" : "false");
    if (success) {
      data += ", \"celsius\":" + String(celsius) + 
              ", \"humidity\":" + String(humidity);
    }
    data += "}";
    client.println(data);

  } else {
    // unknown command, reply with error
    client.println(F("Status: 400"));
    client.println(F("Content-Type: text/plain"));
    client.println();
    client.println("Unknown command: " + command);
  }
  
} // void process(BridgeClient client)

Step 2: Web Application to Display Live Temperature and Humidity Readings

Use a microSD card to store the web application files. Extract the attached archive under /mnt/sda1/arduino/www as shown in the screenshot.

With this, if you navigate to your Yún, -using the URL http://arduino.local/sd/dht22/ - you should see the two gauges displaying the live values.

That's because the webserver on the Yún serves the content from /mnt/sda1/arduino/www under the URL http://arduino.local/sd

The web application uses AJAX to connect back to the REST API, which returns the readings in a JSON payload.

I'm using the HTML5 Canvas gauges from Mikhus. https://github.com/Mikhus/canvas-gauges

It's really cool and you can configure it as you like, in a nice declarative way.

The below code listings show how to use the REST API to get the readings, exposed by the Bridge library on the Yún.

(function() {
  'use strict';

  if (!Array.prototype.forEach) {
      Array.prototype.forEach = function(cb) {
          var i = 0, s = this.length;
          for (; i < s; i++) {
              cb && cb(this[i], i, this);
          }
      }
  }

  document.fonts && document.fonts.forEach(function(font) {
    font.loaded.then(function() {
      if (font.family.match(/Led/)) {
        document.gauges.forEach(function(gauge) {
          gauge.update();
          gauge.options.renderTo.style.visibility = 'visible';
        });
      }
    });
  });

  setInterval(function() {
    var xmlhttp = new XMLHttpRequest();
    xmlhttp.onreadystatechange = function() {
      if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
        try {
          var data = JSON.parse(xmlhttp.responseText);
          if (data.success) {
            document.gauges.get('temp-gauge').value = data.celsius;
            document.gauges.get('humi-gauge').value = data.humidity;
          }
        } catch(err) {
        }
      }
    };
    xmlhttp.open('GET', '/arduino/readsensor', true);
    xmlhttp.send();
  }, 2000);  // Poll every 2 seconds

})();

And this is the python code that gets executed by cron to get the readings.

def get_sensor_readings():
    """
    Get the DHT22 sensor readings from the REST API.    
    Get and parse the JSON from the REST API with the sensor readings,
    if something goes wrong, then raises an exception.
    Returns: Data, the parsed JSON as a dictionary object.
    """
    # Consume the REST API for the sensor readings
    response = requests.get('http://127.0.0.1/arduino/readsensor', auth=('user', 'pass'))
    if response.status_code != 200:
        # This means something went wrong.
        raise Exception('GET /arduino/readsensor/ {}'.format(response.status_code))
    data = response.json()
    if data['success'] == False:
        raise Exception('Reading the sensor has failed')
    return data

Step 3: Upload Readings to Google Sheets

First you need to make a sheet with the name 'DHT22Readings' under your Google account. Also make sure you set the the column names 'Celsius', 'Humidity' and 'Timestamp'. (You can use the attached screenshot as hint.)

Then you should turn on the Google Sheets API. I've used the description provided on the link below: https://developers.google.com/sheets/quickstart/p...

Do the following on the Yún, and not on your computer! You can use ssh to connect to the Yún from Terminal.

Make sure you complete Step 1 and 2 in the above Quickstart. You don't have to do Step 3. Instead download the attached zip and extract it to your Yún, into the home directory. (/root)

Replace the placeholders 'user', 'pass ' and 'ID' in the extracted dht22sheet.py file with root, your Yún password and the ID of the Google Sheet you've created respectively. Wonder what's the ID? The ID is used in the URL.

https://docs.google.com/spreadsheets/d/ID/edit

Before going any further, make sure these are replaced with your actual ones throughout the file.

Now put the client_secret.json -Google has created for you when enabling the API- file to the same location. (/root) Also make sure you've installed the requests (python) module before going on. You can do so by issuing the following:

pip install requests

Finally run:

python dht22sheet.py --noauth_local_webserver

This will complete an OAuth2 flow when running the first time and will store the credentials under the home directory. The option --noauth_local_webserver is important because otherwise it tries to start a browser on the Yún. If we call it with this option, it will print a URL, what you need to copy and paste into a browser window on your computer. Finally it will display a text in the browser what you need to copy and paste back to the python client running on the Yún.

From now on you can call the script without the option, it will run normally using the credentials created when running the first time. The client will consume the same REST API to get the readings as the web page from the previous step, and uses Google's example code to upload the data to the sheet.

To finish this project, there's one more thing to do. We don't want to run this client by hand. Rather we want cron to do the job for us.

To set up the cronjob, run this on the Yún:

crontab -e

Make sure the crontab look like this after editing it:

#min hour day month dayofweek command
0 */1 * * * /usr/bin/env python /root/dht22sheet.py
#crontab must (as fstab) end with the last line as space or a comment

Even if you power down your board the cron job will remain. At the next startup cron will make sure to upload your data to the cloud every hour. You should see something like the screenshot attached. You can also make Google Sheets to draw a nice graph of the data if you like.

Enjoy!