ESP32 mit Arduino Code programmieren

Über den Beitrag

Nachdem ich in meinem vorletzten Beitrag über die I2C Schnittstellen des ESP32 berichtet habe, möchte ich hier nun einen Gesamtüberblick über diesen leistungsstarken Mikrocontroller geben.

Alles, worüber ich hier berichte, ist im Grunde schon mehr als einmal von irgendwem zuvor beschrieben worden. Warum also noch ein Artikel zu diesem Thema? Es ist der Versuch, die grundlegenden Funktionen, Besonderheiten und Stolpersteine in einem einzigen Beitrag zusammenzufassen. Insbesondere richte ich mich dabei an Arduinonutzer, die in den ESP32 einsteigen wollen. Aber vielleicht findet auch der ESP32-Erfahrene hier die eine oder andere interessante Information.

Ich beziehe mich bei meinen Ausführungen und Beispielen hauptsächlich auf den weitverbreiteten ESP32-WROOM-32, bzw. die auf ihm basierten Boards. Das Meiste ist aber auf andere Vertreter wie den ESP32-PICO übertragbar.

Der Beitrag ist recht lang geworden – hier könnt ihr zu den Abschnitten springen:

ESP32 Boards

Es gibt die verschiedensten ESP32 Boards. Die meisten basieren auf dem ESP32-WROOM-32.  Sehr beliebt sind die Entwicklungsboards wie das oben links abgebildete Modell. Es hat allerdings eine für Breadboards nicht ideale Breite, denn ihr habt nur auf einer Seite Platz für Steckverbindungen (s.u.). Legt ihr zwei Breadboards nebeneinander, sind die Module nicht breit genug, um die +/- Leisten zu überbrücken. Das mag ein Luxusproblem sein, aber nervig ist es schon. Als Lösung empfehle ich Mehrfach-Breadboards, die nur eine +/- Leiste zwischen den einzelnen Boards haben.

Nicht alle ESP32 Module passen auf alle Breadboards
Nicht alle ESP32 Module harmonieren mit allen Breadboards

Alternativ könnt ihr auch zum „Arduino UNO-like“ D1 R32 Board greifen. Oder ihr nehmt einen ESP32 Pico, denn der ist etwas schlanker.

Eigenschaften des ESP32-WROOM-32

Die Leistungsfähigkeit der ESP32 Boards lässt AVR basierte Arduinos wie den UNO schon recht alt aussehen. Die vier- bis zwölffach höhere Taktrate und ein im Vergleich riesiger Flashspeicher sind eine andere Dimension. Ein klarer Vorteil für die Arduino (AVR) Boards ist allerdings, dass man die zugrundeliegenden Mikrocontroller sehr einfach auch „standalone“ betreiben kann. Damit lassen sich platz- und energiesparende Projekte realisieren. Einen blanken ESP32 zu verdrahten, ist hingegen nicht unbedingt jedermanns Sache.

Hier nun die wichtigsten Daten des ESP32-WROOM-32 im Überblick:

  • Betriebsspannung: 3.3 Volt
  • Stromverbrauch: 1µA (Hibernation) bis max ca. 240 mA
    • Strombedarf ist abhängig vom Modus und den aktivierten Komponenten
    • im Normalbetrieb ohne WiFi und Bluetooth: ca. 50-70 mA
  • Taktrate bis 240 MHz
  • 520 kByte interner SRAM (für Daten und Befehle)
  • Externer Flash-Speicher bis zu 16 MByte
    • die meisten Module haben 4 MByte Speicher, davon ca. 1 MByte „zur freien Verfügung“
  • 34 Ein-/Ausgänge:
    • nutzbar: 22 GPIOs, 4 GPIs (reine Eingänge)
    • einige mit gewissen Einschränkungen
  • 2 I2C Schnittstellen
  • 2 nutzbare SPI Schnittstellen
  • 3 UART Schnittstellen
  • Echtzeituhr
  • 10 Touchsensoren
  • Integrierter Hallsensor
  • 16 PWM Kanäle
  • 2 „echte“ analoge Ausgänge
  • Bis zu 18 analoge Eingänge
  • WiFi 802.11 b/g/n 2.4 GHz
  • Bluetooth 4.2 / BLE (Bluetooth Low Energy)

Den ESP32 in die Arduino IDE integrieren

Wie ihr die Arduino IDE ESP32-fähig macht, ist schon unendlich oft beschrieben worden. Ich halte mich deswegen kurz.

  1. Geht in der Arduino IDE auf Datei -> Voreinstellungen.
  2. Dort klickt ihr auf das kleine Symbol hinter „Zusätzliche Boardverwalter URLs“.
  3. Tragt „https://dl.espressif.com/dl/package_esp32_index.json“ als separate Zeile ein (ohne Anführungszeichen).
  4. Geht auf Werkzeuge -> Board -> Boardverwalter.
  5. Sucht nach „esp32“ und installiert „esp32 by Espressif Systems“.
  6. Ggf. müsst ihr die Arduino IDE neu starten.

Nach der Installation findet ihr mehrere Dutzend ESP32 Boards zur Auswahl. Da müsst ihr schauen welches passt. Weitverbreitet ist das „ESP32 Dev Module“.

Pinout des ESP32 Development Boards

Es gibt verschiedene Ausführungen ESP32-WROOM-32 basierter Boards. Das bedeutet, dass die Pins bei eurem Board vielleicht anders angeordnet sind und ggf. auch Pins fehlen. Was aber immer übereinstimmt, ist die Mehrfachfunktion der Pins. Das heißt, dass beispielsweise GPIO4 immer auch HSPI_HD, ADC2_CH0, TOUCH_0 und RTC_IO 10 ist.

Pins des ESP32 Development Boards
Pins des ESP32 Development Boards

„Verbotene“ / eingeschränkt nutzbare Pins

Einige Pins sind nur eingeschränkt benutzbar, andere solltet ihr grundsätzlich unverbunden lassen. Zu letzterer Gruppe gehören die Pins GPIO6 bis GPIO11. Sie werden intern für die SPI Kommunikation mit dem Flash-Speicher benutzt. Ich habe sie deshalb grau gekennzeichnet. Ignoriert sie am besten.

Einige Pins senden beim Booten des ESP32 PWM Signale oder gehen kurz in den HIGH Status. Je nachdem, was ihr an die Pins hängt, kann das stören, es muss aber nicht. Dabei handelt es sich um die Pins GPIO0, GPIO1, GPIO3, GPIO5, GPIO14 und GPIO15.

GPIO0, GPIO2, GPIO4, GPIO5, GPIO12 und GPIO15 sind sogenannte „Strapping Pins“, die den ESP32 über ihren Zustand beim Booten in den Bootloader- oder Flashmodus bringen. Wenn ihr dort beim Booten etwas dranhängt, was die Pins in den HIGH- oder LOW-Zustand zieht, kann das zu unerwartetem Verhalten führen. Ihr kennt das vielleicht vom GPIO0 Pin des ESP8266 ESP-01.

Die ADC2_CHx Pins können nicht als analoge Eingänge benutzt werden, wenn Wi-Fi aktiv ist.

Digitale Ein-/Ausgänge des ESP32

Die Nutzung der GPIOs als digitale Ein- und Ausgänge funktioniert im Wesentlichen wie beim Arduino mit den Funktionen pinMode(), digitalWrite() und digitalRead(). Der Arduino (AVR) Umsteiger muss sich dabei vor allem an die reduzierte Spannung von 3.3 Volt gewöhnen. Der maximale Strom sollte 12 mA nicht überschreiten.

Einen wichtigen Unterschied zum Arduino gibt es beim Betrieb der GPIOs als Eingang. Während der Arduino bei Anwendung von pinMode(pin, INPUT) automatisch einen Pull-Down Widerstand zuschaltet, muss das beim ESP32 über INPUT_PULLDOWN geschehen. Wählt Ihr das einfache INPUT, dann ist der Pin-Level undefiniert.

Die Pins GPIO34-39 sind reine Eingänge, weshalb die Bezeichnung GPI passender ist. Außerdem steht an diesen Pins kein interner Pull-Up oder Pull-Down Widerstand zur Verfügung.

Analoge Ein-/Ausgänge des ESP32

Analoge Eingänge

Der ESP32 besitzt zwei A/D-Wandler mit einer Auflösung von 12 Bit. Die ADC1- und die ADC2-Eingänge teilen sich also jeweils einen A/D-Wandler. Das Auslesen erfolgt wie gewohnt mittels analogRead(pin). Die Spannung U ist dann (theoretisch):

U\;\text{[V]} = \frac{3.3 \cdot \text{analogRead()}}{4095}

So hervorragend die Leistung des ESP32 sonst ist – die A/D-Wandler sind es definitv nicht:

  1. Die Streuung der Messwerte ist sehr hoch.
  2. Noch schlimmer: das Verhalten ist nicht linear.

Die Streuung bekommt man in den Griff. Espressif empfiehlt (hier) einen 0.1 F Kondensator an den Eingang zu hängen und Messwerte zu mitteln. Ohne Kondensator habe ich 500 – 1000 Messwerte mitteln müssen, um die Schwankung auf unter 0.01 Volt zu bringen.

Problematischer ist die Nicht-Linearität. Ich habe das mal nachgemessen und mein Ergebnis deckt sich mit den Angaben, die ich an anderen Stellen im Netz gefunden habe:

Messwerte des ESP32 A/D-Wandlers vs. tatsächliche Spannung
Messwerte des ESP32 A/D-Wandlers vs. tatsächliche Spannung

Einige empfehlen den Einsatz von „Look-Up“ Tabellen, um den Fehler auszugleichen (z.B. hier auf GitHub), andere empfehlen Ausgleichspolynome (z.B. hier). Wenn ihr genaue Messwerte braucht, rate ich zu einem vernünftigen externen A/D Wandler wie den ADS1115.

Analoge Ausgänge

Kein analogWrite?

Tatsächlich – kein analogWrite()! Die Funktion ist nicht implementiert. Das ist aber nicht weiter schlimm, denn die analogWrite() Funktion liefert bei den (AVR) Arduinos lediglich ein PWM (Pulsweitenmodulation) Signal. Und das lässt sich mittels der ESP32 PWM Funktionen problemlos nachstellen. Ich komme im Abschnitt PWM darauf zurück.

Die dacWrite Funktion

Anstelle analogWrite() gibt es für den ESP32 die dacWrite() Funktion. Sie liefert ein echtes analoges Signal zwischen 0 und 3.3 Volt, das an den zwei DAC Pins ausgegeben werden kann. So wird die Funktion aufgerufen:

dacWrite(pin, value) mit pin = 25 oder 26 und value = 0, 1, 2 …. 255.

Theoretisch ist die Spannung U:

U\;\text{[V]}=\frac{3.3}{255}\cdot value

Praktisch sieht es so aus:

Theoretische und tatsächliche Spannung an den DAC Pins des ESP32
Theoretische und tatsächliche Spannung an den DAC Pins

Der Minimalwert lag bei meinen Messungen bei 0.086 Volt, der Maximalwert bei 3.18 Volt. Immerhin ist die Steigung weitestgehend linear, sodass man hier mit einer Kalibriergeraden arbeiten kann.

Pulsweitenmodulation (PWM)

Eine detaillierte Einführung in Pulsweitenmodulation (PWM) würde den Rahmen dieses Beitrages sprengen. Also nur so viel: PWM wird unter anderem für die Ansteuerung von Servomotoren oder zum Dimmen von LEDs verwendet. Ein PWM Signal ist ein Rechtecksignal mit bestimmter Frequenz (= 1/Periode) und einem bestimmten Tastgrad (Duty Cycle). Hier ein Beispiel mit einem Tastgrad von 25 %:

PWM-Signal mit 25% Tastgrad (Duty Cycle)

Der ESP32 hat 16 PWM Kanäle, die den GPIO Pins zugeordnet werden können. Die reinen Eingänge GPI32 – GPI39 können jedoch nicht für PWM verwendet werden. Die Grundeinstellung des PWM Signals erfolgt zunächst kanalbezogen mit der Funktion ledcSetup(channel, freq, res). Dabei ist „channel“ ist die Nummer des Kanals, „freq“ die Frequenz und „res“ die Auflösung in Bit.

Mithilfe der Funktion ledcAttachPin(pin, channel) ordnet ihr dem PWM Kanal den Pin zu.

Den Duty Cycle legt ihr mit der Funktion ledcWrite(pin, dutyCycle) fest. Der Duty Cyle in Prozent ist dutyCycle/2res x 100.

Hier ein kleiner Beispielsketch, der eine LED an GPIO16 periodisch dimmt und aufleuchten lässt.

const int pwmPin = 16;  // = GPIO16
const int freq = 1000;
const int pwmChannel = 0;
const int res = 8; // 2^8 = 255
 
void setup(){
  ledcSetup(pwmChannel, freq, res);
  ledcAttachPin(pwmPin, pwmChannel);
}
 
void loop(){
  for(int dutyCycle = 0; dutyCycle <= 255; dutyCycle++){   
    ledcWrite(pwmChannel, dutyCycle);
    delay(3);
  }
  for(int dutyCycle = 255; dutyCycle >= 0; dutyCycle--){
    ledcWrite(pwmChannel, dutyCycle);   
    delay(3);
  }
}

 

Ich komme noch einmal kurz auf analogWrite() zurück. Beim Arduino Uno erzeugt analogWrite() an den meisten Pins ein PWM Signal mit einer Frequenz von 490 Hz und einer Auflösung von 8 Bit (siehe hier auf den Arduino Seiten). Mit ledcSetup(channel, 490, 8) könnt ihr das leicht nachstellen.

Maximale PWM Auflösungen und Frequenzen

Als Auflösung könnt ihr Werte zwischen 1 und 15 Bit wählen. Die Auflösung bestimmt zusammen mit der internen Timerfrequenz (80 MHz) die maximale Frequenz des PWM Signals:

f_{\text{max}}=\frac{80000000}{2^\text{(res in bit)}} \;\;\;\;\;\;\text{z.B.:}\;\; \text{res} = 10\;\text{Bit}\;\;\; =>\;\;\;f_{\text{max}}=\frac{80000000}{2^{10}}= 78125\; \text{[Hz]}

Im Extremfall wählt ihr eine Auflösung von einem Bit. Damit ist der Duty Cycle unveränderbar 50 % und die maximale Frequenz liegt bei gigantischen 40 MHz. Allerdings ist das Signal dann schon nicht mehr so schön.

PWM mit dem ESP32 - links: 78125 Hz, rechts: 40 MHz
PWM mit dem ESP32 – links: 78125 Hz, rechts: 40 MHz, Duty Cyle: 50%

I2C mit dem ESP32

Im Gegensatz zum Arduino (UNO) hat der ESP32 zwei I2C Schnittstellen. Die Standardschnittstelle befindet sich an GPIO21 und GPIO22. Wenn ihr diese verwendet, ist die Bedienung wie beim Arduino. D.h. ihr bindet wie gewohnt die Wire.h Bibliothek ein und benutzt ihre Funktionen.

Ihr müsst aber bedenken, dass der ESP32 auf 3.3 Volt läuft. Wenn ihr ein 5 Volt Bauteil per I2C anschließt, solltet ihr einen Level Shifter oder Spannungsteiler dazwischen setzen. Das gilt natürlich auch für SPI, UART und alle anderen Verbindungen.

Ein weiterer Unterschied ist, dass ihr die I2C Schnittstelle anderen GPIOs zuordnen könnt. Wie ihr das bewerkstelligt, wie ihr die zweite I2C Schnittstelle verwendet und wie ihr die entsprechenden TwoWire Objekte an andere Objekte übergebt, habe ich sehr detailliert hier beschrieben. Ich gehe deswegen nicht noch einmal näher darauf ein.

SPI mit dem ESP32

Der ESP32 besitzt insgesamt vier SPI Schnittstellen. Allerdings stehen euch nur zwei davon zur freien Verfügung, nämlich VSPI und HSPI. Die anderen beiden SPI Schnittstellen werden intern verwendet. VSPI ist die Standardschnittstelle. Sie findet sich an den GPIOs 18 (VSPI_CLK), 19 (VSPI_MISO), 23 (VSPI_MOSI) und 5 (VSPI_CS = Chip Select). Verwendet ihr VSPI, müsst ihr euch nicht umgewöhnen. Ihr bindet SPI.h ein und benutzt die üblichen Funktionen dieser Klasse. Obwohl die Schnittstelle VSPI heißt, sprecht ihr sie mit SPI an.

Wenn ihr zusätzlich HSPI verwenden wollt, dann müsst ihr zunächst das entsprechende SPI Objekt erzeugen. Hier ein einfaches kleines Beispiel, bei dem ich zwei MPU9250 Module an HSPI und VSPI gehängt habe. Die verwendete MPU9250 Bibliothek von Bolderflight ist hier auf Github zu finden. Ihr müsst euch damit aber nicht wirklich beschäftigen. Das Entscheidende sind die ersten Zeilen. Sie sollen das Prinzip verdeutlichen.

#include "MPU9250.h"
#include "SPI.h"

SPIClass SPI_2(HSPI); // create SPI_2 object
MPU9250 IMU_1(SPI,5);  // pass standard SPI object (VSPI) and Chip Select
MPU9250 IMU_2(SPI_2,15);  // pass SPI_2 object (HSPI) and Chip Select

int status;

void setup() {
  Serial.begin(115200);
 
  status = IMU_1.begin();
  if (status < 0) {
    Serial.println("IMU 1 initialization unsuccessful");
    while(1) {}
  }
  status = IMU_2.begin();
  if (status < 0) {
    Serial.println("IMU 2 initialization unsuccessful");
    while(1) {}
  } 
}

void loop() {
  IMU_1.readSensor();
  Serial.println("IMU 1 Acceleration Data:");
  Serial.print(IMU_1.getAccelX_mss(),6);
  Serial.print("\t");
  Serial.print(IMU_1.getAccelY_mss(),6);
  Serial.print("\t");
  Serial.println(IMU_1.getAccelZ_mss(),6);

  IMU_2.readSensor();
  Serial.println("IMU 2 Acceleration Data:");
  Serial.print(IMU_2.getAccelX_mss(),6);
  Serial.print("\t");
  Serial.print(IMU_2.getAccelY_mss(),6);
  Serial.print("\t");
  Serial.println(IMU_2.getAccelZ_mss(),6);
 
  delay(1000);
}

 

Wechsel der Standard SPI Pins

Ihr könnt VSPI und HSPI auch anderen Pins zuordnen. Einen ausführlichen Sketch dazu findet ihr hier auf Github.

UART (Serial) mit dem ESP32

Der ESP32 besitzt drei serielle Schnittstellen, die als U0UXD, U1UXD und U2UXD bezeichnet werden. Die drei Schnittstellen sind folgenden Pins zugeordnet:

Voreinstellung für die UART Pins des ESP32
Voreinstellung für die UART Pins des ESP32

U0UXD ist die Standardschnittstelle, die unter anderem für die Ausgabe auf dem seriellen Monitor benutzt wird. Sie wird wie gewohnt über Serial.begin(baud rate) initialisiert. Wenn ihr U1UXD und U2UXD nutzen wollt, dann müsst ihr zunächst ein HardwareSerial Objekt erzeugen. Dann legt ihr mit Objektname.begin(baud rate, protocol, RXPin, TXPin) seine Eigenschaften fest. Ruft ihr die begin Funktion nur mit der Baudrate auf, greifen die Voreinstellungen.

Sehr theoretisch, aber mit dem Beispiel sollte es klar werden:

#define RX1 16
#define TX1 17
#define RX2 12
#define TX2 13
HardwareSerial Serial1(1);
HardwareSerial Serial2(2);

void setup() {
  Serial.begin(115200);
  Serial1.begin(115200, SERIAL_8N1, RX1, TX2);
  Serial2.begin(115200, SERIAL_8N1, RX2, TX2);
  delay(100);
  if(Serial1){
    Serial.println("Serial1 successfully set up");
  }
  if(Serial2){
    Serial.println("Serial2 successfully set up");
  }
}

void loop(){}

 

Dabei bedeutet SERIAL_8N1: 8 Bits, keine Parität (N = no), 1 Stopp Bit. Das ist der Standard. Eine Sammlung möglicher Protokoll-Parameter habe ich hier auf GitHub gefunden.

Ihr könnt nach demselben Schema auch die Pins für die Standardschnittstelle ändern: Serial.begin(baud rate, protocoll, RXPin, TXPin).

Touch Pins

Die Touch Pins des ESP32 reagieren auf Kapazitäten. Je größer die Kapazität, desto kleiner der Messwert. Da der menschliche Körper auch eine Kapazität darstellt, reagieren die Touch Pins auf Berührung. Der kleine Beispielsketch zeigt, wie es geht:

void setup() {
  Serial.begin(9600);
  while(!Serial){} 
  Serial.println("ESP32 Touch Test");
}

void loop() {
  Serial.println(touchRead(4));  // get value of Touch 0 pin = GPIO 4
  //Serial.println(touchRead(T0)); // alternative 
  delay(1000);
}

 

Ich habe den Touch Pin mit einem Steckbrückenkabel verbunden und das lose Ende des Kabels zunächst nicht berührt, dann berührt und dann mit einem 10 µF Elko verbunden. So sah die Ausgabe dazu aus:

Ausgabe von ESP32_touch_test.ino
Ausgabe von ESP32_touch_test.ino

Hall Sensor

Hall Sensoren hatte ich schon einmal im Detail hier beschrieben. Deshalb an dieser Stelle nur so viel: diese Sensoren reagieren auf magnetische Felder. Das Auslesen ist mit hallRead() denkbar einfach. Aber versprecht euch nicht zu viel davon. Die Empfindlichkeit ist gering, sodass man sich selbst mit einem kräftigen Magneten auf wenige Zentimeter nähern muss, um einen Effekt zu sehen. Hall Sensoren habe generell eine geringe Reichweite, aber der ESP32 Hall Sensor ist besonders unempfindlich. Das liegt wohl nicht zuletzt an der metallenen Abdeckung.

Interrupts

Was die Interruptprogrammierung des ESP32 angeht, könnte ich einen eigenen Beitrag füllen. Ich beschränke mich hier auf externe GPIO Interrupts.

Ihr könnt Interrupts an jedem GPIO Pin einrichten, einschließlich der reinen Eingänge GPI34 – GPI39. Die Handhabung ist wie bei den Arduino Boards, bis auf eine Besonderheit. Wenn ihr bei der Interrupt Service Routine (ISR) das Attribut IRAM_ATTR anwendet, wird der Code nicht im Flash, sondern im RAM gespeichert. Damit wird die Ausführung wesentlich schneller. Dann müsst ihr aber die ISR noch vor setup() platzieren (zumindest ist das so in der Arduino IDE). Klingt vielleicht kompliziert, ist es aber nicht.

In meinem kurzen Beispiel gibt ein Tasterdruck ein HIGH Signal an GPIO4. Das löst einen Interrupt aus. Hier zunächst die höchst komplexe 😉 Schaltung:

ESP32 Interrupt Test an GPIO4
Interrupt Test an GPIO4

Und nun der Sketch:

int interruptPin = 4; // define GPIO4 as interrupt pin
volatile bool event = false;

void IRAM_ATTR eventISR(){
  event = true;
  detachInterrupt(interruptPin);
}

void setup() {
  Serial.begin(115200);
  while(!Serial){}
  pinMode(interruptPin, INPUT_PULLDOWN);
  attachInterrupt(interruptPin, eventISR, RISING);
}

void loop() {
  if(event){
    Serial.println("Interrupt!");
    delay(1000); //debouncing
    event = false;
    attachInterrupt(interruptPin, eventISR, RISING);   
  }
}

 

Ihr könnt auch problemlos mehrere Interrupts definieren. Wäre doch eine schöne Übung!

Strom sparen – Schlafmodi

Der ESP32 ist ebenso leistungsstark wie stromhungrig. Bei batteriebetriebenen Projekten kann das zum Problem werden. Es gibt aber Möglichkeiten, den Stromverbrauch auf einen Bruchteil zu senken. Zunächst einmal könnt ihr bestimmte Komponenten abschalten. Die größten Stromfresser sind WiFi und Bluetooth. Solltet ihr sie benutzen, dann denkt darüber nach, sie, wenn möglich, zwischendurch abzuschalten. Dazu gibt es die Funktionen WiFi.mode(WIFI_OFF) und btStop(). Darüber hinaus gibt es verschiedene Schlafmodi, die ihr aktivieren könnt.

In diesem ESP32 Überblick muss ich mich ein wenig beschränken und werde nur kurz „Light Sleep“ und „Deep Sleep“ behandeln. Mit esp_sleep_enable_timer_wakeup() definiert ihr eine Schlafphase, nach der der ESP32 wieder aufwacht. Als Parameter übergebt ihr die Zeit in Mikrosekunden. Aber erst, wenn ihr esp_deep_sleep_start() oder esp_light_sleep_start() aufruft, beginnt die Schlafphase.

Nach dem Light Sleep fährt das Programm dort fort, wo es gestoppt hat. Nach dem Deep Sleep startet es neu. Ihr könnt das mit dem folgenden Sketch ausprobieren:

const int unsigned long microSecToSec = 1000000;
const int sleepTime = 5; 

void setup(){
  Serial.begin(115200);
  while(!Serial){}

  esp_sleep_enable_timer_wakeup(sleepTime * microSecToSec);
  Serial.println();
  Serial.println("ESP32 will go to sleep for 5 seconds....");
  delay(2000);
  Serial.println("...now");
  esp_deep_sleep_start();
  //esp_light_sleep_start(); 
  Serial.println("This message will only be printed after light sleep");
}

void loop(){ 
}

 

Aufwachen per externem Signal

Ihr könnt den ESP32 auch über ein externes Signal an einem der RTC_IO Pins wecken. Das richtet ihr mit esp_sleep_enable_ext0_wakeup(GPIO_NUM_x, HIGH/LOW) ein. Die Funktion erwartet als erstes Argument die Pin Nummer im Format GPIO_NUM_x. Wenn ihr stattdessen einfach ein Integer übergebt, gibt es eine Fehlermeldung. Der zweite Parameter legt fest, ob ein HIGH oder ein LOW Signal den ESP32 wecken soll.

void setup(){
  Serial.begin(115200);
  while(!Serial){};
  pinMode(GPIO_NUM_4, INPUT_PULLDOWN); 
  Serial.println();
  esp_sleep_enable_ext0_wakeup(GPIO_NUM_4,1);
  Serial.println("ESP32 will go to deep sleep...");
  delay(1000);
  Serial.println("...now");
  esp_deep_sleep_start(); //
  Serial.println("This message will only be printed after light sleep");
}

void loop(){ 
}

 

Aufwachen durch Touch

Das Aufwachen per Berührung (touch) steuert ihr mit der Funktion esp_sleep_enable_touchpad_wakeup(). Vorher müsst ihr aber noch einen Touch Interrupt einrichten. So geht’s:

#define THRESHOLD 40
void callback(){}

void setup(){
  Serial.begin(115200);
  delay(1000);
  touchAttachInterrupt(T4, callback, THRESHOLD); //T4 = GPIO13
  esp_sleep_enable_touchpad_wakeup();
  Serial.println("Going to deep sleep...");
  delay(1000);
  Serial.println("...now");
  esp_deep_sleep_start();
}

void loop(){
}

 

Ihr müsst also nur den GPIO13 berühren (am einfachsten über ein Steckbrückenkabel) und schon wacht der ESP32 auf. Wenn ihr möchtet, könnt ihr die Funktion callback(){} noch mit weiteren Anweisungen füllen. Der Touch Interrupt funktioniert natürlich auch ohne Weckfunktion.

WiFi

Der Gebrauch der WiFi Funktion ist ein komplexes Thema, mit dem man eine ganze Reihe von Beiträgen füllen könnte. Ich verweise deswegen an dieser auf eine andere Seite, auf der ich ein Beispiel gefunden habe, dass auf Anhieb funktionierte. Dort ist beschrieben, wie ihr eine LED über euren Browser schalten könnt. Schaut hier.

„Auf Anhieb“ heißt natürlich erst nach Anpassung des WLAN Namens und des Passwortes. Und es ging nur mit Chrome und Edge. Firefox machte Probleme.

Bluetooth

Auch das Thema Bluetooth ist sehr umfangreich. Ich beschränke mich in diesem Beitrag auf ein einfaches Beipiel, und zwar zeige ich, wie ihr LEDs per Smartphone oder Computer am ESP32 schaltet. Dazu braucht ihr erst einmal ein Bluetooth Terminal Programm. Für den PC schlage ich Bluetooth Serial Terminal vor, für das Smartphone die App Bluetooth Terminal.

Auf der ESP32 Seite hängt ihr zwei LEDs an GPIO4 und GPIO13. Ihr könnt natürlich auch andere Pins wählen und das Beispiel beliebig erweitern.

Ladet den folgenden Sketch hoch, dann koppelt ihr den ESP32 mit dem Smartphone oder PC. Der ESP32 erscheint unter dem Namen, den ihr ihm mit espBT.begin("name") verleiht. Nach dem Koppeln müsst ihr den ESP32 im Computerprogramm oder in der Smartphone-App noch verbinden. In der App geht ihr dazu auf die drei Punkte, im PC Programm ist es offensichtlich. 

Sendet ihr nun beispielsweise eine „+4“ an den ESP32, schaltet ihr die LED an GPIO4 damit ein. Mit „-4“ hingegen schaltet ihr sie aus.

#include "BluetoothSerial.h" 

BluetoothSerial espBT; 

void setup() {
  Serial.begin(115200);
  pinMode(4, OUTPUT);
  pinMode(13, OUTPUT);
  espBT.begin("ESP32 Bluetooth Example");
  Serial.println("Pair your BT device now");  
}

void loop(){
  if(espBT.available()){
    char command = espBT.read();
    int state = LOW;
    int pin = espBT.parseInt();
    Serial.print("Received: "); 
    Serial.print(command);
    Serial.println(pin);

    if((command == '+')||(command == '-')){
      if(command == '+'){
        state = HIGH;
      }
      else state = LOW;
      digitalWrite(pin, state);
    }
    
    espBT.print("Received: "); 
    espBT.print(command);
    espBT.println(pin);
  }     
}

 

Und so sieht das Programmfenster des Bluetooth Serial Terminal aus:

Bluetooth Serial Terminal Programmfenster
Bluetooth Serial Terminal Programmfenster

Noch ein paar kurze Erklärungen:

  • BluetoothSerial espBT erzeugt das Bluetooth Objekt.
  • espBT.read() liest das erste Zeichen, also „+“ oder „–“.
  • espBT.parseInt() identifiziert in den ankommenden Zeichen die nächste Ziffernfolge und liest sie als Integer ein (sehr praktische Funktion).

Den Rest müsstet ihr euch erschließen können. Falls nicht, dann gibt es zum Beispiel hier ein ausführlicheres Tutorial. 

„Exotische“ Pin-Funktionen

Wenn ihr noch einmal zu dem Pinout Diagramm vom Anfang zurückgeht, seht ihr, dass ich die meisten Pinfunktionen erklärt habe. Ein paar fehlen jedoch noch. Wahrscheinlich werden die meisten sie nie nutzen.

Danksagung

Die Wolke im Beitragsbild stammt von Clker-Free-Vector-Images auf Pixabay.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.