For this example I’m using the ESP8266 ESP12E. But you can use the ESP32 as well.
Let’s get started by generating a new Express app using the following command in terminal:
express ota_updater
This creates a fresh instance of Express which we’ll use as the foundation of our version controller.
For the sketch that I want to upload to the ESP8266 I’m using the example Blink sketch from the Arduino library.
void setup() {
// initialize digital pin LED_BUILTIN as an output.
pinMode(LED_BUILTIN, OUTPUT);
}
// the loop function runs over and over again forever
void loop() {
digitalWrite(LED_BUILTIN, HIGH); // turn the LED on (HIGH is the voltage level)
delay(1000); // wait for a second
digitalWrite(LED_BUILTIN, LOW); // turn the LED off by making the voltage LOW
delay(1000); // wait for a second
}
We’ll need the compiled binary to upload to the sketch, which you can find in the debugger once you turn on the option “Show verbose output” for sketch compilations.
After clicking the verify button you can find the compiled binary as per your debugger.
Place your blink.ino.bin inside a new /updates folder and updating our index.js file inside the routes folder to serve up our binary.
var express = require('express');
var path = require('path');
var fs = require("fs");
var router = express.Router();
var md5 = require("md5-file");
router.get('/update', function(req, res, next) {
console.log(req.headers);
var filePath = path.join(__dirname, '../updates/blink.ino.bin');
var options = {
headers: {
"x-MD5": md5.sync(filePath)
}
}
res.sendFile(file, function (err) {
if (err) {
next(err)
} else {
console.log('Sent:', file)
}
});
});
module.exports = router;
After you confirm this works on your local machine it’s time to move your application to an external instance such as EC2 or Linode. After setting this all up you should be presented with the download dialog after navigating to your external domain.
Now that you know your application works you can start testing it out on your ESP8266.
For the ESP8266 sketch we use the ESP8266httpUpdate.h library.
#include <ESP8266httpUpdate.h>
const char* ssid = "ssid"; // Set your router SSID
const char* password = "password"; // Set your router password
void setup() {
Serial.begin(74880);
Serial.setDebugOutput(true);
WiFi.begin(ssid, password);
/*connection to WiFi*/
while (WiFi.status() != WL_CONNECTED) {
Serial.print(".");
delay(1000);
}
t_httpUpdate_return ret = ESPhttpUpdate.update("http://domain.com/update","1.0");
switch(ret) {
case HTTP_UPDATE_FAILED:
Serial.printf("[update] Update failed (%d): %s", ESPhttpUpdate.getLastError(), ESPhttpUpdate.getLastErrorString().c_str());
break;
case HTTP_UPDATE_NO_UPDATES:
Serial.println("[update] Update no Update.");
break;
case HTTP_UPDATE_OK:
Serial.println("[update] Update ok.");
break;
}
}
void loop() {
}
Upload the sketch to your ESP8266 and your console will output the something similar to the following, after which your ESP8266 module resets and executes the blink sketch.
And when looking at our NodeJS app we see information about the ESP8266 be outputted to the screen:
{ connection: 'upgrade',
host: 'domain.com',
'content-length': '0',
'user-agent': 'ESP8266-http-Update',
'x-esp8266-chip-id': '14454826',
'x-esp8266-sta-mac': 'CC:50:E3:DC:90:2A',
'x-esp8266-ap-mac': 'CE:50:E3:DC:90:2A',
'x-esp8266-free-space': '659456',
'x-esp8266-sketch-size': '302832',
'x-esp8266-sketch-md5': '97bbf0d228c88673b9c040df1f7317f4',
'x-esp8266-chip-size': '4194304',
'x-esp8266-sdk-version': '2.2.2-dev(38a443e)',
'x-esp8266-mode': 'sketch',
'x-esp8266-version': '1.0' }
Now this appears to work very nicely. However, now that the blink sketch is uploaded there is no way for us to upload a new sketch to the module. The preferred outcome would be that your ESP8266 will periodically check whether there are new firmware updates available, and update the module if necessary.
For all of that, and more, please see Part 2 of this article.