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