Sie sind auf Seite 1von 16

Mikrocontroller: Programmierung, Schnittstellen, Sensoren, Aktoren

Einführung in die Mikrocontroller-Programmierung mit


dem ESP32
Der ESP32 der Firma Espressif bietet viele Möglichkeiten zum Einstieg in die Programmierung, von
der einfachen Blinkschaltung bis zur Kommunikation im Internet der Dinge.
Der ESP32 zeichnet sich durch folgende Eigenschaften aus:
• Dual-Core 32-Bit Mikrocontroller mit bis zu 240MHz Taktfrequenz, ein dritter Low-Power-
Kern wird vom Betriebssystem FreeRTOS verwaltet
• WLAN (nur 2,4GHz) und Bluetooth 4.0, Ethernet-MAC Schnittstelle
• 2 serielle Schnittstellen, I2C-Bus, SPI-Bus, CAN-Bus, I2S-Schnittstelle
• Bis zu 26 digitale Ein- und Ausgänge, bis zu 16 analoge Eingänge, 2 analoge Ausgänge, 10
Touch-Eingänge
• 4 64-Bit Timer, 6 PWM-Kanäle, 16 LED-Dimmer-Kanäle
• 4 bis 16 MByte Programmspeicher, als Flash-Dateisystem nutzbar
• 96kByte RAM, optional bis zu 4MByte externes RAM (einige Boards, z.B. ESP32-CAM)
Der ESP32 wird in vielen Bauformen mit unterschiedlicher zusätzlicher Ausstattung angeboten. Hier
wird im Weiteren das ESP32-DevKit mit 38 Pins genutzt.

Das ESP32 Demo-Board (mini)


Das ESP32-DevKit kann zwar auf einem Steckbrett genutzt werden, bereits der Aufbau einfacher
Schaltungen benötigt aber viel Zeit. Daher wurde eine Basisplatine für den ESP32 entwickelt.

Bild 1: ESP32 Demo-Board mini

2023-09 Spi Seite 1 Einführung ESP32.odt


Mikrocontroller: Programmierung, Schnittstellen, Sensoren, Aktoren

Auf der Platine ist ein Steckplatz für den ESP32 und ein 0,96“ OLED Grafik-Display (128x64 Pixel)
vorhanden. Ebenso sind vorhanden:
• Je eine rote LED (GPIO 32), grüne LED (GPIO 33), RGB-LED WS2812B an GPIO 26
• Zwei Taster (GPIO 2 und 4) und ein Touch-Feld (Touch T3, GPIO 15)
• Ein Drehencoder (GPIO 34 und 35) mit Taster (GPIO 0) und ein Poti (Analogeingang A0)
• Ein RS-485 Bustreiber an der 2. seriellen Schnittstelle (GPIO 16, 17) mit individuell
schaltbarem Sender (DE an GPIO13) und Empfänger (/RE an GPIO12) für Modbus RTU,
DMX512 etc. (Achtung: der Pin GPIO12 darf beim Booten nicht auf 1 gelegt werden!)
• Ein Steckplatz für I2C-Sensoren, ein Steckplatz für den RFID-Leser RC-522 (auch nutzbar für
SPI-Bus, One-Wire, 6 GPIO, DHT11/22),
• Ein analoger Eingang (Analogeingang A3) mit Anpassung für 0-5V Eingangsspannung.
Versorgung eines externen Sensors mit 3,3V oder 5V.
• Ein DCDC-Wandler (LM2596 Modul) ist optional bestückbar. Damit kann die Platine mit 7-
24V Gleichspannung versorgt werden. (Ausgangsspannung 5V unbedingt vor dem Auflöten
einstellen, sonst kann die RGB-LED beschädigt werden!)
Eine Anleitung zur Bestückung der Platine sowie Schaltplan und Layoutdaten sind unter der auf der
Platine aufgedruckten URL hinterlegt.

Programmierung 1 – digitale Ein- und Ausgänge


Digitale Ein- und Ausgänge können am Mikrocontroller auf vielen Anschlusspins genutzt werden.
Hier ist zu beachten, dass viele Pins Sonderfunktionen haben können und einige wenige Pins nur als
digitale Eingänge genutzt werden können. Die Zuordnung erfolgt immer über die GPIO-Nummer,
nicht über die Pinnummer des Moduls!
Digitale Ein- und Ausgänge müssen mit der Funktion pinMode(Pin, Typ) eingestellt werden. Die
Taster schalten gegen Masse (Low-Aktiv) und benötigen Pull-Up Widerstände. Die LEDs sind
ebenfalls Low-Aktiv geschaltet.
Bei den Arduino-Funktionen digitalWrite und digitalRead kann alternativ LOW, HIGH, true, false,
0 oder 1 genutzt werden. (Groß-Klein-Schreibung beachten!)
// ***** Portpins für Ein-/Ausgänge ***************************************
const int LED_rot=32, LED_gruen=33, Taster_L=2, Taster_R=4;
// ***** Initialisierung **************************************************
void setup() {
pinMode(LED_rot, OUTPUT); // die LEDs beginnen danach
pinMode(LED_gruen, OUTPUT); // zu leuchten!
pinMode(Taster_L, INPUT_PULLUP); // Taster brauchen hier den
pinMode(Taster_R, INPUT_PULLUP); // Pullup-Widerstand
}
// ***** Endlosschleife ***************************************************
void loop() {
if ( digitalRead(Taster_L) == false ) { // low-aktiv, daher false
digitalWrite(LED_rot, LOW); // LED EINschalten

2023-09 Spi Seite 2 Einführung ESP32.odt


Mikrocontroller: Programmierung, Schnittstellen, Sensoren, Aktoren

}
else {
digitalWrite(LED_rot, HIGH); // LED AUSschalten nicht
} // vergessen!
}

Programmierung 2 – Der Drehencoder (Inkrementalgeber)


Der Drehencoder auf der Platine ermöglicht die Erkennung der Drehrichtung und Impulszahl. Mit
dem integrierten Taster (Enc_Taster = GPIO0 – parallel zum FLASH-Taster) sind einfache
Menüsteuerungen möglich. Der Drehencoder hat 20 Raststellungen pro Umdrehung. Bei der Drehung
erzeugt der Encoder zwei um 90° Phasenverschobene Rechtecksignale. (Enc_A GPIO34, Enc_B
GPIO35)

Linksdrehung Rechtsdrehung

Spur A

Spur B

Bild 2: Signalverlauf Drehencoder

Die Auswertung erfolgt hier mittels Flankenerkennung oder Interrupt bei der Spur A. Wenn eine
steigende Flanke erkannt wurde, wird auf die Spur B geschaut. Ein 0-Pegel bedeutet Rechtsdrehung,
ein 1-Pegel Linksdrehung (oder umgekehrt, bei Bedarf einfach anpassen!).
In dem Beispiel wird die Spur A mit Interrupt ausgewertet und eine Variable hoch oder runter gezählt.
Dabei wird für die Loop ein Signal gesetzt, wenn sich der Wert geändert hat. Die Ausgabe erfolgt über
die serielle Schnittstelle, öffnet unter Werkzeuge den Seriellen Monitor und stellt die Baudrate auf
115200 ein.
// ***** Portpins für Ein-/Ausgänge ***************************************
const int Enc_A=34, Enc_B=35, Enc_Taster=0;
// ***** Globale Variablen ************************************************
int Encoder, Status; // zur Kommunikation zw. ISR und loop
// ***** Initialisierung **************************************************
void setup() {
pinMode(Enc_A, INPUT); // externe Pullups auf der Platine
pinMode(Enc_B, INPUT); // Spur B des Encoders
pinMode(Enc_Taster, INPUT); // Taster des Encoders = Flash-Taster!
Serial.begin(115200); // Serielle Schnittstelle mit 115200Bit/s
attachInterrupt(digitalPinToInterrupt(Enc_A), Auswertung, RISING);
// ISR void Auswertung(void)
Encoder = 0;
Status = 0;
}

2023-09 Spi Seite 3 Einführung ESP32.odt


Mikrocontroller: Programmierung, Schnittstellen, Sensoren, Aktoren

// ***** Endlosschleife ***************************************************


void loop() {
if(Status == 1) {
Status = 0; // Signal wieder löschen
Serial.printf("Drehencoder Wert: %4d\n", Encoder);
}
if(digitalRead(Enc_Taster) == false) {
Encoder = 0;
Serial.printf("\n\nZähler zurück gesetzt!\n\n");
while(digitalRead(Enc_Taster) == false); // warte auf loslassen
}
}
// ***** Interrupt-Service Routine Drehencoder ****************************
void Auswertung( ) {
if(digitalRead(Enc_B)==false) Encoder ++;
else Encoder --;
Status = 1; // Signal an loop geben
}

Programmierung 3 – Serielle Schnittstelle


Zunächst muss die serielle Schnittstelle mit Serial.begin( 115200 ) initialisiert werden. Dazu muss die
Baudrate (=Datenübertragungs-Geschwindigkeit in Bit/s) und gegebenenfalls die Konfiguration
(SERIAL_8N1 ist Standard) angegeben werden. Danach können Zeichen, Texte und Variablen mit
Serial.print( ) bzw. inklusive Zeilenschaltung mit Serial.println( ) versendet werden. Mehr
Möglichkeiten für die Formatierung bietet die Printf-Funktion ( https://de.wikipedia.org/wiki/Printf ).
Die wichtigsten Platzhalter bei Printf sind %d für Ganzzahlen mit Vorzeichen, %u für Ganzzahlen
ohne Vorzeichen, %f für Fließkommazahlen und %c zum Einfügen von Sonderzeichen (Char).

Das serielle Terminal in Arduino wird über Werkzeuge/Serieller Monitor oder das Lupen-Icon oben
rechts gestartet. Bei ersten mal muss im Terminal (nicht bei Werkzeuge/Upload-Speed!) die Baudrate
ebenfalls auf den programmierten Wert (hier 115200 Bit/s) eingestellt werden.
Im ersten Beispiel wird ein Wert einer Variablen gesendet
int Zahl=0;
void setup() {
Serial.begin(115200); // Serielle Schnittstelle initialisieren
delay(2000); // etwas Zeit zum Öffnen des seriellen Monitors
Serial.println("Serielle Datenübertragung");
}
void loop() {
Serial.print("Aktueller Wert: "); // keine Zeilenschaltung am Ende
Serial.println(Zahl); // Aktuellen Wert ausgeben, neue Zeile
Zahl ++; // hoch zählen
delay(1000); // 1 Sekunde warten
}

2023-09 Spi Seite 4 Einführung ESP32.odt


Mikrocontroller: Programmierung, Schnittstellen, Sensoren, Aktoren

Zum Empfangen von Zeichen wird die Funktion Serial.available( ) aufgerufen, die prüft ob noch
Zeichen im Empfangspuffer vorhanden sind. Der Rückgabewert der Funktion ist die Anzahl an
vorhanden Zeichen.
char Zeichen;
void setup() {
Serial.begin(115200); // Serielle Schnittstelle initialisieren
Serial.println("Hallo Welt!");
}
void loop() {
if (Serial.available() > 0) { // Wurde etwas empfangen?
Zeichen = Serial.read(); // 1 Zeichen abholen
Serial.print(Zeichen); // Echo
}
}
Zum Steuern eines Mikrocontroller-Programms über das serielle Terminal denkt man sich einfache
Befehle aus, die über einen Buchstaben (ein ASCII-Zeichen) aufgerufen werden. Wenn Zahlenwerte
übergeben werden sollen, kann man die mit parseInt( ) aus den empfangenen Daten auslesen.
char Zeichen;
int Wert, Wert_neu;
void setup() {
Serial.begin(115200); // Serielle Schnittstelle initialisieren
Serial.println("Wert abfragen mit ?, neuer Wert z.B. mit w 1234");
}
void loop() {
if (Serial.available() > 0) { // Wurde etwas empfangen?
Zeichen = Serial.read(); // 1. Zeichen abholen
if (Zeichen == '?') { // Fragezeichen wurde empfangen
Serial.printf("Aktueller Wert %d \n", Wert); // hier mal mit printf
}
if (Zeichen == 'w') { // w empfangen, jetzt kommt eine Zahl
Wert_neu = Serial.parseInt(); // gibt 0 zurück, wenn keine Zahl ge-
if (Wert_neu != 0) Wert = Wert_neu; // funden wurde...
}
}

Programmierung 4 – Das OLED-Display (Textausgabe)


Statt Texte und Variablen über die serielle Schnittstelle auszugeben, soll jetzt das Display genutzt
werden. Auf dem Display ist ein Controller vom Typ SSD1306, es wird dafür die Bibliothek
"ESP8266 and ESP32 Oled Driver for SSD1306 display" by ThingPulse genutzt. Das Display ist über
den I2C-Bus angeschlossen, daher wird noch die Standard-Bibliothek wire.h eingebunden.
Das Grafikdisplay hat keine festen Zeilen. Texte können an beliebiger Stelle (X =[0..127], Y =[0..63])
in verschiedenen Schriftarten und -Größen ausgegeben werden. Daher muss bei der Textausgabe
immer auf die XY-Koordinaten geachtet werden. Die folgende Zeile muss mindestens um die
gewählte Punktzahl der Schriftgröße weiter unten stehen, sonst werden die Texte übereinander
geschrieben.
Für das Display wird zunächst ein Objekt display mit Angabe der I2C-Pins und der I2C-
Bausteinadresse angelegt. Danach erfolgt die Initialisierung und die Auswahl der Schriftart. Dann
kann mit der Funktion display.drawString(x, y, String) ein Textstring in den Puffer des Displays

2023-09 Spi Seite 5 Einführung ESP32.odt


Mikrocontroller: Programmierung, Schnittstellen, Sensoren, Aktoren

geschrieben werden. Für die Ausgabe auf dem Display ist danach immer die Funktion
display.display() aufzurufen.
#include <Wire.h>
#include "SSD1306Wire.h"
#define SCREEN_WIDTH 128 // OLED Display Breite, in Pixel
#define SCREEN_HEIGHT 64 // OLED Display Höhe, in Pixel
SSD1306Wire display(0x3c, 21, 22); // 21=SDA 22=SCL I2C-Bus Pins

//***** globale Variablen *************************************************


long showtime;
int x, y; // Pixel-Koordinaten für das Display
// ***** Initialisierung **************************************************
void setup() {
display.init(); // Initialisierung des OLED
display.flipScreenVertically(); // Ausrichtung passend zur Platine
// Schriftgröße einstellen. ArialMT mit 10/16/24 Punkten
display.setFont(ArialMT_Plain_16); // mittlere Schriftgröße auswählen
display.drawString(0, 0,"Hallo BKI"); // 16 Punkt Höhe ist gewählt
display.drawString(0, 15,"Test äöüß°^€@");// gehen alle Sonderzeichen?
display.drawString(0, 31,"01234567890123456789"); // wie viel passt rein?
display.setTextAlignment(TEXT_ALIGN_CENTER); // Text zentriert
display.drawString(63, 47,"Mittig"); // ausgeben
display.display(); // Texte jetzt ausgeben
delay(5000);
display.setTextAlignment(TEXT_ALIGN_LEFT); // Text wieder linksbündig
x=2; // Startposition setzen
y=2;
}

// ***** Endlosschleife ***************************************************


void loop() {
String Text1 = "Hallo";
if ((millis() - showtime) >= 1000){ // nach 1 Sekunde…
display.clear(); // erst mal alles löschen
showtime=millis(); // letzte Ausführungszeit speichern
display.drawString(x,y,Text1); // String-Variable ausgeben
char Text[20]; // Textformatierung mit sprintf.
sprintf(Text, "%5ld", showtime); // Zeit (in ms) in char-Array
display.drawString(40,10,Text); // schreiben und ausgaben
display.display(); // Ausgabe anzeigen lassen.
y=y+2; // Text ‚Hallo‘ nach unten scrollen,
if ( y>=62 ) y=0; // schon unten? Wieder oben beginnen!
}
}

Programmierung 5 – Analoge Werte einlesen


Auf der Platine befindet sich ein Poti, das am Analogeingang A0 angeschlossen ist. Damit kann man
leicht veränderbare Werte in ein Programm übernehmen. Der zweite analoge Eingang A3 ist mit einer
einfachen Signalanpassung ausgestattet. Eine Eingangsspannung von 0V bis 5V wird dabei mit einem
Spannungsteiler auf den Bereich von 0,16V bis 3,2V abgebildet.

2023-09 Spi Seite 6 Einführung ESP32.odt


Mikrocontroller: Programmierung, Schnittstellen, Sensoren, Aktoren

Pinbelegung des Steckers J5: 5V (Sensorversorgung), 3.3V, Analog Ein, Masse


/* Beschaltung des ADC-Eingangs A3 z.B. für den Sensor MQ-135:
* Ergibt einen Offset von ca. 160mV, da der ADC des ESP32 unter
* 160mV nichts misst.
* Ergibt eine Anpassung der Eingangsspannung 0-5V ==>> 0,16-3,18V
* Das kann mit einer einfachen MAP-Funktion abgebildet werden. */

void loop() {
static int gas, gas_new; // Messwerte des Gas-Sensors MQ-135 bzw. MQ-2
gas_new = map(analogRead(A3), 1900, 2100, 400, 800);// Sensor-Kennlinie
gas = ( gas_new * 40 + gas * 60 ) / 100; // Tiefpass-Filter
Serial.printf("Gas-Sensor MQ-135 CO2: %4d ppm\n", gas);
}

Programmierung 6 – Der Touch-Sensor


Der ESP32 hat 10 touchfähige Eingangspins. Die Touch-Pins werden in Arduino mit T0 bis T9
angesprochen. Der Touchpin T1 (GPIO 0) kann allerdings nicht genutzt werden, weil die Boot-
Schaltung das Touchsignal stört.
Auf der Platine gibt es ein Touch-Pad (Touch T3), das zur Eingabe genutzt werden kann. Die Funktion
touchRead(Pin) liefert Werte von etwa 100, wenn kein Touch erkannt wurde bis hinunter auf 0, wenn
ein sehr niederohmiger Touch (feuchte Finger, Kurzschluss mit Metall) erfolgt.
Werte unter 25 können als sicheres Touch-Ereignis angesehen werden. Allerdings haben die Touch-
Pins häufig sehr kurze Einbrüche, die leicht als falsches Ereignis erkannt werden. Daher wird hier ein
Median-Filter eingesetzt, das bereits bei 3 Messwerten zuverlässig arbeitet. Ein Median-Filter liefert
bei einer ungeraden Anzahl Messwerte immer den mittleren Wert der geordneten Messwerte zurück.
Wenn man die Funktion nicht selbst programmieren will (was aber eine schöne Anwendung für
Sortier-Algorithmen und Arrays wäre), dann kann man die Bibliothek MedianFilter.h
(https://github.com/daPhoosa/MedianFilter) benutzen.
#include <MedianFilter.h>
MedianFilter filter(3, 100); // MedianFilter filterObject(size, seed);

// ***** Initialisierung **************************************************


void setup() { // Touch-Pins ohne pinMode!
Serial.begin(115200);
}

2023-09 Spi Seite 7 Einführung ESP32.odt


Mikrocontroller: Programmierung, Schnittstellen, Sensoren, Aktoren

void loop() {
int Touchwert;
Touchwert = filter.in(touchRead(T3)); // Median Filter
if( Touchwert < 25 )
Serial.prinln("Touch erkannt");
delay(500);
}

Programmierung 7 – Das OLED-Display (Grafikausgabe)


Für die Grafikausgabe wird wieder die Bibliothek SSD1306.h genutzt. Hier gibt es Zeichenfunktionen
um einzelne Pixel, Linien, Kreise oder Rechtecke zu zeichnen.
In dem folgenden Beispiel wird ein Koordinatensystem („Oszilloskop“) gezeichnet und sowohl der
Analogeingang A0 und der Touch-Eingang T3 als Linie dargestellt. Zum Zeichnen des Koordinaten-
systems muss man sich zunächst die Punktanordnung auf dem Display anschauen. X/Y = 0/0 ist die
obere linke Ecke, 127/63 die untere rechte Ecke:

0,0

2,61 Display
=
y

0,0 Messwert

127,63
Bild 3: Pixel-Koordinaten

In der Abbildung wird auch deutlich, dass Messwerte jetzt auf den Bereich von 0 (maximaler
Messwert) bis 61 (minimaler Messwert) abgebildet werden müssen. Dazu wird hier wieder die map-
Funktion genutzt. Zu Zeichnen des Koordinatensystems wird hier eine Funktion genutzt.
#include <Wire.h>
#include "SSD1306Wire.h"
#include <MedianFilter.h>
#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 64 // OLED display height, in pixels
SSD1306Wire display(0x3c, 21, 22); //21=SDA 22=SCL
MedianFilter filter(3, 100); // MedianFilter filterObject(size, seed);
//***** globale Variablen ***********************************************
long showtime, timeold, timenew, duration;
int x, y; // Pixel-coordinates for the display

// ***** Initialisierung ************************************************


void setup() {
display.init(); // Initialisierung des OLED
display.flipScreenVertically(); // Ausrichtung passend zur Platine

2023-09 Spi Seite 8 Einführung ESP32.odt


Mikrocontroller: Programmierung, Schnittstellen, Sensoren, Aktoren

// Schriftgröße einstellen. ArialMT mit 10/16/24 Punkten


display.setFont(ArialMT_Plain_10); // kleine Schriftgröße auswählen
Koordinatensystem(); // Koordinatensystem zeichnen
x=2; // Startposition
}
// ***** Endlosschleife ************************************************
void loop() {
int Digitalwert, Touchwert;
// Messung alle 20ms => Zeit 125 Werte * 20ms = 2,5 Sekunden pro Bild
if ((millis() - showtime) >= 20) {
showtime=millis();
Digitalwert = analogRead(A0);
y = map (Digitalwert, 0, 2800, 61, 0);// Pixel-Mapping
display.setPixel(x,y); // ein Pixel zeichnen
Touchwert = filter.in(touchRead(T3)); // Median Filter
y= map(Touchwert, 0, 70, 61, 0); // zweites Pixel zeichnen
display.setPixel(x,y);
display.display(); // neue Pixel ausgeben
x++; // nächste Spalte
if(x>125) { // Bildschirmende erreicht?
x=2; // x aus Startposition setzen
Koordinatensystem(); // Display, neues KS zeichnen
}
}
}
void Koordinatensystem(void) {
display.clear(); // Display löschen
// x- und y- Achse zeichnen
display.drawLine( 2, 61,127, 61);
display.drawLine( 2, 0, 2, 63);
// Ticks alle 5 Pixel auf den Achsen zeichnen
for (int i = 0; i <=6 ; i++) {
display.drawHorizontalLine( 0, 61 - 10*i, 5); // an der Y-Achse
}
for (int i = 0; i <=12 ; i++) {
display.drawVerticalLine( (10*i + 2), 59, 5); // an der X-Achse
}
display.display(); // Auf dem Display ausgeben
}

Programmierung 8 – Sensoren über I2C-Bus auswerten


Die meisten Sensoren mit I2C-Bus sind heute als fertige Module erhältlich. Davon verwenden viele
die Pinbelegung VCC-GND-SCL-SDA. Diese Sensoren können direkt auf die Buchse J7 gesteckt
werden (Beschriftung ist auf der Unterseite der Platine, Richtung kontrollieren!). Für Sensoren mit
einer anderen Pinbelegung des I2C-Bus oder mit zusätzlichen Signalen (DataReady, Interrupt etc.)
muss mit Dupont-Kabeln eine Verbindung zu J7 und J4 (6 frei verwendbare GPIO, wenn kein RFID-
Modul oder SPI-Bus genutzt wird) hergestellt werden.
Bei den eingesetzten Sensoren muss darauf geachtet werden, dass die I2C Bausteinadresse 0x3C für
das Display belegt ist.

2023-09 Spi Seite 9 Einführung ESP32.odt


Mikrocontroller: Programmierung, Schnittstellen, Sensoren, Aktoren

Beachten Sie, dass die I2C-Bussignale des ESP32 nur 3,3V-Kompatibel sind. Sollte ein Sensor mit
5V Versorgungsspannung eingesetzt werden, muss ein Pegelwandler für die Busleitungen genutzt
werden, sonst kann der ESP32 beschädigt werden!

Direkt einsetzbare Sensormodule (unvollständige Liste):


• BMP180 / BMP280 Luftdruck, Temperatur in der 3,3V Version
• BME280 / BME680 zusätzlich Luftfeuchtigkeit (BME280), Luftqualität (BME680)
• AHT10 Lufttemperatur und Luftfeuchtigkeit
• VEML6070 UV Licht Sensor
• VL53L0X Time-of-Flight Abstandssensor
• HMC5883L 3-Achs Magnetfeldsensor (Modul GY 271, nicht GY 273)
ACHTUNG: auf neueren Modulen ist der Nachbau-Chip QMC5883L verbaut, der eine andere
I2C-Adresse und Registerdefinitionen hat.
• MPU 6050 / MPU 9250 3-Achs Beschleunigungs- und 3-Achs Drehratensensor
• CCS811 CO2 Gassensor
• INA219 I2C Stromsensor mit 12 Bit Auflösung
• MLX90614 Non-Contact IR Thermometer
• MAX44009 Umgebungslicht Sensor
• PAJ7620U2 Gestenerkennungs-Sensor
Über Kabel einsetzbare Sensoren:
• ADS1015 / ADS1115 12Bit/16Bit ADC 4 Kanäle
• SI7021 / SHT20 / SHT21 Luftfeuchtigkeit und Temperatursensor
• ADXL345 3-Achs Beschleunigungssensor
• VEML6075 UVA UVB Licht Sensor
• BH1750FVI Digitaler Licht-Intensitäts Sensor (0-65535 Lux)
• VEML7700 Umgebungslicht Sensor (bis 200000 Lux)
• TSL2591 Umgebungslicht Sensor (188µLux bis 88000 Lux)
• TCS34725 RGB Farb-Sensor
• APDS-9930 RGB Farb-Sensor
• TOF10120 Laser-Entfernungssensormodul (10-180cm im I2C Modus)
• GP2Y0E03 Infrarot-Abstandssensor (4-50cm mit I2C und analogem Ausgang)
Für viele der genannten Sensoren sind Bibliotheken verfügbar. Man kann aber auch nur die Wire-
Bibliothek nutzen und anhand des Datenblatts die Register auslesen. Für die I2B-Buskommunikation

2023-09 Spi Seite 10 Einführung ESP32.odt


Mikrocontroller: Programmierung, Schnittstellen, Sensoren, Aktoren

gibt es nur 3 unterschiedliche Übertragungen: Schreibzyklus, Lesezyklus und kombinierter


Schreib-/Lesezyklus. Die Wire-Bibliothek nutzt immer die 7-Bit Bausteinadresse.

Schreibzyklus
Wire.beginTransmission ( I2C_Adresse );
Wire.write( Datenbyte ); // Datenbyte oder Registeradresse
Wire.endTransmission( );

Lesezyklus
byte Daten1, Daten2;
Wire.requestFrom ( I2C_Adresse, 2 ); // Baustein und Anzahl der Bytes
if( Wire.available( ) >= 2 ) {
Daten1 = Wire.read( );
Daten2 = Wire.read( );
}

Kombinierter Zyklus
byte Daten1, Daten2;
Wire.beginTransmission ( I2C_Adresse );
Wire.write( Registeradresse ); // Auswahl einer internen Registeradresse
Wire.endTransmission( false ); // true oder ( ) sendet ein Stop, false
// sendet kein Stop!
Wire.requestFrom ( I2C_Adresse, 2 );
if( Wire.available( ) >= 2 ) {
Daten1 = Wire.read( );
Daten2 = Wire.read( );
}

Programmierung 9 – I2C-Bus Trainerplatine


Am 6-poligen Pfostenfeldstecker J3 wird die I2C-Bus Trainerplatine angeschlossen. Die
Spannungsversorgung erfolgt über die ESP32-Platine. (die 2mm-Buchsen bleiben ungenutzt)
Auf der Platine kann bei allen vier Bausteinen die I2C-Adresse über 3 Jumper eingestellt werden. Die
vier Bausteine haben alle jeweils 3 Adressleitungen, so dass immer 8 unterschiedliche Adressen
einstellbar sind. Mit dem festen Teil der Adresse (obere 4 Bit, Siehe Bestückungsaufdruck der Platine)
ergibt sich eine 7-Bit Bausteinadresse. Das untere 8. Bit unterscheidet die Schreib- (R/W=0) und die
Leseadresse (R/W=1).

Bei der Arduino-Bibliothek wire.h muss die 7-Bit Bausteinadresse verwendet werden!

2023-09 Spi Seite 11 Einführung ESP32.odt


Mikrocontroller: Programmierung, Schnittstellen, Sensoren, Aktoren

Bild 4: Die I2C-Bus Trainerplatine


Auf der Platine befinden sich:
• ein Portexpander PCF8574 mit 8 DIP-Schaltern (mit Interrupt an GPIO25)
• ein Portexpander PCF8574A mit 8 LEDs (Adresskonflikt mit Display möglich!)
• ein LM75 Temperatursensor und Temperaturwächter (mit Interrupt an GPIO25)
• ein M24C64 EEPROM (64kBit = 8kByte)
Bei allen 4 Bausteinen befindet sich unterhalb ein 3x3 Jumperfeld zum Einstellen der Baustein-
adressen. Jeder Baustein kann auf 8 verschiedene Adressen eingestellt werden, so dass bis zu 8
Platinen parallel betrieben werden könnten.

Beachten Sie, dass keine Adresskonflikte mit dem Display (Adresse 0x3C) oder extern
angeschlossenen I2C Sensoren bestehen. Es kommt dabei zu „Datensalat“ auf dem Bus.

Programmierung 10 – RFID-Leser am SPI-Bus


Der RC522 RFID-Leser ist direkt aufsteckbar.

Bei der ersten Platinenrevision ist die Buchse J4 um 180° gedreht und weiter unten platziert. Ein
RC522 mit abgewinkelten Kontakten kann aufgesteckt werden (am Poti wird es etwas eng), oder man
lötet die Pins am RC522 oben auf die Platine und steckt die Platine mit der Bauteilseite nach unten
auf. Ab der Platinenrevision 2 ist die Buchse gedreht, so dass die RC522-Platine mit der Bauteilseite
und Beschriftung nach oben aufgesteckt werden muss.

Für den RFID-Leser muss die Bibliothek MFRC522 installiert werden. Damit werden auch
verschiedene Beispielprogramme installiert. Diese erscheinen unter Datei/Beispiele/Beispiele aus
eigenen Bibliotheken/MFRC522. Hier wird nur auf die Pinbelegung des Reset und Slave-Select sowie
auf die Instanziierung des Leser-Objektes eingegangen. Die Pins für den SPI-Bus sind in der
Bibliothek festgelegt und müssen nicht deklariert werden.

2023-09 Spi Seite 12 Einführung ESP32.odt


Mikrocontroller: Programmierung, Schnittstellen, Sensoren, Aktoren

#include <SPI.h> // Bibliothek für den SPI-Bus (allgemein)


#include <MFRC522.h> // Bibliothek für den Reader
const int RST_PIN = 14; // Reset Pin
const int SS_PIN = 5; // Slave Select Pin
MFRC522 mfrc522(SS_PIN, RST_PIN); // Objekt für MFRC522 instanziieren

Programmierung 11 – Zweite serielle Schnittstelle mit RS485


Treiber
Die zweite serielle Schnittstelle ist zum Beispiel nutzbar für das DMX512-Protokoll (250000Baud,
Licht- und Bühneneffekte), Profibus DP (veraltet, 19200 Baud) oder Modbus RTU (19200 Baud).
Mit RS-485 Treibern kann man ein Multi-Master Bussystem aufbauen. Daher wurden bei dem
Baustein die Enable-Leitungen für Sender (DE an GPIO13) und Empfänger (/RE an GPIO12) getrennt
an den ESP32 geführt. Somit kann man auch den Empfänger beim Senden einschalten (mithören für
Kollisionserkennung bei Multi-Master Systemen).
Der für RS-485 notwendige Busabschlusswiderstand kann mit der Lötbrücke JP1 aktiviert werden.
Der Abschlusswiderstand muss am Anfang und am Ende des Busses aktiviert werden, sonst treten
Störungen auf.
// ***** Portpins für Ein-/Ausgänge ***************************************
const int RXD2=16, TXD2=17, DE=13, nRE=12; // Receive Enable ist invertiert
void setup() {
pinMode(DE, OUTPUT);
pinMode(nRE, OUTPUT);
digitalWrite(DE, HIGH); // Senden ermöglichen
digitalWrite(nRE, LOW); // Empfang ermöglichen
Serial2.begin(19200, SERIAL_8N1, RXD2, TXD2);
Serial.begin(115200); // Schnittstelle zum PC (über USB)
}
void loop() { // Beispiel Busmonitor: Umleitung Seriell auf Seriell2:
digitalWrite(nRE, LOW); // Empfänger einschalten
while (Serial2.available()) {
Serial.print(char(Serial2.read()));
}
digitalWrite(nRE, HIGH); // Empfänger ausschalten
while (Serial.available()) {
digitalWrite(DE, HIGH); // Sender einschalten
Serial2.print(char(Serial.read()));
digitalWrite(DE, LOW); // Sender einschalten
}
}

Programmierung 12 – Dimmen einer LED


Zum Dimmen von LEDs wird die LED mit einem PWM-Signal angesteuert. Dabei ist die
Periodendauer (und Frequenz) des Signals konstant, die Einschaltdauer (meist in % der Periodendauer
angegeben) bestimmt die Helligkeit der LED.
Damit eine LED nicht störend flimmert, muss eine PWM-Frequenz von mindestens 50Hz erzeugt
werden. Dies kann im Setup festgelegt werden.

2023-09 Spi Seite 13 Einführung ESP32.odt


Mikrocontroller: Programmierung, Schnittstellen, Sensoren, Aktoren

Zur Erzeugung der PWM-Signale gibt es im ESP32 eine spezielle LED-Dimmerbaugruppe mit 16
Kanälen (0-15). Die Baugruppe kann PWM-Signale mit 8-16 Bit Auflösung erzeugen (entsprechend
256 bis 65536 Helligkeitsstufen bei der LED). Als Einschränkung muss beachtet werden, dass die
PWM-Frequenz * 2Auflösung <= 80MHz sein muss.
Für die Nutzung eines Dimmer-Kanals gibt es 3 Funktionen:
ledcSetup(Kanal, Frequenz, Auflösung); // Einstellung des Kanals
ledcAttachPin(Pin, Kanal); // stellt Verbindung in der Hardware her
ledcWrite(Kanal, Wert); // den Kanal, nicht den Pin schreiben!
// 0 <= Wert < 2^Auflösung
Beispiel: LED mit 100Hz, 12 Bit Auflösung, halben Maximalwert ausgeben
ledcSetup(0, 100, 12); // Einstellung des Kanals
ledcAttachPin(LED_rot, 0); // stellt Verbindung in der Hardware her
ledcWrite(0, 2048); // 12 Bit => 0-4095
Zur besseren Lesbarkeit sollte man für den Kanal (0-15) einen Namen definieren.
// ***** Portpins für Ein-/Ausgänge ***************************************
const int LED_rot=32, LED_gruen=33, Taster_L=2, Taster_R=4;
int Hell_1, Hell_2; // Variablen für den aktuellen Dimmer-Wert

#define LED0_Ch 0
#define LED1_Ch 1

void setup() {
pinMode(LED_rot, OUTPUT); // die LEDs beginnen danach
pinMode(LED_gruen, OUTPUT); // zu leuchten!
pinMode(Taster_L, INPUT_PULLUP); // Taster brauchen hier den
pinMode(Taster_R, INPUT_PULLUP); // Pullup-Widerstand
ledcSetup(LED0_Ch, 100, 12); // Einstellung von Kanal 0
ledcAttachPin(LED_rot, 0); // rote LED nutzt Kanal 0
ledcSetup(LED1_Ch, 100, 12); // Einstellung von Kanal 1
ledcAttachPin(LED_gruen, 1); // grüne LED nutzt Kanal 1
Hell_1 = 0;
Hell_2 = 0;
}

void loop() {
ledcWrite(LED0_Ch, 4095 - Hell_1); // LED ist LOW-Aktiv, daher umdrehen
ledcWrite(LED1_Ch, 4095 - Hell_2); // LED ist LOW-Aktiv, daher umdrehen
Hell_1 += 10; // Wert erhöhen
if( Hell_1 > 4095 ) Hell_1 = 0; // Maximalwert prüfen, 0 setzen
if ( digitalRead(Taster_R) == false ) { // low-aktiv, daher false
Hell_2 += 10; // LED heller
}
if ( digitalRead(Taster_L) == false ) { // low-aktiv, daher false
Hell_2 -= 10; // LED dunkler
}
Hell_2 = constrain( Hell_2, 0, 4095 ); // Wertebereich begrenzen
delay(50); // Wichtig, sonst läuft die Helligkeit bei langem
// Tastendruck zu schnell hoch!
}

2023-09 Spi Seite 14 Einführung ESP32.odt


Mikrocontroller: Programmierung, Schnittstellen, Sensoren, Aktoren

Demoplatine mit Tasmota-Firmware nutzen


Installieren Sie Tasmota auf der Demoplatine. Öffnen Sie in Chrome, Chromium oder Edge
https://tasmota.github.io/install/
Verbinden Sie sich unmittelbar nach dem Flashen von Tasmota und Konfiguration der WLAN-
Zugangsdaten im Browser mit dem Board, öffnen sie die Konsole und geben Sie die beiden Befehle
ein:
setoption1 1
setoption65 1
Damit wird verhindert, dass der ESP32 bei mehrfachen oder langen Tastendrücken seine
Konfiguration zurücksetzt. Wenn die angezeigte Uhrzeit in der Konsole um 1 Stunde abweicht geben
Sie folgenden Befehl ein:
timezone 99
Konfigurieren Sie den ESP32 (manuelle Konfiguration unter Configure Module oder Configure/Other,
Template von hier kopieren, im Feld Template einfügen und Häkchen bei Activate).

Template1 (nur LEDs, Taster und Analogeingänge)


{"NAME":"ESP32-Demo-Minimal","GPIO":
[1,1,96,1,97,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,0,1,1376,1,0,0,0,0,256,257,1,1,4704,0,0,4705],"FLAG":0,"B
ASE":1}

Template2 (mit Drehencoder, I2C, 2. Serieller Schnittstelle, SPI-Bus)


{"NAME":"ESP32-Demo-Max","GPIO":
[1,0,96,0,97,928,1,1,1,1,1,1,3232,3200,896,832,0,640,608,864,0,1,1376,1,0,0,0,0,256,257,6752,6784,
4704,0,0,4705],"FLAG":0,"BASE":1}
Der Drehencoder verändert hier die Farbe der WS2812-LED!

2023-09 Spi Seite 15 Einführung ESP32.odt


Mikrocontroller: Programmierung, Schnittstellen, Sensoren, Aktoren

Anhang Schaltplan der Platine

Layout der Platine

2023-09 Spi Seite 16 Einführung ESP32.odt

Das könnte Ihnen auch gefallen