Gliederung


ESP-1 Modul


WiFi Monitor mit RGB-LED und ESP-01 (Quelltext)


ESP-07 Modul


ESP-12F Modul


PSF-A85 Modul


NodeMCU Board mit ESP-12F Modul


WIFI Kit 8 Board (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 TCP/IP stack (LwIp) von Adam Dunkels. Es werden zahlreiche handliche Module mit diesen Mikrochips verkauft. Am rechten Rand habe ich eine kleine Auswahl davon abgebildet.

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 mit großem Abstand die preisgünstigste Netzwerk-Anbindung zum Basteln.

Technische Daten vom Chip

Der Unterschied zwischen den beiden Chips ist, daß der ESP8266 einen externen Flash Speicher Chip benötigt, während der neuere ESP8285 intern über 1M Byte verfügt. Ansonsten sind die beiden Chips funktional identisch.

Die Basis-Firmare 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-01 Modul basiert auf dem ESP8266. Es wird ohne Abschirmung mit 512k byte (in blau) oder 1M Byte (in schwarz) Flash Speicher verkauft.

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
4/RESETLow=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
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
Auf dem ESP-01 Modul befindet sich eine rote Power-LED, die etwa 0,5mA Strom aufnimmt. Beide Module haben eine blaue LED, die mit TxD verbunden ist, sie leuchtet bei Low Pegel

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 problemlos direkt neben ATmega Mikrocontrollern benutzen.

ESP-07 und ESP-12 Module

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. Es gibt außerdem die komplett abgeschirmte Variante ESP-07S, die man nur mit externer Antenne betreiben kann.

Das ESP-12 Modul ist meistens mit 4M Byte Flash ausgestattet. Als Antenne dient hier eine aufgedruckte Leiterschleife. Die Anschlussleisten haben 2mm Raster.

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


Modul PinArduino PinNameBeschreibung
1/RESETLow=Reset, hat schwachen Pull-Up Widerstand auf High
2ADCAnaloger Eingang 0..1V
3CH_PDMuss auf High gezogen werden, damit der Chip arbeitet. Low=Power Down
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.
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
*) Wenn man den Flash Speicher im langsamen DIO Modus anspricht, kann man Pin 12 eingeschränkt als GPIO10 nutzen. Die Einschränkung ist, daß 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.

Auf dem ESP-07 Modul befindet sich eine rote Power-LED, die etwa 0,5mA Strom aufnimmt. Die auf beiden Modulen vorhandene blaue LED ist mit GPIO2 verbunden und leuchtet bei Low Pegel.

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.

Diese beiden Module haben zwei zusätzliche I/O Pins frei, nämlich GPIO9 und GPIO10. Die Anschlussleisten haben 1,27mm Raster, sie sind daher schwieriger zu löten.


Modul PinArduino PinNameBeschreibung
1AntAntenne (nur PSF-85A)
2ADCAnaloger Eingang 0..1V
3CH_PDMuss auf High gezogen werden, damit der Chip arbeitet. Low=Power Down
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
24/RESETLow=Reset, hat schwachen Pull-Up Widerstand auf High

Die Minimal-Beschaltung zur Nutzung der AT-Firmware ist mit dem obigen ESP-12 Modul identisch (andere Pin Reihenfolge beachten!). Weiter unten finden Sie eine etwas umfangreichere Grundschaltung.

NodeMCU Board

Das NodeMCU Board besteht aus einem ESP-12 Modul, ergänzt um ein 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
3/RESETHat 12k Pull-Up Widerstand und einen Taster mit 470 Ohm nach GND
4CH_PDLow=Power Down, hat 12k Pull-Up
53,3VSpannungsversorgung 3,3V
6GND
7Durch den Flash Speicher belegt, nicht verwendbar *)
8
9
10
11
12
13Reserviert
14
15A017ADCAnaloger Eingang für 0..3,3V weil mit Spannungsteiler 220k+100k Ohm
*) Wenn man den Flash Speicher im langsamen DIO Modus anspricht, kann man J2 Pin 12 eingeschränkt als GPIO10 nutzen. Die Einschränkung ist, daß 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.
Board J1NodeMCU PinArduino PinNameBeschreibung
13,3VSpannungsversorgung 3,3V
2GND
3D101TxD (GPIO1)Serielle Daten über 470 Ohm mit dem USB-UART verbunden, flackert beim Start, darf beim Start nicht auf Low gezogen werden
4D93 RxD (GPIO3)Serielle Daten über 470 Ohm 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 Ohm 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.

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

DTRRTS/ResetGPIO0
AusAusHighHigh
EinEinHighHigh
AusEinLowHigh
EinAusHighLow
Alternativ kann man den Firmware-Upgrade Modus 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 aufgedruckten D-Nummern sind an das NodeMCU Projekt angelehnt. Ich empfehle allerdings, die Arduino Pin Nummern zu verwenden da sie den GPIO Nummern des Chips entsprechen und unabhängig vom eingestellten Board sind.

Der folgende Plan zeigt die richtigen Verbindungen zum Display in Klammern:

Board ObenNodeMCU PinArduino PinESP NameOLED PinBeschreibung
1GND
25VSpannungsversorgung 5V
33,3VSpannungsversorgung 3,3V
4GND
5/CTS Eingang vom USB-UART
6/DTR Ausgang vom USB-UART
7D15GPIO5SCLI²C Takt zum Display. Hat 10k Pull-Up Widerstand
8D101TxD (GPIO1)Serielle Daten oder normaler I/O Pin, direkt mit dem USB-UART verbunden, flackert beim Start, darf beim Start nicht auf Low gezogen werden
9D93RxD (GPIO3)Serielle Daten, direkt mit dem USB-UART verbunden, daher nicht als GPIO verwendbar.
10/RESETHat 1,2k Pull-Up Widerstand und einen Taster nach GND
11D42GPIO2Normaler I/O Pin, flackert beim Start, darf beim Start nicht auf Low gezogen werden
12CH_PDMuss auf High liegen, damit der Chip arbeitet. Hat 10k Pull-Up Widerstand. Low=Power Down
Board UntenNodeMCU PinArduino PinESP NameOLED PinBeschreibung
1GND
25VSpannungsversorgung 5V
33,3VSpannungsversorgung 3,3V
4GND
5D24GPIO4SDAI²C Daten zum Display. Hat 10k Pull-Up Widerstand
6D30GPIO0Low beim Start aktiviert Firmware-Upload, hat 10k Pull-Up Widerstand und einen Taster nach GND, flackert beim Start
7D815GPIO15 (CS)Normaler I/O Pin oder SPI Chip Select, muss beim Start Low sein, hat 10k Pull-Down Widerstand, flackert beim Start
8D713GPIO13 (MOSI)Normaler I/O Pin oder SPI Daten
9D612GPIO12 (MISO)Normaler I/O Pin oder SPI Daten
10D514GPIO14 (SCK)Normaler I/O Pin oder SPI Takt
11D016GPIO16/RESSetzt das Display zurück. Wenn mit /RESET verbunden, kann ein Timer den µC aus dem Deep-Sleep aufwecken.
12A017ADCAnaloger Eingang für 0..3,3V weil mit Spannungsteiler 220k+100k Ohm

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 Modul hat einen Anschluss für Lithium Akkus mit etwa 1000mAH Kapazität. Allerdings lässt er sich kaum sinnvoll nutzen, weil die Ruhestromaufnahme des Moduls nie unter 6mA geht, und einen richtgen Aus-Schalter gibt es auch nicht. Schlimmer noch: wenn der Akku auf 3,3V entladen ist, fällt der Mikrocontroller aus und die Stromaufnahme steigt dann auf 80mA. Hier wäre ein Tiefentladeschutz nötig, ist aber nicht vorhanden. Beim Laden des Akkus wird die Platine ziemlich warm, für die Lebensdauer des Displays ist das nicht gut.

Die Reset Methode zum automatischen Aktivieren des Firmware-Upgrade Modus entspricht dem obigen 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 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 etwa 1cm konnte ich das Problem beheben.

Zur Programmierung des Displays empfehle ich entweder meine OLED Klasse oder die Libraries SSD1306 und GFX von Adafruit.

USB-UART Kabel

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

Für USB-UART Kabel 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 Ergebnisse stehen für Millionen von Multiplikationen pro Minute, wobei ich globale volatile integer Variablen verwendet habe.

CPU Freq.Flash Freq.Flash ModeLatenzRechenleistung
160 Mhz80MhzQIO9ms871
DIO10ms870
40MhzQIO11ms870
DIO12ms870
DOUT12ms869
80 Mhz80MhzQIO9ms435
DIO10ms435
40MhzQIO11ms434
DIO12ms434
DOUT12ms434
Wie man sieht, erreicht der Chip auch mit langsamen Flash Speicher eine ordentliche Leistung, und die Taktfrequenz der CPU hat praktisch keinen Einfluss auf die Latenz.

Deep-Sleep / Power-Down

Sowohl im Deep-Sleep als auch im Power-Down Modus wird die CPU und das RAM abgeschaltet. Der ESP Chip nimmt dann zusammen mit seinem Flash Speicher ca. 25µA bei 3,3V auf.

Nur die integrierte Echtzeituhr (RTC) und ein Wakeup-Timer laufen weiter. Der Inhalt vom RAM geht verloren, so daß die CPU beim Aufwecken komplett neu starten muss.

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

Es gibt drei Möglichkeiten, den ESP Chip wieder aufzuwecken. Alle drei lösen einem kompletten Neustart der Firmware aus:

Die Echtzeituhr stellt 512 bytes RAM bereit, welcher bei Deep-Sleep und Power-Down nicht verloren geht.

Während der /RESET Eingang auf Low gezogen wird nimmt der Chip ca. 25mA auf. Wenn der 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.

Maßnahmen gegen Fehlfunktionen

Sporadische Fehlfunktionen werden meistens durch mangelhafte Stromversorgung verursacht.

Da die Stromaufnahme sehr starke Spitzen hat, empfehle ich dringend, einen 100µF (oder 220µF) Kondensator direkt an die VCC (3,3V) und GND Anschlüsse des Moduls zu löten. Außerdem sollte man den Spannungsregler sorgfältig auswählen. Einige Low-Drop Regler und manche Schaltnetzteile sind ungeeignet, weil sie die extremen Stromschwankungen zu träge ausregeln. Ich habe gute Erfahrungen mit dem LF33CV gemacht, der eignet sich auch für den Betrieb an Batterien bis knapp unter 3 Volt.

Allerdings hat der LF33CV eine Ruhestromaufnahme von etwa 500µA. Für Langzeit-Betrieb empfehle ich daher eher den direkten Betrieb an einer LiFePo4 Zelle, ohne Spannungsregler. Der direkte Betrieb an 3 NiMh Akkus, sowie an LiIo und LiPo Zellen ist nicht ratsam, da diese in vollem Zustand zu viel Spannung abgeben.

Die GND Leitungen sollen möglichst kurz sein und sternförmig zusammen laufen. Achte darauf, dass alle steckbaren 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 Ohm 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. Am besten richtet man das Modul so aus, daß die Antenne an einer Seite über den Rand der Platine hinaus ragt.

Meine Tests mit älteren Firmware Versionen ergaben mehrere Abstürze pro Woche. Aber seit dem SDK 1.5.4 bin ich zufrieden. Mein WiFi-Monitor läuft sogar seit April 2017 ohne Fehlfunktion durch. Zwischendurch habe ich ihn auf SDK 2.0.0 aktualisiert - läuft auch stabil.

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 auffällig langsam. Das Übertragen einer Nachricht mitsamt Antwort dauert meistens 100 - 200ms. Allerdings muss man im WLAN Netz ohnehin mit Verzögerungen in dieser Größenordnung rechnen.

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

4013=512k Byte, 4014=1M Byte, 4015=2M Byte, 4016=4M 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 prinzipell 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 Absatz beschreibe ich nicht alle AT Befehle, was auch schwierig wäre, weil sie sich von Version zu Version unterscheiden. Die hier genannten Befehle funktionieren auf allen mir bekannten Firmware Versionen von 0.21.0.0 bis 1.1.0.0 Die vollständige Dokumentation findest Du als PDF Dokument auf der Webseite von Espressif, sowie in den obigen Firmware downloads. Beachte beim Programmieren, dass das exakte 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 mit Linux auskennen und darf nicht vor gruseliger Dokumentation zurück schrecken.

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) nachzuvollziehen, 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".
  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 Frequency: 40Mhz
    • Flash Mode: QIO
    • Flash Size: 512k (no SPIFFS)
    • Debug Port: disabled
    • Debug Level: keine
    • Reset Method: nodemcu
    • 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.

Alle meine ESP8266 Module liefen mit 80Mhz im QIO Modus stabil. Der ESP8285 muss seinen Flash jedoch mit 40Mhz im langsamen DOUT Modus ansprechen.

Als Flash Größe kann man ruhig weniger einstellen, als tatsächlich vorhanden ist.

Lies die Dokumentation von Arduino und der ESP8266 Erweiterung.

Beispiel Sketches

Beispielprogramme für die ESP8266WiFi Library findest du im Verzeichnis
C:\Users\DeinName\AppData\Local\Arduino15\packages\esp8266\libraries\ESP8266WiFi\examples
oder in
C:\Arduino\hardware\esp8266com\esp8266\libraries\ESP8266WiFi\examples
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 kommen die Pakete in veränderter Reihenfolge beim Empfänger an, das passiert aber nur im Internet.

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.

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. Mein Tipp: Schreibe Dein Programm so, dass es gar keinen Heap belegt. Verzichte möglichst auf malloc(), calloc(), den new Operator und die Verwendung der String Klasse.

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. Die maximale Paketgröße steht in WIFICLIENT_MAX_PACKET_SIZE und ist 1460 Bytes. Wenn man in einem Aufruf zu viele Daten sendet, teilt die Firmware sie automatisch auf mehrere Pakete auf und wartet ggf. auch 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 zurück liefern, vor allem wenn die Daten schnell aufeinander folgend eintreffen. Passe 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.

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 kann man nur einzelne Nachrichten mit maximal 1460 Bytes senden oder empfangen. Unter Umständen schränken Netzwerk-Komponenten die maximale Paketgröße weiter ein. Im heimischen WLAN Netz kannst du dich jedoch auf die 1460 Bytes verlassen.

SSID und Passwort des acces point 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.

NetBeans als besseren Text-Editor für Sketches verwenden

Der Text-Editor von Arduino ist gelinde gesagt knapp ausgestattet. NetBeans hat einen viel besseren Editor, deswegen nutze ich diesen gerne. NetBeans erkennt viele Fehler schon direkt während der Eingabe und hilft Dir beim Tippen. Wenn Du zum Beispiel "Serial." eintippst, listet NetBeans alle Felder und Methoden der Serial Klasse auf. Wenn Du die Strg-Taste gedrückt hälst und dann auf eine #include Datei, einen Klassen-Namen oder einen Methoden-Namen klickst, dann öffnet NetBeans die zugehörige Datei und springt zu der entsprechenden Zeile. Im Navigator Fenster zeigt NetBeans die Struktur der aktuellen Datei an, mit deren Hilfe du blitzschnell zu anderen Codeabschnitten springen kannst.

Einmalige Einrichtung

Installiere dazu Java JDK 8 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.

Finde heraus, in welches Verzeichnis die ESP8266 Erweiterung für Arduino installiert wurde. Wahrscheinlich befindet sie sich entweder in C:\Users\DeinName\AppData\Local\Arduino15\packages\esp8266 oder in C:\Arduino\hardware\esp8266com\esp8266. Bei der folgenden Konfiguration sollst du anstelle von <esp8266> den vollständigen Pfad eingeben.

Gehe dann ins Menü Tools/Options/C/C++/Build Tools.

Wechsele dann zu dem Reiter "Code Assistance". Wähle bei "Tool Collection" die "Arduino_ESP8266" aus und füge dann darunter beim C++ Compiler folgende "Include Directories" hinzu:

Das genügt erstmal für die obigen Beispiel Sketches. In Zukunft wirst du eventuell noch weitere Verzeichnisse unter <esp8266>\libraries hinzufügen müssen, damit NetBeans deine #include Anweisungen auswerten kann.

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

Arduino Sketch in Netbeans öffnen

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.

Wenn du vorher alles richtig konfiguriert hast, kannst du nun in NetBeans deinen Sketch (die *.ino Datei) öffnen und bearbeiten. Die obigen Beispiel-Sketche sind fehlerfrei, sie eignen sich daher sehr gut, die Konfiguration von NetBeans zu überprüfen. Es sollten keine gelben oder gar rote Markierungen erscheinen.

Ich finde diese Anleitung gut, wo die Bedienung des Editors erklärt wird.

Um den Sketch zu überprüfen (compilieren) und hochzuladen musst du weiterhin die Arduino IDE verwenden.

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.

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, daß an eine ernsthafte Nutzung nicht zu denken war. Inzwischen läuft sie weitgehend stabil.

Der ESP8266 Chip kann nur mit der closed-source Firmware des Herstellers genutzt werden, da viele wichtige Details der Hardware geheim gehalten werden. Jede Anwendung ist zwangsläufig auf Gedeih und Verderb 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 heisst es dann, daß man diese in Software implementieren kann.

Sehr viele Händler geben an, dass ihre Module im Deep Sleep Modus nur 10µA Strom aufnehmen würden. Diese Angabe stammt aus einem alten Datenblatt-Entwurf und ist definitiv falsch. Sie berücksichtigt außerdem nicht die Stromaufnahme der On-Board Komponenten, die um den ESP Chip herum platziert wurden: Flash-Speicher, Spannungsregler, Power-LED, Widerstände, USB-UART, etc.

Ich habe 25µA sowohl beim ESP-01 als auch beim ESP-12F gemessen (ohne Power-LED).

Somit übertrifft die Ruhestromaufnahme des ESP Chips einige Konkurrenz-Produkte. Korrekterweise hat der Hersteller seinen ursprünglichen Superlativ "ultra-low-power" schrittweise auf ein moderates "energy-efficient" reduziert. Die ehemals falsche Angabe zur Ruhestromaufnahme wurde auf "typisch" 20µA bei 2,5V erhöht. Der viel interessantere Wert für 3,3V ist leider unbekannt.

Beim Aufwachen aus dem Deep-Sleep Modus geht der RAM Inhalt verloren und die ganze Firmware muss neu booten. Warum der Chip nach dem Aufwachen zusätzlich einen Reset-Impuls benötigt, verrät der Hersteller nicht. Für mich fühlt sich das wie ein grober Designfehler an.

Der "Power Off" Modus mit angeblich 0,5µA Stromaufnahme scheint nicht zu existieren. Falls doch, würde ich mich über jeden konkreten Hinweis freuen.

Als maximale Stromaufnahme gibt der Hersteller 170mA an, aber mehrere Leute (auch ich) haben unabhängig voneinander Impulse im Bereich zwischen 300 und 400mA gemessen.

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.

Ich finde es ein Unding, dass Espressif den Namen des CH_PD Pins mehrfach geändert hat. Inzwischen haben sie wohl selbst den Durchblick verloren, denn im aktuellen Datenblatt Version 1.3 ist die Zeichnung mit "CHIP_EN" beschriftet aber im Text darunter heisst er "CHIP_PU". Ich halte jedenfalls an der ursprünglichen Bezeichnung CH_PD fest, da diese 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.