Der WiFi Monitor überwacht die Erreichbarkeit eines Webservers und zeigt den Status mit einer bunten LED an (Quelltext)
Startseite

ESP8266 und ESP8285 Module Starter Anleitung

Auf dieser Seite findest du alles was man für den Einstieg wissen muss, um mit diesen Produkten eigene WLAN-fähige Geräte zu bauen und zu programmieren.

Die ESP8266 und ESP8285 Chips von Espressif sind 32bit Mikrocontroller mit integrierter WiFi Schnittstelle. Ihre Firmware basiert auf dem Lightweight IP stack, der ursprünglich von Adam Dunkels entwickelt wurde. Es werden zahlreiche handliche Module mit diesen Mikrochips verkauft.

Normalerweise werden die kleinen ESP-Module mit der sogenannten "AT-Firmware" verkauft, die es ermöglicht, sie so ähnlich wie analoge Modems oder Bluetooth Module zu benutzen. Der seriell angeschlossene Computer oder Mikrocontroller sendet dann Befehle an das Modul, um Verbindungen aufzubauen und Daten zu übertragen. In meinem Buch Einstieg in die Elektronik mit Mikrocontrollern habe ich gezeigt, wie man auf diese Weise einen kleinen Webserver zum Fernsteuern einer Lampe baut.

Man kann aber auch eigene Programme direkt durch den ESP Chip ausführen lassen. Mehr dazu weiter unten.

Mit Preisen ab ca. 2 Euro sind die ESP Module eine sehr preisgünstige Netzwerk-Anbindung zum Basteln.

Technische Daten vom Chip

Der Unterschied zwischen den beiden Chips ist, dass der ESP8266 einen externen Flash Speicher benötigt, während der neuere ESP8285 intern über 1M Byte verfügt. Allerdings ist der ESP8285 etwas langsamer.

Die Basis-Firmware v1.5.4 belegt 250k Byte vom Flash Speicher. Sie lässt 47k Byte RAM für eigene Anwendungen frei.

Module

Ein Hinweis vorab: Der Anschluss CH_PD wird an anderen Stellen auch CHIP_EN, CHIP_PU, EN oder ENABLE genannt.

ESP-1 und ESP-01 Modul

Das ESP-1 Modul basiert auf dem ESP8285. Es hat 1M Byte Flash Speicher und einen Deckel zur Abschirmung. Das ESP-1 Modul hat keine Power-LED. Das ESP-01 Modul basiert auf dem ESP8266. Es wird ohne Abschirmung mit 512k Byte (in blau) oder 1M Byte (in schwarz) Flash Speicher verkauft. Die rote Power-LED nimmt etwa 0,5mA Strom auf.

Beide Module sind funktional identisch, das ESP-1 ist allerdings etwas langsamer. Die kleine 8-Polige Stiftleiste ist das auffälligste Merkmal dieser Module. Dadurch lassen sie sich leicht mit einem kurzen Flachkabel absetzen, um den Empfang zu optimieren.

Die Minimal-Beschaltung zur Nutzung der AT-Firmware sieht so aus:


Modul PinArduino PinNameBeschreibung
13RxD (GPIO3)serieller Eingang oder normaler I/O Pin
2VCCSpannungsversorgung 3,3V 400mA
30GPIO0Low beim Start aktiviert Firmware-Upload, muss zum normalen Start offen sein oder auf High liegen
4RESETLow=Reset, hat schwachen Pull-Up Widerstand auf High
52GPIO2Flackert beim Start, darf beim Start nicht auf Low gezogen werden
6CH_PDMuss auf High gezogen werden, damit der Chip arbeitet. Low=Power Down, High=Enabled
7GNDMasse
81TxD (GPIO1)serieller Ausgang oder normaler I/O Pin, ist mit der blauen LED verbunden, flackert beim Start, darf beim Start nicht auf Low gezogen werden

Die Anschluss-leisten beider Module haben 2,5mm Raster. Sie haben beide eine blaue LED, die mit GPIO2 verbunden ist und bei Low Pegel leuchtet.

GPIO15 ist fest mit GND verbunden. Passe auf, dass du ihn nicht versehentlich als Ausgang mit High Pegel programmierst, denn der Kurzschluss könnte den Pin so zerstören, dass der Chip nicht mehr startet.

Die Reichweite der primitiven Antenne (Leiterschleife) ist mit Smartphones vergleichbar. Ohne Abschirmung strahlt dieses Modul stärker ab, als die anderen. Dennoch konnte ich es bisher erfolgreich direkt neben ATmega Mikrocontrollern benutzen.

ESP-07 und ESP-12 Modul

Das ESP-07 Modul ist normalerweise mit einem 1M Byte Flash Chip, einer Keramikantenne und einer Steckbuchse für externe Antennen ausgestattet. Es hat alle sinnvoll nutzbaren Pins herausgeführt. Die rote Power-LED nimmt etwa 0,5mA Strom auf.
Die Variante ESP-07S, ist komplett abgeschirmt und kann nur mit externer Antenne betrieben werden.
Das ESP-12 Modul ist meistens mit 4M Byte Flash ausgestattet. Als Antenne dient hier eine aufgedruckte Leiterschleife. Das ESP-12 Modul hat keine Power-LED.

Die Minimal-Beschaltung zur Nutzung der AT-Firmware sieht für diese Module so aus:


Modul PinArduino PinNameBeschreibung
1RESETLow=Reset, hat schwachen Pull-Up Widerstand auf High
217ADCAnaloger Eingang 0..1V
3CH_PDMuss auf High gezogen werden, damit der Chip arbeitet. Low=Power Down, High=Enabled
416GPIO16Wenn mit RESET verbunden, kann ein Timer den µC aus dem Deep-Sleep aufwecken.
514GPIO14 (SCK)Normaler I/O Pin oder SPI Takt
612GPIO12 (MISO)Normaler I/O Pin oder SPI Daten
713GPIO13 (MOSI)Normaler I/O Pin oder SPI Daten
8VCCSpannungsversorgung 3,3V 400mA
9Durch den Flash Speicher belegt, nicht verwendbar *), nur beim ESP-12E und ESP-12F vorhanden.

*) Wenn man den Flash Speicher im langsamen DIO Modus anspricht, kann man Pin 12 eingeschränkt als GPIO10 nutzen. Die Einschränkung ist, dass ein Low Pegel den Schreibschutz des Flash Speichers aktiviert. Pin 11 = GPIO9 kann man nicht verwenden, denn ein Low Pegel würde den Flash Speicher deaktivieren.

10
11
12
13
14
15GNDMasse
1615GPIO15 (CS)Normaler I/O Pin oder SPI Chip Select, muss beim Start auf Low gezogen werden, flackert beim Start
172GPIO2Ist mit der blauen LED verbunden, flackert beim Start, darf beim Start nicht auf Low gezogen werden
180GPIO0Low beim Start aktiviert Firmware-Upload, muss zum normalen Start offen sein oder auf High liegen
194GPIO4Normaler I/O Pin
205GPIO5Normaler I/O Pin
213RxD (GPIO3)serieller Eingang oder normaler I/O Pin
221TxD (GPIO1)serieller Ausgang oder normaler I/O Pin, flackert beim Start, darf beim Start nicht auf Low gezogen werden

Die Anschlussleisten beider Module haben 2mm Raster. Sie haben beide eine blaue LED, die mit GPIO2 verbunden ist und bei Low Pegel leuchtet.

Die Reichweite der on-board Antenne ist mit Smartphones vergleichbar. Mit einer guten externen Antenne soll das ESP-07 sogar weiter kommen.

PSF-A85 und PSF-B85 Modul

Das PSF-A85 Modul von der Firma Itead hat einen ESP8285 mit Stecker und Lötanschluss für eine externe Antenne. Das PSF-B85 Modul hat hingegen eine sehr kleine keramische Antenne on-board.

Pinbelegung:

Modul PinArduino PinNameBeschreibung
1AntAntenne (nur PSF-85A)
217ADCAnaloger Eingang 0..1V
3CH_PDMuss auf High gezogen werden, damit der Chip arbeitet. Low=Power Down, High=Enabled
416GPIO16Wenn mit RESET verbunden, kann ein Timer den µC aus dem Deep-Sleep aufwecken.
514GPIO14 (SCK)Normaler I/O Pin oder SPI Takt
612GPIO12 (MISO)Normaler I/O Pin oder SPI Daten
713GPIO13 (MOSI)Normaler I/O Pin oder SPI Daten
815GPIO15 (CS)Normaler I/O Pin oder SPI Chip Select, muss beim Start auf Low gezogen werden, flackert beim Start
92GPIO2Flackert beim Start, darf beim Start nicht auf Low gezogen werden
100GPIO0Low beim Start aktiviert Firmware-Upload, muss zum normalen Start offen sein oder auf High liegen
114GPIO4Normaler I/O Pin
12GNDMasse
139GPIO9Normaler I/O Pin
1410GPIO10Normaler I/O Pin
15Durch den internen Flash Speicher belegt, nicht verwendbar
16
17
18
195GPIO5Normaler I/O Pin
20GNDMasse
213RxD (GPIO3)serieller Eingang oder normaler I/O Pin
221TxD (GPIO1)serieller Ausgang oder normaler I/O Pin, flackert beim Start, darf beim Start nicht auf Low gezogen werden
23VCCSpannungsversorgung 3,3V 500mA
24RESETLow=Reset, hat schwachen Pull-Up Widerstand auf High

Diese beiden Module haben im Vergleich zu allen anderen hier vorgestellten Modulen zwei zusätzliche I/O Pins frei, nämlich GPIO9 und GPIO10. Die Anschlussleisten haben 1,27mm Raster, sie sind daher sehr klein und schwieriger zu löten.

Die Minimal-Beschaltung zur Nutzung der AT-Firmware ist abgesehen von der anderen Pin-Reihenfolge mit dem obigen ESP-12 Modul identisch. Weiter unten finden Sie eine etwas umfangreichere Grundschaltung.

NodeMCU Board

Das NodeMCU Board besteht aus einem ESP-12 Modul mit Spannungsregler und USB-UART Interface. Der Flash Speicher ist üblicherweise 4 Megabytes groß. Für die erstem Programmierversuche ist das die ideale Ausstattung.

In Arduino kann man die NodeMCU Pin-Nummern nutzen, wenn man als Board das "NodeMCU" einstellt. Ich empfehle allerdings, die Arduino Pin Nummern zu verwenden da sie den GPIO Nummern des Chips entsprechen und unabhängig vom eingestellten Board sind.


Board J2NodeMCU PinArduino PinNameBeschreibung
1VINSpannungsversorgung 5..9V
2GND
3RESETHat 12k Pull-Up Widerstand und einen Taster mit 470Ω nach GND
4CH_PDLow=Power Down, High=Enabled, hat 12k Pull-Up
53,3VSpannungsversorgung 3,3V
6GND
7Durch den Flash Speicher belegt, nicht verwendbar *)

*) Wenn man den Flash Speicher im langsamen DIO Modus anspricht, kann man J2 Pin 12 eingeschränkt als GPIO10 nutzen. Die Einschränkung ist, dass ein Low Pegel den Schreibschutz des Flash Speichers aktiviert. Pin 11 = GPIO9 kann man nicht verwenden, denn ein Low Pegel würde den Flash Speicher deaktivieren.

8
9
10
11
12
13Reserviert
14
15A017ADCAnaloger Eingang für 0..3,3V weil mit Spannungsteiler 220kΩ + 100kΩ

Board J1NodeMCU PinArduino PinNameBeschreibung
13,3VSpannungsversorgung 3,3V
2GND
3D101TxD (GPIO1)Serieller Ausgang des ESP über 470Ω mit dem USB-UART verbunden, flackert beim Start, darf beim Start nicht auf Low gezogen werden
4D93 RxD (GPIO3)Serieller Eingang des ESP über 470Ω mit dem USB-UART verbunden
5D815GPIO15 (CS)Normaler I/O Pin oder SPI Chip Select, muss beim Start Low sein, hat 12k Pull-Down Widerstand, flackert beim Start
6D713GPIO13 (MOSI)Normaler I/O Pin oder SPI Daten
7D612GPIO12 (MISO)Normaler I/O Pin oder SPI Daten
8D514GPIO14 (SCK)Normaler I/O Pin oder SPI Takt
9GND
103,3VSpannungsversorgung 3,3V
11D42GPIO2Muss beim Start high sein, hat 12k Pull-Up Widerstand, flackert beim Start
12D30GPIO0Low beim Start aktiviert Firmware-Upload, hat 12k Pull-Up Widerstand und einen Taster mit 470Ω nach GND, flackert beim Start
13D24GPIO4Normaler I/O Pin
14D15GPIO5Normaler I/O Pin
15D016GPIO16Ist mit der blauen LED verbunden, die bei Low leuchtet. Die optionale Brücke R3 verbindet diesen Pin mit RESET, dann kann ein Timer den µC aus dem Deep-Sleep aufwecken.

Die Stromversorgung erfolgt wahlweise über USB, ein 3,3V Netzteil oder ein 5..9V Netzteil. Der Eingebaute Spannungsregler hat für Erweiterungen maximal 300mA Leistungsreserve bei 5V Eingangsspannung. Für Batteriebetrieb eignet sich das Board eher nicht, da es eine Ruhestromaufnahme von etwa 15mA hat.

Auf einem Steckbrett läuft das Board zuverlässiger, wenn man die Stifte entfernt, die zum Flash Speicher führen.

Die Reset Leitung und GPIO0 können durch den USB-UART angesteuert werden. Damit aktiviert die Arduino IDE den Firmware-Upgrade Modus vollautomatisch:

DTRRTSRESETGPIO0
AusAusHighHigh
EinEinHighHigh
AusEinLowHigh
EinAusHighLow

Im Schaltplan sind dafür die beiden über Kreuz verbundenen Transistoren zuständig. Alternativ kann man den Firmware-Upgrade Modus manuell aktivieren, indem man beide Tasten drückt und dann den Reset-Taster zuerst loslässt.

WIFI Kit8 Board

Das WIFI Kit 8 Board (alias Heltec HTIT-W8266) aus China hat 4M Byte Flash Speicher, sowie ein winzig kleines OLED Display mit 128x32 Pixeln. Darauf kann man Grafiken und bis zu 4x21 Zeichen Text ausgeben. Der Display Controller vom Typ SSD1306 ist über I²C mit dem ESP8266 verbunden. Die USB Buchse führt zu einem USB-UART von Silabs, Modell CP2104.

Die aufgedruckte Beschriftung und das Pinout-Diagram sind an mehreren Stellen falsch! Die folgenden Zeichnungen zeigen die richtige Pinbelegung, und in Klammern die Verbindungen zum Display:

Obere Stiftleiste:

StiftVersion AVersion BArduino PinBeschreibung
1GND
25VSpannungsversorgung 5V
33,3VSpannungsversorgung 3,3V
4GND
5/CTSClear-To-Send Eingang vom USB-UART, Low aktiv
6/DTRData-Terminal-Ready Ausgang vom USB-UART, Low aktiv
7GPIO55Version A: I²C Takt zum Display mit 10k Pull-Up Widerstand
Version B: Normaler I/O Pin.
8TxD (GPIO1)1Serieller Ausgang des ESP oder normaler I/O Pin, direkt mit dem USB-UART verbunden, flackert beim Start, darf beim Start nicht auf Low gezogen werden
9RxD (GPIO3)3Serieller Eingang des ESP, direkt mit dem Ausgang des USB-UART verbunden, daher nicht als GPIO verwendbar.
10RESETHat 1,2k Pull-Up Widerstand und einen Taster nach GND
11GPIO2GPIO42 bzw. 4Version A: Normaler I/O Pin, flackert beim Start, darf beim Start nicht auf Low gezogen werden.
Version B: Reset Signal zum Display, Low aktiv.
12CH_PDMuss auf High liegen, damit der Chip arbeitet. Hat 10k Pull-Up Widerstand. Low=Power Down, High=Enabled

Untere Stiftleiste:

StiftVersion AVersion BArduino PinBeschreibung
1GND
25VSpannungsversorgung 5V
33,3VSpannungsversorgung 3,3V
4GND
5GPIO4GPIO04 bzw. 0Version A: I²C Daten zum Display mit 10k Pull-Up Widerstand.
Version B: Low beim Start aktiviert Firmware-Upload, hat 10k Pull-Up Widerstand und einen Taster nach GND, flackert beim Start
6GPIO0GPIO20 bzw. 2Version A: Low beim Start aktiviert Firmware-Upload, hat 10k Pull-Up Widerstand und einen Taster nach GND, flackert beim Start.
Version B: Normaler I/O Pin, flackert beim Start, darf beim Start nicht auf Low gezogen werden.
7GPIO15 (CS)15Normaler I/O Pin oder SPI Chip Select, muss beim Start Low sein, hat 10k Pull-Down Widerstand, flackert beim Start
8GPIO13 (MOSI)13Normaler I/O Pin oder SPI Daten
9GPIO12 (MISO)12Normaler I/O Pin oder SPI Daten
10GPIO14 (SCK)14Version A: Normaler I/O Pin oder SPI Takt.
Version B: I²C Takt zum Display mit 10k Pull-Up Widerstand
11GPIO1616Version A: Setzt das Display bei Low Pegel zurück.
Beide Versionen: Wenn mit RESET verbunden, kann ein Timer den µC aus dem Deep-Sleep aufwecken.
12ADC17Analoger Eingang für 0..3,3V weil mit Spannungsteiler 220kΩ + 100kΩ. In Arduino heißt der Pin A0 oder Nummer 17.

Die Stromversorgung erfolgt wahlweise über USB, ein 3,3V Netzteil oder ein 5V Netzteil. Das Display nimmt zusätzlich zum ESP Chip ca. 30mA auf. Der eingebaute Spannungsregler hat für Erweiterungen maximal 200mA Leistungsreserve. Da der 5V Anschluss direkt mit der USB Buchse verbunden ist, soll bei Nutzung von USB nicht gleichzeitig ein 5V Netzteil verwendet werden.

Das Board eignet sich trotz Akku-Anschluss kaum für den Batterie-Betrieb, da seine Ruhestromaufnahme nie unter 6mA fällt. Unterhalb von 3,3V Akku-Spannung fällt das Board aus und saugt dann den Akku mit 80mA leer, bis er kaputt geht. Beim Aufladen wird die Platine ziemlich warm, was für die Lebensdauer des Displays schlecht ist.

Die Reset Methode zum automatischen Aktivieren des Firmware-Upgrade Modus entspricht dem NodeMCU Board. Die beiden Taster ziehen die Leitungen RESET und GPIO0 direkt ohne Schutzwiderstände auf Low. Auch der USB-UART ist direkt mit den I/O Pins für TxD und RxD verbunden, deswegen kann man den RxD Pin (=GPIO3) nicht für andere Zwecke verwenden.

Ich hatte beim Experimentieren auf einem Steckbrett äußerst schlechten WLAN Empfang. Durch Verlängern der Anschlussbeine um 7mm konnte ich das Problem beheben.

Zur Programmierung des Displays empfehle ich entweder meine OLED Klasse oder die Libraries SSD1306 und GFX von Adafruit. Die Adafruit Library muss allerdings für Version B des Moduls angepasst werden (Pin Nummern als Parameter zu Wire.begin() hinzufügen).

USB-UART Kabel

Für die Module ohne USB Anschluss, brauchst du ein USB-UART Kabel oder Adapter, um es mit dem PC zu verbinden. Dabei ist wichtig, dass das Ding 3,3V Pegel hat. Ich schleife immer 1kΩ Widerstände in die Signal-Leitungen ein, zum Schutz gegen Verpolung und Überspannung.

Für USB-UART Produkte mit altem oder gefälschtem PL2303 Chip habe ich hier einen passenden alten Windows Treiber, denn der aktuelle Treiber mag diese Chips nicht.

Anschluss an 5V Mikrocontroller

Der ESP Chip verträgt maximal 3,6V an allen Pins. Die Signale von 5V Mikrocontrollern kann man mit einem Spannungsteiler herabsetzen. Das klappt bei 115200 Baud einwandfrei, höhere Baudraten habe ich nicht ausprobiert.

Signale in umgekehrte Richtung (vom ESP Chip zum Mikrocontroller) benötigen normalerweise keine Anpassung, da die gängigen Mikrocontroller 3,3V bereits als gültigen High Pegel akzeptieren.

Flash Speicher

Der ESP8266 Chip kopiert seine Firmware blockweise von einem externen Flash Speicher in sein internes RAM. Von dort aus wird der Code dann ausgeführt. Der ESP kann bis zu 16M Byte Flash Speicher adressieren, davon jedoch nur maximal 1M Byte als Programmspeicher.

Der ESP8266 unterstützt folgende Zugriffsarten auf den Flash Speicher:

Offensichtlich sind alle ESP8266 Module mit Flash Speicher ausgestattet, der alle vier Modi mit 80Mhz unterstützt.

Der ESP8285 hat einen internen Flash Speicher mit 1M Byte Größe. Dieser muss im DOUT Modus mit 40Mhz angesprochen werden, das ist etwas langsamer. Dafür hat man zwei weitere I/O Pins frei, nämlich GPIO9 und GPIO10.

Die Zugriffsart und Geschwindigkeit wird durch die Firmware festgelegt. Zum Ändern muss sie neu compiliert werden.

Performance

Um einen Eindruck zu bekommen, wie stark sich die Varianten der Flash Anbindung auf die Gesamt-Performance auswirken, habe ich zwei Testreihen durchgeführt.

Die erste Testreihe erfasste die durchschnittliche Antwortzeit bei Anpingen mit jeweils 1kB während das Modul zugleich alle 10 Sekunden die aktuelle Uhrzeit aus dem Internet abruft und auf einem Display anzeigt.

Die zweite Testreihe ermittelte die Rechenleistung des Chips, während er mit einem AP verbunden ist. Die Zahlen stehen für Millionen von Multiplikationen pro Minute.

CPU Freq.Flash Freq.Flash ModeLatenzRechenleistung
160Mhz80MhzQIO und QOUT9ms871
DIO und DOUT10ms870
40MhzQIO und QOUT11ms870
DIO und DOUT12ms870
80Mhz80MhzQIO und QOUT9ms435
DIO und DOUT10ms435
40MhzQIO und QOUT11ms434
DIO und DOUT12ms434

Der Flash Speicher hat also nur einen geringen Einfluss auf die Geschwindigkeit der Programmausführung.

Deep-Sleep / Power-Down

Sowohl im Deep-Sleep als auch im Power-Down Modus wird die CPU und das RAM abgeschaltet. Nur ein Wakeup-Timer und die integrierte Echtzeituhr (RTC) laufen weiter. Der ESP Chip nimmt dann zusammen mit seinem Flash Speicher ca. 25µA bei 3,3V auf.

Es gibt zwei Möglichkeiten, diese Stromspar-Modi zu erreichen:

Beim Aufwachen startet die Firmware neu, dabei geht der Inhalt des RAM verloren. Dazu gibt es drei Möglichkeiten:

Mit ESP.deepSleep(ms) kann der Chip maximal 71 Minuten schlafen. Die Zahl 0 lässt ihn für immer oder bis zum nächsten Hardware-Reset schlafen.

Während der RESET Eingang auf Low gezogen wird nimmt der Chip ca. 25mA auf. Wenn CH_PD zu früh (bevor die Firmware gestartet ist) auf Low gezogen wird, nimmt der Chip ebenfalls ca. 25mA auf und die Firmware startet nicht. Sie startet dann erst, wenn sowohl RESET als auch CH_PD High Pegel haben.

Die Echtzeituhr stellt 512 Bytes zusätzlichen Speicher bereit, dessen Inhalt erhalten bleibt.

Betrieb an Batterien/Akkus

Für Batteriebetrieb eignen sich LiFePo4 Zellen am besten, da ihre Spannung bereits ohne Regler passt.

Für alle anderen Batterietypen braucht man einen Spannungsregler. Besonders sparsam ist der im mikrocontroller.net Forum empfohlene HT7833 mit 4µA Ruhestrom (wenn man einen guten erwischt hat). Die gesamte Ruhestromaufnahme erhöht sich dadurch im Besten Fall auf etwa 29µA.

Bei Betrieb an Akkus sollte man bedenken, dass sich der ESP Chip bei Unterspannung nicht abschaltet. Er nimmt dann dauerhaft etwa 80mA auf. Außerdem steigt die Ruhestromaufnahme vieler Spannungsregler stark an, wenn die Eingangsspannung zu niedrig wird. Bei Akkus ist daher ein zusätzlicher Tiefentladeschutz unbedingt notwendig.

Tipp: Wenn der ESP nur selten aktiv sein soll, empfehle ich einen ATtiny13A als Timer und Tiefentladeschutz zu verwenden. Der kann nämlich direkt an der Batterie hängen und den 3,3V Regler für den ESP nur bei Bedarf einschalten. Dadurch kommt man elegant auf ca. 5µA Ruhestromaufnahme.

Maßnahmen gegen Fehlfunktionen

Sporadische Fehlfunktionen werden meistens durch mangelhafte Stromversorgung verursacht. Die extremen und schnellen Schwankungen der Stromaufnahme (von 25µA auf 400mA) stellen ungewöhnlich hohe Anforderungen an die Spannungsregelung. Es sind längst nicht alle Netzteile und Regler geeignet.

Ich habe sehr gute Erfahrungen mit der Kombination aus 5V Steckernetzteilen (von Huawei und Samsung) mit nachgeschaltetem Spannungsregler vom Typ LF33CV gemacht. Prinzipiell eignet dieser sich auch für den Betrieb an Batterien bis herunter auf 3 Volt, allerdings nimmt er 500µA Ruhestrom auf.

Egal woher die Stromversorgung kommt, empfehle ich dringend, stets einen 100µF (oder 220µF) Kondensator direkt an die Anschlüsse VCC und GND des ESP-Moduls zu löten (beim NodeMCU Board bereits vorhanden).

Die GND Leitungen sollen möglichst kurz sein und sternförmig zusammen laufen. Achte darauf, dass alle Verbindungen zur Stromversorgung richtig fest sitzen. Steckbretter sind daher nicht so gut geeignet.

Damit der Chip sich nicht ungewollt zurück setzt, sollte man sowohl den RESET Pin als auch den CH_PD Pin entweder direkt oder durch einen 2,2kΩ Widerstand mit VCC verbinden. Zu große Widerstände machen die Pins für elektromagnetische Wellen empfänglich. Das gilt übrigens auch für sämtliche weitere Elektronik in der Nähe des Funkmoduls.

In der Nähe der Antenne sind Metallteile zu vermeiden.

Im Kapitel Fallstricke weiter unten findest Du weitere Hinweise, die bei der Programmierung eigener Firmware zu beachten sind.

Firmware Hochladen

Der ESP Chip enthält einen unveränderlichen Bootloader, welcher ein Firmware-Upgrade über den seriellen Port ermöglicht. Beim Reset erwartet der Chip folgende Steuersignale:

Firmware-Upload:

Normaler Start: Alle anderen Kombinationen an diesen vier Pins sind für das Booten über SDIO Interface (als WLAN Netzwerkadapter im PC) und für Test-Zwecke reserviert. Nach dem Start können diese Pins jedoch als frei programmierbare I/O Anschlüsse verwendet werden. Daraus ergibt sich die folgende sinnvolle Grundschaltung:

Die Widerstände vor GPIO0, GPIO2 und GPIO15 schützen vor Kurzschluss, falls man den Pin als Ausgang programmiert. Der Wakeup-Jumper muss verbunden sein, wenn man den Deep-Sleep Modus mit Wakeup-Timer verwendet.

Um den ESP-Chip in den Firmware-Upload Modus zu versetzen, musst du beide Taster gleichzeitig drücken und dann den Reset Taster zuerst loslassen. Der ESP Chip erwartet dann Kommandos und Daten vom PC.

Es gibt zahlreiche grafische Programme zum hochladen der Firmware, aber die scheinen alle etwas zickig zu sein. Daher empfehle ich das Script esptool.py, welches meine beiden weiter unten bereitgestellten Firmware-Pakete enthalten. Zum Ausführen musst Du vorher Python installieren. Achte darauf dass es zur PATH Variable hinzugefügt wird. Anschließend installiere die Library für serielle Ports, indem du im CMD Fenster (Eingabeaufforderung) pip install pyserial eingibst.

Original AT-Firmware

Die AT-Firmware ermöglicht es, das Modul wie ein Modem zu benutzen. Durch das Absetzen von Befehlen auf der seriellen Schnittstelle konfiguriert man das Modul, baut Verbindungen auf und überträgt Daten. Das Modul dient also als Netzwerk-Schnittstelle für einen angeschlossenen Mikrocontroller.

Die AT-Firmware arbeitet mit Windows auffällig langsam zusammen, weil Windows einzelne IP Pakete mit rund 200ms Verzögerung quittiert. Allerdings muss man im WLAN Netz ohnehin mit Verzögerungen in dieser Größenordnung rechnen. Selbst geschriebene Windows Programme können dies durch setzen der Option TCP_NODELAY verbessern.

Falls deine AT-Firmware älter als 0.50.0.0 ist, rate ich zu einem Upgrade, da frühere Versionen noch ziemlich instabil liefen. Die Firma Espressif stellt ihre AT Firmware im bin Verzeichnis des SDK zur Verfügung. Dort befindet sich auch die Textdatei README.md, in der drin steht, welche Dateien abhängig von der Größe des Flash Speichers an welche Position geladen werden müssen.

Die Größe des Flash Speichers erkennt man anhand seiner ID-Nummer. Das esptool kann diese ID-Nummer auslesen:

python esptool.py --port COM6 flash_id

4013512k Byte
40141M Byte
40152M Byte
40164M Byte

Für Module mit nur 512k Byte Flash Speicher muss man die AT-Firmware 0.50.0.0 vom SDK 1.4.0 verwenden. Das ist die letzte Version, die noch dort hinein passt. Für alle größeren Module kannst du die Firmware aus dem aktuellen SDK verwenden. Ich habe mir die AT-Firmware 1.1.0.0 vom SDK 1.5.4 gesichert, weil diese in meinem Langzeit-Test tadellos funktionierte.

Für diese beiden Versionen sind die folgenden Befehle zum Hochladen richtig:

512k Bytepython esptool.py --port COM6 write_flash 0x000000 noboot/eagle.flash.bin 0x040000 noboot/eagle.irom0text.bin 0x03e000 blank.bin 0x07e000 blank.bin 0x07c000 esp_init_data_default.bin
1M Bytepython esptool.py --port COM6 write_flash 0x000000 boot_v1.5.bin 0x001000 512+512\user1.1024.new.2.bin 0x0fc000 esp_init_data_default.bin 0x07e000 blank.bin 0x0fe000 blank.bin
2M Bytepython esptool.py --port COM6 write_flash 0x000000 boot_v1.5.bin 0x001000 512+512\user1.1024.new.2.bin 0x01fc000 esp_init_data_default.bin 0x07e000 blank.bin 0x1fe000 blank.bin
4M Bytepython esptool.py --port COM6 write_flash 0x000000 boot_v1.5.bin 0x001000 512+512\user1.1024.new.2.bin 0x03fc000 esp_init_data_default.bin 0x07e000 blank.bin 0x3fe000 blank.bin

Auf diese Weise kannst du prinzipiell auch andere nicht originale Firmware hochladen. Wobei diese meistens nur aus einer bin Datei bestehen, die an Adresse 0x00000 geladen wird. Im Zweifelsfall schaue in die dazugehörige Dokumentation. Keine Angst: Wenn das Hochladen der Firmware (warum auch immer) fehlschlägt, kann man es einfach nochmal versuchen. Also Taster drücken und nochmal das esptool starten.

AT Befehle

In diesem Kapitel beschreibe ich nur die Befehle, die mir wichtig sind. Sie funktionieren mindestens mit der AT-Firmware von Version 0.21.0.0 bis 1.1.0.0, vielleicht auch mit neueren. Beachte beim Programmieren, dass das Format der Antworten je nach Firmware Version etwas variiert.

Zum Testen von Netzwerkverbindungen empfehle ich das Kommandozeilen-Programm Netcat. Außerdem solltest Du Dir unbedingt das Programm Wireshark anschauen, falls du es noch nicht kennst.

Als Terminalprogramm zur manuellen Eingabe der AT Befehle empfehle ich das Hammer Terminal. Stelle oben "Newline at CR+LF" ein und unten "Send on Enter CR-LF". Die Baudrate ist normalerweise 115200, ansonsten versuche es mit 57600 und 9600 Baud. Nach dem Hardware-Reset zeigt das Terminal Programm einige unlesbare Zeilen an (das ist normal), und dann "ready". Danach kannst du Befehle eintippen.

AT
wird einfach mit "OK" beantwortet. Der Befehl hat keine weitere Funktion, er eignet sich prima, um die serielle Verbindung zu testen.

ATE0
schaltet das Echo der Befehle aus. Dieser Befehl kann die Programmierung vereinfachen und die Geschwindigkeit geringfügig verbessern. Mit der Zahl 1 schaltet man das Echo wieder an. Für manuelles Testen lässt man das Echo am besten eingeschaltet.

AT+GMR
Zeigt die Version der Firmware an, zum Beispiel:

AT version:1.1.0.0(May 11 2016 18:09:56)
SDK version:1.5.4(baaeaebb)
compile time:May 20 2016 15:06:44
OK

AT+CWMODE=1
stellt ein, dass das Modul sich in ein bestehendes WLAN Netz einbuchen soll.

AT+RST
löst einen Software-Neustart aus. Dies ist nach dem Wechsel von CWMODE notwendig.

AT+CWLAP
listet alle erreichbaren Access Points auf. Zum Beispiel:

+CWLAP:(4,"EasyBox-4D2D18",-72,"18:83:bf:4d:2d:b2",2,-46)
+CWLAP:(4,"UPC2827302",-63,"88:f7:c7:52:40:9d",6,-22)
+CWLAP:(0,"Unitymedia WifiSpot",-64,"8a:f7:c7:52:40:9f",6,-22)
+CWLAP:(3,"Muschikatze",-45,"5c:49:79:2d:5b:cd",7,-4)
OK

AT+CWJAP="Muschikatze","supergeheim"
das Modul verbindet sich mit dem WLAN Netz "Muschikatze" und dem angegebenen WPA/WPA2 Passwort. Diese Einstellung wird automatisch dauerhaft gespeichert und beim nächsten Neustart aktiv.

AT+CWJAP?
zeigt an, mit welchem Access Point das Modul gerade verbunden ist. Zum Beispiel:

+CWJAP:"Muschikatze","5c:49:79:2d:5b:cd",7,-60
OK

AT+CIFSR
zeigt die IP-Adresse und MAC Adresse des Moduls an zum Beispiel:

+CIFSR:STAIP,"192.168.0.111"
+CIFSR:STAMAC,"5c:cf:7f:8b:a9:f1"
OK

AT+CIPMUX=1
Stellt ein, dass das Modul mehrere Verbindungen erlauben soll. In diesem Modus sind bis zu 5 gleichzeitige Verbindungen möglich. Die folgenden Beispiele setzen alle voraus, dass dieser Modus aktiviert wurde.
Das Gegenteil wäre der AT+CIPMUX=0, wo das Modul nur als Client und mit nur einer Verbindung genutzt werden kann. In diesem Fall entfällt bei den drauf folgenden Befehlen die Kanalnummer.

AT Firmware als TCP Server benutzen

AT+CIPSERVER=1,5000
dieser Befehl schaltet den Server ein, so dass er Verbindungen auf Port 5000 annimmt. Er kann danach (je nach Firmware Version) maximal 4 oder 5 Verbindungen gleichzeitig annehmen. Danach könntest du zum Beispiel mit Netcat eine Netzwerkverbindung zu diesem Mini-Server aufbauen:

nc 192.168.0.111 5000
Hallo
Welten!

Am seriellen Port des Moduls erscheinen dabei folgende Meldungen:

0,CONNECT
+IPD,0,7:Hallo
+IPD,0,9:Welten!
"0,CONNECT" bedeutet, dass der TCP Server eine Verbindung auf Kanal 0 angenommen hat. "+IPD,0,7:" bedeutet, dass der Server auf Kanal 0 genau 7 Bytes (nämlich "Hallo\r\n") empfangen hat. Nach dem Doppelpunkt folgen genau diese 7 Bytes. Achtung: Wenn der empfangene Text nicht mit einem Zeilenumbruch endet, dann kommt die nächste +IPD Meldung direkt dahinter in der selben Zeile!

AT+CIPSEND=0,18
> Ebenfalls Hallo!
Mit diesem Befehl sendest du eine Antwort an den Computer. Die 0 ist wieder die Kanalnummer und die 18 sind die Anzahl der zu sendenden Bytes (maximal 2048). Nachdem das Modul mit ">" Antwortet, gibst du die zu sendenden Zeichen (oder Bytes) ein. Das Modul bestätigt den Sendevorgang nach kurzer Zeit mit

SEND OK

AT+CIPCLOSE=0
mit diesem Befehl schließt du die Verbindung auf Kanal 0. Das Modul antwortet mit

0,CLOSED
OK

AT Firmware als TCP Client benutzen

AT+CIPSTART=0,"TCP","mail.stefanfrings.de",25
Kanal 0 baut eine TCP Verbindung zu dem angegebenen Server auf Port 25 auf. Anstelle des Hostnamens kannst du auch eine IP Adresse verwenden. Die Antwort lautet zum Beispiel:
0,CONNECT
OK
+IPD,0,96:220 wp039.webpack.hosteurope.de 
   ESMTP Host Europe Mail Service 
   Sat, 08 Apr 2017 23:37:19 +0200

Da dieser Mail-Server sofort antwortet, siehst du auch direkt eine +IPD Zeile. Der Text hinter dem Doppelpunkt ist die Antwort des Servers. Danach kann man mit AT+CIPSEND wie bereits oben beschrieben Daten an den Server senden. Mit AT+CIPCLOSE schließt man die Verbindung.

AT Firmware für UDP benutzen

Beim UDP Protokoll entfällt der Verbindungsaufbau, und damit auch die Begrenzung auf 5 gleichzeitige Verbindungen. Man sendet einzelne Nachrichten an die gewünschten Empfänger und hofft, dass sie zügig dort ankommen. Im Gegensatz zu TCP verzichtet das UDP Protokoll auf die Wiederholung von verlorenen Paketen. Es garantiert nicht einmal, dass alle Pakete in der richtigen Reihenfolge beim Empfänger ankommen (das passiert in lokalen Netzen allerdings praktisch nie).

UDP ist einfacher zu programmieren und nutzt die Übertragungsstrecke effizienter als TCP. Es eignet sich besonders gut zur regelmäßigen Übertragung von Messwerten, wenn einzelne Aussetzer tolerierbar sind (z.B. Temperaturfühler). Leider können Javascripte in Webseiten das UDP Protokoll nicht nutzen.

AT+CIPSTART=0,"UDP","0",0,3002,0
Nach diesem Befehl kann das Modul UDP Nachrichten auf Port 3002 von beliebigen Absendern empfangen. Es kann aber keine Nachrichten senden. Zum Test kannst du das Programm Netcat benutzen:

nc -u 192.168.0.111 3002
Jetzt geht's los!

Wobei du die IP-Adresse deines WLAN Moduls angeben musst. Wenn Du dann in Netcat etwas zum senden eintippst, wird das Modul den Empfang der Nachricht mit +IPD signalisieren.

AT+CIPSTART=0,"UDP","0",0,3002,2
Das Modul empfängt UDP Nachrichten auf Port 3002 von beliebigen Absendern, und es kann Antworten mit AT+CIPSEND zurück schicken. Die Antworten werden immer an den Computer gesendet, von dem zuletzt eine Nachricht empfangen wurde.

AT+CIPSTART=0,"UDP","192.168.0.5",3001,3002,0
Das Modul kann UDP Nachrichten auf Port 3002 von beliebigen Absendern empfangen. Mit dem AT+CIPSEND Befehl wird es Nachrichten an den Computer 192.168.0.5 Port 3001 senden.

Access-Point Modus

Wo kein WLAN Netz existiert, kann das ESP-Modul selbst ein kleines provisorisches Netz aufspannen. Bis zu 4 andere Geräte können sich dann mit dem Modul verbinden, aber sie können sich nicht gegenseitig sehen. Der Access-Point Modus eignet sich sehr gut dazu, ESP Module miteinander zu verbinden. Die Nutzung mit Smartphones und Laptops kann ich nicht empfehlen, da sie bei vielen Geräten unzuverlässig funktioniert. Im Access-Point Modus erreichst du das WLAN Modul unter der IP-Adresse 192.168.4.1.

AT+CWMODE=2
AT+CWSAP="ESP-Modul","supergeheim",5,3
AT+RST
Diese Befehlsfolge aktiviert den Access-Point Modus, und stellt den Namen des Netzes und das Passwort ein. Die 5 gibt den WLAN Kanal an (0-13), und die 3 legt fest, dass WPA2 Verschlüsselung verwendet wird. Diese Einstellung wird automatisch dauerhaft gespeichert und beim nächsten Neustart aktiv.

Es gibt auch einen Kombinierten Modus (AT+CWMODE=3), in welchem sich das Modul mit einem bestehenden WLAN Netz verbindet und außerdem ein eigenes zweites Netz aufspannt.

Möglichkeiten der Programmierung

Anfängern ohne Erfahrung mit 32 Bit Mikrocontrollern empfehle ich, zuerst die Grundlagen mit dem Buch Einblick in die moderne Elektronik ohne viel Theorie zu lernen.

Der chinesische Hersteller Espressif stellt ein SDK bereit, mit dem man eigene Programme in C schreiben kann, die direkt von dem Prozessor des WLAN Moduls ausgeführt werden. Dazu muss man sich allerdings sehr gut mit Linux auskennen und mit unvollständiger Dokumentation arbeiten.

Das NodeMCU Projekt bemüht sich darum, die Programmierung stark zu vereinfachen, indem man auf LUA Scripte setzt. Allerdings leidet dieser Lösungsansatz an notorischem Speichermangel. Das Script und die Daten müssen in ca. 40k Byte RAM gequetscht werden. Immerhin kann man ganz viele Scripte in den Flash Speicher laden, die sich gegenseitig aufrufen. Scrolle auf der Projektseite nach unten, dort gibt es einen Link zu dem "custom firmware build service", wo man sich eine individuell zusammengestellte Firmware erzeugen lässt.

Die meiner Meinung nach Bastel-freundlichste Lösung ist hingegen die ESP8266 Erweiterung für die Arduino IDE. Man programmiert dort in C++. Die Installation der IDE ist kinderleicht, dafür ist die Dokumentation leider nur ausreichend.

Alle drei Varianten haben eines gemeinsam: Es gibt keinen Debugger. Man kann nur durch Ausgeben von Log-Meldungen (auf dem seriellen Port) nachvollziehen, was das Programm gerade macht.

Erste Schritte mit Arduino

Das Arduino Plugin kombiniert das SDK des Herstellers mit dem Arduino Framework, um die Programmierung in C++ zu erleichtern. Das damit erzeugte Compilat enthält daher immer die originale Firmware des Herstellers plus dein eigenes Anwendungsprogramm.
  1. Installiere die Arduino IDE
  2. Starte dann die IDE und gehe ins Menü Datei/Voreinstellungen. Gebe ins Feld "Zusätzliche Boardverwalter-URLs" die Adresse http://arduino.esp8266.com/stable/​package_esp8266com_index.json ein.
  3. Gehe ins Menü Werkzeuge/Board/Boardverwalter. Suche dort nach "esp8266". Installiere die Erweiterung "esp8266 by ESP8266 Community". Wenn du auf "Mehr Infos" klickst, kannst du die Version auswählen. Ich habe Version 2.3.0 intensiv und erfolgreich getestet.
  4. Stelle im Werkzeuge Menü das "Generic ESP8266 Module" ein, bzw. was auch immer du für ein Modul vorliegen hast.
  5. Stelle dann darunter im Werkzeuge Menü die Details zum Board ein, im Zweifelsfall:
    • Flash Mode: meistens geht QIO, sonst versuche DOUT
    • Flash Size: 512k (no SPIFFS)
    • Debug Port: disabled
    • Debug Level: keine
    • lwIP Variant: v1.4 Prebuild (Higher Bandwidth) (ab Version 2.4.0 einstellbar)
    • Reset Method: NodeMCU
    • Crystal Frequency: 26MHz
    • Flash Frequency: 40Mhz
    • CPU Frequency: 80Mhz
    • Upload Speed: 115200
    • Port: Entsprechend deinem USB-UART Kabel
  6. Schreibe dein Programm (Arduino nennt es "Sketch").
  7. Compiliere das Programm, mit dem Menüpunkt Sketch/Überprüfen&Compilieren.
  8. Lade dein Programm hoch, mit dem Menüpunkt Sketch/Hochladen.

Lies die Dokumentation von Arduino und der ESP8266 Erweiterung.

Beispiel Sketches

Beispielprogramme für die ESP8266WiFi Library findest du typischerweise hier: Dort findest du auch noch andere nützliche Beispiele für weitere Libraries, die Du Dir anschauen solltest.

Darüber hinaus zeige ich Dir hier Sketches, die Du für erste Versuche 1:1 in deine Arduino IDE kopieren kannst.

LED Blinker

Der erste Sketch lässt die blaue LED auf dem ESP-12 Modul blinken und gibt auf dem Seriellen Port immer abwechselnd "Tick" und "Tack" aus.
// Beim ESP-12 ist an GPIO2 eine blaue LED
#define LED 2

/** Wird beim Start einmal ausgeführt */
void setup() 
{
    pinMode(LED, OUTPUT);
    Serial.begin(115200);
}

/** Hauptschleife, wird wiederholt ausgeführt. */
void loop() 
{
    digitalWrite(LED, LOW);
    Serial.println(F("Tick"));
    delay(500);

    digitalWrite(LED, HIGH);
    Serial.println(F("Tack"));
    delay(500);
}

UDP Server

Der zweite Sketch empfängt UDP Nachrichten und sendet sie als Echo zurück:
#include <ESP8266WiFi.h>
#include <WiFiUDP.h>

// Beim ESP-12 ist an GPIO2 eine blaue LED
#define LED 2

// Name und Passwort des Access Points
#define SSID "Muschikatze"
#define PASSWORD "supergeheim"

// Der Server nimmt Verbindungen auf diesem Port an
#define PORT 5444
WiFiUDP udpServer;

// Puffer für hereinkommende UDP Nachrichten
char udp_buffer[WIFICLIENT_MAX_PACKET_SIZE+1];


/** Wird beim Start einmal ausgeführt */
void setup()
{
    // LED aus
    pinMode(LED, OUTPUT);
    digitalWrite(LED, HIGH);

    // Seriellen Port initialisieren
    Serial.begin(115200);
    
    // Gib dem seriellen Port Monitor von der Arduino
    // IDE ein bisschen Zeit.
    delay(500);

    // Benutze einen externen AP
    WiFi.mode(WIFI_STA);
    WiFi.begin(SSID, PASSWORD);

    // UDP Server starten
    udpServer.begin(PORT);
}


/** Hauptschleife, wird wiederholt ausgeführt. */
void loop()
{
    process_incoming_udp();
    check_ap_connection();
}


/** Empfange UDP Nachrichten und sende Echo zurück */
void process_incoming_udp()
{   
    if (udpServer.parsePacket()) 
    {
        // Nachricht abholen
        int len=udpServer.read(udp_buffer,sizeof(udp_buffer)-1);
        udp_buffer[len] = 0;
                
        // Nachricht anzeigen
        Serial.print(F("Empfangen von "));
        Serial.print(udpServer.remoteIP());
        Serial.print(":");
        Serial.print(udpServer.remotePort());
        Serial.print(": ");
        Serial.println(udp_buffer);
        
        // Nachricht als Echo zurück senden
        udpServer.beginPacket(udpServer.remoteIP(), udpServer.remotePort());
        udpServer.print(F("Echo: "));
        udpServer.print(udp_buffer); 
        udpServer.endPacket();
        
        // Auf bestimmte Befehle reagieren
        if (strstr(udp_buffer, "an"))
        {
            digitalWrite(LED, LOW);
            udpServer.println(F("LED ist an"));
        }
        else if (strstr(udp_buffer, "aus"))
        {
            digitalWrite(LED, HIGH);
            udpServer.println(F("LED ist aus"));
        }
    }    
}


/** Optional: Zeige an, wenn die Verbindung zum AP geändert wurde. */
void check_ap_connection()
{
    static wl_status_t preStatus = WL_DISCONNECTED;
    
    wl_status_t newStatus = WiFi.status();
    if (newStatus != preStatus)
    {
        if (newStatus == WL_CONNECTED)
        {
            digitalWrite(LED, LOW);
            
            // Die eigene IP-Adresse anzeigen
            Serial.print(F("AP Verbindung aufgebaut, lausche auf "));
            Serial.print(WiFi.localIP());
            Serial.print(":");
            Serial.println(PORT);
        }
        else
        {
            digitalWrite(LED, HIGH);
            Serial.println(F("AP Verbindung verloren"));
        }
        preStatus = newStatus;
    }
}
Wenn die Verbindung zum AP aufgebaut wurde, geht die blaue LED an. Auf dem seriellen Port gibt das Programm hilfreiche Statusmeldungen aus. So kannst du den Server mit Netcat testen:
nc -u 192.168.0.111 5444
Affen machen alles nach
Echo: Affen machen alles nach
aus
LED ist aus
an
LED ist an
Der UDP Service ist sehr einfach zu programmieren, weil man sich darauf verlassen kann, dass Nachrichten 1:1 wie gesendet eintreffen, also weder zerstückelt noch zusammengefügt. Außerdem braucht man sich nicht um den Verbindungs-Aufbau kümmern. Der größte Nachteil ist, dass UDP Nachrichten manchmal unbemerkt verloren gehen - besonders in drahtlosen Netzen. In seltenen Fällen erreichen schnell aufeinander folgende Pakete ihren Empfänger in veränderter Reihenfolge.

TCP Server

Der dritte Sketch zeigt einen TCP Server, der mehrere Verbindungen gleichzeitig bedient:
#include <ESP8266WiFi.h>

// Beim ESP-12 ist an GPIO2 eine blaue LED
#define LED 2

// Name und Passwort des Access Points
#define SSID "Muschikatze"
#define PASSWORD "supergeheim"

// Der Server nimmt Verbindungen auf diesem Port an
#define PORT 5333
WiFiServer tcpServer(PORT);

// Objekte für Verbindungen
#define MAX_TCP_CONNECTIONS 5
WiFiClient clients[MAX_TCP_CONNECTIONS];

// Puffer für hereinkommende Textzeilen
char tcp_buffer[MAX_TCP_CONNECTIONS][30];


/** Wird beim Start einmal ausgeführt */
void setup()
{
    // LED aus
    pinMode(LED, OUTPUT);
    digitalWrite(LED, HIGH);

    // Seriellen Port initialisieren
    Serial.begin(115200);
    
    // Gib dem seriellen Port Monitor von der Arduino
    // IDE ein bisschen Zeit.
    delay(500);

    // Benutze einen externen AP
    WiFi.mode(WIFI_STA);
    WiFi.begin(SSID, PASSWORD);

    // TCP Server starten
    tcpServer.begin();
}


/** Hauptschleife, wird wiederholt ausgeführt. */
void loop()
{
    handle_new_connections();
    process_incoming_tcp();
    check_ap_connection();
}


/** 
 * Lege neue Verbindungen im Array ab und 
 * sende eine Willkommens-Nachricht.
 */
void handle_new_connections()
{
    WiFiClient client = tcpServer.available();
    if (client)
    {
        Serial.print(F("Neue Verbindung von "));
        Serial.println(client.remoteIP().toString());
        
        // Finde einen freien Platz im Array        
        for (int i = 0; i < MAX_TCP_CONNECTIONS; i++)
        {
            if (!clients[i].connected())
            {
                // Freien Platz gefunden
                clients[i] = client;
                tcp_buffer[i][0]=0;
                Serial.print(F("Kanal="));
                Serial.println(i);
                
                // Mit einer Willkommes-Meldung begrüßen
                client.println(F("Hallo Welt!"));
                return;
            }
        }
        Serial.println(F("Zu viele Verbindungen"));
        client.stop();
    }
}


/** Empfange TCP Nachrichten und sende Echo zurück */
void process_incoming_tcp()
{   
    static int i=0;
    
    // Bei jedem Aufruf wird nur ein TCP client abgefragt
    if (clients[i].available())
    {
        // Eine Zeile einlesen
        if (append_until(clients[i],tcp_buffer[i],sizeof(tcp_buffer[i]),'\n'))
        {        
            // Nachricht anzeigen
            Serial.print(F("Empfangen von "));
            Serial.print(i);
            Serial.print(": ");
            Serial.print(tcp_buffer[i]);

            // Nachricht als Echo zurück senden
            clients[i].print(F("Echo: "));
            clients[i].print(tcp_buffer[i]);
            
            // Auf bestimmte Befehle reagieren
            if (strstr(tcp_buffer[i], "an"))
            {
                digitalWrite(LED, LOW);
                clients[i].println(F("LED ist an"));
            }
            else if (strstr(tcp_buffer[i], "aus"))
            {
                digitalWrite(LED, HIGH);
                clients[i].println(F("LED ist aus"));
            }    
            
            // Bereit machen für das Empfangen der nächsten Zeile
            tcp_buffer[i][0]=0;
        }
    }
    
    // Zum nächsten TCP client weiter schalten
    if (++i >= MAX_TCP_CONNECTIONS)
    {
        i=0;
    }
}


/**
 * Hänge Zeichen von einem Stream an einen Puffer an, bis das terminierende Zeichen
 * erreicht wurde. Wenn der Puffer überläuft, lies trotzdem weiter aber verwerfe
 * die überschüssigen Zeichen.
 *
 * @param source Der Quell-Stream.
 * @param buffer Der Ziel-Puffer, muss mit '\0' zerminiert sein.
 * @param bufSize Größe des Ziel-Puffers
 * @param terminator Das letzte Zeichen, das gelesen werden soll, zum Beispiel '\n'.
 * @return True wennn das terminierende Zeichen erreicht wurde.
 */
bool append_until(Stream& source, char* buffer, int bufSize, char terminator)
{
    int data=source.read();
    if (data>=0)
    {
        int len=strlen(buffer);
        do
        {
            if (len<bufSize-1)
            {
                buffer[len++]=data;
            }
            if (data==terminator)
            {
                buffer[len]=0;
                return true;
            }
            data=source.read();
        }
        while (data>=0);
        buffer[len]=0;  
    }
    return false;
}


/** Optional: Zeige an, wenn die Verbindung zum AP geändert wurde. */
void check_ap_connection()
{
    static wl_status_t preStatus = WL_DISCONNECTED;
    
    wl_status_t newStatus = WiFi.status();
    if (newStatus != preStatus)
    {
        if (newStatus == WL_CONNECTED)
        {
            digitalWrite(LED, LOW);
            
            // Die eigene IP-Adresse anzeigen
            Serial.print(F("AP Verbindung aufgebaut, lausche auf "));
            Serial.print(WiFi.localIP());
            Serial.print(":");
            Serial.println(PORT);
        }
        else
        {
            digitalWrite(LED, HIGH);
            Serial.println(F("AP Verbindung verloren"));
        }
        preStatus = newStatus;
    }
}
Wenn die Verbindung zum AP aufgebaut wurde, geht die blaue LED an. Auf dem seriellen Port gibt das Programm hilfreiche Statusmeldungen aus. So kannst du die Anwendung mit Netcat testen:
nc 192.168.0.111 5333
Hallo Welt!
Ich bin Groot
Echo: Ich bin Groot
aus
LED ist aus
an
LED ist an
Bei TCP kann man sich im Gegensatz zu UDP absolut sicher sein, dass innerhalb einer Verbindung keine Nachrichten verloren gehen und dass die Reihenfolge nicht durcheinander gerät. Aber eine Textzeile kommt unter Umständen zerteilt in mehreren Segmenten an, selbst wenn sie kurz ist. Oder ein Segment liefert zwei Textzeilen, die ursprünglich einzeln gesendet wurden.

Deswegen habe ich die Funktion append_until() entwickelt, die Eingaben Zeilenweise unabhängig von der Segmentierung einsammelt. Im Gegensatz zu Arduinos readStringUntil() hat sie folgende Vorteile:

WiFi Config Service

Bei den obigen Beispielen wurden die Zugangsparameter für den WLAN Access Point einfach fest in den Quelltext geschrieben. Für Nicht-Entwickler ist diese Vorgehensweise jedoch ungeeignet. Eigentlich wurde für diesen Zweck WPS erfunden, doch das hat bei mir so oft nicht funktioniert, dass ich es gar nicht mehr verwende.

Eine naheliegende Alternative wäre, ein provisorisches WLAN Netz zu erzeugen und darüber eine Webseite zur Konfiguration bereit zu stellen. Mein kleines WiFi-Monitor Projekt enthält die Klasse ConfigService, welche genau das tut. An dem folgenden verkürzten Quelltext siehst du, wie einfach der WiFi Config Service anzuwenden ist:

#include "ConfigService.h"

ConfigService configService(80);

void setup()
{
    configService.begin(5,"WiFi Monitor","");
    ...
}

void loop()
{
    configService.run();
    ...
}
Nach dem Einschalten der Stromversorgung aktiviert das Programm für 5 Minuten ein provisorisches Netz mit dem Namen "WiFi Monitor" ohne Passwort. Du kannst deinen Computer oder Smartphone mit diesem Netz verbinden und dann die Seite http://192.168.4.1 aufrufen:

Dort kannst du nun die Zugangsdaten deiner Fritz Box (oder was auch immer du für einen WLAN Router hast) eingeben und absenden. Wenn sie richtig sind, baut das Modul sofort eine Verbindung zum Access Point auf. Das Hauptprogramm in diesem Beispielprojekt testet die Erreichbarkeit des konfigurierten Webservers und zeigt den Status anhand einer bunten LED an.

WLAN vorübergehend aus schalten

Ohne WLAN nimmt der Chip 10-15mA Strom auf. Um die WLAN Schnittstelle vorübergehend aus zu schalten, muss man den Wakeup-Timer einbeziehen:
  ESP.deepSleep(1, WAKE_RF_DISABLED); 
  delay(100);
Der Mikrocontroller legt sich schlafen und startet sofort mit deaktiviertem WLAN neu durch. So reaktiviert man die Schnittstelle wieder:
  ESP.deepSleep(1, WAKE_RFCAL); 
  delay(100);
Dabei muss der GPIO16 Pin mit dem Reset Eingang verbunden sein. Ein abgeschaltetes WLAN kann man nur auf diese Art wieder einschalten. Vielen Dank an Michael U. für diesen wertvollen Hinweis.

Fallstricke

Wenn dein Programm unzuverlässig läuft, kontrolliere zuerst meine Ratschläge aus dem Kapitel Maßnahmen gegen Fehlfunktionen. Wenn das alles Ok ist, dann kontrolliere die folgenden Aspekte in deinem eigenen Programm:

Es ist wichtig, das die Firmware genügend Rechenzeit ab bekommt, sonst fällt das Modul aus. Alle eigenen Programmteile müssen spätestens nach 50ms (empfohlen werden 20ms) die Kontrolle an die Firmware abgeben, indem entweder die loop() Funktion verlassen wird, oder delay(ms) oder yield() aufgerufen wird.

Für eigene Programme stehen nur ungefähr 4k Byte Stack zur Verfügung. Wenn lokale Variablen in Prozeduren mehr Platz belegen, stürzt das Programm ab. Größere Datenmengen (z.B. Puffer für Daten) sollen daher vorzugsweise global deklariert werden, damit sie außerhalb des Stack liegen.

Bedenke das String Objekte und Zeichenketten normalerweise im RAM liegen. Hier ist beschrieben, wie man sie stattdessen im Flash Speicher anlegt, um RAM zu sparen.

Dynamische Speicherverwaltung auf dem Heap verlangt besondere Sorgfalt, damit der Heap nicht fragmentiert wird. Sobald der eigene Code die Schlüsselwörter delete oder free() enthält, sollte man die Langzeit-Stabilität der Anwendung gründlich überprüfen. Außerdem rate ich dringend dazu, String Objekte zu meiden, weil sie davon intensiv Gebrauch machen.

Wenn man Daten sendet, wird aus jedem Client::print() und Client::write() Aufruf ein einzelnes Ethernet Paket. Viele kleine Pakete reduzieren die Performance, da sie genau so lange dauern, wie große Pakete. Wenn man in einem Aufruf zu viele Daten sendet, teilt die Firmware sie automatisch auf mehrere Pakete auf und wartet notfalls sogar darauf, dass Platz im Sendepuffer frei wird.

Da der Empfangspuffer mehrere Pakete aufnehmen kann, muss man bei der Programmierung davon ausgehen, dass Client::available() und Client::readBytes() einige Kilobytes am Stück zurück liefern, vor allem wenn die Daten schnell aufeinander folgend eintreffen. Passe daher beim Auslesen der Daten auf, weder deine Puffer noch den Stack zu überladen.

Benutze die Methoden readString() und readStringUntil() am besten gar nicht, weil sie beliebig lange Strings ansammeln, die leicht einen Speicher-Überlauf auslösen. Besser geht es mit meiner append_until() Funktion aus dem obigen Beispiel-Sketch.

Verlasse dich nicht darauf, dass gesendete TCP Nachrichten den Empfänger in der gleichen Segmentierung erreichen, wie sie gesendet wurden. Die Netzwerk-Komponenten dürfen die Segmente des TCP Datenstromes zerlegen und neu zusammenfügen. In dieser Diskussion wurden die Effekte der Segmentierung sehr schön erklärt.

Beim UDP Protokoll sendet und empfängt man Pakete mit der oben genannten maximalen Größe. Diese kann unter Umständen durch Netzwerk Komponenten (Router, Modems, etc.) noch weiter eingeschränkt sein.

SSID und Passwort des Acces points werden automatisch dauerhaft gespeichert, aber nur wenn sie gültig sind. Das Passwort muss entweder ein Leerstring ("") sein oder mindestens 8 Zeichen lang sein.

Interrupt-Handler Routinen müssen mit dem Makro ICACHE_RAM_ATTR gekennzeichnet werden, sonst stürzt der Mikrocontroller ab.

Es wird empfohlen, nach ESP.deepSleep() immer einen delay(100) einzufügen, damit das Einschlafen zuverlässig funktioniert.

Andere IDE als besseren Text-Editor verwenden

Der Text-Editor von Arduino ist gelinde gesagt knapp ausgestattet. Andere Entwicklungsumgebungen sind viel komfortabler, deswegen nutze ich diesen gerne. Sie erkennen viele Fehler schon direkt während der Eingabe und helfen beim Tippen, indem sie Vorschläge anzeigen. Wenn Du zum Beispiel "Serial." eintippst, listet sie IDE alle Felder und Methoden der Serial Klasse auf. Wenn Du die Strg-Taste gedrückt hältst und dann auf eine #include Datei, einen Klassen-Namen oder einen Methoden-Namen klickst, dann öffnet sich die zugehörige Datei und der Cursor springt zu der entsprechenden Zeile.

Ich habe sowohl mit Netbeans als auch mit Qt-Creator gute Erfahrungen gemacht. Beide IDEs halte ich für gleich gut geeignet.

In den folgenden Absätzen habe ich das Installationsverzeichnis der Arduino IDE (z.B. C:\Program files (x86)\Arduino) mit <arduino> abgekürzt. Das Verzeichnis der Arduino ESP8266 Erweiterung habe ich mit <esp8266> abgekürzt:

In der Arduino IDE sollst du in das Menü Datei/Voreinstellungen gehen und die Option "Externen Editor verwenden" einschalten.

NetBeans

Installiere das aktuelle Java JDK und NetBeans in der Variante für C/C++. Wenn du NetBeans bereits für andere Programmiersprachen installiert hast, dann genügt es, das C/C++ Plugin hinzuzufügen.

Starte NetBeans und gehe dann ins Menü Tools/Options/Miscellaneous/Files. Wähle die Dateiendung C++ aus. Klicke dann auf den "New..." Knopf und füge die Endung "ino" hinzu. Danach erkennt NetBeans die *.ino Dateien als C++ Quelltext an.

Gehe ins Menü Tools/Options/C/C++/Build Tools. Füge eine neue "Tool Collection" mit dem Basis-Verzeichnis <esp8266>/tools/xtensa-lx106-elf-gcc (oder so ähnlich) hinzu. Darunter muss die "Tool Collection Family" auf "GNU MinGW" (unter Windows) bzw. "GNU" (unter Linux) eingestellt werden. Der Name für die neue Tool Collection soll "ESP8266" sein.

Der C-Compiler heißt bin/xtensa-lx106-elf-gcc, und der C++ Compiler heißt bin/xtensa-lx106-elf-cpp. Die anderen Programm-Felder darunter dürfen leer bleiben:

Wechsle dann zu dem Reiter "Code Assistance". Wähle bei "Tool Collection" die "ESP8266" aus und füge dann darunter beim C++ Compiler unter "Include Directories" alle verwendeten ESP8266 Libraries hinzu, und die avr/core Library von Arduino:

Darunter fügst du eine "Macro Definition" mit dem Wert "ARDUINO=010805" ein, das ist die Versionsnummer 1.8.5 deiner Arduino IDE.

Erstelle in deinem Arduino Projektverzeichnis (wo sich die *.ino Datei befindet) eine leere Datei mit dem Namen "Makefile" (achte dabei auf die groß/klein-Schreibung). Gehe danach in NetBeans auf den Menüpunkt File/New Project. Wähle als Projekt-Typ "C/C++ with Existing Sources" aus. Im nächsten Dialog gibst du das Projektverzeichnis und die Tool Collection ist "ESP8266" an. Die Option "Use Build Analyzer" soll ausgeschaltet werden.

Nun kannst du deinen Sketch mit Netbeans editieren. Um ihn zu überprüfen (compilieren) und hochzuladen musst du weiterhin die Arduino IDE verwenden. Eventuell magst du dir diese Anleitung anschauen, wo die Bedienung des Editors erklärt wird.

Manchmal funktionieren die ganzen erweiterten Editor-Funktionen bei einzelnen Dateien nicht. Klicke dann in der Projekt-Ansicht mit der rechten Maustaste auf den Dateinamen und dann auf Properties. In dem folgenden Dialog musst Du die Option "Add To Parse" einschalten, dann funktioniert es.

Qt-Creator

Verwendet den Installer vom Qt SDK, um das Programm Qt-Creator zu installieren. Die anderen Komponenten brauchst du nicht. Starte das Programm und gehe dann ins Menü Extras/Einstellungen/Erstellung und Ausführung/Compiler. Dort musst du den C Compiler und den C++ Compiler aus der ESP8266 Erweiterung für Arduino als Typ "MinGW" (unter Windows) bzw. "GCC" (unter Linux) hinzufügen. Bei mir sieht das unter Linux so aus:

Der C-Compiler heißt xtensa-lx106-elf-gcc, und der C++ Compiler heißt xtensa-lx106-elf-cpp. Beide Programme liegen im Verzeichnis <esp8266>/tools/xtensa-lx106-elf\bin (oder so ähnlich). Qt-Creator nutzt diese beiden Befehle, um die Standard-C Libraries automatisch zu finden. Danach gehe in den "Kits" Reiter und lege dort ein neues Kit mit den gerade angelegten Compilern an:

Die CMAKE Konfiguration spielt keine Rolle, lasse sie einfach unverändert.

Anschließend kannst du das Arduino-Projekt über das Menü "Datei/Neu/Projekt importieren/Import eines existierenden Projekts" importieren. Gebe danach in der Datei "ProjektName.files" alle Dateien an, die du mit Qt-Creator editieren möchtest - also die *.ino, *.h und *.cpp Dateien.

In der Datei "ProjektName.includes" musst du die Verzeichnisse der verwendeten ESP8266 Libraries hinzufügen, sowie die avr/core Library von Arduino:

<esp8266>/hardware/esp8266/2.4.0/​cores/esp8266
<esp8266>/hardware/esp8266/2.4.0/​variants/generic
<esp8266>/hardware/esp8266/2.4.0/​tools/sdk/include
<esp8266>/hardware/esp8266/2.4.0/​libraries/ESP8266WiFi/src
<esp8266>/hardware/esp8266/2.4.0/​libraries/EEPROM
<esp8266>/hardware/esp8266/2.4.0/​libraries/Wire
<esp8266>/hardware/esp8266/2.4.0/​libraries/SPI
<arduino>/hardware/arduino/avr/​cores/arduino

In der Datei "ProjektName.config" gibst du die Version 1.8.5 deiner Arduino IDE an:

#define ARDUINO 010805

Nun kannst du deinen Sketch mit Qt-Creator editieren. Um ihn zu überprüfen (compilieren) und hochzuladen musst du weiterhin die Arduino IDE verwenden.

Kritik

Der Hersteller hat seinen Chip zu früh und mit falschen Versprechungen (insbesondere bezüglich Stromaufnahme und Leistungsdaten) vermarktet, während die Firmware noch extrem instabil war. In den ersten zwei Jahren war sie noch so unzuverlässig, dass an eine ernsthafte Nutzung nicht zu denken war. Inzwischen läuft sie jedoch stabil.

Der ESP8266 Chip kann nur mit der closed-source Firmware des Herstellers genutzt werden, da viele wichtige Details der Hardware geheim gehalten werden. Sogar die Arduino Erweiterung und NodeMCU enthalten die Firmware von Espressif. Jede Anwendung ist zwangsläufig den Eigenheiten dieser Firmware ausgeliefert.

Das Datenblatt erwähnt immer noch zahlreiche Features, die der Chip gar nicht hat (z.B. I²C und PWM). Im "Kleingedruckten" dazu heißt es dann, dass man diese in Software implementieren "kann".

Sehr viele Händler geben die Stromaufnahme ihrer Module falsch an. Halte dich besser an meine obigen Angaben. Der im Datenblatt genannte "Power Off" Modus mit angeblich 0,5µA Stromaufnahme scheint nicht zu existieren. Falls doch, würde ich mich über jeden konkreten Hinweis freuen.

Bei dem Deep-Sleep Modus muss dem Hersteller ein grober Design-Fehler unterlaufen sein, denn der Chip hängt sich beim Aufwachen auf. Daraus folgt der Workaround, zusätzlich einen Reset auslösen zu müssen. Aber so muss nun die ganze Firmware neu starten und der Inhalt des RAM geht verloren. Das ist eigentlich nicht der Sinn eines Sleep Modus.

Der ADC Eingang misst 0 bis 1 Volt mit 10 Bit, aber wegen der Einstreuung des eigenen HF Signals muss man mit 0,2V (!) Abweichung rechnen. Eigentlich kann man den Eingang nur gebrauchen, während der HF Teil deaktiviert ist, was wiederum Neustarts erfordert. Im Datenblatt findet man diese Info nur gut versteckt zwischen den Zeilen.

Espressif hat den Namen des CH_PD Pins mehrfach geändert hat. Inzwischen haben sie wohl selbst den Durchblick verloren, denn im Datenblatt vom 11.2017 ist die Zeichnung mit "CHIP_EN" beschriftet aber im Text darunter heißt er "CHIP_PU". Ich halte jedenfalls an der ursprünglichen Bezeichnung CH_PD fest, da sie am weitesten verbreitet ist.

Der Watchdog erfüllt seinen Zweck nicht gut. Bei Programmfehlern (z.B. Stack Überlauf) kommt es sehr häufig vor, dass der Chip nach einem Watchdog-Reset nicht mehr richtig startet. Ein externer Hardware Reset funktioniert aber zuverlässig.

Auf der Webseite von Dipl. Ing. (BA) Frank Carius findest du noch weitere Infos zu dem ESP8266 und ganz viele Links zu relevanten Webseiten.