Skip to Content

← All functionalities

Geotechnical slope monitoringESP32 PLC 14 (×4, Ethernet)HTTPResilience / OTA

Remote OTA firmware updates over Ethernet

Four monitoring stations on a slope, each one a climb to reach with a laptop — updating firmware by cable does not scale. This example gives every ESP32 PLC Ethernet station two HTTP endpoints: GET /status returns live sensor state as JSON, and POST /update accepts a new firmware binary over the network. We use exactly this pattern to maintain a fleet of geotechnical monitoring stations without site visits.

Pull and push, both

The central Node-RED polls /status when it wants, but each station also pushes its state every 60 s. The push is the watchdog: if a station goes silent, the concentrator notices within a minute — silence is itself an alarm.

OTA with plain HTTP tooling

No custom updater needed: curl -F "[email protected]" http://10.10.10.31/update flashes a station. The handler streams the upload into the OTA partition and reboots on success. Version is reported in /status so the fleet's firmware state is always visible.

Scaling to N stations

Stations are identical except STATION_ID and IP. The concentrator keeps one .bin per hardware revision and a list of IPs — updating the whole fleet is a for-loop.

A snippet from the implementation

Straight from the example as deployed on the ESP32 PLC 14 (×4, Ethernet) — copy it freely:

void setup() {
  Serial.begin(115200);
  for (int i = 0; i < NPINS; i++) pinMode(pins[i], INPUT);

  ETH.begin();
  ETH.config(ip, gw, mask);

  // --- GET /status: the concentrator (or a browser) queries the station
  server.on("/status", HTTP_GET, [] {
    server.send(200, "application/json", statusJson());
  });

  // --- POST /update: OTA — upload the new .bin from Node-RED or curl:
  //     curl -F "[email protected]" http://10.10.10.31/update
  server.on("/update", HTTP_POST,
    [] {
      server.sendHeader("Connection", "close");
      server.send(200, "text/plain", Update.hasError() ? "FAIL" : "OK");
      delay(1000);
      ESP.restart();                          // boots with the new firmware
    },
    [] {
      HTTPUpload &up = server.upload();
      if (up.status == UPLOAD_FILE_START) {
        Serial.printf("OTA: %s\n", up.filename.c_str());
        Update.begin(UPDATE_SIZE_UNKNOWN);
      } else if (up.status == UPLOAD_FILE_WRITE) {
        Update.write(up.buf, up.currentSize);
      } else if (up.status == UPLOAD_FILE_END) {
        if (Update.end(true)) Serial.printf("OTA OK: %u bytes\n", up.totalSize);
      }
    });

  server.begin();
  Serial.printf("Station %d at http://%s\n", STATION_ID, ETH.localIP().toString().c_str());
}

The full example is a complete program — wiring header, setup and main loop — ready to adapt to your application.

Frequently asked questions

Is the OTA endpoint safe to expose?

Keep it on an isolated OT network or behind the concentrator. For extra protection add HTTP basic auth and a firmware signature check before Update.end().

What happens if the upload fails midway?

Update.end() reports the error and the PLC keeps running the old firmware — the OTA partition scheme makes half-written uploads harmless.

Can I do this over WiFi instead of Ethernet?

Yes, the WebServer/Update code is identical; Ethernet is preferred for fixed stations because of reliability and lightning-protected cabling.

Related functionalities