Hausautomatisierung mit ESP32 und Websteuerung per Smartphone - AZ-Delivery

In diesem Projekt werden wir unser lokales WiFi-Netzwerk nutzen, um Elemente unseres Hauses über unser Mobiltelefon zu steuern, z. B. einen Rollladen, die Beleuchtung, den Ventilator in einem Zimmer, das Garagentor, oder die Markise in unserem Garten. Wir werden Komponenten für ein Modellhaus verwenden, aber die Grundprinzipien des Projekts können auch für die Umsetzung in realen Fällen entwickelt werden.

Wir werden mit der Entwicklungsplatine ESP-32 Dev Kit C V4 zwei Gleichstrommotoren steuern. Einer dieser Motoren wird über den integrierten Schaltkreis L293D angeschlossen. Damit kann die Drehrichtung verändert und so eine Jalousie hoch und runter bewegt werden. Der andere Motor dreht einen Ventilator. Außerdem steuern wir zwei SG90-Servomotoren für das Garagentor und den Antrieb einer Markise. Für die Beleuchtung eines Raums werden wir eine LED verwenden. Mit Hilfe eines Logiklevelkonverters wie dem TXS0108E wird die Spannung an die 5V Betriebsspannung der Komponenten angepasst. Wenn wir dieses Projekt in einem realen Fall umsetzen wollten, müssten wir eine Relaisplatine verwenden, da dort die Komponenten an das 230V-Netz angeschlossen sind. Um die Elemente zu steuern, werden wir ein Mobiltelefon oder ein Tablet verwenden, das sich über Wifi mit dem ESP32 verbindet, der als Webserver fungiert.

Verwendete Komponenten

1

ESP32 Dev Kit C V4 unverlötet

2

Logic Level Converter TXS0108E 8 Kanal

1

1-Relais 5V KY-019 Modul High-Level-Trigger

1

MB 102 Breadboard Kit

2

SG90 Micro Servo Motor 9G

1

LED Leuchtdioden Sortiment Kit

1

L293d 74hc595 Stepper Motor Treiber (Amazon)

1

DC3V-12V DC Getriebemotor

1

5V DC Motor (Amazon)

 

Mehrere der oben genannten Komponenten sind im Elektronik Super Starter Kit enthalten.

Verwendete Software und Dateien

Schaltplan

Schaltplan

Download des Schaltplans

Die Steuerung der Schaltung erfolgt über das ESP32-Entwicklungsboard. Dessen GPIO-Ports haben eine Ausgangsspannung von 3,3V. Wir müssen einen Logikkonverter wie den TXS0108E verwenden, um die 5V an die Komponenten zu liefern (in diesem Projekt verwende ich zwei 4-Kanal-Logikkonverter). Die LED, die die Beleuchtung simuliert, wird direkt an GPIO 19 angeschlossen, da dieser die richtige Spannung dafür liefert (3V). Um die Drehrichtung des Jalousiemotors zu steuern, müssen wir den integrierten Schaltkreis L293D verwenden, der über die GPIO-Ports 32 und 33 gesteuert wird. Der Lüftermotor dreht sich nur in eine Richtung. Er wird nach dem Wandler an ein Relais angeschlossen, um Impedanzprobleme an GPIO-Port 14 zu vermeiden. GPIO-Port 22 steuert den Garagentor-Servomotor, während GPIO-Port 23 den Markisen-Servomotor ansteuert. Die Signalleitungen der Servomotoren sind mit den Ausgängen des Wandlers verbunden.

Um die verschiedenen Elemente zu steuern, müssen wir uns über WLAN mit dem ESP32-Entwicklungsboard verbinden, das als Webserver fungieren wird. Für dieses Projekt wurden die notwendigen Dateien für die grafische Umgebung (HTML-Seite und CSS-Datei) vom Arduino-Sketch getrennt. Die beiden zusätzlichen Dateien müssen mit dem "SPI Flash File System" in das ESP32-Dateisystem hochgeladen werden. Dafür müssen Sie den "ESP32 Filesystem Uploader" in der Arduino IDE installiert haben.

Beschreibung der Programmcodes (INO, HTML und CSS)

Vom Handy oder Tablet aus wird im Internetbrowser die IP des ESP32-Boards aufgerufen. Die Adresse wird im seriellen Monitor ausgegeben. Sie wird sich für diesen ESP nie ändern. Nach dem Aufruf wird die Webseite mit Schaltflächen angezeigt, mit denen Sie die gewünschte Aktion für jede Komponente auswählen können. Es wird auch der Status oder die durchgeführte Aktion angezeigt.

Für die Erstellung der Webseite erstellen wir zunächst das Stylesheet style.css, die Datei mit den Gestaltungsmerkmalen der Texte und Schaltflächen.

Im Block mit dem html-Tag konfigurieren wir die Schriftart (Helvetica), den Textblock (inline-block), die Abstände (margin) oben und unten (0px), sowie die Seiten (automatisch). Außerdem wird der Text zentriert dargestellt (text-align).

html {
 font-family: Helvetica;
 display: inline-block;
 margin: 0px auto;
 text-align: center;
}

Im Block mit der Bezeichnung h1 werden die Werte für Überschriften mit der Textfarbe Schwarz, dem Abstand vom Rand, der mindestens 1 % der Höhe des Bildschirms betragen muss und der Größe des Textes von 20 Pixeln konfiguriert.

h1{
 color: Black;
 padding: 1vh;
 font-size: 20px;
}

Für Text, der mit dem p-Tag versehen wird, beträgt die Schriftgröße 1 rem, was 16 Pixeln in HTML entspricht.

p{
 font-size: 1rem;
}

Um die Werte für die Schaltflächen zu definieren, legen wir Werte für button und button2 fest. Zweites erbt alle unveränderten Werte von button. Die Schaltflächen, die sich innerhalb desselben Blocks befinden, werden auf derselben Zeile dargestellt. Die Farbe wird grün sein (wir definieren sie mit dem Hexadezimalwert #008CBA), der Rand der Schaltfläche wird nicht gezeichnet, die Abrundung der Ecken der Schaltfläche wird einen Radius von 4 Pixeln haben, die Farbe des Textes der Schaltflächen wird weiß sein, der Abstand vom oberen und unteren Rand des Textes zum Rand der Schaltfläche beträgt 6 Pixel und die Seiten 30 Pixel. Es gibt keinen grafischen Effekt, die Größe des Textes beträgt 15 Pixel, der freie Raum um die Schaltflächen beträgt 2 Pixel und mit einem cursor: pointer wird ein Zeiger angezeigt, wenn man die Maus über die Schaltfläche bewegt. Der einzige Wert, der sich für button2 von den vorherigen unterscheidet, ist die Farbe der Schaltfläche. Sie wird rot angezeigt und auch mit einem hexadezimalen Wert definiert.

.button {
 display: inline-block;
 background-color: #008CBA;
 border: none;
 border-radius: 4px;
 color: white;
 padding: 6px 30px;
 text-decoration: none;
 font-size: 15px;
 margin: 2px;
 cursor: pointer;
}
.button2 {
 background-color: #f44336;
}

Es folgt nun die Datei index.html, die die Webseite mit den Titeln und Schaltflächen enthält. Sie erscheinen, wenn man die IP-Adresse in den Browser eingibt. Mit <!DOCTYPE html> geben wir an, dass es sich um ein HTML-Dokument handelt. Der <head>-Tag enthält Informationen zur Darstellung der Seite in Browsern, der <title>-Tag ist der Text, der in der Browser-Registerkarte erscheint. Die Werte unter dem <meta>-Tag dienen der Darstellung der Webseite in den verschiedenen Browsern. Mit dem ersten <link>-Tag geben wir das Bild an, das in der Registerkarte der Webseite angezeigt werden soll, wir stellen kein Bild dar. Im zweiten <link>-Tag geben wir an, dass wir das Stylesheet style.css verwenden werden, um das Aussehen der Webseiten-Komponenten zu konfigurieren.

<!DOCTYPE html>
<html>

<head>
 <title>Domotic House Project</title>
 <meta name="viewport" content="width=devide-width, initial-scale=1">
 <link rel="icon" href="data:,">
 <link rel="stylesheet" type="text/css" href="style.css">
</head>

Der Hauptteil der Webseite muss in das Tag <body> … </body> geschrieben werden. Das erste Element ist der Titel als Überschrift innerhalb des <h1>-Tags. Die optischen Eigenschaften dieses Textes wurden im Stylesheet style.css konfiguriert. Als Nächstes folgt ein Text mit dem Tag <p>. Er enthält den Namen der Komponente, auf die die beiden Schaltflächen wirken, die in den folgenden beiden Zeilen definiert werden. Zwischen den Prozentzeichen (%shutter_up_state_web%) steht die aktuelle Positionsmarkierung der Komponente, die wir aus dem ESP32-Sketch senden. Mit dem Element <a href="/fr/upload"> geben wir den Link der ersten Schaltfläche an. Damit wird im Sketch des ESP32 registriert, dass dieser Button gedrückt wurde und wir eine Aktion ausführen müssen. Mit <button class="button">Upload</button> wird mit den Werten aus der Style.css Datei eine Schaltfläche dargestellt, deren Text „Upload“ lautet.  Die nächste Schaltfläche wird ähnlich wie die vorherige erzeugt mit der URL <a href="/fr/stop_upload"> , allerdings mit den Eigenschaften des Tags button2.  Er erbte alle Eigenschaften von „button“, bis auf die andere Farbe. Alle folgenden Schaltflächen werden ebenfalls auf diese Weise erzeugt. Es ändernd sich jeweils nur die ID und die Texte.

<body>
 <h1>DOMOTIC HOUSE PROJECT</h1>

 <p>Living room shutter: <Strong> %shutter_up_state_web%</strong></p>
 <p><a href="/upload"> <button class="button">Upload</button></a>
    <a href="/stop_upload"> <button class="button button2">Stop</button></a></p>

 <p>Living room shutter: <Strong> %shutter_down_state_web%</strong></p>
 <p><a href="/descent"> <button class="button">Descent</button></a>
    <a href="/stop_descent"> <button class="button button2">Stop</button></a></p>

 <p>Living room light: <Strong> %light_state_web%</strong></p>
 <p><a href="/on_light"> <button class="button">On</button></a>
    <a href="/off_light"> <button class="button button2">Off</button></a></p>

 <p>Awning: <Strong> %awning_state_web%</strong></p>
 <p><a href="/retracted"> <button class="button">Retracted</button></a>
    <a href="/extended"> <button class="button button2">Extended</button></a></p>

 <p>Garage door: <Strong> %garage_door_state_web%</strong></p>
 <p><a href="/open"> <button class="button">Open</button></a>
    <a href="/close"> <button class="button button2">Close</button></a></p>

 <p>Fan: <Strong> %fan_state_web%</strong></p>
 <p><a href="/fan_on"> <button class="button">On</button></a>
    <a href="/fan_off"> <button class="button button2">Off</button></a></p>

</body>

Die beiden vorherigen Dateien sollten in einem Ordner namens data im Projektordner gespeichert werden.

Wir kommen nun zum Sketch und beginnen mit den nötigen Bibliotheken SPIFFS.h, ESPAsyncWebServer.h und AsyncTCP.h. Sie können Sie auch über den Bibliotheksverwalter der Arduino IDE installieren. Die letzte Bibliothek ist notwendig, um ESPAsyncWebServer.h nutzen zu können. Wenn Sie diese Bibliotheken von Github heruntergeladen haben und manuell installieren, steht im Namen wahrscheinlich der Zusatz "-master". Der muss nach dem Entpacken aus dem Ordnernamen entfernt werden.

Nachdem wir die Bibliotheken installiert haben, können sie zu Beginn des Sketches inkludiert werden. WiFi.h ist notwendig für die Kommunikation über WiFi zwischen dem ESP32-Board und dem mobilen Gerät. ESPAsyncWebServer.h wird benötigt, damit der ESP32 als Webserver funktioniert. SPIFFS.h stellt die Datenstruktur zur Verfügung, um die HTML- und CSS-Dateien in den ESP32 laden zu können. Für die Servomotoren ist die Bibliothek <ESP32Servo.h> zuständig.

#include "WiFi.h"
#include "ESPAsyncWebServer.h"
#include "SPIFFS.h"
#include <ESP32Servo.h>

Um eine Verbindung mit dem lokalen WiFi-Netzwerk erstellen zu können, müssen der Namen des Netzwerks und das Passwort eingegeben werden.

const char* ssid = "replace with the name of your network";
const char* password = "replace with your network password";

Für die Servomotoren, die unsere Gartenmarkise und das Garagentor steuern, wird je ein Objekt aus der Servomotor-Bibliothek implementieren.

Servo awning_servo;
Servo garage_door_servo;

Als Nächstes müssen wir einige Konstanten für die GPIO-Ports erstellen, an die jede Komponente angeschlossen wird.

const int shutter_upload_gpio = 32;
const int shutter_descent_gpio = 33;
const int light_gpio = 19;
const int awning_servo_gpio = 23;
const int garage_door_servo_gpio = 22;
const int fan_relay_gpio = 14;

Dann folgen Variablen, in denen die Zustände der Komponenten als Zeichenketten gespeichert werden.

String shutter_upload_state;
String shutter_descent_state;
String light_state;
String awning_state;
String garage_door_state;
String fan_state; 

Für die Kommunikation mit dem Webserver wird aus der Bibliothek ESPAsyncWebServer.h ein Objekt namens server erstellt. Wir weisen ihm den Port 80 zu.

AsyncWebServer server(80);
Der nächste Teil ist die Funktion String processor(const String& var). Mit ihr wird der Wert der Positionswert der Komponenten an den Platzhalter im HTML-Code (%shutter_up_state_web%) gesendet. Die Funktion bekommt den Wert übergeben und gibt eine Zeichenkette mit dem Zustand der Schaltfläche zurück.
String processor(const String& var){

Es werden zwei verschachtelte Bedingungen verwendet. In der äußeren Bedingung wird der Platzhalter geprüft. In der inneren Bedingung, wird der Zustand des dazugehörigen GPIO-Pins abgefragt. Abhängig davon wird der passende Text in den String geschrieben und zurückgegeben.

if(var == "shutter_up_state_web"){
  if(digitalRead(shutter_upload_gpio)){
    shutter_upload_state = "Upload";
  }
  else{
    shutter_upload_state = "Stop";
  }
  Serial.print(shutter_upload_state);
  return shutter_upload_state;
}

Das funktioniert für den Wert der LED-Diode und der DC-Motoren. Die Steuerung der Servomotoren ist etwas anders, da der GPIO-Pin als PWM-Ausgang verwendet wird. Damit wird der Winkel der Servoachse eingestellt. Die Position der Achse wird überprüft, um die Nachricht der Positionsmarkierung zu ändern. Im Fall der Markise wird die Funktion awning_servo.read() verwendet. Damit wird der Wert ausgelesen und abgefragt, ob die aktuelle Position größer oder gleich 80 Grad ist. Trifft das zu, wird die Markise als „ausgefahren“ angezeigt. Ist der Wert kleiner als 80, ist die Markise eingefahren, was auch entsprechend angezeigt wird. Genauso funktioniert auch der Status für die Servoposition des Garagentors.

if(var == "awning_state_web"){ 
  if(awning_servo.read() >= 80){
  awning_state = "Extended";
         }
         else{
               awning_state = "Retracted";
         }
         Serial.print(awning_state);
         return awning_state;
      }

Für alle anderen Platzhalter im HTML-Code ist die Verfahrensweise gleich. Nur der Name, der zu prüfende GPIO-Port und die Meldungen, die in den Platzhaltern angezeigt werden, ändern sich.

Fahren wir fort mit der setup() Funktion. Zuerst wird die serielle Schnittstelle zur Anzeige auf dem seriellen Monitor initialisiert. Außerdem werden die GPIO-Pins als Ausgänge konfiguriert.

Serial.begin(115200);

pinMode(shutter_upload_gpio, OUTPUT);
pinMode(shutter_descent_gpio, OUTPUT);
      
pinMode(light_gpio, OUTPUT);
pinMode(awning_servo_gpio, OUTPUT);
pinMode(garage_door_servo_gpio, OUTPUT);
pinMode(fan_relay_gpio, OUTPUT)

Danach werden die Servos für die Markise und das Garagentor initialisiert. Es werden die GPIO-Pins und die Ausgangsposition festgelegt.

awning_servo.attach(awning_servo_gpio);
awning_servo.write(0);
garage_door_servo.attach(garage_door_servo_gpio);
garage_door_servo.write(0);

Es wird dann das SPIFFS-System zum Laden von Dateien in den ESP32 initialisiert, um ihn als Webserver nutzen zu können. Da das Dateisystem notwendig für das Programm ist, wird abgefragt, ob es erstellt werden konnte.

if(!SPIFFS.begin(true)){
  Serial.println("An Error has occurred while mounting SPIFFS");
  return;
}

Für die drahtlose Kommunikation wird das WiFi-Modul initialisiert. Die zuvor erstellten Konstanten mit der SSID und dem Passwort des lokalen Netzwerkes werden als Parameter übergeben. Im seriellen Monitor wird der Verbindungsstatus ausgegeben. Wurde die Verbindung hergestellt, wird die IP-Adresse ausgegeben. Das ist die Adresse, die im Internetbrowsers des Smartphones oder Tablets eingegeben werden muss, um die Webseite mit den Schaltflächen anzuzeigen.

WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
  delay(1000);
  Serial.println("Connecting to WiFi..");
}

Serial.println(WiFi.localIP());

Mit dem zuvor implementierten server-Objekt rufen wir den Event-Handler server.on der Bibliothek ESPAsyncWebServer.h auf und stellen zwei Anfragen. Die erste Anfrage geht an die Server-Root ("/" ). Mit request->send werden die Informationen zur Webseite "/index.html" gesendet. Außerdem findet ein Aufruf der processor-Funktion statt, um die Platzhalter in der Webseite durch ihre aktuellen Werte zu ersetzen.

server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
request->send(SPIFFS, "/index.html", String(), false, processor);
});

Die zweite Anfrage ist der Aufruf der CSS-Datei „/style.css", um die Webseite mit der Konfiguration der Texte und Schaltflächen gemäß aus der style.css-Datei korrekt anzuzeigen.

server.on("/style.css", HTTP_GET, [](AsyncWebServerRequest *request){
request->send(SPIFFS, "/style.css", "text/css");
});

Als Nächstes müssen wir definieren, was der ESP32 ausführen soll, wenn wir eine der Schaltflächen auf der Webseite betätigen. Der Webserver wartet darauf, einen Befehl zu erhalten. Die Taste, die in dem folgenden Beispiel gedrückt wird, fährt die Rollläden hoch. Sie hat die Kennung "/upload". Wenn diese Taste betätigt wird, holt der Webserver den Befehl ab (HTTP_GET) und führt aus, was zwischen den Tasten geschrieben steht. In diesem Fall wird der GPIO-Pin mit "shutter_upload_gpio" auf HIGH gesetzt. Dann wird der Aufruf an die zu ändernde Datei "index. html" in dem SPIFFS-Dateisystem als String gesendet. Die Funktion processor ist für die Änderungen zuständig. Da der GPIO auf HIGH gesetzt wird, ist der Zustand dadurch auf "Upload" gesetzt. Der Platzhalter dafür ist %shutter_up_state_web%, der damit ersetzt wird. Alle anderen Aufrufe arbeiten auf die gleiche Weise.

server.on("/upload", HTTP_GET, [](AsyncWebServerRequest *request){
            digitalWrite(shutter_upload_gpio, HIGH); 
            request->send(SPIFFS, "/index.html", String(), false, processor);
});

Wenn wir mit den Tasten die Servomotoren der Markise und des Garagentors steuern wollen, wird es nicht reichen, den GPIO-Pin in den Zustand HIGH oder LOW zu versetzen. In diesen Befehlen müssen wir, wie oben bereits erwähnt, die Position in Grad angeben (0 oder 90 Grad). Dies wird mit der Methode awning_servo.write(0) bzw. awning_servo.write(90) durchgeführt.

server.on ("/extended", HTTP_GET, [](AsyncWebServerRequest *request){
            awning_servo.write(90); 
            request->send(SPIFFS, "/index.html", String(), false, processor)  

Die letzte Zeile der setup()-Funktion ist server.begin() und führt die Initialisierung des ESP32-Webservers durch.

server.begin();

Die loop()-Funktion des ESP32 ist leer. Es wird keine kontinuierliche Aktion ausgeführt, sondern im „Hintergrund“ auf einen Befehl gewartet.

Der Sketch kann nun auf den ESP32 geladen werden.

Jetzt müssen wir die Dateien index.html und style.css in das Dateisystem des ESP32 laden. Um das zu tun, klicken Sie auf Tools (Werkzeuge) und im Dropdown-Menü auf ESP32 Sketch Data Upload.

Wie Sie im folgenden Bild sehen können, wird nach dem Upload der beiden Dateien auch ein Reset des ESP32-Moduls durchgeführt.

Anschließend öffnen wir den seriellen Monitor mit 115200 Baud und drücken den ENABLE-Taster unseres ESP32. Es wird uns im seriellen Monitor die IP-Adresse unseres Mikrocontrollers angezeigt. Diese Adresse rufen wir in einem Browser unseres Smartphones oder Tablets auf.

Ich hoffe, dass Sie dieses Projekt interessant und nützlich finden. Ich bedanke mich für Ihr Interesse und freue mich auf Ihre Kommentare mit Anregungen und Vorschlägen.

 

Sollte das Video nicht angezeigt werden, überprüfen Sie die Cookie-Einstellungen Ihres Browsers.

Esp-32Für arduinoProjekte für anfängerSmart home

9 commentaires

Andi

Andi

Ich glaub hier ging es eher um die Veranschaulichung, wie man etwas am Handy über einen Webserver steuern kann ;-)

Andreas Wolter

Andreas Wolter

@PK: Wir haben den Schaltplan ersetzt. Danke für den Hinweis.

Grüße,
Andreas Wolter.
AZ-Delivery Blog
i.A. Miguel Torres Gordo

Miguel Torres

Miguel Torres

Hello Peter,

I tried to reproduce the error and it didn’t appear, I tried with two ESP32 and it loaded both files fine in both of them.

When you loaded the sketch, did you get the reset message: "Hard resetting via RTS pin…, before trying to load the html and css files?
Try resetting the ESP with the physical switch of the module after loading the sketch and before loading the html and css files.

I have searched the internet and found this address https://github.com/me-no-dev/arduino-esp32fs-plugin
Download the file from the “releases page” and follow the installation instructions.

Best regards

Miguel Torres Gordo

Miguel Torres

Miguel Torres

Hello Martin,

I haven’t explored that communication protocol, but I’m writing it down to do some research and see about the possibility of realizing a project.

Best regards

Miguel Torres Gordo

Miguel Torres

Miguel Torres

Hello Samba,

I agree that all projects can always be improved, in this project, in addition to drive some components from a mobile device, also explains how to use an ESP as a web server and what is the process of implementing it, so this project can also serve as an initial idea to improve it by adding new components and features.

Best regards

Miguel Torres Gordo

Peter

Peter

Ich bekomme beim Hochladen der index un der css folgenden Fehler: SPIFFS Not Supported on esp32

Martin

Martin

Vielen Dank für das coole Projekt. Wenn ich es richtig verstehe, gibt es einen MC der über das Web alle Aktuatoren steuert. Im schlimmsten Fall müssten dann durch das ganze Haus ja dahin Kabel gelegt werden. Gibt es auch einen Lösungsansatz , wo über einen Master (der über die Web Seite gesteuert wird) die fern stehenden Aktuatoren OTA gesteuert werden (also mit mehreren Slaves)???

PK

PK

Hallo, im Schaltplan fehlt die positive Spannungsversorgung am ESP ….
Gruß PK

Samba

Samba

Moin moin,

ich muss ja sagen, ihr bekommt mich durch die Newsletter unheimlich häufig zu lesen der Blogeinträge, oder auch zum kaufen von einigen Komponenten :-)

Allerdings ist es für mich unverständlich in einem “Hausautomatisierung” Projekt keinerlei Automatisierung vorzusehen. Es wäre doch viel besser gewesen die Rollladen per Lichtsensor zu steuern und den Ventilator per Temperatur und Anwesenheitssensor.

Hier wird einem ja nur der Tastendruck erspart und es gibt dabei nicht einmal ein Feedback, auf welchem Niveau die Rollladen stehen" Nach dem ich hier viel im Blog lese, weiss ich auf jeden Fall das ihr es besser könnt ;-)

Laisser un commentaire

Tous les commentaires sont modérés avant d'être publiés

Articles de blog recommandés

  1. ESP32 jetzt über den Boardverwalter installieren - AZ-Delivery
  2. Internet-Radio mit dem ESP32 - UPDATE - AZ-Delivery
  3. Arduino IDE - Programmieren für Einsteiger - Teil 1 - AZ-Delivery
  4. ESP32 - das Multitalent - AZ-Delivery