ESP32 OTA Updates: The Complete 2026 Guide to Wireless Firmware Updates
If you've ever unplugged an ESP32 from a project box, walked it to your desk, plugged in a USB cable, flashed new firmware, and walked it back to wherever it lives — you already know why OTA updates matter. Do that once and it's fine. Do it every time you tweak a line of code on a board that's mounted inside a wall, buried in a garden sensor enclosure, or sitting on top of a fridge, and it stops being a minor annoyance and starts being the reason you dread iterating on your project.
OTA (Over-The-Air) updates let your ESP32 pull new firmware over Wi-Fi instead of a cable. Once it's set up properly, you can update ten boards scattered across your house — or a hundred scattered across a client's building — without touching a single one physically. This guide walks through exactly how to set it up, the two methods that actually matter in 2026, the errors people hit most often, and the security basics you shouldn't skip.
Why OTA Actually Matters (Beyond Convenience)
It's easy to file OTA under "nice to have," but it solves three real problems:
- Physical access is often impossible. A sensor node behind drywall or up on a roof isn't getting a USB cable connected to it twice.
- Fleet updates don't scale with cables. If you're running more than a handful of ESP32 devices, wired reflashing turns into a part-time job.
- Bug fixes need to ship fast. A security patch or a fix for a crash bug is only useful if you can actually deploy it without a site visit.
Two OTA Methods You'll Actually Use
There are a few ways to do OTA on ESP32, but in practice almost everyone lands on one of these two.
1. Arduino OTA (Local Network)
This is the built-in method using the ArduinoOTA library. Your ESP32 shows up as a network port inside the Arduino IDE, and you upload to it exactly like you would over USB — except it's wireless. It's the fastest way to iterate during development because there's zero extra infrastructure required.
2. HTTP OTA (Remote / Production)
This method pulls a .bin firmware file from a web server URL. It's what you want for devices that aren't on your local network, or for pushing updates to many devices at once from a central server. It's a bit more setup, but it's the method that actually scales.
Most serious projects end up using Arduino OTA while developing, then switching to HTTP OTA once the product is out in the field.
Setting Up Arduino OTA (Step by Step)
Here's the minimal working setup. Add this to your existing sketch.
1. Install the library — ArduinoOTA ships with the ESP32 board package, so you likely already have it. No extra install needed if you're on a recent board package version.
2. Add the includes and connect to Wi-Fi as normal:
#include <WiFi.h>
#include <ArduinoOTA.h>
const char* ssid = "YOUR_WIFI_SSID";
const char* password = "YOUR_WIFI_PASSWORD";
void setup() {
Serial.begin(115200);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("\nConnected. IP: " + WiFi.localIP().toString());
3. Initialize OTA inside setup(), right after Wi-Fi connects:
ArduinoOTA.setHostname("esp32-livingroom"); // name it something you'll recognize
ArduinoOTA.onStart([]() {
Serial.println("OTA update starting...");
});
ArduinoOTA.onEnd([]() {
Serial.println("\nOTA update complete.");
});
ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) {
Serial.printf("Progress: %u%%\r", (progress / (total / 100)));
});
ArduinoOTA.onError([](ota_error_t error) {
Serial.printf("Error[%u]\n", error);
});
ArduinoOTA.begin();
}
4. Keep OTA listening inside loop():
void loop() {
ArduinoOTA.handle();
// your regular code goes here
}
5. Flash this once over USB. After that, open the Arduino IDE, go to Tools > Port, and you'll see your ESP32 listed under "Network Ports" using the hostname you set. Select it, hit upload, and your code will fly over Wi-Fi instead of the cable.
That's it for local development. Every future upload is wireless from here on.
Setting Up HTTP OTA (For Devices in the Field)
For devices you can't reach on your local network, you need the ESP32 to check a URL, download a .bin file, and flash itself. The core logic looks like this:
#include <HTTPUpdate.h>
void checkForUpdate() {
WiFiClient client;
httpUpdate.update(client, "http://yourserver.com/firmware/latest.bin");
}
Call checkForUpdate() on a timer — say, once every 24 hours, or triggered by a specific MQTT message — and any device with internet access can update itself, no matter where it's physically located. For production use, host the firmware over HTTPS, not plain HTTP (more on why below).
Common OTA Errors (And How to Actually Fix Them)
"OTA device not showing up in Arduino IDE" Almost always a network issue, not a code issue. Your computer and ESP32 need to be on the same Wi-Fi network — this breaks instantly on guest networks, VLANs, or if your router has AP/client isolation turned on (common on mesh routers and some ISP-provided routers). Also confirm your firewall isn't blocking mDNS (port 5353).
"Error[4]: OTA End Failed" or update fails partway through This is usually a partition scheme problem. Your board needs enough flash space allocated for OTA — go to Tools > Partition Scheme and pick one that includes OTA space (anything labeled "Minimal SPIFFS" or a scheme with "OTA" in the name works well).
"Auth Failed" errors
If you set an OTA password with ArduinoOTA.setPassword(), double-check it matches exactly on both ends — this one's almost always a typo.
Device connects but update stalls at a specific percentage Weak Wi-Fi signal is the usual culprit. OTA is more sensitive to signal drops than normal Wi-Fi traffic because the update has to complete in one unbroken pass, or it fails and the device can end up needing a manual reflash. If a board is on the edge of your Wi-Fi range, consider an external antenna board or moving it closer during testing.
Security: Don't Skip This Part
OTA opens a wireless door to reflash your device's entire firmware — which means it's also a door an attacker could use if you leave it unlocked. Before shipping anything beyond a hobby project:
- Always set an OTA password with
ArduinoOTA.setPassword("something-strong"), never leave it open on the default. - Serve HTTP OTA firmware over HTTPS, so the
.binfile can't be intercepted or swapped mid-download. - Verify firmware signatures if you're deploying at any real scale — the ESP-IDF (not just Arduino framework) supports signed OTA images, which stops a device from accepting firmware that wasn't signed by you.
- Don't expose your update server to the whole internet without authentication if it's serving anything beyond a public open-source project.
Quick FAQ
Does OTA work with ESP32-S3 and ESP32-C6?
Yes — the ArduinoOTA and HTTPUpdate libraries work the same way across the ESP32, S3, and C6 variants. Just make sure your board package is up to date.
Can I brick my ESP32 with a bad OTA update? It's rare but possible if the update fails mid-flash and the device loses power at exactly the wrong moment. This is why most production setups keep a "rollback" partition — if the new firmware fails a health check on boot, it automatically reverts to the last working version.
Is OTA slower than USB flashing? Slightly, especially over HTTP OTA on a weak connection, but for most sketches the difference is a few seconds — a trade worth making for not having to physically access the device.
Wrapping Up
OTA is one of those features that feels like a "someday" addition right up until the moment you desperately need it — usually the first time a device is somewhere inconvenient and needs a fix now. Setting it up early, even on a hobby project, saves a lot of future frustration. Start with Arduino OTA for development, move to HTTP OTA once you're deploying to the field, and don't skip the password.
If you're building out a full ESP32 project — sensors, sleep modes, connectivity — check out the other ESP32 guides on this site for the pieces that pair well with OTA, like ESP32 Sleep Modes and ESP32 MQTT Tutorial.
