A fantastic feature of any WiFi-enabled microcontroller like ESP32 is the ability to update its firmware wirelessly. This is known as Over-The-Air (OTA) programming.

What is OTA programming in ESP32?

The OTA programming allows updating/uploading a new program to ESP32 using Wi-Fi instead of requiring the user to connect the ESP32 to a computer via USB to perform the update.

OTA functionality is extremely useful in case of no physical access to the ESP module. It helps reduce the amount of time spent for updating each ESP module at the time of maintenance.

One important feature of OTA is that one central location can send an update to multiple ESPs sharing same network.

The only disadvantage is that you have to add an extra code for OTA with every sketch you upload, so that you’re able to use OTA in the next update.

Ways To Implement OTA In ESP32

There are two ways to implement OTA functionality in ESP32.

  • Basic OTA – Over-the-air updates are sent through Arduino IDE.
  • Web Updater OTA – Over-the-air updates are sent through a web browser.

Each one has its own advantages. You can implement any one according to your project’s requirement.

Below tutorial covers Basic OTA implementation. If you want to learn more about web updater OTA, please check this tutorial out.

Tutorial For Programming ESP32 Over The Air OTA With Web Updater

ESP32 Web Updater Over The Air (OTA) Programming In Arduino IDE

A fantastic feature of any WiFi-enabled microcontroller like ESP32 is the ability to update its firmware wirelessly. This is known as Over-The-Air (OTA) programming. What...

3 Simple Steps To Use Basic OTA with ESP32

  1. Install Python 2.7.x series
    The first step is to install Python 2.7.x series in your computer.
  2. Upload Basic OTA Firmware Serially
    Upload the sketch containing OTA firmware serially. It’s a mandatory step, so that you’re able to do the next updates/uploads over-the-air.
  3. Upload New Sketch Over-The-Air
    Now, you can upload new sketches to the ESP32 from Arduino IDE over-the-air.

Step 1: Install Python 2.7.x series

In order to use OTA functionality, you need to install the Python 2.7.x version, if not already installed on your machine.

Go to Python’s official website and download 2.7.x (specific release) for Windows (MSI installer)

Download Python 2.7.x Series

Open the installer and follow the installation wizard.

Install Python 2.7.x Series On PC

In Customize Python 2.7.X section, make sure the last option Add python.exe to Path is enabled.

Enable Add Python.exe to Path While Python Installation

Step 2: Upload OTA Routine Serially

The factory image in ESP32 doesn’t have an OTA Upgrade capability. So, you need to load the OTA firmware on the ESP32 through serial interface first.

It’s a mandatory step to initially update the firmware, so that you’re able to do the next updates/uploads over-the-air.

The ESP32 add-on for the Arduino IDE comes with a OTA library & BasicOTA example. You can access it through File > Examples > ArduinoOTA > BasicOTA.

Open ESP32 BasicOTA Sketch In Arduino IDE

The following code should load. But, before you head for uploading the sketch, you need to make some changes to make it work for you. You need to modify the following two variables with your network credentials, so that ESP32 can establish a connection with existing network.

const char* ssid = "..........";
const char* password = "..........";

Once you are done, go ahead and upload the sketch.

#include <WiFi.h>
#include <ESPmDNS.h>
#include <WiFiUdp.h>
#include <ArduinoOTA.h>

const char* ssid = "..........";
const char* password = "..........";

void setup() {
  Serial.begin(115200);
  Serial.println("Booting");
  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, password);
  while (WiFi.waitForConnectResult() != WL_CONNECTED) {
    Serial.println("Connection Failed! Rebooting...");
    delay(5000);
    ESP.restart();
  }

  // Port defaults to 3232
  // ArduinoOTA.setPort(3232);

  // Hostname defaults to esp3232-[MAC]
  // ArduinoOTA.setHostname("myesp32");

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

  // Password can be set with it's md5 value as well
  // MD5(admin) = 21232f297a57a5a743894a0e4a801fc3
  // ArduinoOTA.setPasswordHash("21232f297a57a5a743894a0e4a801fc3");

  ArduinoOTA
    .onStart([]() {
      String type;
      if (ArduinoOTA.getCommand() == U_FLASH)
        type = "sketch";
      else // U_SPIFFS
        type = "filesystem";

      // NOTE: if updating SPIFFS this would be the place to unmount SPIFFS using SPIFFS.end()
      Serial.println("Start updating " + type);
    })
    .onEnd([]() {
      Serial.println("\nEnd");
    })
    .onProgress([](unsigned int progress, unsigned int total) {
      Serial.printf("Progress: %u%%\r", (progress / (total / 100)));
    })
    .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() {
  ArduinoOTA.handle();
}

Now, open the Serial Monitor at a baud rate of 115200. And press the EN button on ESP32. If everything is OK, it will output the dynamic IP address obtained from your router. Note it down.

Note Down IP Address Allotted to ESP32

Step 3: Upload New Sketch Over-The-Air

Now, let’s upload a new sketch over-the-air.

Remember! you need to add the code for OTA in every sketch you upload. Otherwise, you’ll loose OTA capability and will not be able to do next uploads over-the-air. So, it’s recommended to modify the above code to include your new code.

As an example we will include a simple Blink sketch in the Basic OTA code. Remember to modify the SSID and password variables with your network credentials.

Changes in the Basic OTA program are highlighted in Blue.

#include <WiFi.h>
#include <ESPmDNS.h>
#include <WiFiUdp.h>
#include <ArduinoOTA.h>

const char* ssid = "..........";
const char* password = "..........";

//variabls for blinking an LED with Millis
const int led = 2; // ESP32 Pin to which onboard LED is connected
unsigned long previousMillis = 0;  // will store last time LED was updated
const long interval = 1000;  // interval at which to blink (milliseconds)
int ledState = LOW;  // ledState used to set the LED
void setup() {

pinMode(led, OUTPUT);  
  Serial.begin(115200);
  Serial.println("Booting");
  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, password);
  while (WiFi.waitForConnectResult() != WL_CONNECTED) {
    Serial.println("Connection Failed! Rebooting...");
    delay(5000);
    ESP.restart();
  }

  // Port defaults to 3232
  // ArduinoOTA.setPort(3232);

  // Hostname defaults to esp3232-[MAC]
  // ArduinoOTA.setHostname("myesp32");

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

  // Password can be set with it's md5 value as well
  // MD5(admin) = 21232f297a57a5a743894a0e4a801fc3
  // ArduinoOTA.setPasswordHash("21232f297a57a5a743894a0e4a801fc3");

  ArduinoOTA
    .onStart([]() {
      String type;
      if (ArduinoOTA.getCommand() == U_FLASH)
        type = "sketch";
      else // U_SPIFFS
        type = "filesystem";

      // NOTE: if updating SPIFFS this would be the place to unmount SPIFFS using SPIFFS.end()
      Serial.println("Start updating " + type);
    })
    .onEnd([]() {
      Serial.println("\nEnd");
    })
    .onProgress([](unsigned int progress, unsigned int total) {
      Serial.printf("Progress: %u%%\r", (progress / (total / 100)));
    })
    .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() {
  ArduinoOTA.handle();  
  //loop to blink without delay
  unsigned long currentMillis = millis();
  if (currentMillis - previousMillis >= interval) {
  // save the last time you blinked the LED
  previousMillis = currentMillis;
  // if the LED is off turn it on and vice-versa:
  ledState = not(ledState);
  // set the LED with the ledState of the variable:
  digitalWrite(led,  ledState);
  }
}

NOTE

In above program, we have not used delay() for blinking an LED, because ESP32 pauses your program during the delay(). If next OTA request is generated while Arduino is paused waiting for the delay() to pass, your program will miss that request.

Once you copy above sketch to your Arduino IDE, go to Tools > Port option and you should see something like this: esp32-xxxxxx at your_esp_ip_address  If you can’t find it, you may need to restart your IDE.

Select OTA Port in Arduino IDE

Select the port and click Upload button. Within a few seconds, the new sketch will be uploaded. And you should see the on-board LED blinking.

ESP32 Onboard LED Blinking