Cut the Cord: How to Implement Over-The-Air (OTA) Updates on ESP8266

Cut the Cord: How to Implement Over-The-Air (OTA) Updates on ESP8266


📑Table of Contents
What You'll Need 4 items
NodeMCU ESP8266 (ESP-12E)
Buy
USB Wall Charger (5V 1A)
Buy
LED
Buy
220Ω Resistor
Buy

Affiliate links help support this site at no extra cost to you.

Cut the Cord: How to Implement Over-The-Air (OTA) Updates on ESP8266

Imagine you have installed a weather station on your roof. It is February. It is raining. You discover a bug in your code. Do you: A) Get the ladder, climb the slippery roof, unscrew the waterproof case, and plug in a USB cable? B) Sit on your couch, press “Upload” in Arduino IDE, and watch the magic happen?

If you chose B, welcome to OTA (Over-The-Air) Updates. This is the difference between a “Hobbyist Project” and a “Professional Product”. Today, we will learn how to flash firmware wirelessly, secure it with passwords, and understand the deep memory mechanics that make it possible.

Hero OTA Wireless Update


The Concept: How Does It Work?

Normally, the ESP8266 bootloader waits for a signal on the UART (Serial) pins to accept new code. With OTA, the logic changes:

  1. The Code runs: The ESP8266 connects to WiFi.
  2. The Code listens: It waits for a specific “Handshake” on a specific Port (usually 8266).
  3. The Trigger: When you press Upload, the computer sends an Invite.
  4. The Download: The ESP8266 accepts the file, writes it to a separate part of the flash memory.
  5. The Swap: It reboots, checks the new code, and swaps the “Active” partition.

The Magic of Partitions (Deep Dive)

Your ESP8266 (e.g., 4MB Flash) is not just one big bucket. It is sliced up.

  • Bootloader (0x0000): The tiny program that runs first. It decides which App to load.
  • App0 (Active): Where your current code lives.
  • App1 (OTA / Passive): An empty space of equal size.
  • SPIFFS/LittleFS: File storage.

When you do an OTA update, you are NOT overwriting App0. You are writing to App1. Only after a successful checksum verification does the Bootloader flip the switch to boot from App1 next time.

  • Safety: If the power cuts out halfway through the download, App0 is untouched. The device still boots.

Partition Table Visual


The Basic OTA Code

We use the standard ArduinoOTA library. It makes this process shockingly easy.

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

const char* ssid = "MyWiFi";
const char* password = "MyPassword";

void setup() {
  Serial.begin(115200);
  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, password);

  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }

  // --- OTA CONFIGURATION ---
  
  // Port defaults to 8266
  ArduinoOTA.setPort(8266);

  // Hostname defaults to esp8266-[ChipID]
  ArduinoOTA.setHostname("LivingRoom-Sensor");

  // No authentication by default
  // ArduinoOTA.setPassword("admin");

  // Define what happens during the update
  ArduinoOTA.onStart([]() {
    String type;
    if (ArduinoOTA.getCommand() == U_FLASH) {
      type = "sketch";
    } else { // U_FS
      type = "filesystem";
    }
    // NOTE: if updating FS this would be the place to unmount FS using SPIFFS.end()
    Serial.println("Start updating " + type);
  });

  ArduinoOTA.onEnd([]() {
    Serial.println("\nEnd");
  });

  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]: ", error);
    if (error == OTA_AUTH_ERROR) Serial.println("Auth Failed");
    else if (error == OTA_BEGIN_ERROR) Serial.println("Begin Failed");
    else if (error == OTA_CONNECT_ERROR) Serial.println("Connect Failed");
    else if (error == OTA_RECEIVE_ERROR) Serial.println("Receive Failed");
    else if (error == OTA_END_ERROR) Serial.println("End Failed");
  });

  ArduinoOTA.begin();
  Serial.println("Ready");
  Serial.print("IP address: ");
  Serial.println(WiFi.localIP());
}

void loop() {
  // CRITICAL: You must call this frequently
  ArduinoOTA.handle(); 
  
  // Your normal code goes here
}

The “First Flash”

There is a Catch-22. To update wirelessly, the code must already have OTA support. So, for the very first time, you must use the USB cable to upload this sketch. Once it is running:

  1. Check Serial Monitor. Confirm it prints an IP.
  2. Restart Arduino IDE.
  3. Look at Tools -> Port.
  4. You will see a new section: Network Ports.
  5. Select LivingRoom-Sensor at 192.168.1.X.

Arduino IDE Network Port

Now, simply click Upload. The IDE will compile the code, find the ESP over the network, handshake, and blast the firmware over WiFi. Speed Note: It is often faster than USB (1MB/s vs 115200 baud).


Securing the Door

By default, anyone on your WiFi can upload code to your ESP. If you are at a Hackathon or a Coffee Shop, this is a disaster. Someone could upload a “Brick” sketch or malware.

The Fix: Set a Password.

// Add this in setup()
ArduinoOTA.setPassword("SuperSecret123");

Now, when you click Upload in the IDE, a popup window will appear asking for the credentials.

  • Pro Tip: You can also use setPasswordHash if you don’t even want the plain text password in your compiled binary (preventing reverse engineering).

Password Auth Popup


Handling OTA Events

When an update starts, your device stops doing its main job. If this is a Light Switch, you might want to blink an LED rapidly to show “I am updating”. If this is a Robot, you MUST stop the motors. Update-in-motion is dangerous.

The onStart Callback: This is where you make the device safe.

ArduinoOTA.onStart([]() {
  // 1. Stop high power loads
  digitalWrite(MOTOR_PIN, LOW);
  digitalWrite(RELAY_PIN, LOW);
  
  // 2. Unmount File System (Crucial!)
  LittleFS.end();
  
  // 3. Visual Indication
  digitalWrite(LED_BUILTIN, HIGH); 
  Serial.println("OTA Started...");
});

The onProgress Callback: You can draw a progress bar on an OLED screen or Serial Monitor.

ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) {
  int percent = (progress / (total / 100));
  display.clearDisplay();
  display.setCursor(0,0);
  display.print("Updating: ");
  display.print(percent);
  display.print("%");
  display.display();
});

Serial Monitor Progress Bar


Understanding the “Clone” Problem

When you upload via OTA, you are uploading the Currently Open Sketch. WARNING: If you upload a sketch that does not have the OTA code inside it, you permanently lose OTA capability. The next update will have to be via USB cable.

Best Practice: Create a ota_handler.h file. Include it in every single project you make. Never write the boilerplate again.

OTA Update Flowchart


What if the Update Fails? (The Brick)

  1. WiFi Loss: If WiFi drops at 50%, the ESP detects the disconnection/timeout. It discards the half-written App1 partition and simply boots back into App0. Safe.
  2. Power Loss: Same as above. The Bootloader Config isn’t updated until the very end.
  3. Bad Code: You uploaded code that crashes immediately (WDT Reset).
    • This is the real danger. If the new code goes into a Reboot Loop, it never connects to WiFi, and you can’t OTA again.
    • The Fix: Implement a “Safe Mode”.
    • On Boot, check a pin. If logic is HIGH, enter a minimalist “OTA Only” mode and skip the main application logic.

Bricked Device Graveyard


Advanced: HTTP Updates (The Netflix Model)

ArduinoOTA pushes code from your PC. But what if you want to update 1,000 devices deployed in customers’ homes? You can’t visit them all. The device must Pull updates from a server.

ESP8266httpUpdate Library:

  1. The ESP checks a URL: http://myserver.com/firmware/version.json.
  2. It sees a newer version exists.
  3. It downloads the .bin file from the server.
  4. It flashes itself.
#include <ESP8266httpUpdate.h>

void checkForUpdate() {
  t_httpUpdate_return ret = ESPhttpUpdate.update(client, "http://myserver.com/firmware.bin");

  switch (ret) {
    case HTTP_UPDATE_FAILED:
      Serial.printf("HTTP_UPDATE_FAILD Error (%d): %s\n", ESPhttpUpdate.getLastError(), ESPhttpUpdate.getLastErrorString().c_str());
      break;

    case HTTP_UPDATE_NO_UPDATES:
      Serial.println("HTTP_UPDATE_NO_UPDATES");
      break;

    case HTTP_UPDATE_OK:
      Serial.println("HTTP_UPDATE_OK");
      break;
  }
}

HTTP Server Update Diagram


Partition Tables Explained

(Technical Deep Dive)

Why do you sometimes see “1MB (FS:64KB OTA:~470KB)” in Arduino Tools? Because you need SPACE for the update.

  • Case 1: 4MB Flash (NodeMCU):
    • App0 (1MB)
    • App1_Reserve (1MB)
    • File System (1MB)
    • Reserved/WiFi (1MB)
    • Result: You can have huge code.
  • Case 2: 1MB Flash (ESP-01):
    • App0 (490KB)
    • App1_Reserve (490KB)
    • Result: Your code CANNOT be larger than 490KB, or OTA will fail (Not enough space to download the copy).

Tip: If OTA fails with “No Space”, reduce your File System size in the Tools menu to free up Flash for the App.

Dual Bank Boot Mechanism


Bonus: Browser-Based OTA (The Router Interface)

What if you don’t have Arduino IDE? What if you want to update from your iPhone? You can run a mini Website on the ESP8266 that accepts a .bin file upload.

The Code:

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

ESP8266WebServer httpServer(80);
ESP8266HTTPUpdateServer httpUpdater;

void setup() {
  // ... WiFi Setup ...
  
  // This adds the /update page
  httpUpdater.setup(&httpServer);
  
  httpServer.begin();
  Serial.println("HTTP Update Server Ready");
  Serial.printf("Open http://%s/update in your browser\n", WiFi.localIP().toString().c_str());
}

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

How to Use:

  1. In Arduino IDE, go to Sketch -> Export Compiled Binary.
  2. It creates a .bin file in your project folder.
  3. Open http://192.168.1.X/update.
  4. Drag and drop the .bin file.
  5. Done. The device reboots with new code.

Glossary: The OTA Dictionary

  • Bootloader: The code that runs before your code. Handles the partition swapping.
  • Magic Byte: A specific byte at the start of the binary that tells the bootloader “This is a valid program”.
  • MD5 Checksum: A cryptographic math problem used to verify the file didn’t get corrupted during download.
  • mDNS: Multicast DNS. How “esp8266.local” is discovered without a central DNS server.
  • Partition Scheme: The partition map definition (CSV file) that tells the compiler how to slice the pizza.

Final Project: The “Remote Toggle”

We will build a device that we never plug in again.

  1. Initial Flash: Flash the OTA-enabled Blink sketch via USB.
  2. Deploy: Plug the ESP into a USB Wall Charger in the kitchen.
  3. Update 1: Change the blink speed from 1000ms to 100ms. Upload from your Bedroom.
  4. Update 2: Add a Web Server. Upload.
  5. Verify: Watch the LED change behavior instantly.

Final Setup Photo No Cables

Copyright © 2026 TechnoChips. Open Source Hardware (OSHW).

Comments