IoT with an ESP8266 (Part 2) – Arduino Sketch

by | Feb 7, 2017

Programming the Huzzah

The Huzzah comes from Adafruit preprogrammed with NodeMCU’s Lua interpreter. You can use this to program the device directly. Or you can do what I did: completely overwrite it, use the Arduino IDE instead, and create what is known as an Arduino “sketch”.

Adafruit has info on how to do this in their tutorial. It includes an example of how to connect to Wi-Fi and make a quick check against a public site to confirm the Huzzah is talking. This code would later form the base for my communications with my service, which you can see later in my example. While I did successfully load and run their example, I won’t be including their code here.

What I ended up writing was a combination of several examples plus some of my own code. The full/complete code is available on GitHub here, but for a brief history of its creation, continue reading.

Measurements

After checking Wi-Fi connectivity with the example code, I moved on to writing something that would read the sensors.

There are a couple things to note here. The DHT22 works best if you delay at least 2 seconds between measurements. An Arduino sketch has a method called “millis()” which returns an unsigned long containing the number of milliseconds since the device was started.

My default setting in the code was to check for and post measurements every minute. This value can be changed, though, and could conceivably be set to less than 2 seconds. Since I hadn’t yet decided how often to measure, I left the 2 second check in place and pulled the two values into a couple of floats.

#include <DHT.h>

DHT dht(DHTPIN, DHTTYPE, 11); 
float humidity, temp_f, light;
String webString = "";
int retries = 35;

void getReadings() {
  unsigned long currentMillis = millis();

 &nbsp;if(currentMillis - previousMillis >= interval) {
    previousMillis = currentMillis; &nbsp;&nbsp;
    humidity = dht.readHumidity();
    temp_f = dht.readTemperature(true); &nbsp;&nbsp;&nbsp;&nbsp;// Read temperature as Fahrenheit

 &nbsp;&nbsp;&nbsp;// Check if any reads failed and exit early (to try again).
    if (isnan(humidity) || isnan(temp_f)) {
      return;
 &nbsp;&nbsp;&nbsp;}
 &nbsp;}

&nbsp; light = ((float)analogRead(A0) / (float)1024) * 100; &nbsp;
}

There is also some code included to check light levels. Checking light levels is a little arbitrary. The analog read method checks for voltage levels and returns a value between 0 and 1024. The photocell is designed to resist less with more light. So as it gets brighter, the value I get back would be closer to 1024.

To give this some meaning and a value closer to what I am getting from the other sensors, I divided the value by 1024 and multiply by 100. My light level should now be represented as a percentage of what’s possible: 0% is completely dark, and 100% is as much as it can measure, or really darn bright. This sets the light value closer to the range of the other two.

Webserver

With the inclusion of another library, the ESP8266 can serve as a webserver. This meant that my Huzzah not only could send measurements to an external source, but anyone could browse to it and immediately see the measurements. Including a library and then starting the server is fairly easy:

#include <ESP8266WebServer.h>

ESP8266WebServer server(80);

Then in the setup method the server needs to be made aware of which method in the Arduino sketch will service a call to the root:

server.on("/", handle_root);
server.begin();

My “handle_root” method calls getReadings and then builds a simple web page which it sends to the caller. I also turn on my “read” or blue LED at the start and turn it off once the page has been sent. So I can see that the device is working.

void handle_root() {
  digitalWrite(READLED, HIGH);
  getReadings();

  webString = "<html><head><title>Temperature and humidity via ESP8266 and DHT22 (v2)</title></head><body>";  
  webString = webString + "<h3>Environment Watch <br> Temperature, humidity, and light levels via ESP8266</h3>";
  webString = webString + "<p></br>";
  webString = webString + "Device ID: " + deviceId + "</br>";
  webString = webString + "Temp: " + floatAsString(temp_f) + " F</br>";
  webString = webString + "Humidity: " + floatAsString(humidity) + "%</br>";
  webString = webString + "Light: " + floatAsString(light) + "%</br>";
  webString = webString + "</br></p>";
  webString = webString + "<p>Refresh your browser to see the most recent readings.</p>";
  webString = webString + "</body></html>";

  server.send(200, "text/html", webString);
 
  delay(500);
  digitalWrite(READLED, LOW);
}

In the main loop I handle the request, if there is one:

server.handleClient();

Posting Measurements

While I am able to treat the Huzzah as a webserver, I also wanted a way to send measurements to a service and ultimately into a database. I briefly considered “pulling” those measurements from the web site directly, but later scrapped that idea. A pull scenario would work well in a situation where I knew the internal IP address of each unit, but it wouldn’t work if my web application/service was not hosted in the internal network.

So I switched to a “push” method. The Huzzah itself would contact an external site and push its readings to the site as simple query strings. The site would be an ASP.NET web application and would include a RESTful method to which I could feed the values. This site ended up as an Azure hosted App Service since I’d started using my device in multiple locations other than just my office (more on this later).

The process in the Arduino sketch worked as follows. Globally I’d designate a host and a port:

char* host = "MyWebSite.azurewebsites.net";
const int postPort = 80;

Then I created a single method to handle the task:

void post_data() {

  // turn on both LEDs
  digitalWrite(READLED, HIGH);
  digitalWrite(ERRORLED, HIGH);

  getReadings();
 
  // Use WiFiClient class to create TCP connections
  WiFiClient client;

  // exit if unable to connect
  if (!client.connect(host, postPort)) { return; }

  
  // got this far so turn off error/red LED
  digitalWrite(ERRORLED, LOW);

  // We now create a URI for the request
  String url = String("/Home/PostData") +
  String("?id=") + deviceId +
  String("&ip=") + WiFi.localIP().toString() +
  String("&temp=") + floatAsString(temp_f) +
  String("&humidity=") + floatAsString(humidity) +
  String("&light=") + floatAsString(light);
 
  // This will send the request to the server
  client.print(String("GET ") + url + " HTTP/1.1\r\n" +
               "Host: " + host + "\r\n" + 
               "Connection: close\r\n\r\n");

  // wait half a second for a response
  delay(500);
  
  // Read all lines of reply from server and print to Serial
  while(client.available()){
    String line = client.readStringUntil('\r');
 }
  
  // turn off read/blue LED
  digitalWrite(READLED, LOW);
}

My setup included a blue and red LED to provide some visual cue that something was going on. At the start of the method, both are turned on. Then I retrieve the readings/measurements. Next I attempt to connect to the host URL on the designated port. If all that works, I turn off the red LED. My assumption is at this point I should be able to communicate. Or, if I can’t, I won’t receive any errors anyway.

I decided on using a GET request, since I already had an example for this. The controller name and action are hardcoded here, along with the query strings. The values for the strings are pulled from the global variables I was using for measurements.

My service/action will respond with content indicating either success or failure/exception. I considered parsing this information and leaving the red LED on or off depending on whether I saw success or not, but Arduino code does not include very helpful utilities for checking whether a string contains text, so I skipped this task for now & planned on tackling it as some later point.

Setup and Loop

At this point I had my device capable of both sending data as well as providing data upon request. If you are familiar with Arduino then you know each Arduino sketch includes two main methods: setup and loop. My setup was responsible for initializing serial communications, connecting and checking that Wi-Fi is working, and wiring up the root web request handler:

void setup(void)
{
  // initialize LED pins for visual cues
  pinMode(READLED, OUTPUT);
  pinMode(ERRORLED, OUTPUT);
  pinMode(A0, INPUT);

  // initialize temperature sensor
  dht.begin();           
  int keyIndex = 1;

  // start wi-fi
  WiFi.begin(ssid, password);

  // Try to connect to Wi-Fi a pre-set number of times
  int tried = 0;
  while (WiFi.status() != WL_CONNECTED) {
    digitalWrite(READLED, HIGH);
    delay(250);
    digitalWrite(READLED, LOW);

    digitalWrite(ERRORLED, HIGH);
    tried = tried + 1;
    delay(250);
    digitalWrite(ERRORLED, LOW);

    if (tried >= retries) {
      connected = false;
      break;
    }    
  }

  if (!connected) { return; }
  
  server.on("/", handle_root);
  server.begin();

  // set last post to duration so we post immediately in main loop
 lastPost = postDuration;
}

This method will, during the while loop where it attempts to connect Wi-Fi, alternately blink the blue and red LEDs. This again provides a visual cue. If they stop blinking it means it eventually connected successfully. If the red LED remains on, then something went wrong during setup and we could not connect.

The last statement sets the last post time to the max duration. When the loop starts and the process checks whether it should post, it will do so immediately.

void loop(void)
{
  if (connected) {
    server.handleClient();

    unsigned long diff = millis() - lastPost;

    if (diff > postDuration) {
      post_data();
      lastPost = millis();
    }
  } else {
    digitalWrite(ERRORLED, HIGH);
    Serial.println(" - Could not connect!");
    delay(1000);
    digitalWrite(ERRORLED, LOW);
  }
} 

The loop runs as long as the device is active. It does 3 things:

If connected to Wi-Fi

  • Respond to any web requests
  • Gets readings and post them to the service

If not connected to Wi-Fi

  • Leave the red LED on and send a message through serial indicating we could not connect

It’s worth noting that the code examples do not include serial prints. This was to keep the code brief and to the point. The Arduino sketch attached to this blog post will include them.

Assuming the Huzzah is connected via an FTDI adapter and the Arduino IDE is running, you can output the text in a monitor. Here’s what the initial startup and first post looks like:

initial startup and first post - Arduino sketch

It’s also worth noting that the code examples only point at one Wi-Fi location. In reality I ended up moving my device from home to work and some other locations. Having to constantly update the Arduino sketch and reload it was becoming problematic. In the version of the sketch attached is a more sophisticated process for connecting.

The ESP8266 includes in its non-volatile memory/cache some of the Wi-Fi SSID’s and passwords it has successfully connected to in the past. As such, you can attempt to call WiFi.begin() without including SSID or a password. If it has already connected to the Wi-Fi network, it’ll just connect and you’re good-to-go.

So my updated code tries that first. If it works, it moves on. If it doesn’t successfully connect, it’ll try connecting to a list of SSIDs and passwords set in the sketch itself.

Armed with a device that can both respond to requests as well as post, I was ready to create an application to receive the data. But first I needed to decide how to store the data.

Give the other posts in this series a read:

IoT with an ESP8266 (Part 1) – The Hardware

IoT with an ESP8266 (Part 3) – ASP.NET Core Project and Database Setup

IoT with an ESP8266 (Part 4) – IoT Web Application

IoT with an ESP8266 (Part 5) – IoT with Azure