Skip to content
Tutorial
4 min read
October 17, 2026

ESP8266 OTA Updates: Flash Code Over the Air

Push firmware to a deployed ESP8266 over WiFi instead of unplugging it -- ArduinoOTA for active development, a browser-based updater for field devices, and the flash-space mistake that silently breaks OTA uploads.

ESP8266 OTA Updates: Flash Code Over the Air

An ESP8266 mounted inside a wall box, hidden inside a lamp base, or bolted to the top of a fence post has one thing in common with every other deployed project: getting a USB cable back to it for a firmware update is a genuine hassle. OTA (Over-The-Air) updates solve exactly that -- pushing new firmware to the board over WiFi, with no cable required after the very first flash.

Image pending: ESP8266 NodeMCU board receiving a firmware update wirelessly, no USB cable connected

Two Different OTA Approaches

ApproachBest forHow it works
ArduinoOTAActive development on your own networkBoard appears as a Network Port in the Arduino IDE; upload like a normal USB flash, just over WiFi
Web-based update (HTTPUpdateServer)Field-deployed devices, or updating from any browser without the IDEBoard hosts a simple upload page; drag a compiled .bin file in

ArduinoOTA is the better fit while a project is still being actively developed on the same local network as your computer. The web-based approach is more practical once a device has shipped somewhere you won't have your dev machine handy, or when someone other than the original developer needs to push an update.

Setting Up ArduinoOTA

#include <ESP8266WiFi.h>
#include <ArduinoOTA.h>

const char* ssid = "YourWiFiName";
const char* password = "YourWiFiPassword";

void setup() {
  Serial.begin(115200);
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
  }

  ArduinoOTA.setHostname("esp8266-project");
  ArduinoOTA.setPassword("choose-a-real-password");

  ArduinoOTA.onStart([]() { Serial.println("OTA starting"); });
  ArduinoOTA.onEnd([]()   { Serial.println("OTA complete"); });
  ArduinoOTA.onError([](ota_error_t error) {
    Serial.printf("OTA error: %u\n", error);
  });

  ArduinoOTA.begin();
  Serial.println("Ready. IP: " + WiFi.localIP().toString());
}

void loop() {
  ArduinoOTA.handle();  // must run every loop iteration, unblocked
  // ... rest of your project code
}

Once this is flashed once over USB, the board shows up under Tools > Port as a network port in the Arduino IDE from then on -- selecting it and hitting upload sends the new sketch over WiFi instead of a cable. A genuinely robust WiFi connection matters more here than in most sketches, since a flaky connection can drop mid-upload -- see Connecting ESP8266 NodeMCU to WiFi for a reconnect loop worth pairing with any OTA-enabled project.

Image pending: Arduino IDE Tools > Port menu showing an ESP8266 board available over the network

Setting Up a Web-Based Update Page

#include <ESP8266WiFi.h>
#include <ESP8266WebServer.h>
#include <ESP8266HTTPUpdateServer.h>

const char* ssid = "YourWiFiName";
const char* password = "YourWiFiPassword";

ESP8266WebServer server(80);
ESP8266HTTPUpdateServer httpUpdater;

void setup() {
  Serial.begin(115200);
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
  }

  httpUpdater.setup(&server, "/update", "admin", "choose-a-real-password");
  server.begin();

  Serial.println("Update page at: http://" + WiFi.localIP().toString() + "/update");
}

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

Visiting /update on the board's IP address shows a basic upload form -- choosing a compiled .bin file (exported from the Arduino IDE via Sketch > Export Compiled Binary) and submitting it flashes the new firmware directly from any browser, no IDE required on the updating machine at all.

Flash Size and Why Uploads Sometimes Fail

OTA needs enough free flash to hold both the currently running firmware and the incoming update simultaneously during the flash process. The board's flash size and partition split (set under Tools > Flash Size in the Arduino IDE) determines how much room is actually available for this -- a sketch that's grown close to the board's total flash capacity can fail an OTA upload with a space error, even though the exact same sketch flashed fine over USB the first time, since USB flashing doesn't need to keep both copies around at once.

Common Mistakes

Not leaving enough flash headroom for OTA. A sketch needs roughly double its own size available during the update -- check Tools > Flash Size if uploads that used to work start failing as a project grows.

Leaving the default or no OTA password. Anyone else on the same network can push firmware to an unprotected OTA endpoint -- always set a real password with setPassword() or in the web updater setup.

Blocking loop() with delay(). ArduinoOTA.handle() needs to run frequently to catch an incoming update -- a sketch full of long blocking delays can miss update requests or fail mid-transfer.

Forgetting to call the handler at all. ArduinoOTA.handle() (or server.handleClient() for the web approach) has to execute every loop() iteration -- omitting it silently disables OTA entirely, without any error.

Losing power mid-update. A power cut while new firmware is being written is a genuine bricking risk on a plain ESP8266 -- there's no automatic fallback to the previous firmware if the write is interrupted, so OTA updates are best performed with a stable power source, not right as a device is about to lose power.

Where to Go Next

OTA is only as reliable as the WiFi connection underneath it -- Connecting ESP8266 NodeMCU to WiFi: A Robust, Non-Blocking Reconnect Loop is the natural companion piece for any device that needs to stay reachable for updates over the long term, not just on the first flash.

Signal In

Built from what makers search for

The lookups piling up in SolderHub's search bar every day shape what we publish next — new component pages, board guides, and firmware notes, sent out as they go live.

By subscribing you agree to our Privacy Policy. Unsubscribe anytime.