ESP32 OTA (over-the air) Updates

Bei manchen Projekten kann der Zugang zum ESP32 mit USB aufwändig sein, z.B. wenn der ESP32 in einem Outdoor-Gehäuse oder einem Gerät an unzugänglichem Ort verbaut ist. Wird doch ein Update des Codes benötigt, steht ein umständlicher Ausbau an. In solchen Fällen hilft ein Code-Update per WLAN, over-the-air (OTA) genannt.

ElegantOTA

Die wohl einfachste Möglichkeit ESP32 OTA umzusetzen ist das Paket ElegantOTA, das ein fertiges Webinterface zum Upload neuer Projektdateien mitbringt. ElegantOTA gibt es als Lite Version frei zur Nutzung. In kommerziellen Projekten ist die Nutzung aber dahingehend eingeschränkt, dass der Source Code ebenfalls offen zugänglich gemacht wird. Für closed source Projekte kann aber auch eine Pro-Lizenz erworben werden.

Weitere Infos findest Du auf der Projektwebseite: https://docs.elegantota.pro/

Download und Installation der Library

Zunächst muss dazu die ElegantOTA library über den Library Manager der Aurduino IDE installiert werden.

Da ich in allen meinen Projekten den ESPAsyncWebserver als Webserver-Lösung einsetze, gehe ich hier auch nur auf die Nutzung von ElegantOTA zusammen mit dem ESPAsyncWebserver ein. Folgende Libraries müssen dazu auch installiert sein:

  • AsyncTCP
  • ESPAsyncWebserver

Konfiguration für ESPAsyncwebserver

Im Standard erwartet ElegantOTA nicht den ESPAsyncWebserver als Webserver-Paket. Im Libraries-Unterordner des Arduino-Projektordners findet sich nach der Installation der Library ein Ordner „ElegantOTA“ und darin ein Ordner „src“. Dort muss die ElegantOTA.h (in Windows als „C Header-Quelldatei“ angezeigt) mit einem geeigneten Editor (z.B. Windows Editor, Geany, etc.) editiert werden:

define ELEGANTOTA_USE_ASYNC_WEBSERVER muss von 0 auf 1 gesetzt werden. Danach speichern.

Einbinden in ein Projekt

Voraussetzung ist ein Projekt mit einer bereits funktionierenden WLAN-Verbindung und dem ESPAsyncWebserver, z.B. wie in Teil 5: Mit ESPAsyncWebServer das Handy als Steuerpult vorgestellt.

WICHTIG: Um Unbefugten nicht das Ändern des Codes auf dem ESP32 zu ermöglichen, darf natürlich nur ein WLAN genutzt werden, dass nur Befugten zugänglich ist, da die Update-Seite ohne Weiteres direkt zugänglich ist!

Die vorausgesetzt, reichen zur Einbindung wenige Schritte:

1. Einbinden der Library im Kopfteil des Sketches

#include <ElegantOTA.h>

Es sollten somit mindestens diese Libraries vorhanden sein:

#include <WiFi.h>
#include <AsyncTCP.h>
#include <ESPAsyncWebServer.h>
#include <ElegantOTA.h>

2. Hinzufügen der ElegantOTA Webseite

Vor dem Start des Webservers wird über den Aufruf

// setup OTA Update page
  ElegantOTA.begin(&server);

die Update-Seite hinzugefügt. Dies geschieht analog zu den server.on-Aufrufen für sonstige Webserver Seiten und Funktionen.

3. Aufruf von ElegantOTA im Loop

In die Loop-Funktion wird

ElegantOTA.loop();

hinzugefügt.

Damit ist die Minimalfunktionalität von ElegantOTA gegeben. Optional kann man noch die Start/Progress/End Callbacks und zugehörige Funktionen einfügen, wie in der offiziellen Dokumentation von ElegantOTA gezeigt: https://github.com/ayushsharma82/ElegantOTA/blob/master/examples/AsyncDemo/AsyncDemo.ino

Export des Projekts und Upload

Ein fertiges Beispiel

Das oben verlinkte Projekt (Teil 5: Mit ESPAsyncWebServer das Handy als Steuerpult ) würde mit der Erweiterung um ElegantOTA nun wie folgt aussehen. Ausbauen des Controllers aus der Eisenbahn und Updates am PC mit möglichwerweise gleichzeitigem Hantieren mit für den USB-Port ungesunden Versorgungsspannungen der Eisenbahn gehören damit der Vergangenheit an!

#include <WiFi.h>
#include <AsyncTCP.h>
#include <ESPAsyncWebServer.h>
#include <ElegantOTA.h>
AsyncWebServer server(80);
String sliderValue = "0";
const char* PARAM_INPUT = "value";

// WLAN Zugangsdaten
const char* ssid = "mhfra1";
const char* password = "HennricH08";

const int enPin = 27;
const int freq = 19531;
const int resolution = 8;
const int minPwm = 0;
const int fwdPin = 32;
const int bckPin = 33;
int lastSlider = 0;
int direction = 0;  // +1 for FWD, -1 for REV

const char index_html[] PROGMEM = R"stringliteral(
<!DOCTYPE HTML>
<html>
<head>
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>ESP32 Modellbahn Steuerung</title>
  <style>
    html {font-family: Arial; display: inline-block; text-align: center; color: lightgrey;}
    body {max-width: 1000px; margin:0px auto; padding-bottom: 25px; background-color: #222222;}
    section {display: block; width: 95%%; margin-top: 30px; padding: 15px; background: #2A2A2A}
    h2 {font-size: 2.3rem; color: lightgrey;}
    p {font-size: 1.9rem; color: lightgrey;}
     
    .slider { -webkit-appearance: none; margin: 14px; width: 800px; height: 25px; background: #555555; outline: none; -webkit-transition: .2s; transition: opacity .2s;}
    .slider::-webkit-slider-thumb {-webkit-appearance: none; appearance: none; width: 35px; height: 35px; background: #003249; cursor: pointer;}
    .slider::-moz-range-thumb { width: 35px; height: 35px; background: #003249; cursor: pointer; }
    .button  {background-color: #555555; border: none; color: white; padding: 16px 40px; text-decoration: none; font-size: 30px; margin: 2px; cursor: pointer;}
  </style>
</head>
<body>
  <h2>Modellbahn</h2>
  <section>
    <p><span id="textSliderValue">0</span></p>
    <p><input type="range" oninput="updateSliderPWM(this)" id="pwmSlider" min="-255" max="255" value="0" step="1" value="0" class="slider"></p>
    <p><button onclick="zeroSlider(this)" class="button buttonRed">STOP</button></p>
  </section>
 
<script>
function updateSliderPWM(element) {
  var sliderValue = document.getElementById("pwmSlider").value;
  document.getElementById("textSliderValue").innerHTML = sliderValue;
  console.log(sliderValue);
  var xhr = new XMLHttpRequest();
  xhr.open("GET", "/slider?value="+sliderValue, true);
  xhr.send();
}
 
function zeroSlider(element) {
  document.getElementById("pwmSlider").value = 0;
  updateSliderPWM(element);
}
</script>
</body>
</html>
)stringliteral";

void WifiSetup() {
  // Connect to Wi-Fi network with SSID and password
  Serial.print("Connecting Wifi to ");
  Serial.println(ssid);
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println(""); 
 
  // Print local IP address
  Serial.println("WiFi connected.");
  Serial.print("IP address: ");
  Serial.println(WiFi.localIP());
}

void WebserverSetup() {
  // Route for root / web page
  server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
    request->send_P(200, "text/html", index_html);
  });
 
  // Reply to GET request to <ESP_IP>/slider?value=<inputMessage>
  server.on("/slider", HTTP_GET, [] (AsyncWebServerRequest *request) {
    String inputMessageSlider;
    // GET input1 value on <ESP_IP>/slider?value=<inputMessage>
    if (request->hasParam(PARAM_INPUT)) {
      inputMessageSlider = request->getParam(PARAM_INPUT)->value();
      sliderValue = inputMessageSlider;
    }
    else {
      inputMessageSlider = "No valid message sent";
    }
    //Serial.println(inputMessageSlider);
    request->send(200, "text/plain", "OK");
  });
 
  // Start server
  server.begin();
  Serial.println((String)"Webserver started.");
}

void setCommonPwm() {
  int pwmOutput = 0;
  int slider = sliderValue.toInt();
  if (lastSlider != slider) {
    // update of PWM-Output required
    lastSlider = slider;

    // SET COMMON MOTOR SPEED AND DIRECTION
    if (slider > -25 && slider < 25) {  
      // Neutral range
      pwmOutput = 0;
      if (direction != 0) {
        direction = 0;
        Serial.println("Speed STOP");
        digitalWrite(fwdPin, LOW);
        digitalWrite(bckPin, LOW);
      }
    }
    else {
      if (slider > 0) {
        // Forward
        pwmOutput = map(slider, 0, 255, minPwm , 255);
        if (direction != 1) {
          direction = 1;
          Serial.println("Speed FWD");
          digitalWrite(fwdPin, HIGH);
          digitalWrite(bckPin, LOW);
        }
      }
      else {
        // Back      
        pwmOutput = map(slider*-1, 0, 255, minPwm , 255);
        if (direction != -1) {
        direction = -1;
          Serial.println("Speed REV");
          digitalWrite(fwdPin, LOW);
          digitalWrite(bckPin, HIGH);
        }
      }
    }
    Serial.println(pwmOutput);
    ledcWrite(enPin, pwmOutput);
  }
}

void setup() {
  // Start Serial
  Serial.begin(115200);
  
  // Set pin modes
  pinMode(fwdPin, OUTPUT);
  pinMode(bckPin, OUTPUT);

  ledcAttach(enPin, freq, resolution);

  WifiSetup();
  ElegantOTA.begin(&server);  // setup OTA Update page
  WebserverSetup();
}

void loop() {
  ElegantOTA.loop();
  setCommonPwm();
  delay(100);
}
WordPress Cookie Plugin von Real Cookie Banner