OTA update in ESP8266

I was working on a project and need to update my esp8266 over wifi. I was trying it and I found AsyncElegantOTA, but that is not what suits the situation. I would require to fetch the .bin file from a global server and write it to the ESP8266 flash and then run it. Please help me find a way to the objective.

1 Like

Your topic was MOVED to its current forum category as it is more suitable than the original

It was certainly nothing to do with Covid

This
Describes the 3 methods i know.
This is another explanation of doing it through the IDE.
And This link may be the best explanation.
In short there are 3 ways

  • Through the IDE
  • Through navigating to remote source
  • Through a webform

The method i use while developing is through the IDE.
Once i have the ESP in place i use a webform to access the file system of the device i'm accessing the ESP webserver with and upload the updated binary from there. This can be done using phone or computer.
The basic part of the sketch dealing with the uploading and restarting are the same for all methods. The way these are initiated is where they differ.

Can you share an example code for the third way of OTA update (Through a webform).

This is my sample code which also includes a webserver for controlling a LED.


#include <ESP8266WiFi.h>
#include <WiFiClient.h>
#include <ArduinoOTA.h>
#include <PatchDNSServer.h>
#include <ESP8266WebServer.h>
#include <ESP8266mDNS.h>
#include <StreamString.h>
#include <WiFiUdp.h>
#include <FS.h>

ESP8266WebServer server(80);

const int ledpin = 2;  // I'm running this on an ESP-01
// i have a led connected to this active LOW
// i can't use the internal (pin 1) for the use of Serial

uint8_t ledstatus = 3; // this is keeping track of the state (of our statemachine)

const char *ssid = "Network";
const char *password = "Password";
const char *apname = "esp8266";
const char *appass = "password"; // minimum length 8 characters

bool spiffsmounted = false;

const char* update_path = "/firmware";

const char
*pageheader = "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\"\"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">",
 *htmlhead = "<html><head><title>AP OTA Update Example</title><meta http-equiv=\"Content-Type\" content=\"text/html;charset=utf-8\" ></head>",
  *bodystyle = "<body style=\"color: dimgray; background-color: palegoldenrod; font-size: 12pt; font-family: sans-serif;\">",
   *accessIP = "http://192.168.4.1",
    *htmlclose = "</body></html>";

String webIP;

void setup() {
  Serial.begin(115200);
  webIP.reserve(30);  // prevent fragments
  pinMode(ledpin, OUTPUT);
  digitalWrite(ledpin, LOW);

  WiFi.softAP(apname, appass); // start AP mode
  webIP = accessIP;
  Serial.print("Started Access Point \"");
  Serial.print(apname);
  Serial.println("\"");
  Serial.print("With password \"");
  Serial.print(appass);
  Serial.println("\"");
  WiFi.begin(ssid, password);  // attempt starting STA mode
  Serial.println("Attempting to start Station mode");

  uint32_t moment = millis();
  while ((WiFi.status() != WL_CONNECTED) && (millis() < moment + 8000)) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("");
  if (WiFi.status() == WL_CONNECTED) {
    Serial.print("Connected to ");
    Serial.println(ssid);
    Serial.print("IP address: ");
    Serial.println(WiFi.localIP());

    webIP = StationIP();
  }
  else if (WiFi.status() == WL_CONNECT_FAILED) {
    Serial.print("Connecting to ");
    Serial.print(ssid);
    Serial.println(" Unsuccessful.");
  }
  else if (WiFi.status() == WL_NO_SSID_AVAIL) {
    Serial.print("Network ");
    Serial.print(ssid);
    Serial.println(" not available.");
  }
  WiFi.reconnect();   // reconnect AP after attempting to connect STA


  OTAUpdateSetup();
  spiffsmounted = SPIFFS.begin();

  server.on("/", handleRoot);
  server.on(update_path, HTTP_GET, handleUpdate);
  server.on(update_path, HTTP_POST, handlePostUpdate, handleFileUpload);

  server.begin();
}

void loop() {
  ArduinoOTA.handle();
  yield();
  server.handleClient();
  yield();
  checkLedStatus();
}

void handleUpdate() {

  String s = "";
  s += pageheader;
  s += htmlhead;
  s += bodystyle;
  s += "<h1>OTA Firmware Update</h1>";

  s += "<pre><form method=\"post\" action=\"\" enctype=\"multipart/form-data\">";
  s += "<input type=\"file\" name=\"update\">";
  s += "           <input type=\"submit\" value=\"  Update  \"></form></pre>";
  s += htmlclose;
  server.send(200, "text/html", s);
}


void handlePostUpdate() {

  if (Update.hasError()) {
    StreamString str;
    Update.printError(str);
    str;
    String s = "";
    s += pageheader;
    s += htmlhead;
    s += bodystyle;
    s += "<h1>Update Error </h1>";
    s += str;
    s += htmlclose;
    server.send(200, "text / html", s);
  }
  else {
    String s = "";
    s += pageheader;
    s += htmlhead;
    s += bodystyle;
    s += "<META http-equiv=\"refresh\" content=\"30;URL=/\">Update Success ! Rebooting...\n";
    s += htmlclose;
    server.client().setNoDelay(true);
    server.send(200, "text / html", s);
    delay(100);
    server.client().stop();
    ESP.restart();
  }
}

void handleFileUpload() {

  HTTPUpload& upload = server.upload();
  String updateerror = "";
  if (upload.status == UPLOAD_FILE_START) {
    WiFiUDP::stopAll();
    uint32_t maxSketchSpace = (ESP.getFreeSketchSpace() - 0x1000) & 0xFFFFF000;
    if (!Update.begin(maxSketchSpace)) { //start with max available size
      StreamString str;
      Update.printError(str);
      updateerror = str;
    }
  }
  else if (upload.status == UPLOAD_FILE_WRITE) {
    if (Update.write(upload.buf, upload.currentSize) != upload.currentSize) {
      StreamString str;
      Update.printError(str);
      updateerror = str;
    }
  }
  else if (upload.status == UPLOAD_FILE_END) {
    if (Update.end(true)) { //true to set the size to the current progress
      StreamString str;
      Update.printError(str);
      updateerror = str;
    }
    else if (upload.status == UPLOAD_FILE_ABORTED) {
      Update.end();
    }
    yield();
  }
}


void OTAUpdateSetup() {
  const char* hostName = "ThisAp";  // this is also the mDNS responder name
  
  Serial.print("MDNS responder started, type ");
  Serial.print (hostName);
  Serial.println(".local/ in your browser"); 
                                  
  ArduinoOTA.setHostname(hostName);
  ArduinoOTA.onStart([]() {
    String type;
    if (ArduinoOTA.getCommand() == U_FLASH) {

    }
    else { // U_SPIFFS
      type = "filesystem";
      if (spiffsmounted) {
        SPIFFS.end();
        spiffsmounted = false;
      }
    }

    // NOTE: if updating SPIFFS this would be the place to unmount SPIFFS using SPIFFS.end()
    //  Serial.println("Start updating " + type);
  });
  ArduinoOTA.onEnd([]() {
    if (!spiffsmounted) spiffsmounted = SPIFFS.begin();
    delay(1000);
    // Serial.println("\nEnd");
  });
  ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) {
    //  Serial.printf("Progress: % 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();
}

void checkLedStatus() {  // our statemachine
  switch (ledstatus) {
    case 0: {
        digitalWrite(ledpin, HIGH);
        return;
      }
    case 1: {
        digitalWrite(ledpin, LOW);
        return;
      }
    case 2: {
        if (ledBlink(500)) ; // here the return value (of ledBlink() ) gets discarded
        return;
      }
    case 3: {
        modulateLed();
        return;
      }
  }
}

void modulateLed() {
  static uint16_t ms = 100;
  static bool increase = true;

  if (!ledBlink(ms)) return;
  if (ms > 250) increase = false;
  if (ms < 20) increase = true;
  if (increase) ms = (ms * 10) / 9;
  else ms = (ms * 9) / 10;
}

bool ledBlink(uint32_t wait) {
  static bool pinstate = false;
  static uint32_t moment = millis();
  if (millis() > moment + wait) {
    pinstate = !pinstate;
    moment = millis();
    if (pinstate) digitalWrite(ledpin, LOW);
    else digitalWrite(ledpin, HIGH);
    return pinstate;  // if pinstate is true and the pinstate has changed, the modulator will change speed
  }
  return false;
}

void handleRoot() {
  String ledstatusupdate;
  if (server.hasArg("led")) {
    if (server.arg("led") == "off") {
      ledstatus = 0;
      ledstatusupdate = "The LED has been turned Off<br>";
    }
    else if (server.arg("led") == "on") {
      ledstatus = 1;
      ledstatusupdate = "The LED has been turned On<br>";
    }
    else if (server.arg("led") == "blink") {
      ledstatus = 2;
      ledstatusupdate = "The LED has been set to Blink<br>";
    }
    else if (server.arg("led") == "modulate") {
      ledstatus = 3;
      ledstatusupdate = "The LED has been set to Modulate<br>";
    }
  }

  String s = "";
  s += pageheader;
  s += htmlhead;
  s += bodystyle;

  s += "<h1>Welcome to ESP webserver</h1><p>From here you can control your LED making it blink or just turn on or off. ";
  s += "</p>";

  s += ledstatusupdate;
  s += "<br>";

  s += "<form action=\"";
  s += webIP;
  s += "\" method=\"get\" name=\"button\">";
  s += "<input type=\"hidden\" name=\"led\" value=\"on\">"; // the hidden parameter gets included
  s += "<input type=\"submit\" value=\" LED ON \"></form><br>"; // the button simply submits the form

  s += "<form action=\"";
  s += webIP;
  s += "\" method=\"get\" name=\"button\">";
  s += "<input type=\"hidden\" name=\"led\" value=\"off\">";
  s += "<input type=\"submit\" value=\" LED OFF\"></form><br>";

  s += "<form action=\"";
  s += webIP;
  s += "\" method=\"get\" name=\"button\">";
  s += "<input type=\"hidden\" name=\"led\" value=\"blink\">";
  s += "<input type=\"submit\" value=\"  BLINK  \"></form><br>";

  s += "<form action=\"";
  s += webIP;
  s += "\" method=\"get\" name=\"button\">";
  s += "<input type=\"hidden\" name=\"led\" value=\"modulate\">";
  s += "<input type=\"submit\" value=\"MODULATE\"></form><br>";

  s += "<form action=\"";
  s += webIP;
  s += update_path;
  s += "\" method=\"get\" name=\"button\">";
  s += "<input type=\"submit\" value=\"UPDATE\"></form><br>";

  s += htmlclose;
  yield();  // not stricktly neccesary, though the String class can be slow
  server.send(200, "text/html", s); //Send web page
}


String StationIP() {
  String stationIP = "http://";
  stationIP += WiFi.localIP().toString();
  return stationIP;
}

You can update from any device you connect through, either AP or STA and navigate from the root.
It is also possible to update through the IDE.

Keep in mind that after uploading the initial sketch using Serial, you will need to hard-reset the ESP at least once for OTA updates to work, or the ESP will not be in the proper boot mode .For automated flash & reset like a nodeMCU this may not be needed, but just press the reset button once if you get an error message.

Thank you Deva_Rishi for your support.

I just had one more query, this code that you have shared is using a Server hosted by the ESP8266 itself. Is there any way to fetch the .bin file from a server hosted by some host over Internet?

Of course there is. Instead of using a form on the webserver, you can host a form on the remote server that transfers the binary to the ESP. You can make the ESP navigate to that remote server as normal and then make it post or get the request.
I do not have a remote server i can test examples with. The basic principle differs only on how the file is accessed. The ESP will have to act as a client to the server. The 1st & 3rd links in #2 do discuss this, and even on how to automate the process.

A simple 'in between' method would be of course to first download the new binary and then upload it to the ESP. This is the method i am using with my clients at the moment.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.