INTRODUCTION
The following summary is taken from the the ESP8266 package documentation:
https://arduino-esp8266.readthedocs.io/en/latest/ota_updates/readme.html
"OTA (Over the Air) update is the process of uploading firmware to an ESP module using a Wi-Fi connection rather than a serial port. Such functionality becomes extremely useful in case of limited or no physical access to the module.
OTA may be done using:
The Arduino IDE option is intended primarily for the software development phase. The other two options would be more useful after deployment, to provide the module with application updates either manually with a web browser, or automatically using an HTTP server."
In this Blog I intend to look at method 1, using the Arduino IDE. I will look at method 3, the Automatic approach, using an HTTP Server, in my next Blog.
I am assuming that the ESP8266 package has been correctly installed, along with Python, as described in the documentation. I am also assuming that the correct ESP8266 board has been selected in the Arduino IDE, under Tools/Board/( in my case)"LOLIN (WEMOS) D1 R2 & mini".
OTA Firmware Updates using the Arduino IDE
An initial "starter" sketch, containing appropriate code segments, is loaded into the ESP8266 device via the serial port. If this initial sketch has been correctly implemented, then subsequent uploads can be carried out over the air. Once the upload is complete, the device restarts and the new firmware is executed,
"By default, there is no imposed security for the OTA process. It is up to the developer to ensure that updates are allowed only from legitimate / trusted sources."
See the documentation for advice on implementing security measures
An example sketch can be found in the Arduino IDE, under File/Examples/Examples for (in my case)"LOLIN (WEMOS) D1 R2 & mini"/ArduinoOTA/BasicOTA. This sketch carries out a number of functions:
1. Load the necessary libraries:
#include <ESP8266WiFi.h> #include <ESP8266mDNS.h> #include <WiFiUdp.h> #include <ArduinoOTA.h>
2. Connect to the local network. Note the need to supply your local network ssid and pasword.
const char* ssid = ".........."; const char* password = ".........."; void setup() { Serial.begin(115200); Serial.println("Booting"); WiFi.mode(WIFI_STA); WiFi.begin(ssid, password); while (WiFi.waitForConnectResult() != WL_CONNECTED) { Serial.println("Connection Failed! Rebooting..."); delay(5000); ESP.restart(); }
3. Give some information regarding various defaults:
// Port defaults to 8266 // ArduinoOTA.setPort(8266); // Hostname defaults to esp8266-[ChipID] // ArduinoOTA.setHostname("myesp8266"); // No authentication by default // ArduinoOTA.setPassword("admin"); // Password can be set with it's md5 value as well // MD5(admin) = 21232f297a57a5a743894a0e4a801fc3 // ArduinoOTA.setPasswordHash("21232f297a57a5a743894a0e4a801fc3");
4. Initiate the OTA Process:
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); });
Some of the above code is actually unnecessary at the level I am describing; I have reproduced it here because it is present in the example Sketch. Later on I will show am alternative, simpler starter sketch.
5. Deal with the progress and end of the update process
ArduinoOTA.onEnd([]() { Serial.println("\nEnd"); }); ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) { Serial.printf("Progress: %u%%\r", (progress / (total / 100))); });
7. Handle Errors
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"); } });
8. Wait to be Updated
ArduinoOTA.begin(); Serial.println("Ready"); Serial.print("IP address: "); Serial.println(WiFi.localIP()); } void loop() { ArduinoOTA.handle(); }
This starter sketch is uploaded as usual via the serial interface. Once this has been accomplished and the messages in segment 8 seen on the Serial Monitor, the sketch should be working and the the ESP8266 device connected to the local WiFi network.
If garbage appears in the Serial Monitor, check baud rates - in fact, I tend to always run mine at 9,600. Once corrected, recompile and upload.
If you only see Booting in the Serial Monitor, and nothing else, the ESP8266 is failing to connect to your local WiFi network. Try resetting the ESP8266. If this fails, double check the ssid and password of your local WiFi network. Correct, if necessary, recompile and upload again.
Once the sketch is working, reset the ESP8266 and restart the Arduino IDE.
If you now go to Tools, scroll down to Port: and click it, you should now see a network port, of the form: "esp8266-239ae3 at 192.168.1.17", where 239ae3 is the lower half of the ESP8266 device's MAC address.
This poor quality photograph (I tried several times!) shows the network port within the Arduino IDE menu:
Below, I show the minimal code that I have found necessary for this to work:
/************************************************************* * BASIC OTA sketch, adapted from Example sketch - BasicOTA * * Neil Kenyon * 1 December 2019 * ************************************************************* */ #include <ESP8266WiFi.h> #include <ESP8266mDNS.h> #include <WiFiUdp.h> #include <ArduinoOTA.h> // Replace with your network credentials const char* ssid = "........."; const char* password = "........"; void setup() { Serial.begin(9600); delay(500); Serial.println("\nBooting"); WiFi.mode(WIFI_STA); WiFi.begin(ssid, password); while (WiFi.waitForConnectResult() != WL_CONNECTED) { Serial.println("Connection Failed! Rebooting..."); delay(5000); ESP.restart(); } ArduinoOTA.onStart([]() { Serial.println("Start Updating"); }); ArduinoOTA.onEnd([]() { Serial.println("\nUpdate Finished"); }); ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) { Serial.printf("\nProgress: %u%%\r", (progress / (total / 100))); }); 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"); }); ArduinoOTA.begin(); Serial.println("Ready"); Serial.print("IP address: "); Serial.println(WiFi.localIP()); } Void loop() { ArduinoOTA.handle(); }
Now we have a problem: the Arduino IDE cannot connect simultaneously to a network port - "esp8266-239ae3 at 192.168.1.17" - and a conventional serial port. This means that you cannot both update the ESP8266 OTA and view the output from the Sketch(es) on the Serial Monitor, using a single PC/MAC.
In order to test the OTA process fully, and, if necessary, debug the sketches, we have to use two separate PCs or MACs, with both machines connected to the local WIFi network and running the Arduino IDE.
The PC/MAC on the right has the ESP8266 connected to it via the usual USB cable, and the appropriate serial port selected within the IDE. The Serial Monitor is open - it doesn't matter which sketch is loaded in the IDE - allowing the ESP8266 to output messages to the screen
The PC/MAC on the left has the ESP8266 network port selected within the IDE and is used to modify, recompile and upload the sketches.
Demonstrating OTA via Network Port
The video below begins by showing the Serial Monitor output after my modified starter sketch has been uploaded and the ESP8266 reset.
The video then goes on to show the Serial Monitor output during an OTA update of a trivially modified version of the starter sketch:
I found it useful to include the timestamp in the Serial Monitor output, to understand just how quickly this process takes place.
After some furtherl modifications to the sketch:
In Declarations:
const int LED = 2; int onTime = 1000; int offTime = 1000;
In Setup():
pinMode(LED, OUTPUT); Serial.println("\nI have been updated OTA"); // This is original Serial.println("\nI have been updated OTA a second time!\n"); // This is is an addition
And finally, in Loop()
digitalWrite(LED, LOW); Serial.print("LED ON! for "); Serial.print(onTime); Serial.println(" mS"); delay(onTime); digitalWrite(LED, HIGH); Serial.print("LED OFF! for "); Serial.print(offTime); Serial.println(" mS"); delay(offTime);
The sketch was re-compiled and uploaded OTA:
Unfortunately, you can't see the LED flashing - honest it is flashing!!
How Useful is this?
Once you are confident that the OTA process works, there is obviously no need to use a two PC/MAC approach, as described here. You can comment out all the diagnostics and just have the ESP8266 running from an appropriate PSU, independent of a PC/MAC
This OTA method comes into it's own if, for example, your ESP8266 system is "not next to your PC or MAC", updating the firmware can be a pain. You either have to take a laptop/MACbook to wherever the system is - eg in my case, out in one of my greenhouses - and update via USB, or bring the system to wherever your PC/MAC lives , and update the firmware there. You then have to take the system back to wherever it operates.
Even if your system is "next to your PC or MAC", connecting the USB cable may mean disturbing a delicate rat's nest of jumper cables!! Always a risky exercise!!
However
If your system is powered by batteries, like my greenhouse temperature monitor, it will spend most of its time in "Deep Sleep" and therefore unavailable to be updated using this method. Hence the need to use method 3, mentioned above.
I will describe method 3, which uses an HTTP server, in my next blog
Top Comments