Update ESP32 over the air
Developing Arduino boards is actually easier than one might think. You plug the development board onto your computer’s USB, fire up Arduino IDE, and with just a few key strokes and clicks you can upload a first sketch to your development board. But what do you do if you just finished a more complex project and your development board is hard to reach in a door bell, water tank or robot you built?
Arduino OTA
Arduino also supports so-called “over the air” (OTA) updates. These allow you to flash a new sketch to your development board via Wifi. For this you need the library ArduinoOTA
and a Wifi-enabled development board. For the ESP32 board - which I am using - this dependency is already in the development libraries of that board.
The Sketch
In order to make basically every Arduino Sketch OTA compatible, you just need to implement the setupWebUpdater
function shown below and call it from your setup
function. Furthermore, your need to make sure to call ArduinoOTA.handle()
in every execution of the loop
function. As we don’t want everyone in our network to be able to easily flash new updates, we will define a password for the OTA updates: The password is provided as an MD5 hash (please remember: MD5 is not a good choice for hashing passwords). You can generate your hash easily by running e.g. echo -n "MySecretPassword"|md5
in your terminal.
#include <WiFi.h>
#include <ESPmDNS.h>
#include <WiFiUdp.h>
#include <ArduinoOTA.h>
#define WIFI_SSID "YOUR-WIFI-SSID"
#define WIFI_PASSWORD "YOUR-WIFI-PASSWORD"
#define OTA_HASH "7315a012ecad1059a3634f8be1347846" // echo -n "MySecretPassword"|md5
void setup()
{
Serial.begin(9600);
WiFi.setHostname("OTAExample");
WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
Serial.print("Connecting to "); Serial.println(WIFI_SSID);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("");
Serial.println("WiFi connected. IP address: ");
Serial.println(WiFi.localIP());
setupWebUpdater();
}
void loop()
{
// your logic
ArduinoOTA.handle();
delay(250);
}
void setupWebUpdater(void) {
ArduinoOTA.setHostname("OTAExample");
ArduinoOTA.setPasswordHash(OTA_HASH);
ArduinoOTA
.onStart([]() {
String type;
if (ArduinoOTA.getCommand() == U_FLASH)
type = "sketch";
else // U_SPIFFS
type = "filesystem";
// NOTE: if updating SPIFFS this would be the place to unmount SPIFFS using SPIFFS.end()
Serial.println("Start updating " + type);
})
.onEnd([]() {
Serial.println("\nEnd");
})
.onProgress([](unsigned int progress, unsigned int total) {
Serial.printf("Progress: %u%%\r", (progress / (total / 100)));
})
.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");
});
ArduinoOTA.begin();
}
Summary
I like this approach quite a lot: It doesn’t add too much boilerplate to the sketch and therefore can become a default part of all your projects. There is even a “OTA Web Updater” example in the “ArduinoOTA” package - but that approach adds a lot of clutter and is less automated as it cannot be triggered directly from the ArduinoIDE.