Flashing ESP32 wifi devices over the air(OTA) using node-red?

Hello. I am working on a project where I have a single raspberry PI configured as mqtt broker and I need to monitor/control up to 50 or more remote esp32 devices in a close proximity that will be taking various sensor readings. I have a basic setup already where I can ping each device LED ( I currently have just 3 for testing purpose).

My question is how can I flash the program to individual ESP32 devices? I want to press a button on my node-red interface and initiate a device update.

I have opened arduino example where they update the esp32 remotely using webserver. Since my application is a little bit different I am not too sure how would I do that?

This is the arduino example code for flashing the device using a webserver

#include <ESP8266WiFi.h>
#include <WiFiClient.h>
#include <ESP8266WebServer.h>
#include <ESP8266mDNS.h>

#ifndef STASSID
#define STASSID "your-ssid"
#define STAPSK  "your-password"
#endif

const char* host = "esp8266-webupdate";
const char* ssid = STASSID;
const char* password = STAPSK;

ESP8266WebServer server(80);
const char* serverIndex = "<form method='POST' action='/update' enctype='multipart/form-data'><input type='file' name='update'><input type='submit' value='Update'></form>";

void setup(void) {
  Serial.begin(115200);
  Serial.println();
  Serial.println("Booting Sketch...");
  WiFi.mode(WIFI_AP_STA);
  WiFi.begin(ssid, password);
  if (WiFi.waitForConnectResult() == WL_CONNECTED) {
    MDNS.begin(host);
    server.on("/", HTTP_GET, []() {
      server.sendHeader("Connection", "close");
      server.send(200, "text/html", serverIndex);
    });
    server.on("/update", HTTP_POST, []() {
      server.sendHeader("Connection", "close");
      server.send(200, "text/plain", (Update.hasError()) ? "FAIL" : "OK");
      ESP.restart();
    }, []() {
      HTTPUpload& upload = server.upload();
      if (upload.status == UPLOAD_FILE_START) {
        Serial.setDebugOutput(true);
        WiFiUDP::stopAll();
        Serial.printf("Update: %s\n", upload.filename.c_str());
        uint32_t maxSketchSpace = (ESP.getFreeSketchSpace() - 0x1000) & 0xFFFFF000;
        if (!Update.begin(maxSketchSpace)) { //start with max available size
          Update.printError(Serial);
        }
      } else if (upload.status == UPLOAD_FILE_WRITE) {
        if (Update.write(upload.buf, upload.currentSize) != upload.currentSize) {
          Update.printError(Serial);
        }
      } else if (upload.status == UPLOAD_FILE_END) {
        if (Update.end(true)) { //true to set the size to the current progress
          Serial.printf("Update Success: %u\nRebooting...\n", upload.totalSize);
        } else {
          Update.printError(Serial);
        }
        Serial.setDebugOutput(false);
      }
      yield();
    });
    server.begin();
    MDNS.addService("http", "tcp", 80);

    Serial.printf("Ready! Open http://%s.local in your browser\n", host);
  } else {
    Serial.println("WiFi Failed");
  }
}

void loop(void) {
  server.handleClient();
  MDNS.update();
}

Have a look at this example flow: https://flows.nodered.org/flow/888b4cd95250197eb429b2f40d188185
It's for an esp8266 however I don't think that the mechanism is that much difference.
It's at least a good starting point.

Else to can always call the otatool.py with the exec node.

1 Like

Don't we all :slight_smile:

Getting a running program to auto-update itself is a good trick on a good day - never mind on a severely memory constrained device such as an ESP32!

Where is the original article that you got that code snippet from?

The closes example of what I want to accomplish is:

However this man did not publish any details/ piece of code.

The second closes thing I could find is this article:
https://arduino-esp8266.readthedocs.io/en/latest/ota_updates/readme.html#http-server
If you follow the article down to HTTP server OTA update .

So if I understood correctly, I need to create a webserver and store my .bin file there and just call function :
ESPhttpUpdate.update("http://server/file.bin"); //Location of your binary file

Surely thats not that simple right?

So it looks possible :slight_smile:

I think if you can get that arduino code working on an ESP32, then doing the actual web request would be relatively simple

Get that bit working first (suggest a visit to an ESP32 forum) and then come back here afterwards to add the Node-RED bit

Be aware of what it says in that article about having enough space to load the new sketch at the same time as the old one is running

I'm using mainly ESP8266 based devices, but some ESP32 are in the pipeline. All use my own code. The devices usually send sensor measurements and internal status info via MQTT to the broker. node-red reads this stuff and does things with it. I built also a communication from node-red to the devices to send commands, either to switch a relay, to reboot, or to fetch new firmware.
I hard-wired the 'fetch firmware via HTTP' into the code. This makes it more resilient against hacking since there is no (easy) way to trick the device with malicious firmware.

Hi,

I'm doing this very thing. I'm using ' HTTPUpdate' for ESP32 and 'ESP8266httpUpdate' for 8266.

#if defined(ESP8266)
  #include <ESP8266WiFi.h>
  #include <ESP8266httpUpdate.h>

#elif defined(ESP32)
  #include <WiFi.h>
  #include <HTTPUpdate.h>

#endif

.....

#if defined(ESP32)
      // flash LED while updating
      httpUpdate.setLedPin(LED_BUILTIN, HIGH);
      t_httpUpdate_return ret = httpUpdate.update(client, firmwareLocation);
    #elif defined(ESP8266)
      ESPhttpUpdate.setLedPin(LED_BUILTIN, HIGH);
      t_httpUpdate_return ret = ESPhttpUpdate.update(client, firmwareLocation);
    #endif
    switch (ret) {
      case HTTP_UPDATE_FAILED:
        #if defined(ESP32)
          message["message"] = printf("HTTP_UPDATE_FAILED Error (%d): %s\n", httpUpdate.getLastError(), httpUpdate.getLastErrorString().c_str());
        #elif defined(ESP8266)
          message["message"] = printf("HTTP_UPDATE_FAILED Error (%d): %s\n", ESPhttpUpdate.getLastError(), ESPhttpUpdate.getLastErrorString().c_str());
        #endif
        break;
      case HTTP_UPDATE_NO_UPDATES:
        message["message"] = "HTTP_UPDATE_NO_UPDATES";
        break;
      case HTTP_UPDATE_OK:
        message["message"] = "HTTP_UPDATE_OK";
        break;
    }
    sendMessage(message, topic);
    return false;

I communicate with MQTT, the device request an update and I reply with the file eg: http://192.168.1.17/hardware/carport-controller_1.1.bin which becomes the firmwareLocation above.

So the file is stored in web server location in my case linux /var/www/html/...

You could easily send MQTT message with the press of a button.

REF: [https://randomnerdtutorials.com/esp32-over-the-air-ota-programming/]

don't know if you know these libraries but they are rich in ideas:


cheers
1 Like

Thanks for shining some light.
I am using HTTPUpdate example code - just changed the ID and PSWD of my network and URL of ESPhttpUpdate.

I pass the IP of the raspberry PI , port 80 and then the directory where i hold the binary file which is /var/www/html/uploads/dev1.bin
t_httpUpdate_return ret = ESPhttpUpdate.update("192.168.100.158",80, "uploads/dev1.bin");
however, I get this error:

If instead of using IP address and port 80 I use "client" as you suggested, I get error message: connection failed

Fixed the problem,, turned out I had to include an additional "/" in my destination path such as: (/uploads/...)
t_httpUpdate_return ret = ESPhttpUpdate.update(client,"192.168.100.158",80, "/uploads/dev1.bin");

However, right off the bat I am facing memory problems - I am able to succesfully load small blink sketch over OTA but I get flash memory problems when im trying to upload slightly larger sketches

Can you post your working Node-RED flow for us to look at/re-use please?

I Havent yet added the node-red bit. Im currently just working with ESP8266 flashing OTA from my raspberry server. Once I solve my memory problem I will try to integrate it into the node-red flow

1 Like

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