Webroots 1: OTA updating and Interrupts

In this series of tutorials, we will utilize the wifi-capabilities of the ESP8266 chip and talk to the internet.

We will first enable the Over-The-Air ability of our ESP8266 chip for future convenience. Please select your board under Tools > Board.

Plugging and unplugging the ESP8266 can be a hassle especially if you want to use it with a power source that is not your computer. Fortunately, the ESP8266 comes with an ability to update itself Over-The-Air (OTA). What this means that the board connects to an Access Point (AP) also known as your router, and awaits instructions. Think of it as an invisible USB cable that is connected from your computer to your board via WiFi.

In this tutorial, we will learn how to extract useful code from other examples. This is a useful skill as sometimes you might only want some parts of the project.

Now open File > Examples > ArduinoOTA > BasicOTA in a separate Arduino window. For this project we will use most of the code that is provided and explain what each part does.

Now we will need to include a couple of libraries for our OTA to work:

#include <ESP8266WiFi.h>
#include <ESP8266mDNS.h>
#include <WiFiUdp.h>
#include <ArduinoOTA.h>

Now we want to give our chip the SSID (name) of the network and the password:

const char* ssid = "MyNetwork";
const char* password = "StrongPassword";

Now going into setup, we want to perform some startup procedures on our chip:

 WiFi.mode(WIFI_STA); //we are going to be a client! 
 WiFi.begin(ssid,password); //Start the wifi, with this ssid and password

In order to ensure that some boot process didn't mess up our wifi chip, we are going to restart the WiFi chip if we didn't connect after awhile:

while (WiFi.waitForConnectResult() != WL_CONNECTED) 
{
    Serial.println("Connection Failed. Rebooting ... ");
    delay(5000);
    ESP.restart();
}

Now we need some indicators to tell us if we are done and/or we faced errors. These functions that we call from ArduinoOTA tell us if we have started, ended, and what our progress is:

ArduinoOTA.onStart([]() {
  Serial.println("Start");
});
ArduinoOTA.onEnd([]() {
  Serial.println("\nEnd");
});
ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) {
  Serial.printf("Progress: %u%%\r", (progress / (total / 100)));
});

The following code helps us to diagnose the error depending on what is . Usually this process is quite reliable and I usually remove some of this code to keep the sketch shorter, however if this is your first time, having this code is certainly very useful.

ArduinoOTA.onError([](ota_error_t error) {
  Serial.printf("Error[%u]: ", error);
  if (error == OTA_AUTH_ERROR) Serial.println("Auth Failed");
  else if (error == OTA_BEGIN_ERROR) Serial.println("Begin Failed");
  else if (error == OTA_CONNECT_ERROR) Serial.println("Connect Failed");
  else if (error == OTA_RECEIVE_ERROR) Serial.println("Receive Failed");
  else if (error == OTA_END_ERROR) Serial.println("End Failed");
});

Now we have finally set up everything, it is time to start:

ArduinoOTA.begin();
Serial.println("Ready");
Serial.print("IP address: ");
Serial.println(WiFi.localIP());

The only thing you need to put in the loop is:

ArduinoOTA.handle();

So now that you have this bunch of code, you can add visual indicators that separate them from other code you don't when you are implementing it for other projects:

//============= Don't Touch This ==================
code code code
code code code
//============= End of Don't Touch This =============

Having visual sections for your code helps you to keep track of where you are better.

There you go! Now if you are coming from the Lightbeans tutorial, try changing the color of the light and uploading it wirelessly! If not, try changing the blink rate of the on board LED on the ESP8266 board.

You should find the ESP8266 listed as another port on your Arduino IDE if you are connected to the same network. Note that the OTA only works on your home network (you can't access it from the internet).

As long as you include this code on all your projects you should be able to update the ESP8266 wirelessly.

What you learned

  1. How to update your ESP8266 chip wirelessly.
  2. How to segment code visually.
  3. How to extract useful code from examples.

Additional Projects

  1. Remove all debugging code to make the OTA section as short as possible.

Interrupts

Now you might want to turn off the night light, so having a button would be useful for that.

Instead of asking the button if it has been pressed, we'll let the button come to us, metaphorically speaking.

The first method is called polling because we ask a sensor for data. However this method can be a little wasteful because we have to wait for the sensor to get back to us each time, and when there is no change for long periods of time, then this little waste builds up.

The second method is called interrupts because the chip is interrupted when there is a change in status. This is better as it allows the chip to react immediately when there is a change, and it can do other things if there is no change.

For our chip, we can attach an interrupt to a pin for our button. The interrupt will then call a function which we can then use to do whatever action we intend that interrupt to have.

Firstly, the syntax for interrupts are as follows:

setup (){
  pinMode(interruptPin,INPUT); //interruptPin was defined earlier in the code
  attachInterrupt(digitalPinToInterrupt(interruptPin), buttonPress, FALLING);
}

We must first set the pin up as as input, then we must specify the pin we intend to interrupt, the function we want to call, and the type of interrupt.

The type of interrupt requires a little knowledge of signals, but you've already done this! The interrupt can detect when a signal changes, or the state of a signal. A table below summarises the types.

Type of interrupt Signal
HIGH Triggers when signal is high
LOW Triggers when signal is low
RISING Triggers when signal goes from low to high
FALLING Triggers when signal goes from high to low
CHANGE Triggers when signal changes from either state

Now let's write the button function:

void buttonPress(){
  state =! state;
}

Note that your function cannot take any arguments nor can it return any arguments. This is a special type of function. Because this function may be called at any time, having it interact with an argument might result in an unexpected outcome. That argument might not have been defined yet, or the value might have changed.

Note that arguments are not the same as global variables. Global variables are values that are initialised outside of setup or loop.

The equation =! is to set the state the opposite boolean value that it was originally. If it were true, it will now become false and vice versa. This helps us turn the light on or off based on the same input (button press). It is equal to saying 'not', so for example 'not true' is false. There are a number of boolean operations, but they will be introduced when we need them.

Any global variable an interrupt function uses must be specified as follows:

volatile bool state = true;

It must have the keyword volatile attached to it because it can change any time, and we have to let the program to know that.

Since it is a global variable we can include it in our nightlight code:

...
if (pinval < 900 and state==true){
  fadeIn();
}
if (pinval > 900 or state==false){
  fadeOut();
}
...

Now, wire it up as follows in the diagram below. Note that you should only add this circuit to your existing circuit instead of pulling it out.

Figure 1. Circuit Schematic

Figure 2. Circuit Layout

Note that we are using a 3.3V input and not 5V input. While the 5050 LEDs run off 5V, the pins on the chip cannot take 5V logic.

Why is there a need for the additional resistor? Well, when we are not pressing the button, we cannot leave it hanging, or it will read random data. Therefore, we have to bring it to zero when we are not pressing it. When we press it, we want to bring it to high (but it is still connected to ground!) so to prevent a short, we put a resistor there.

Now the pin reads a HIGH value on button press, and a LOW value when the button is not pressed. This is known as a pullup resistor and if you flipped the operation, that would be known as a pulldown resistor.

Two of the inputs of the button are connected to each other as seen in the diagram above, and they can be identified because the legs stick out slightly in the same direction for the connected corners. If your button doesn't work on the first go, try rotating it by 90 degrees.

Excellent! Now when you upload your code, you should be able to turn off your nightlight at will. Turning it on requires that the state be on and the room is dark.

What you learned

  1. Interrupts and how to use them
  2. Keeping state
  3. How to wire a button
  4. How to use pullup and pulldown resistors.

Additional Projects

  1. Modify your code to fit your nightlight needs. Maybe you want it to light up regardless if the state is true or false?

Now your nightlight doesn't need any interaction from you. It only has to be plugged into a power source. It can update, can be turned off, and can react to the light in the room without having to unplug it, pretty convenient!