LG201: IoT Prototype Development - Embedded Programming

It’s a Super Loop, Super Loop… It’s Super Loopy … As Rick James might have said if he’d had more of an interest in embedded programming. I mean, I’m just presuming he didn’t of course… anyway, let’s interrupt that loop.

Coming from an industry where I deliver distributed web applications, running on top of powerful infinitely scalable cloud-based operating systems, the simplicity of the structure of Embedded Programming is a beautiful thing to me, and is reminiscent of my early ventures into programming.

I came up using a piece of software called Macromedia Director which was used to create CD-ROMs. These would be self-booting on insertion to a PC and would run in full-screen kiosk mode. The programmes were frame-based and you’d set the frame rate you wanted them to run at. You’d have a loop for the home page, with buttons, stretched across all the frames, clicking these would interrupt the loop to display other loops of content.

Whilst the software was used to create what was arguably the precursor of websites, you could also create complex applications. The secret was to set-up your variables on Frame 1 and set your loop to only ever go back as far as Frame 2. So, this combined with my aforementioned interest in early games programming on the ZX Spectrum, means the Super Loop architecture makes loads of sense to me.

// declare stuff here...

void setup() {
  // set stuff up here...
}

void loop() {
  // do this forever...
}

Above: Everything you need to know about embedded programming in 7 lines of code.

You’re on your own, there’s no operating system to rely on, your programme must set things up, loop forever, only to be briefly interrupted by input or scheduled tasks before returning to its endless loop.

That’s not to say there aren’t challenges, 4MB of Flash to hold everything and power management issues in battery-powered scenarios. The ESP32 is also a relatively new board and there will be a few workarounds to get things up and running.

Let’s run through the tasks set out in my Specification.

3a - Test Functionality

One of the challenges of Headless programming is knowing what the thing is doing… is it even on?! The ESP32 has a serial interface over the USB port, so we can output helpful feedback, such as “Starting Up”, or “Button Pressed”. This functionality combined with the RGB Pixel output, will guide our Code and confirm everything is correctly wired together.

A few lines of code later and we’re writing to a serial window when button state changes, but there is an issue… the loop runs so fast that the tactile switch sometimes registers multiple click events. This is a known issue called Bouncing, you can read more about it in this Adafruit Article the solution Debouncing is included in the Arduino IDE as an example sketch…

I’ve used it to ensure we only register single button events; this will be important as we need to use the button to change modes.

// Button Variables...

#define BUTTON A3
int buttonState;          
int lastButtonState = HIGH;

// Button Bounce Fix...

long bounceTime = 0; 
long bounceDelay = 50; 

void loop() {

  int reading = digitalRead(BUTTON);
 
  // If button state has changed previously...

  if (reading != lastButtonState) 
  {
    bounceTime = millis();
  }

  // If it's been 50ms since previous...

  if ((millis() - bounceTime) > bounceDelay) 
  {
    // If button state has changed again...

    if (reading != buttonState) 
    {     
      buttonState = reading; 
     
      if (buttonState == LOW) {    
 
         Serial.println("Button Up"); 

      }
    }
  }
  
  lastButtonState = reading;

}

Above: Stop Bouncing, you're putting me off my code!

3b - Multi-Mode Functionality

So, if we’re running one loop of code forever, how do we multiple modes? A good question… well, we do some of the Set-Up upfront, things like the Serial Interface, the Button and the LEDs… then we keep track of which mode we are in and if we’ve done the set-up for that mode. So conditional regions of the super loop and conditional set-up functions.

// 2 Modes - 1 Loop

void setup() {
  
   int deviceMode = 1;
   bool mode1Setup = false; 
   bool mode2Setup = false; 
   
}

void mode1() {
  
  // setup sensors and modules for mode1 here...
  mode1Setup = true; 
  mode2Setup = false;
}

void mode2() {

  // setup sensors and modules for mode2 here...
  mode2Setup = true; 
  mode1Setup = false;
}

void loop() {

  if (digitalRead(21) == HIGH)
  {
    mode1();
    deviceMode = 1;
  }

  if (digitalRead(22) == HIGH)
  {
    mode2();
    deviceMode = 2;
  }

  // run if mode1 is ready...
  if (deviceMode == 1 && mode1Setup)
  {
    // Mode1 code loop
  }

  // run if mode2 is ready...
  if (deviceMode == 2 && mode2Setup)
  {
    // Mode2 code loop
  }
  
}

Above: Multi Loop Architecture, at least that's what I'm calling it.

I combined the functionality of Task 3a and Task 3b into a sketch and uploaded it to the device. We’ll set the LEDs to Red during set-up then go into Green mode. Pressing the button will toggle between Green and Blue modes and pressing it the button 10 seconds will put the device into Red Animation mode. None of these modes does anything yet, but the LEDs are now an output we can double-check against the Serial Port Output.

The video shows the device passing the tests and behaving itself, the full sketch is also available in the download link below.

Above: I like the pretty lights...

3C - Internet Functionality

Connecting the ESP32 to the internet is as simple as including the WiFi.h file in the header and entering an SSID and Password into the code. Well, it isn’t, there are a few issues, as highlighted here and here… We need a reliable workaround as we won’t have access to the Reset button, and we won’t get much feedback if we have problems. WiFiMulti.h run twice in a row, seems to work reliably, so that’s my chosen solution. This also will enable us to save multiple sets of credentials, so my device works instantly at Work, Uni and Home, now we just need to be able to configure it.

Our ultimate goal is to connect our Thing to the Internet, but first of all, we need to connect to our Thing. In Set-Up mode, we need to start the Wi-Fi and scan for available networks, store this list in a variable, and re-boot in Wi-Fi Access Point mode so we can select a network and enter a password.

We use a DNS Server set to redirect all traffic to our device, to capture the Captive Portal requests of various operating systems.

This means when you select the device from available networks, you get sent to a page to configure the device. We then store the credentials and re-boot in Wi-Fi-Station mode.

3d - Sensor Module

We’re online! Now let’s send some data to the internet and make that data available on a local webpage too. I’ve connected an i2c BME280 Sensor, which records temperature, humidity and barometric pressure. We will use web-sockets to connect to my website and send the data. There are Arduino libraries for both of these, so much of the heavy lifting is once again done for us.

As this article explains, the hardware I2C pins for the ESP32 are GPIO 22 (SCL) and GPIO 21 (SDA). So I spent a good few hours wondering why none of my i2c breakouts worked… turns out the ESP32 Hazzah actual uses different pins, you need Wire.begin(23,22), I found this solution two pages down in the comments on Stack Overflow, so I’ll save you the trouble.

I’ll post the sketch and video below once I have a web-socket server to test it against, that’s the last area of development for this module.

Above: Finished code demonstrated in full, the device can now be used to test the system we develop in the next module.