ESP8266 NodeMCU OTA (Over-the-Air) Updates – AsyncElegantOTA using Arduino IDE

In this guide, you’ll learn how to do over-the-air (OTA) updates to your ESP8266 NodeMCU boards using the AsyncElegantOTA library. This library creates a web server that allows you to update new firmware (a new sketch) to your board without the need to make a serial connection between the ESP8266 and your computer.

Additionally, you can also upload new files to the ESP8266 filesystem (LittleFS). The library is very easy to use, and it’s compatible with the ESPAsyncWebServer library that we often use to build web server projects.

ESP8266 NodeMCU OTA Over-the-Air Updates AsyncElegantOTA using Arduino IDE

By the end of this tutorial, you’ll be able to easily add OTA capabilities to your web server projects with the ESP8266 to upload new firmware and files to the filesystem wirelessly in the future.

We have a similar tutorial for the ESP32 board: ESP32 OTA (Over-the-Air) Updates – AsyncElegantOTA using Arduino IDE

Watch the Video Tutorial

This project is available in video format and in written format. You can watch the video below or you can scroll down for the written instructions.

Overview

This tutorial covers:

We recommend that you follow all the steps in this tutorial to understand how ElegantOTA works and how you can use it in your projects. To demonstrate how to do this, we’ll upload files to build different web server projects.

OTA (Over-the-Air) Programming

OTA (Over-the-Air) update is the process of loading new firmware to the ESP8266 NodeMCU board using a Wi-Fi connection rather than serial communication. This functionality is extremely useful in case of no physical access to the ESP8266 board.

There are different ways to perform OTA updates. In this tutorial, we’ll cover how to do that using the AsyncElegantOTA library. In our opinion, this is one of the best and easiest ways to perform OTA updates.

The AsyncElegantOTA library creates a web server that you can access on your local network to upload new firmware or files to the filesystem (LittleFS). The files you upload should be in .bin format. We’ll show you later in the tutorial how to convert your files to .bin format.

Async ElegantOTA Web Server How it Works ESP8266

The only disadvantage of OTA programming is that you need to add the code for OTA in every sketch you upload so that you’re able to use OTA in the future. In the case of the AsyncElegantOTA library, it consists of just three lines of code.

AsyncElegantOTA Library

As mentioned previously, there are a bunch of alternatives for OTA programming with the ESP8266 boards. For example, in the Arduino IDE, under the Examples folder, there is the BasicOTA example (that never worked well for us); and many other examples from different libraries.

Most of our web server projects with the ESP8266 use the ESPAsyncWebServer library. So, we wanted a solution that was compatible with that library. The AsyncElegantOTA library is just perfect for what we want:

AsyncElegantOTA Library
  • It is compatible with the ESPAsyncWebServer library;
  • You just need to add three lines of code to add OTA capabilities to your “regular” Async Web Server;
  • It allows you to update not only new firmware to the board but also files to the ESP8266 filesystem (LittleFS);
  • It provides a beautiful and modern web server interface;
  • It works extremely well.

If you like this library and you’ll use it in your projects, consider supporting the developer’s work.

OTA Updates with AsyncElegantOTA Library – Quick Summary

To add OTA capabilities to your projects using the AsyncElegantOTA library, follow these steps:

  1. Install AsyncElegantOTA, ESPAsyncTCP and ESPAsyncWebServer libraries;
  2. Include AsyncElegantOTA library at the top of the Arduino sketch: #include <AsyncElegantOTA.h>;
  3. Add this line AsyncElegantOTA.begin(&server); before server.begin();
  4. Open your browser and go to http://<IPAddress>/update, where <IPAddress> is your ESP8266 IP address.

Continue reading the tutorial for more detailed steps.

How does OTA Web Updater Work?

  • The first sketch should be uploaded via the serial port. This sketch should contain the code to create the OTA Web Updater so that you can upload code later using your browser.
  • The OTA Web Updater sketch creates a web server you can access to upload a new sketch via a web browser.
  • Then, you need to implement OTA routines in every sketch you upload so that you’re able to do the next updates/uploads over-the-air.
  • If you upload a code without an OTA routine, you’ll no longer be able to access the web server and upload a new sketch over-the-air.

Install AsyncElegantOTA Library

In this tutorial, the ESP8266 will be programmed using Arduino IDE. If you want to learn how to do the same using VS Code + PlatformIO, follow the next tutorial: ESP8266 NodeMCU OTA (Over-the-Air) Updates – AsyncElegantOTA (VS Code + PlatformIO).

You can install the AsyncElegantOTA library using the Arduino Library Manager. In your Arduino IDE, go to Sketch > Include Library > Manage Libraries… Search for “AsyncElegantOTA” and install it.

Install Async Elegant OTA Library Arduino IDE

Install ESPAsyncWebServer and ESPAsyncTCP Libraries

You also need to install the ESPAsyncTCP and the ESPAsyncWebServer libraries. Click the links below to download the libraries.

These libraries aren’t available to install through the Arduino Library Manager, so you need to copy the library files to the Arduino Installation Libraries folder. Alternatively, in your Arduino IDE, you can go to Sketch Include Library > Add .zip Library and select the libraries you’ve just downloaded.

AsyncElegantOTA ESP8266 Basic Example

Let’s start with the basic example provided by the library. This example creates a simple web server with the ESP8266. The root URL displays some text, and the /update URL displays the interface to update firmware and filesystem.

Copy the following code to your Arduino IDE.

/*
  Rui Santos
  Complete project details
   - Arduino IDE: https://RandomNerdTutorials.com/esp8266-nodemcu-ota-over-the-air-arduino/
   - VS Code: https://RandomNerdTutorials.com/esp8266-nodemcu-ota-over-the-air-vs-code/
     
  This sketch shows a Basic example from the AsyncElegantOTA library: ESP8266_Async_Demo
  https://github.com/ayushsharma82/AsyncElegantOTA
*/

#include <Arduino.h>
#include <ESP8266WiFi.h>
#include <ESPAsyncTCP.h>
#include <ESPAsyncWebServer.h>
#include <AsyncElegantOTA.h>

const char* ssid = "REPLACE_WITH_YOUR_SSID";
const char* password = "REPLACE_WITH_YOUR_PASSWORD";

AsyncWebServer server(80);

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

  // Wait for connection
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("");
  Serial.print("Connected to ");
  Serial.println(ssid);
  Serial.print("IP address: ");
  Serial.println(WiFi.localIP());

  server.on("/", HTTP_GET, [](AsyncWebServerRequest *request) {
    request->send(200, "text/plain", "Hi! I am ESP8266.");
  });

  AsyncElegantOTA.begin(&server);    // Start ElegantOTA
  server.begin();
  Serial.println("HTTP server started");
}

void loop(void) {
  AsyncElegantOTA.loop();
}

View raw code

Insert your network credentials and the code should work straight away:

const char* ssid = "REPLACE_WITH_YOUR_SSID";
const char* password = "REPLACE_WITH_YOUR_PASSWORD";

How the Code Works

First, include the necessary libraries:

#include <ESP8266WiFi.h>
#include <ESPAsyncTCP.h>
#include <ESPAsyncWebServer.h>
#include <AsyncElegantOTA.h>

Insert your network credentials in the following variables so that the ESP8266 can connect to your local network.

const char* ssid = "REPLACE_WITH_YOUR_SSID";
const char* password = "REPLACE_WITH_YOUR_PASSWORD";

Create an AsyncWebServer object on port 80:

AsyncWebServer server(80);

In the setup(), initialize the Serial Monitor:

Serial.begin(115200);

Initialize Wi-Fi:

WiFi.mode(WIFI_STA);
WiFi.begin(ssid, password);
Serial.println("");

// Wait for connection
while (WiFi.status() != WL_CONNECTED) {
  delay(500);
  Serial.print(".");
}
Serial.println("");
Serial.print("Connected to ");
Serial.println(ssid);
Serial.print("IP address: ");
Serial.println(WiFi.localIP());

Then, handle the client requests. The following lines, send some text Hi! I am ESP8266. when you access the root (/) URL:

server.on("/", HTTP_GET, [](AsyncWebServerRequest *request) {
  request->send(200, "text/plain", "Hi! I am ESP8266.");
});

If your web server needs to handle more requests you can add them (we’ll show you in the next example).

Then, add the next line to start ElegantOTA:

AsyncElegantOTA.begin(&server);    // Start ElegantOTA

Finally, initialize the server:

server.begin();

Lastly, add the following in the loop():

void loop(void) {
  AsyncElegantOTA.loop();
}

Access the Web Server

After uploading code to the board, open the Serial Monitor at a baud rate of 115200. Press the ESP8266 on-board RST button. It should display the ESP IP address as follows (yours may be different):

Async ElegantOTA Get ESP IP Address Serial Monitor

In your local network, open your browser and type the ESP8266 IP address. You should get access the root (/) web page with some text displayed.

ESP8266 Async ElegantOTA Demo Example Web Server Root URL

Now, imagine that you want to modify your web server code. To do that via OTA, go to the ESP IP address followed by /update. The following web page should load.

ESP8266 Async ElegantOTA Update Page

Follow the next sections to learn how to upload new firmware using the AsyncElegantOTA.

Upload New Firmware OTA (Over-the-Air) Updates – ESP8266

Every file that you upload via OTA should be in .bin format. You can generate a .bin file from your sketch using the Arduino IDE.

With your sketch opened, you need to go to Sketch > Export Compiled Binary. A .bin file will be generated from your sketch. The generated file will be saved under your project folder.

That’s that .bin file you should upload using the AsyncElegantOTA web page if you want to upload new firmware.

Upload a New Web Server Sketch – Example

Let’s see a practical example. Imagine that after uploading the previous sketch, you want to upload a new one that allows you to control an LED via a web interface like this project. Here’s the steps you need to follow:

1. Copy the following code to your Arduino IDE. Don’t forget to insert your network credentials.

/*
  Rui Santos
  Complete project details
   - Arduino IDE: https://RandomNerdTutorials.com/esp8266-nodemcu-ota-over-the-air-arduino/
   - VS Code: https://RandomNerdTutorials.com/esp8266-nodemcu-ota-over-the-air-vs-code/
     
  Permission is hereby granted, free of charge, to any person obtaining a copy
  of this software and associated documentation files.
  
  The above copyright notice and this permission notice shall be included in all
  copies or substantial portions of the Software.
*/

// Import required libraries
#include <Arduino.h>
#include <ESP8266WiFi.h>
#include <ESPAsyncTCP.h>
#include <ESPAsyncWebServer.h>
#include <AsyncElegantOTA.h>

// Replace with your network credentials
const char* ssid = "REPLACE_WITH_YOUR_SSID";
const char* password = "REPLACE_WITH_YOUR_PASSWORD";

bool ledState = 0;
const int ledPin = 2;

// Create AsyncWebServer object on port 80
AsyncWebServer server(80);
AsyncWebSocket ws("/ws");

const char index_html[] PROGMEM = R"rawliteral(
<!DOCTYPE HTML><html>
<head>
  <title>ESP Web Server</title>
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link rel="icon" href="data:,">
  <style>
  html {
    font-family: Arial, Helvetica, sans-serif;
    text-align: center;
  }
  h1 {
    font-size: 1.8rem;
    color: white;
  }
  h2{
    font-size: 1.5rem;
    font-weight: bold;
    color: #143642;
  }
  .topnav {
    overflow: hidden;
    background-color: #143642;
  }
  body {
    margin: 0;
  }
  .content {
    padding: 30px;
    max-width: 600px;
    margin: 0 auto;
  }
  .card {
    background-color: #F8F7F9;;
    box-shadow: 2px 2px 12px 1px rgba(140,140,140,.5);
    padding-top:10px;
    padding-bottom:20px;
  }
  .button {
    padding: 15px 50px;
    font-size: 24px;
    text-align: center;
    outline: none;
    color: #fff;
    background-color: #0f8b8d;
    border: none;
    border-radius: 5px;
    -webkit-touch-callout: none;
    -webkit-user-select: none;
    -khtml-user-select: none;
    -moz-user-select: none;
    -ms-user-select: none;
    user-select: none;
    -webkit-tap-highlight-color: rgba(0,0,0,0);
   }
   /*.button:hover {background-color: #0f8b8d}*/
   .button:active {
     background-color: #0f8b8d;
     box-shadow: 2 2px #CDCDCD;
     transform: translateY(2px);
   }
   .state {
     font-size: 1.5rem;
     color:#8c8c8c;
     font-weight: bold;
   }
  </style>
<title>ESP Web Server</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" href="data:,">
</head>
<body>
  <div class="topnav">
    <h1>ESP WebSocket Server</h1>
  </div>
  <div class="content">
    <div class="card">
      <h2>Output - GPIO 2</h2>
      <p class="state">state: <span id="state">%STATE%</span></p>
      <p><button id="button" class="button">Toggle</button></p>
    </div>
  </div>
<script>
  var gateway = `ws://${window.location.hostname}/ws`;
  var websocket;
  window.addEventListener('load', onLoad);
  function initWebSocket() {
    console.log('Trying to open a WebSocket connection...');
    websocket = new WebSocket(gateway);
    websocket.onopen    = onOpen;
    websocket.onclose   = onClose;
    websocket.onmessage = onMessage; // <-- add this line
  }
  function onOpen(event) {
    console.log('Connection opened');
  }
  function onClose(event) {
    console.log('Connection closed');
    setTimeout(initWebSocket, 2000);
  }
  function onMessage(event) {
    var state;
    if (event.data == "1"){
      state = "ON";
    }
    else{
      state = "OFF";
    }
    document.getElementById('state').innerHTML = state;
  }
  function onLoad(event) {
    initWebSocket();
    initButton();
  }
  function initButton() {
    document.getElementById('button').addEventListener('click', toggle);
  }
  function toggle(){
    websocket.send('toggle');
  }
</script>
</body>
</html>)rawliteral";

void notifyClients() {
  ws.textAll(String(ledState));
}

void handleWebSocketMessage(void *arg, uint8_t *data, size_t len) {
  AwsFrameInfo *info = (AwsFrameInfo*)arg;
  if (info->final && info->index == 0 && info->len == len && info->opcode == WS_TEXT) {
    data[len] = 0;
    if (strcmp((char*)data, "toggle") == 0) {
      ledState = !ledState;
      notifyClients();
    }
  }
}

void onEvent(AsyncWebSocket *server, AsyncWebSocketClient *client, AwsEventType type,
             void *arg, uint8_t *data, size_t len) {
  switch (type) {
    case WS_EVT_CONNECT:
      Serial.printf("WebSocket client #%u connected from %s\n", client->id(), client->remoteIP().toString().c_str());
      break;
    case WS_EVT_DISCONNECT:
      Serial.printf("WebSocket client #%u disconnected\n", client->id());
      break;
    case WS_EVT_DATA:
      handleWebSocketMessage(arg, data, len);
      break;
    case WS_EVT_PONG:
    case WS_EVT_ERROR:
      break;
  }
}

void initWebSocket() {
  ws.onEvent(onEvent);
  server.addHandler(&ws);
}

String processor(const String& var){
  Serial.println(var);
  if(var == "STATE"){
    if (ledState){
      return "ON";
    }
    else{
      return "OFF";
    }
  }
  return String();
}

void setup(){
  // Serial port for debugging purposes
  Serial.begin(115200);

  pinMode(ledPin, OUTPUT);
  digitalWrite(ledPin, LOW);
  
  // Connect to Wi-Fi
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(1000);
    Serial.println("Connecting to WiFi..");
  }

  // Print ESP Local IP Address
  Serial.println(WiFi.localIP());

  initWebSocket();

  // Route for root / web page
  server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
    request->send_P(200, "text/html", index_html, processor);
  });

  // Start ElegantOTA
  AsyncElegantOTA.begin(&server);
  // Start server
  server.begin();
}

void loop() {
  AsyncElegantOTA.loop();
  ws.cleanupClients();
  digitalWrite(ledPin, ledState);
}

View raw code

This is the same code used in this project, but it contains the needed lines of code to handle ElegantOTA:

#include <AsyncElegantOTA.h>
AsyncElegantOTA.begin(&server);
AsyncElegantOTA.loop();

2. Save your sketch: File > Save and give it a name. For example: Web_Server_LED_OTA_ESP8266.

3. Generate a .bin file from your sketch. Go to Sketch > Export Compiled Binary. A new .bin file should be created under the project folder.

ESP8266 Bin File generated for OTA Updates Arduino IDE

4. Now you just need to upload that file using the ElegantOTA page. Go to your ESP IP address followed by /update. Make sure you have the firmware option selected. Click on Choose File and select the .bin file you’ve just generated.

ESP8266 Update New Firmware Elegant OTA

5. When it’s finished, click on the Back button.

ESP8266 NodeMCU Upload New Firmware Elegant OTA success

6. Then, you can go to the root (/) URL to access the new web server. This is the page that you should see when you access the ESP IP address on the root (/) URL.

ESP8266 NodeMCU Websocket Server Control Outputs

You can click on the button to turn the ESP8266 on-board LED on and off.

ESP8266 NodeMCU board Built in LED turned on HIGH

Because we’ve also added OTA capabilities to this new web server, we can upload a new sketch in the future if needed. You just need to go to the ESP8266 IP address followed by /update.

Congratulations, you’ve uploaded new code to your ESP8266 via Wi-Fi using ElegantOTA.

Continue reading if you want to learn how to upload files to the ESP8266 filesystem (LittleFS) using AsyncElegantOTA.

Upload Files to Filesystem OTA (Over-the-Air) Updates – ESP8266

In this section, you’ll learn to upload files to the ESP8266 filesystem (LittleFS) using AsyncElegantOTA.

ESP8266 LittleFS Filesystem Upload Plugin

Before proceeding, you need to have the ESP8266 LittleFS Uploader Plugin installed in your Arduino IDE. Follow the next tutorial before proceeding:

Web Server with Files from LittleFS

Imagine the scenario that you need to upload files to the ESP8266 filesystem, for example, configuration files, HTML, CSS, and JavaScript files to update the web server page or any other file that you may want to save in LittleFS via OTA.

To show you how to do this, we’ll create a new web server that serves files from the filesystem (LittleFS): HTML, CSS, and JavaScript files to build a web page that controls the ESP8266 GPIOs remotely.

Before proceeding make sure you have the Arduino_JSON library by Arduino version 0.1.0 installed. You can install this library in the Arduino IDE Library Manager. Just go to Sketch Include Library > Manage Libraries and search for the library name as follows: Arduino_JSON.

Copy the following code to your Arduino IDE.

/*
  Rui Santos
  Complete project details
   - Arduino IDE: https://RandomNerdTutorials.com/esp8266-nodemcu-ota-over-the-air-arduino/
   - VS Code: https://RandomNerdTutorials.com/esp8266-nodemcu-ota-over-the-air-vs-code/
     
  Permission is hereby granted, free of charge, to any person obtaining a copy
  of this software and associated documentation files.
  
  The above copyright notice and this permission notice shall be included in all
  copies or substantial portions of the Software.
*/

// Import required libraries
#include <Arduino.h>
#include <ESP8266WiFi.h>
#include <ESPAsyncTCP.h>
#include <ESPAsyncWebServer.h>
#include "LittleFS.h"
#include <Arduino_JSON.h>
#include <AsyncElegantOTA.h>

// Replace with your network credentials
const char* ssid = "REPLACE_WITH_YOUR_SSID";
const char* password = "REPLACE_WITH_YOUR_PASSWORD";

// Create AsyncWebServer object on port 80
AsyncWebServer server(80);

// Create a WebSocket object
AsyncWebSocket ws("/ws");

// Set number of outputs
#define NUM_OUTPUTS  4

// Assign each GPIO to an output
int outputGPIOs[NUM_OUTPUTS] = {2, 4, 12, 14};

// Initialize LittleFS
void initLittleFS() {
  if (!LittleFS.begin()) {
    Serial.println("An error has occurred while mounting LittleFS");
  }
  Serial.println("LittleFS mounted successfully");
}

// Initialize WiFi
void initWiFi() {
  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, password);
  Serial.print("Connecting to WiFi ..");
  while (WiFi.status() != WL_CONNECTED) {
    Serial.print('.');
    delay(1000);
  }
  Serial.println(WiFi.localIP());
}

String getOutputStates(){
  JSONVar myArray;
  for (int i =0; i<NUM_OUTPUTS; i++){
    myArray["gpios"][i]["output"] = String(outputGPIOs[i]);
    myArray["gpios"][i]["state"] = String(digitalRead(outputGPIOs[i]));
  }
  String jsonString = JSON.stringify(myArray);
  return jsonString;
}

void notifyClients(String state) {
  ws.textAll(state);
}

void handleWebSocketMessage(void *arg, uint8_t *data, size_t len) {
    AwsFrameInfo *info = (AwsFrameInfo*)arg;
  if (info->final && info->index == 0 && info->len == len && info->opcode == WS_TEXT) {
    data[len] = 0;
    if (strcmp((char*)data, "states") == 0) {
      notifyClients(getOutputStates());
    }
    else{
      int gpio = atoi((char*)data);
      digitalWrite(gpio, !digitalRead(gpio));
      notifyClients(getOutputStates());
    }
  }
}

void onEvent(AsyncWebSocket *server, AsyncWebSocketClient *client,AwsEventType type,
             void *arg, uint8_t *data, size_t len) {
  switch (type) {
    case WS_EVT_CONNECT:
      Serial.printf("WebSocket client #%u connected from %s\n", client->id(), client->remoteIP().toString().c_str());
      break;
    case WS_EVT_DISCONNECT:
      Serial.printf("WebSocket client #%u disconnected\n", client->id());
      break;
    case WS_EVT_DATA:
      handleWebSocketMessage(arg, data, len);
      break;
    case WS_EVT_PONG:
    case WS_EVT_ERROR:
      break;
  }
}

void initWebSocket() {
  ws.onEvent(onEvent);
  server.addHandler(&ws);
}

void setup(){
  // Serial port for debugging purposes
  Serial.begin(115200);

  // Set GPIOs as outputs
  for (int i =0; i<NUM_OUTPUTS; i++){
    pinMode(outputGPIOs[i], OUTPUT);
  }
  initLittleFS();
  initWiFi();
  initWebSocket();

  // Route for root / web page
  server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
    request->send(LittleFS, "/index.html", "text/html",false);
  });

  server.serveStatic("/", LittleFS, "/");

  // Start ElegantOTA
  AsyncElegantOTA.begin(&server);
  
  // Start server
  server.begin();
}

void loop() {
  AsyncElegantOTA.loop();
  ws.cleanupClients();
}

View raw code

Insert your network credentials in the following variables and save the code.

const char* ssid = "REPLACE_WITH_YOUR_SSID";
const char* password = "REPLACE_WITH_YOUR_PASSWORD";

Update Firmware

Create a .bin file from this sketch as shown previously (this sketch includes the needed lines of code to provide OTA capabilities).

Go to the ESP8266 IP address followed by /update and upload the new firmware.

Next, we’ll see how to upload the files.

Update Filesystem

Under the project folder create a folder called data and paste the following HTML, CSS and JavaScript files (click on the links to download the files):

To find your project folder, you can go to Sketch > Show Sketch Folder.

This is where your data folder should be located and how it looks:

Data Folder Structure ESP8266 Async ElegantOTA Example

After this, with the ESP8266 disconnected from your computer (that’s the whole purpose of OTA), click on ESP8266 LittleFS Data Upload.

ESP8266 LittleFS Data Upload Arduino IDE

You’ll get an error because there isn’t any ESP8266 board connected to your computer – don’t worry.

Scroll up on the debugging window until you find the .mklittlefs.bin file location. That’s that file that you should upload (in our case the file is called Web_Server_OTA_ESP8266_Example_2.mklittlefs.bin.

Get LittleFS Bin File Path ESP8266

And this is the path where our file is located:

C:\Users\sarin\AppData\Local\Temp\arduino_build_375940\Web_server_OTA_ESP8266_Example_2.mklittlefs.bin

To access that file on my computer, I need to make hidden files visible (the AppData folder was not visible). Check if that’s also your case.

Arduino IDE ESP32 ESP8266 NodeMCU Board Add-on fix install

Once you reach the folder path, you want to get the file with .mklittlefs.bin extension.

LittleFS Bin File ESP8266

To make things easier you can copy that file to your project folder.

Now that we have a .bin file from the data folder, we can upload that file. Go to your ESP8266 IP address followed by /update. Make sure you have the Filesystem option selected.

Upload Files Filesystem ElegantOTA ESP8266

Then, select the file with the .mklittlefs.bin extension.

After successfully uploading, click the Back button. And go to the root (/) URL again. You should get access to the following web page that controls the ESP8266 outputs using Web Socket protocol.

Control Multiple ESP32 ESP8266 Outputs Websocket Web Server

To see the web server working, you can connect 4 LEDs to your ESP8266 on GPIOS: 2, 4, 12 and 14. You should be able to control those outputs from the web server.

If you need to update something on your project, you just need to go to your ESP8266 IP address followed by /update.

Congratulations! You’ve successfully uploaded files to the ESP8266 filesystem using ElegantOTA.

Wrapping Up

In this tutorial, you’ve learned how to add OTA capabilities to your Async Web Servers using the AsyncElegantOTA library. This library is simple to use and allows you to upload new firmware or files to the filesystem effortlessly using a web page. In our opinion, the AsyncElegantOTA library is one of the best options to handle OTA web updates.

We hope you’ve found this tutorial useful.

Learn more about the ESP8266 with our resources:

Thanks for reading.



Build Web Server projects with the ESP32 and ESP8266 boards to control outputs and monitor sensors remotely. Learn HTML, CSS, JavaScript and client-server communication protocols DOWNLOAD »

Build Web Server projects with the ESP32 and ESP8266 boards to control outputs and monitor sensors remotely. Learn HTML, CSS, JavaScript and client-server communication protocols DOWNLOAD »


Enjoyed this project? Stay updated by subscribing our newsletter!

42 thoughts on “ESP8266 NodeMCU OTA (Over-the-Air) Updates – AsyncElegantOTA using Arduino IDE”

  1. Hi Sara and Rui,
    well done this tutorial.
    The democode runs with a direct copy & paste
    But somehow I seem to be the one to discover adapting-problems:

    I avoid the command delay() even in the smallest democodes and use a non-blocking-delay based on millis().
    So I adapted your demo-code to this and got a continious reset each time the ESP8266 just connected to my router.

    The while-loop looks like this:
    while (WiFi.status() != WL_CONNECTED) {
    BlinkHeartBeatLED(OnBoard_LED,100);

    if ( TimePeriodIsOver(MyTestTimer,1000) ) {
    Serial.print(".");
    if (myCount++ > 15) {
    Serial.println();
    myCount = 0;
    }
    }

    }

    This while-loop runs through at maximum speed because the functions
    BlinkHeartBeatLED(OnBoard_LED,100); and if ( TimePeriodIsOver(MyTestTimer,1000) )

    are non-blocking
    My first suspicion was right:
    The hile-loop runs too fast for the system
    So I inserted a
    yield();
    inside the while-loop
    to enable processor-time for all the backround-things and now it works

    best regards Stefan

    Reply
  2. And I think I found another small rather cosmetic thing:

    On an nodeMCU-ESP8266-module the onboard-LED is connected to +Vcc.
    Which means the LED has inverted logic. The LED lights up if the IO-port is LOW
    This means inside the script-code
    function onMessage(event) {
    var state;
    if (event.data == “1”){
    state = “ON”;
    }
    else{
    state = “OFF”;
    }

    must be inverted to
    if (event.data == “0”){

    best regards Stefan

    Reply
  3. Hi Sara and Rui,
    I like this tutorial very much because it’s a nice feature to ad to IoT devices/nodes. With two small sketches on a ESP-01 it works perfect, but if I first do a normal flash of a user interface (including FS data) WITH ElegantOTA included and then try an OTA with one of the first small sketches I got during that OTA the error: [HTTP ERROR] Bad Request. Any idea what the problem could be? I can send you al resources if you want.
    Thanks and regards…

    Reply
    • Hi.

      Thanks for following our work.
      Did you have any problems with our examples? Or only with your examples?

      Some people also face the same issue, as you can see here: https://github.com/ayushsharma82/AsyncElegantOTA/issues.
      But, nobody gave a solution to that problem yet.
      All examples that I tried worked just fine, but I haven’t tried them with the ESP-01.

      Do you think it could be related to the library or to your code?

      Regards,
      Sara

      Reply
  4. Hello Sara,
    The two small sketches (ESP8266-esp01_STA_OTA1.ino end ESP8266-esp01_STA_OTA2.ino) for my first tests are the same as your small basic example test file. I only changes the response text in the HTML part to show its the old or new new update. That combination works perfect. In my new sketch I use not only firmware but also data (index.html and style.css). That sketch (with ElegantOTA) worked (standalone) perfect after programming via USB. Also the 192.178.168.34/update works fine, zo ElegantOTA is coming up. After I select a firmware update, select the .bin file the update stocks with the error. I readed the issue. ESP-01 can’t be the problem I think. Maybe it’s the old FS.h I use? I will try… Can I drop a .zip file with the resources somewhere? Send me an address by email.
    Thanks and regards…

    Reply
    • Hello Sara,
      Your example without LittleFS.h works perfect. I have not installed LittleFS.h because I don’t know what it does with my other applications. Because the update comes with a HTTP ERROR I think it has to do with the index.html in the data. I had also problems with the % sign in HTML and the processor function. Can you give me some direction?
      Thanks and regards…

      Reply
      • #include <FS.h>
        #include <ESP8266WiFi.h>
        #include <ESPAsyncTCP.h>
        #include <ESPAsyncWebServer.h>
        #include <AsyncElegantOTA.h>

        // Set number of sliders
        #define NMB_SLIDERS 5

        const char* ssid = “ALF4all”;
        const char* password = “02851346004201626459”;

        // Create AsyncWebServer object on port 80
        AsyncWebServer server(80);

        const char* INPUT_NAME = “slider”; // GET name
        const char* INPUT_VALUE = “value”; // GET value

        String sliderValue[NMB_SLIDERS] = {“-50″,”-25″,”0″,”+25″,”+50″};

        String processor(const String& var){
        if (var == “SLIDERVALUE_0”){
        return sliderValue[0];
        }
        else if (var == “SLIDERVALUE_1”){
        return sliderValue[1];
        }
        else if (var == “SLIDERVALUE_2”){
        return sliderValue[2];
        }
        else if (var == “SLIDERVALUE_3”){
        return sliderValue[3];
        }
        else if (var == “SLIDERVALUE_4”){
        return sliderValue[4];
        }

        return String();
        }

        void setup(){
        Serial.begin(115200);

        // Connect to Wi-Fi
        //——————
        WiFi.begin(ssid, password);
        while (WiFi.status() != WL_CONNECTED) {
        delay(1000);
        Serial.println(“Connecting to WiFi..”);
        };
        Serial.println(WiFi.localIP());

        // Init file system
        //——————
        if (!SPIFFS.begin()) {
        Serial.println(“An Error has occurred while mounting SPIFFS”);
        return;
        }

        // Route for root / web page
        //—————————
        server.on(“/”, HTTP_GET, [](AsyncWebServerRequest *request){
        request->send(SPIFFS, “/index.html”, String(), false, processor);
        });

        // Route to load style.css file
        //—————————
        server.on(“/style.css”, HTTP_GET, [](AsyncWebServerRequest *request){
        request->send(SPIFFS, “/style.css”, “text/css”);
        });

        // Send a GET request to /slider?value=
        //————————————————————
        server.on(“/control”, HTTP_GET, [] (AsyncWebServerRequest *request) {
        String inputName;
        String inputValue;

        // GET input slider and value on /control?slider= value=
        if (request->hasParam(INPUT_NAME) & request->hasParam(INPUT_VALUE)) {
        inputName = request->getParam(INPUT_NAME)->value();
        inputValue = request->getParam(INPUT_VALUE)->value();
        // update sliderValue
        sliderValue[inputName.toInt()] = inputValue;
        Serial.println(inputName + “,” + inputValue);
        } else {
        inputName = “none”;
        inputValue = “none”;
        }

        request->send(200, “text/plain”, “OK”);
        });

        // Start servers
        //—————
        AsyncElegantOTA.begin(&server); // Start ElegantOTA
        server.begin();
        };

        void loop() {
        AsyncElegantOTA.loop();
        };

        Reply
  5. I have found that OTA does not work when my ESP8266 is connect through a wireless extender and not directly to the wireless router.
    Has anyone else experienced this and if so, were you able to make it work through a wireless extender?

    Reply
    • If your ESP8266 can connect to the WiFi at all it should work.
      except for very weak signal which result in a low bandwith.

      Maybe: If the OTA-process uses a special portnumber and this portnumber is blocked it would not work. But I don’t know if this is the case at all
      Do you get a “WiFi connected” message at all?

      What I have tested is a ESP32 inside a WiFi-network with an AVM-Fritz!Box-router 7490 and a AVM-Fritz!-Repeater 310 which build a MESH.

      A mesh is a specialty developed by AVM on top of the usual WiFi-network which enables to seamless connect to that repeater/router with the strongest signal and using the same SSID.

      Anyway I had problems at the start too. So I added a serial.print-message to print the RSSI in the serial monitor. with RSSI below -70 db I could connect to the ESP32 but the OTA failed. For OTA the signal-strength had to be above -60 dB to make it work.

      After re-positioning the repeater to get a signal around – 55 dB the OTA works.

      best regards Stefan

      Reply
  6. After an update, the update page it does not work anymore, 192.168.1.x/update show just a small ”OK” in top left corner.

    Reply
  7. First, thanks for this and all of your other great tutorials. I subscribed to your book on home automation, but find these standalone tutorials are sometimes easier to follow.

    I have a follow up to one of the comments above, and the appropriate (javascript) update workflow.

    As Stefan mentioned, the onboard led uses ‘inverted’ logic, so I added code to the javascript to invert the update of the UI to match:

    // invert logic for gpio2 (onboard led, according to comments to article)
    if (output == "2") {
    state = (state === "1") ? "0" : "1";
    }

    I had an error on the first try, and then discovered that I need to execute the LittleFS Data Upload Tool (from the dropdown), and that generates a new sketch_mar19a.mklittlefs.bin which I then need to copy over to my sketch directory and upload it (knowing it is in my /var/folders/m1/…/arduino_build_649793 directory).

    Is this the right workflow to update the web javascript (or any of the other web files)?

    Is there a simpler, more direct way to do this?

    Thanks in advance,
    John

    Reply
    • Hi John.
      That’s the right workflow.
      However, you don’t need to copy the file to the sketch directory, but it is easier to find it when you want to select it to upload.
      If you want, you can simply navigate to the right directory at the time of selecting the file in the OTA upload page.
      Regards,
      Sara

      Reply
    • Hi Dragos,

      do all the users here have glas-spheres to look into in what you have done in DETAIL? No.

      How should others users know if you are a very smart and careful programmer or a “hurry-up-guy?”
      From your short post the estimation goes more into direction of you seem to be a “hurry-up-guy”

      Did you do these steps?
      To add OTA capabilities to your projects using the AsyncElegantOTA library, follow these steps:

      Install AsyncElegantOTA, ESPAsyncTCP and ESPAsyncWebServer libraries;
      Include AsyncElegantOTA library at the top of the Arduino sketch: #include <AsyncElegantOTA.h>;
      Add this line AsyncElegantOTA.begin(&server); before server.begin();
      Open your browser and go to http:///update, where is your ESP8266 IP address.

      You did not tell us

      Did you successfully run the demo-code?
      You did not tell us

      I’m no expert about html-coding. It might be that the scripts of the relay-code interfere with the example-3-scripts

      Did you try the opposite: Taking the working OTA-example after having tested it on YOUR hardware and added the relay-code to the OTA-example?

      all in all: @Rui & Sara: The tutorials you are creating are very good.
      This comment-function is really BAD for helping each other with code-problems
      because code can’t be posted as code-sections. I’m near to recommend to any user if you have a code-problem just leave a link to the main Arduino-forum here to discuss the code-problem at the arduinoforum.
      How about ADDING a code-discussion-forum to your website?
      This should’ nt be too hard.

      best regards Stefan

      Reply
  8. Hi Stefan, thank you for replay.
    I install the library, that is not a problem. I tested OTA example and it works fine. I added OTA lines to one of my existing project and works fine. I try to add OTA lines in another of my project, which is a sum of two project (relay control and 18b20 sensor web server) and after first update further update was no longer possible. I do not know what is wrong. I try then to test separated18b20 monitor project and relay control project. I discover that modified with OTA lines 18b20 code works fine but modified with OTA line relay control does not.

    Reply
  9. that ”OK” I get instead of update page, is from relay code from line:
    request->send(200, “text/plain”, “OK”);
    But I dont know why.

    Reply
  10. it seems that the problem is from the function that starts with:
    server.on (“/update”, HTTP_GET, [] (AsyncWebServerRequest * request {…..
    and ends with:
    request-> send (200, “text / plain”, “OK I am still here”);});
    somehow that “/update” coincides with the path to the update page

    I tried to replace “/ update” with “/relay” but although the update page works, the relays don’t work anymore

    Reply
  11. I change ”update” from relay code with ”relay”:
    I had to make 3 changes in lines:
    1.server.on(“/relay”, HTTP_GET, [] (AsyncWebServerRequest *request) {……..
    2.if(element.checked){ xhr.open(“GET”, “/relay?relay=”+element.id+”&state=1”, true); }
    3.else { xhr.open(“GET”, “/relay?relay=”+element.id+”&state=0”, true); }
    Now it works.

    Reply
  12. Compilation crashes at this point:
    #include <Arduino_JSON.h>
    Where can I get it? (Yes, I have tried to google it, but it finds just “arduinojson”.

    Reply
    • Hi Pavel.
      In the Arduino library manager search for “Arduino_Json” and install the Arduino_JSON library by Arduino version 0.1.0.
      Regards,
      Sara

      Reply
  13. Has anybody tried to use WiFiMulti and/or static IP? I was quite unsuccesfull. Without it, it’s unusable. Maybe for some tries.

    Reply
  14. Thank you for this tutorial. I have a mega2560+esp8266 and it really saves my time. I just have to put the server on the esp side, then I work on the mega and if I need to change some code on the esp, I update it via this server.

    Reply
  15. Thanks for this tutorial. How do I combine AsyncElegantOTA & Wifimanager for ESP8266?
    I tried to include both libraries in my code and got compile error.
    It seemed both libraries included the same ESP8266WIFI libraries.

    Anyway to let these two work together?

    Thanks!

    Reply
  16. Followed this well and I am able to upload the firmware but cannot upload the filesystems…I am getting an error on this
    LittleFS Upload failed!
    [LittleFS] data : C:\Users\Mutwiri\OneDrive\Arduino\Using Node MCU Wifi module\Temp_in_googlesheet_and_send_email ds18b20 chip OTA\Temp_in_googlesheet_and_send_email\data
    [LittleFS] size : 1000
    [LittleFS] page : 256
    [LittleFS] block : 8192
    /Base64.cpp
    /Base64.h
    /Gsender.cpp
    /NodeMcu_ESP8266_Gmail_Sender_by_FMT.h
    unable to open ‘/NodeMcu_ESP8266_Gmail_Sender_by_FMT.h.
    error adding file!

    Reply

Leave a Reply to Pavel Cancel reply

Download our Free eBooks and Resources

Get instant access to our FREE eBooks, Resources, and Exclusive Electronics Projects by entering your email address below.