Sunday, April 14, 2019

Puttering in the Shop - The Internet of Stuff

A couple of small couple of small electronic parts and the world will know how could my home is

As a bit of a departure from my normal posts, I'm going to be getting more into software than softwood, but there will be a woodworking tie in at some point in the future. We just need to lay down some groundwork first.

In recent years, there has been a lot of buzz going on about having what used to be very mundane everyday household items become "smart" by being able to be connected to the internet and communicate with you or with other smart devices.

This concept - more commonly referred to as the "Internet of Things" (or IOT) - has unleashed an avalanche of smart thermostats, front door locks and refrigerators - all with the ability to let you know whether you need milk or that you've left the back door open and now the cat has escaped and is terrorizing the neighborhood.

Being someone who has made a career in the Information Technology field, I was starting to feel a wee bit out of the loop about these things - not a good place to be for any self-respecting IT guy.

I could have just simply read up on IOT devices, but why waste a perfectly good excuse for a bit of puttering in the shop...

As I mentioned in my previous post, you can literally build anything with a little electronic "LEGO". A quick search of the internet showed me many examples of people building their own IOT devices in such a way.

One common thing that I found with these projects was that a lot of them used a small Arduino compatible device based on the ESP8266 board.

Behold - the ESP8266
Behold - the ESP8266


The really nice thing about these boards is that they are:
  • fairly small (maybe 3 inches long and about an inch wide)
  • have WiFi capability 
  • easily programmable with a number of computer languages
  • extremely cheap (around $4 on e-bay)
As a starting project, I wanted to do something fairly simple and fairly useful.

I was always a bit of a weather nut, so I decided that I wanted to build a wireless thermometer that would let me see what the temperature was while I was away from home. I figured that this would be a good first project. 

After some more searching on the internet, I found a really good tutorial at Losant.com on how to build such a device with the ESP8266 and a DHT22 temperature/humidity module. (You can pick up a DHT22 for a couple of dollars on e-Bay)

After some research, I settled on using the NodeMCU ESP8266 board

Once I gathered all the various pieces together I followed the instructions in the tutorial on how to wire up the NodeMCU to the DHT22. I then plugged in the supplied program code, hooked my NodeMCU to a power source and watched it spring to life.

ESP8622 and DHT22 working together
ESP8622 and DHT22 working together


Buoyed by my success I wanted to make a few minor alterations to the program that Losant provided:
  • I wanted to add a little heartbeat LED to let me know that the board was working
  • I also wanted to reduce the data send rate to 10 seconds (the original program was set to 2 seconds) in order to conserve power if I wanted to run the board from a battery. 
  • I occasionally found that the NodeMCU board would "freeze" after a few hours, so I wanted the board to reboot itself every hour or so as a "refresh" mechanism. 
Taking the original code that Losant developed, I modified it to this:

/**
 * Example for sending temperature and humidity
 * to the cloud using the DHT22 and ESP8266
 *
 * Copyright (c) 2016 Losant IoT. All rights reserved.
 * https://www.losant.com
 */

#include "DHT.h"
#include <ESP8266WiFi.h>
#include <ESP8266HTTPClient.h>
#include <Losant.h>
#include <ArduinoJson.h>
#define DHTPIN 4     // what digital pin the DHT22 is conected to
#define DHTTYPE DHT22   // There are multiple kinds of DHT sensors
DHT dht(DHTPIN, DHTTYPE);
// WiFi credentials.
const char* WIFI_SSID = "<your ssid>";
const char* WIFI_PASS = "<your password>";
// Losant credentials.
const char* LOSANT_DEVICE_ID = "<you can register your id at losant>";
const char* LOSANT_ACCESS_KEY = "
<you can register your id at losant>";
const char* LOSANT_ACCESS_SECRET = "
<you can register your id at losant>";

WiFiClientSecure wifiClient;
LosantDevice device(LOSANT_DEVICE_ID);
void connect() {
  // Connect to Wifi.
  Serial.println();
  Serial.println();
  Serial.print("Connecting to ");
  Serial.println(WIFI_SSID);
  // WiFi fix: https://github.com/esp8266/Arduino/issues/2186
  WiFi.persistent(false);
  WiFi.mode(WIFI_OFF);
  WiFi.mode(WIFI_STA);
  WiFi.begin(WIFI_SSID, WIFI_PASS);
  unsigned long wifiConnectStart = millis();
  while (WiFi.status() != WL_CONNECTED) {
    // Check to see if
    if (WiFi.status() == WL_CONNECT_FAILED) {
      Serial.println("Failed to connect to WIFI. Please verify credentials: ");
      Serial.println();
      Serial.print("SSID: ");
      Serial.println(WIFI_SSID);
      Serial.print("Password: ");
      Serial.println(WIFI_PASS);
      Serial.println();
    }
    delay(500);
    Serial.println("...");
    // Only try for 5 seconds.
    if(millis() - wifiConnectStart > 5000) {
      Serial.println("Failed to connect to WiFi");
      Serial.println("Please attempt to send updated configuration parameters.");
      return;
    }
  }
  Serial.println();
  Serial.println("WiFi connected");
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());
  Serial.println();
  Serial.print("Authenticating Device...");
  HTTPClient http;
  http.begin("http://api.losant.com/auth/device");
  http.addHeader("Content-Type", "application/json");
  http.addHeader("Accept", "application/json");
  /* Create JSON payload to sent to Losant
   *
   *   {
   *     "deviceId": "575ecf887ae143cd83dc4aa2",
   *     "key": "this_would_be_the_key",
   *     "secret": "this_would_be_the_secret"
   *   }
   *
   */
  StaticJsonBuffer<200> jsonBuffer;
  JsonObject& root = jsonBuffer.createObject();
  root["deviceId"] = LOSANT_DEVICE_ID;
  root["key"] = LOSANT_ACCESS_KEY;
  root["secret"] = LOSANT_ACCESS_SECRET;
  String buffer;
  root.printTo(buffer);
  int httpCode = http.POST(buffer);
  if(httpCode > 0) {
      if(httpCode == HTTP_CODE_OK) {
          Serial.println("This device is authorized!");
      } else {
        Serial.println("Failed to authorize device to Losant.");
        if(httpCode == 400) {
          Serial.println("Validation error: The device ID, access key, or access secret is not in the proper format.");
        } else if(httpCode == 401) {
          Serial.println("Invalid credentials to Losant: Please double-check the device ID, access key, and access secret.");
        } else {
           Serial.println("Unknown response from API");
        }
      }
    } else {
        Serial.println("Failed to connect to Losant API.");
   }
  http.end();
  // Connect to Losant.
  Serial.println();
  Serial.print("Connecting to Losant...");
  device.connectSecure(wifiClient, LOSANT_ACCESS_KEY, LOSANT_ACCESS_SECRET);
  while(!device.connected()) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("Connected!");
  Serial.println();
  Serial.println("This device is now ready for use!");
}
void setup() {
  Serial.begin(9600);
  Serial.setTimeout(2000);
  pinMode(BUILTIN_LED, OUTPUT);
  // Wait for serial to initialize.
  while(!Serial) { }
  Serial.println("Device Started");
  Serial.println("-------------------------------------");
  Serial.println("Running DHT!");
  Serial.println("-------------------------------------");
  connect();
}
void report(double humidity, double tempC, double heatIndexC)
{
  StaticJsonBuffer<400> jsonBuffer;
  JsonObject& root = jsonBuffer.createObject();
  root["humidity"] = humidity;
  root["tempC"] = tempC;
  root["heatIndexC"] = heatIndexC;
  device.sendState(root);
  Serial.println("Reported!");
}
int timeSinceLastRead = 0;
int timeTotal = 0;
void loop() {
   bool toReconnect = false;
  if (WiFi.status() != WL_CONNECTED) {
    Serial.println("Disconnected from WiFi");
    toReconnect = true;
  }
  if (!device.connected()) {
    Serial.println("Disconnected from MQTT");
    Serial.println(device.mqttClient.state());
    toReconnect = true;
  }
  if (toReconnect) {
    connect();
  }
  device.loop();
  // Reboot roughly every hour.
  if (timeTotal > 30000) {
    Serial.println("Reboot");
    ESP.restart();
  }
  // Report every 10 seconds.
  if(timeSinceLastRead > 10000) {
    digitalWrite(BUILTIN_LED, HIGH);
    Serial.println(timeTotal);
    // Reading temperature or humidity takes about 250 milliseconds!
    // Sensor readings may also be up to 2 seconds 'old' (its a very slow sensor)
    float h = dht.readHumidity();
    // Read temperature as Celsius (the default)
    float t = dht.readTemperature();
    // Read temperature as Fahrenheit (isFahrenheit = true)
    float f = dht.readTemperature(true);
    // Check if any reads failed and exit early (to try again).
    if (isnan(h) || isnan(t) || isnan(f)) {
      Serial.println("Failed to read from DHT sensor!");
      timeSinceLastRead = 0;
      return;
    }
    // Compute heat index in Fahrenheit (the default)
    float hif = dht.computeHeatIndex(f, h);
    // Compute heat index in Celsius (isFahreheit = false)
    float hic = dht.computeHeatIndex(t, h, false);
    Serial.print("Humidity: ");
    Serial.print(h);
    Serial.print(" %\t");
    Serial.print("Temperature: ");
    Serial.print(t);
    Serial.print(" *C ");
    Serial.print(f);
    Serial.print(" *F\t");
    Serial.print("Heat index: ");
    Serial.print(hic);
    Serial.print(" *C ");
    Serial.print(hif);
    Serial.println(" *F");
    report(h, t, hic);
    timeSinceLastRead = 0;
  }
  delay(100);
  timeSinceLastRead += 100;
  timeTotal += 1;
  digitalWrite(BUILTIN_LED, LOW);
  }


The key items to note with this code:
  • I added a counter called timeTotal that incremented by one during each loop cycle. When the count reached 30000, a system restart was initiated. 
  • The LED flashes every time data is sent over the WiFi via the digitalWrite command.
  • I changed the timeSinceLastRead value check in the loop to send data at 10000 milliseconds versus the default of 2000 milliseconds(or 10 seconds versus 2 seconds)
Once I got the code compiled and loaded into my NodeMCU, I then attached a white LED to the D1 port of the board and hooked everything back up to a power source. 

Blinking while data is being sent
Blinking while data is being sent

Watching the LED blinking every 10 seconds was a very good sign.

Now that the board was alive, I needed it to talk to the internet. One of the main services that Losant provides is cloud hosting of data from IOT devices. They provide a series of very easy to set up dashboard widgets that you could use with your device.

Referring back to their tutorial I set up a Losant account, set up a device id and key (which I had to plug into my program code and recompile back into the NodeMCU) and created a few dashboard widgets on the temperature data that I was sending.

The nice thing was that it was also pretty easy to embed these dashboard widgets into any website.

Oh, by the way, is anyone curious about the temperature outside my door?

View my Losant-powered widget "Atwood Ontario Temperature" View my Losant-powered widget "Atwood Ontario Weather Trends"

And with that, I built my first Internet of Things device.

At the moment it's just a couple of electronic bits sitting on a breadboard, relying on an external power source, but my ultimate goal is to have this in a more proper home for my new creation that is better suited for living outside in the elements, nowhere near any place that it could plug into for juice.

But of course, that will be for another post. 😊

No comments:

Post a Comment