PlatformIO OTA on ESP8266

Hi Community,

i am almost finish with a project i am working on, and i want to add OTA functionality, based on the already used espasyncwebserver.h libary. Therefore i want to add the core functionalities directly to the main code.

What i already did, is adding a interface on the webserver, where you can coose your precompiled binary from Explorer amd update the esp8266. Now i want to further add a functionality, that work with the internal espota.py tool, so i do not need to go via the explorer variant to update the Sourcecode.

Does anybody have a idea for me how i should implement such functionality?

Tank you :slight_smile:

What you are looking for is ArduinoOTA.
This is a built-in library. You can use the example directly without having to add any additional libraries.

Yeah exactly something like arduino OTA but based on async libary. Because if i add the arduinoOTA.h lib, it needs to much space :frowning:
And the async webserver instance is present.

I tried the elegantOTA way, but i could not get further uploading the code, based on that custom script, used to upload within platformio…

That makes no sense to me. There is nothing asynchronous. The ESP receives an invitation via UDP and then establishes a connection to the PC via TCP to download the firmware.

Do you mean flash memory or RAM?

If possible, you should switch to one of the ESP32 models. The ESP8266 is very limited in its features (flash / RAM) and capabilities.

That is the point… Therefore i want to use the existing ESPAsyncWebserver Instance which i am already using within the project. I do not want to add seperatly header files, which are setting up another Webserver, Listenerport and more features which are actually not really necessary, because the Server Instance is still present.

Do you know, how the Handshake, Request is working in the backend?

If I add the whole ArduinoOTA Libary, it blows the code up in flash, in that way, that i am at around of 55%…So, OTA will not work any more. Also the RAM capabilities are at around 80% but the ESP8266 is not stable running any more. I think because of the big Include the Memory Fragmentation is a problem… That is the reason why I only want to Implement the OTA Core functionalities on EXISTING Webserver Instance…

The point is that ArduinoOTA does not need a web server at all. Therefore, it makes no difference whether your sketch uses an asynchronous or synchronous web server.

Only one UDP server listens to port 8266 and waits for an invitation from the PC.

The ArduinoOTA library is already written very efficiently and requires approximately 47,873 bytes of flash memory. I therefore doubt that you will succeed in creating an even leaner implementation.

I don’t have a description to hand at the moment, but you can view the implementation here:

Thank you @sivar2311.
I got the OTA update via platformio IDE now working. It is like you say: UDP is listening and waiting for connection, handling authentification and enables the wificlient if everything is up.

I got now another question/trouble - regarding the webserver based update. Within the webserver i added a file upload selector.

  1. after the firmware is uploaded, i got issue with resetting the esp with esp.restart() but esp.reset() works but should be avoided.
  2. LittleFS/ SPIFFS Upload does never start due of lack of space.

Could it both be possible, that i have an calculation issue in my update.write() function? I found a reference in the commendwhich calculates in a differ way(?)

void SSEWrapper::handleDoUpdate(AsyncWebServerRequest *request, const String& filename, size_t index, uint8_t *data, size_t len, bool final) {
  unsigned long deltaT = millis();
  if (!index){
    DEBUG_SERIAL.print("[OTA] Update: ");
    DEBUG_SERIAL.println(filename);
    SSEWrapper::content_len = request->contentLength();
    DEBUG_SERIAL.print("[OTA] Filesize recived [KB]: ");
    DEBUG_SERIAL.println(SSEWrapper::content_len);

    // if filename includes spiffs/littlefs, update the spiffs/littlefs partition
    int cmd = ((filename.indexOf("spiffs") > -1) || (filename.indexOf("littlefs") > -1)) ? U_FS : U_FLASH;
    Update.runAsync(true);

    /*
      uint32_t update_size = 0;
      // Filesystem (SPIFFS // LITTLEFS)
      if (cmd = U_FS){
        update_size = ((ESP.getFreeSketchSpace() - 0x1000) & 0xFFFFF000);
      } // Firmware
      else if (cmd = U_FLASH){
        update_size = (size_t)FS_end - (size_t)FS_start);
      }
    */

    if (!Update.begin(SSEWrapper::content_len, cmd)) {
      #ifdef SERIAL_OUTPUT_DEBUGGING
        Update.printError(Serial);
      #endif
    }

    request->send(200, "text/html", "<head><meta http-equiv='refresh' content='20;URL=/'/></head><body>Upload started! Please wait while the device reboots</body>");
  }

  if (Update.write(data, len) != len) {
    #ifdef SERIAL_OUTPUT_DEBUGGING
      Update.printError(Serial);
    #endif
  }else{
    size_t temp_size = (Update.progress()*100)/Update.size();
    if(old_size != temp_size){
      DEBUG_SERIAL.print("[OTA] Progress: ");
      DEBUG_SERIAL.println(temp_size);
      old_size = temp_size;
    }
  }

  if(final){
    if(Update.hasError()){
      DEBUG_SERIAL.println(Update.getErrorString());
    }
    if(!Update.end(true)){
      #ifdef SERIAL_OUTPUT_DEBUGGING
        Update.printError(Serial);
      #endif
    } else {
      DEBUG_SERIAL.println("[OTA] Update complete!");
      DEBUG_SERIAL.println("[OTA] Reboot!");

      // Deactivating WiFi 
      WiFi.mode(WIFI_OFF);

      // Wait 1s until reboot
      delay(1000);

      // ESP8266 Restart after Update
      // Using reset() Methode instead of restart() because with restart there is no "real" restart of ESP8266!
      ESP.reset();
    }
  }
  DEBUG_SERIAL_INFORMATION.print("handleDoUpdate [ms]: ");
  DEBUG_SERIAL_INFORMATION.println(millis()-deltaT);
}

Edit by sivar2311: Code formatted as preformatted text for better readability.
Please note: How to post logs and code in PlatformIO Community Forum

Here you are still in the middle of an asynchronous callback!

It is better to set just a flag here, which is evaluated in the loop() function and performs the restart.

bool shouldRestart = false;

void triggerRestart() {
  shouldRestart = true;
}

void doRestart() {
  // Deactivating WiFi 
  WiFi.mode(WIFI_OFF);

  // Wait 1s until reboot
  delay(1000);

  // ESP8266 Restart after Update
  // Using reset() Methode instead of restart() because with restart there is no "real" restart of ESP8266!
  ESP.reset();
}

void SSEWrapper::handleDoUpdate(AsyncWebServerRequest *request, const String& filename, size_t index, uint8_t *data, size_t len, bool final) {
...
  if(final){
    if(Update.hasError()){
      DEBUG_SERIAL.println(Update.getErrorString());
    }
    if(!Update.end(true)){
      #ifdef SERIAL_OUTPUT_DEBUGGING
        Update.printError(Serial);
      #endif
    } else {
      DEBUG_SERIAL.println("[OTA] Update complete!");
      DEBUG_SERIAL.println("[OTA] Reboot!");
      triggerRestart();
    }
  }
...
}

loop() {
...
  if (shouldRestart) doRestart();
...
}

OK Thank you - I will give it a try and outsource the restart condition from the callback function into the main loop()!

Is the Update.begin() Method as i programmed it OK? Because i Found other solutions from the original OTA Update conditions which are calculating the content length in a different way (see the commented Code Section below). The current way is actually assuming as content size the actual Filesize provided by the file itself. The alternative example uses static memory size independent the provided file at all…It is from the ElegantOTA libary.

I have not yet implemented anything like this myself. So I don’t have the experience. But it looks to me like you are implementing something like ElegantOTA.
Take a look at the code for this, then you have something to compare it to.