Skip to Content

← Web Server OTA Firmware Updates on an ESP32 PLC

Textile monitoring (weaving)ESP32 PLCHTTPResilience / OTA

Web Server OTA Firmware Updates on an ESP32 PLC — full example

Embedded web server with OTA firmware updates on an ESP32 PLC: status page, JSON endpoint and browser .bin upload — no USB cable on the factory floor.

Complete, runnable program for the ESP32 PLC (webserver-ota-firmware.ino): wiring header, requirements and integration notes included.

Download the full project pack — freeThis example + the related ones + bill of materials

Read-only preview.

/*
 * COMPLETE EXAMPLE — Embedded web server with OTA firmware update
 *
 * Hardware: ESP32 PLC (Industrial Shields, any model with WiFi)
 * Based on: textile monitoring project, modul_WebServer_v2.h
 *
 * Network:
 *   The PLCs are spread across the plant, next to each knitting machine:
 *   reflashing over USB means climbing a ladder. With OTA the update is
 *   done from the browser: http:///
 *
 * Endpoints:
 *   GET  /        HTML status page with a firmware upload form
 *   GET  /status  JSON with digital inputs, uptime and free heap
 *   POST /update  Receives the compiled .bin, flashes it and reboots
 *
 * Works together with other catalog examples:
 *   - nonblocking-wifi-reconnection.ino (the server only responds with network)
 *   - rtc-ntp-synchronization.ino (timestamp in /status)
 *   - mqtt-remote-commands.ino (complementary diagnostics without a browser)
 */

#include 
#include 
#include 

const char *WIFI_SSID = "WIFI_SSID";
const char *WIFI_PASS = "WIFI_PASS";
const int   PLC_ID    = 1;

WebServer server(80);

const int pins[] = {I0_0, I0_1, I0_2, I0_3, I0_4, I0_5};
const int NPINS = 6;

// ------------------------------------------------- Pages
void handleRoot() {
  String html =
    "PLC " + String(PLC_ID) + ""
    "

ESP32 PLC " + String(PLC_ID) + " — Textile monitoring

" "

Uptime: " + String(millis() / 1000) + " s | " "Free heap: " + String(ESP.getFreeHeap()) + " bytes

" "

/status (JSON)

" "

Update firmware (OTA)

" "
" " " "
" ""; server.send(200, "text/html", html); } void handleStatus() { String json = "{\"id\":" + String(PLC_ID) + ",\"uptime_s\":" + String(millis() / 1000) + ",\"heap\":" + String(ESP.getFreeHeap()) + ",\"inputs\":{"; for (int i = 0; i < NPINS; i++) json += "\"I0_" + String(i) + "\":" + String(digitalRead(pins[i])) + (i < NPINS - 1 ? "," : ""); json += "}}"; server.send(200, "application/json", json); } // ------------------------------------------------- OTA over HTTP POST // First callback: final response + reboot. Second: chunks of the .bin void setupOTA() { server.on("/update", HTTP_POST, []() { server.sendHeader("Connection", "close"); server.send(200, "text/plain", Update.hasError() ? "FAIL" : "OK - rebooting"); delay(1000); ESP.restart(); // boots with the new firmware }, []() { HTTPUpload &up = server.upload(); if (up.status == UPLOAD_FILE_START) { Serial.println("OTA: receiving " + up.filename); if (!Update.begin(UPDATE_SIZE_UNKNOWN)) Update.printError(Serial); } else if (up.status == UPLOAD_FILE_WRITE) { if (Update.write(up.buf, up.currentSize) != up.currentSize) Update.printError(Serial); } else if (up.status == UPLOAD_FILE_END) { if (Update.end(true)) Serial.println("OTA OK: " + String(up.totalSize) + " bytes"); else Update.printError(Serial); } }); } void setup() { Serial.begin(115200); for (int i = 0; i < NPINS; i++) pinMode(pins[i], INPUT); WiFi.mode(WIFI_STA); WiFi.begin(WIFI_SSID, WIFI_PASS); uint32_t t0 = millis(); while (WiFi.status() != WL_CONNECTED && millis() - t0 < 15000) delay(500); Serial.println("IP: " + WiFi.localIP().toString()); server.on("/", HTTP_GET, handleRoot); server.on("/status", HTTP_GET, handleStatus); setupOTA(); server.begin(); Serial.println("OTA web server ready on port 80"); } void loop() { server.handleClient(); // serve web requests // ... the normal PLC work continues here (pulses, MQTT, SD): // the web server only adds a few microseconds per loop iteration delay(5); }
Download the full project pack — freeThis example + the related ones + bill of materials