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.