Tags: ,

,

A very practical feature in the IoT field is the ability to program a connected microcontroller , such as an ESP8266, via Wifi. This technique is called Over-The-Air (OTA) Programming.

Material

  • NodeMCU ESP8266
  • USB A Male Cable
  • Computer
  • WiFi network

Principle of operation

In principle, the NodeMCU ESP8266 is programmed via the USB port. The computer communicates the program to the microcontroller. When two devices are connected to the same Wifi network, they can communicate together. So you can upload a code through the Wifi network without having to connect to each microcontroller.

It is also possible to upload a code to the NodeMCU when it is configured as a Wi-Fi Access Point. In this case, the computer used for programming must be connected to the NodeMCU network.

Add the ArduinoOTA firmware

The ArduinoOTA library is available when you install the ESP8266 manager. To be able to set up the OTA programming, you must install the firmware. For this, you can use the BasicOTA.ino example available in the Arduino IDE.

Be sure to replace ssid and password with the credentials of your WiFi network.

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

#ifndef STASSID
#define STASSID "your-ssid"
#define STAPSK  "your-password"
#endif

const char* ssid = STASSID;
const char* password = STAPSK;

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 8266
  // ArduinoOTA.setPort(8266);

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

  // 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_FS
      type = "filesystem";
    }

    // NOTE: if updating FS this would be the place to unmount FS using FS.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() {
  ArduinoOTA.handle();
}

ota-programming-esp8266-basicota-firmware-result Programming an ESP8266 via WiFi with the Arduino IDE (OTA)

Be sure to note the local IP address of the device (here: 192.168.1.78).

Once this code is uploaded via serial, you will be able to upload a code via Wifi. Press the RST or EN button on your ESP and restart the Arduino IDE.

Edit your program before uploading it

We place the initialization code in the initOTA() function. It will then be easy to copy it for other projects. And we will add a display on the serial port to check that the code has been modified.

WARNING: The code related to the initialization of ArduinoOTA must be present in all the codes that you upload or you will lose the possibility to do air transmission.

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

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

unsigned long previousMillis;

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();
  }

  initOTA();

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

void loop() {
  ArduinoOTA.handle();

  if (millis() - previousMillis >= 500) {
    previousMillis = millis();
    Serial.println(F("Code has been update"));
  }
}

void initOTA() {
  // Port defaults to 8266
  // ArduinoOTA.setPort(8266);

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

  // 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_FS
      type = "filesystem";
    }

    // NOTE: if updating FS this would be the place to unmount FS using FS.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();
}

Once you have modified your code, under “Tool> Port”, select your card in the network ports. (if the network port is not displayed, see next chapter below)

ota-programming-esp8266-network-port Programming an ESP8266 via WiFi with the Arduino IDE (OTA)

You can then upload your code as you would with serial communication.

ota-programming-arduino-esp8266-uploading Programming an ESP8266 via WiFi with the Arduino IDE (OTA)

Select the serial port again, then open the serial monitor to verify that the code has been changed.

ota-programming-result Programming an ESP8266 via WiFi with the Arduino IDE (OTA)

If the Network Port is not displayed in the Arduino IDE

If the network port is not present in the options of the Arduino IDE, you will have to do a little manipulation. You must go to the Network and Sharing Center

windows-network-center Programming an ESP8266 via WiFi with the Arduino IDE (OTA)

Then go to “Modify map settings”.

Right click on your network and go to properties

You can then uncheck the option “Internet Protocol version 6 (TCP

windows-network-adapter-disable-ipv6 Programming an ESP8266 via WiFi with the Arduino IDE (OTA)

If you go back to the Arduino IDE, in “Tool > Port” you should see the network port option.

You can check the IPv6 protocol again once your network port is recognized.

Bonus: Serial message display in OTA

The possibility of uploading a program by Wifi is very appreciable, but it is worth noting that we lose the ability to debug with the serial monitor. It is possible to create a Web interface on which you can display information coming from the NodeMCU ESP8266.

We will see here the use of the RemoteDebug library which will allow us to connect in telnet to the microcontroller and to recover the sent messages.

  • Start by installing the library by downloading the ZIP or via the library manager.
arduino-ide-ota-prorgamming-remotedebug Programming an ESP8266 via WiFi with the Arduino IDE (OTA)
  • Create a RemoteDebug Debug object; after including the
  • Initialize the debug with Debug.begin(“ESP8266”);
  • Then replace all Serial.print with Debug.print
#include <ESP8266WiFi.h>
#include <ESP8266mDNS.h>
#include <WiFiUdp.h>
#include <ArduinoOTA.h>
#include <RemoteDebug.h>

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

RemoteDebug Debug;

unsigned long previousMillis;

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();
  }

  // init remote debug
  Debug.begin("ESP8266"); 
  
  initOTA();

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

void loop() {
  ArduinoOTA.handle();
  Debug.handle();
  
  if (millis() - previousMillis >= 500) {
    previousMillis = millis();
    Debug.println(F("Code has been update"));
  }
}

void initOTA() {
  // Port defaults to 8266
  // ArduinoOTA.setPort(8266);

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

  // 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_FS
      type = "filesystem";
    }

    // NOTE: if updating FS this would be the place to unmount FS using FS.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();
}

To view the messages sent via RemoteDebug, we will use the PuTTy software that we have seen several times already. For this application, we will configure PuTTy in telnet using the IP address retrieved from the serial monitor.

putty-telnet Programming an ESP8266 via WiFi with the Arduino IDE (OTA)
putty-telnet-ota-esp8266-result Programming an ESP8266 via WiFi with the Arduino IDE (OTA)

Sources