Laufende Erkenntnisse bei ESP8266-Projekten.
Inhaltsverzeichnis
Die Klasse WifiClient wird für zwei Zwecke genutzt.
Über die Methoden der Klasse WifiClient findet dann der Datenaustauch statt.
WifiClient verwaltet eine internen Pointer auf eine Variable vom Typ ClientContext*. Ist dieser Pointer ungültig (=0, nullptr) ist der Client nicht verbunden.
WiFiClient::operator bool() liefert true, wenn ein ClientContext existiert, also eine TCP-Verbindung besteht. Dieser Operator liefert ggf. einen falschen Wert, wenn eine bestehende Verbindung unvorhergesehen unterbrochen wurde (Kabel gezogen, Stromausfall. o.ä). TCP besitzt keinen internen Mechanismus, der laufend eine bestehende Verbindung überprüft. Eine nicht mehr bestehende wird erst dann erkannt, wenn versucht wird, Daten zu senden. Konkret läuft dies auf ein Timeout beim Warten auf die Empfangsbestätigung vom Empfänger hinaus.
Der Standard-Konstruktor WiFiClient::WiFiClient() liefert ein nicht verbundenes WiFiClient-Objekt aus, d.h. ohne ClientContext.
WifiServer::available() lie liefert je nachdem, ob eine neue TCP-Verbindungsanforderung existiert, ein WiFiClient-Objekt mit oder ohne ClientContext aus. Per
// Check if a client has connected
WiFiClient WifiClient = WifiServer.available();
if (!WifiClient)
{ return;
}
kann abgefragt werden, ob eine TCP-Verbindung besteht.
WiFiClient::stop() schließt die TCP-Verbindung, der ClientContext wird gelöscht. Die nächste Abfrage mit WiFiClient::operator bool() liefert also false.
Die WiFiClient-Klasse bietet eine Variante der Methode write(), mit der ganze Dateien blockweise übertragen werden können. In "WiFiClient.h" ist die Methode
template <typename T>
inline size_t WiFiClient::write(T& source, size_t unitSize)
definiert, die dazu geeignet ist. T muss die Methoden int available() (Anzahl der (noch) vorhandenen Zeichen) und size_t read(uint8_t * buf, size_t size) zur Verfügung stellen. Der Dateiinhalt wird paketweise übertragen. unitSize gibt die Paketgröße an. Die Instanz der Klasse T muss so vorbereitet sein, dass available und read ausgeführt werden können. Im Falle eines File-Objekts muss die Datei geöffnet sein.
Beispiel:
File MyFile = SPIFFS.open("/MyData", "r");
if (MyFile)
{ client.write(MyFile, 1000); // Übertragung mit Blockgröße 1000
}
MyFile.close();
Über das Objekt WiFi kann auf die grundlegenden WLAN-unktionen zugegriffen werden.
Hinweis: Seit der Version 2.4.0 sind die Funktionalitäten aufgeteilt. Die meisten der im Folgenden Elemente findet man nun unter ESP8266WiFiSTA...
Über WPS (Wi-Fi Protected Setup), hier PCB (Push Button Configuration), kann die Verbindung mit einem Router hergestellt werden, ohne Anmeldedaten zu besitzen. Wird am Router der WPS-Knopf gedrückt, werden für kurze Zeit über das PCB-Protokoll die Anmeldedaten zum WLAN einem anfragenden Netzwerkteilnehmer zur Verfügung gestellt. Die Klasse ESP8266WiFiClass (und damit deren einzige Instanz Wifi) stellt entsprechende WPS-Methoden zur Verfügung.
Zuständig ist die Methode WiFi.beginWPSConfig(), die true zurückliefert, wenn der Verbindungsaufbau erfolgreich war, ansonsten false. Leider liefert die aktuelle Version (es müsste die vom 30.11.2015 sein) auch dann true, wenn keine Verbindung zustande gekommen ist. Grund ist ein nicht ausgewerteter Rückgabe Status-Wert. Auf folgende Weise kann das Problem behoben werden:
...
/**
* WPS config
* so far only WPS_TYPE_PBC is supported (SDK 1.2.0)
*/
bool beginWPSConfig(void);
int wps_cb_status; // <------ Diese Zeile einfügen !!!
/*
* Output WiFi settings to an object derived from Print interface (like Serial).
*
*/
...
bool ESP8266WiFiClass::beginWPSConfig(void) {
...
if(!wifi_wps_start()) {
DEBUGV("wps start failed\n");
return false;
}
esp_yield();
// will return here when wifi_wps_status_cb fires
if (WiFi.wps_cb_status !=0) // <------ Diese Zeile einfügen !!!
return false; // <------ Diese Zeile einfügen !!!
return true;
}
void wifi_wps_status_cb(wps_cb_status status)
{ WiFi.wps_cb_status = status; // <------ Diese Zeile einfügen !!!
DEBUGV("wps cb status: %d\r\n", status);
switch (status) {
...
WiFi.wps_cb_status kann die folgenden, in userinterface.h hinterlegten Werte annehmen:
enum wps_cb_status {
WPS_CB_ST_SUCCESS = 0,
WPS_CB_ST_FAILED,
WPS_CB_ST_TIMEOUT,
WPS_CB_ST_WEP,
};
wifi_wps_status_cb() ist keine Member-Funktion. Deshalb kann der Status nicht an beliebige Instanzen der Klasse ESP8266WiFiClass übergeben werden. Der Status wird immer in der Member-Variable der Instanz WiFi gespeichert. Dies ist aber kein Problem, da es keinen Bedarf gibt, eine zweite Instanz der Klasse anzulegen. Es wird immer die Instanz WiFi genutzt.
Der Router muss sich bereits im WPS-Modus befinden, bevor WiFi.beginWPSConfig() aufgerufen wird. Umgekehrt funktioniert es nicht!
Das Verbindungsverhalten zum WLAN wird über mehrere Parameter gesteuert. Zunächst gibt es gespeicherte Konfigurationsdaten. Hierin sind vor allem die SSID und das Passwort hinterlegt, die zur Anmeldung an einen Access Point (Router) benötigt werden. Die Konfigurationsdaten werden einmal persistent im Flash abgelegt und lokal im RAM gehalten. Die Methoden, die für die Verbindungherstellung notwendig sind, arbeiten immer mit den lokalen RAM-Daten.
Beim Programmstart werden die Konfigurationsdaten aus dem Flash-Bereich in den RAM-Bereich kopiert. So kann man Verbindungsdaten über einen Restart des Prozessors retten. Dies ist insbesondere dann wichtig, wenn die Konfigurationsdaten via WPS (s.o.) ermittelt werden, also außerhalb der Laufzeit gar nicht vorliegen.
Die Konfigurationsdaten werden über einen Satz von vier Funktionen kontrolliert. Dies sind:
Methode | Wirkung |
---|---|
wifi_station_get_config | Liest die lokalen Konfigurationsdaten aus dem RAM aus. |
wifi_station_get_config_default | Liest die persistenten Konfigurationsdaten aus dem Flash aus. |
wifi_station_set_config_current | Verändert die lokalen Konfigurationsdaten im RAM. Dies wirkt sich auf den nächsten
Verbindungsversuch aus. Achtung! Eine bestehende Verbindung zum WLAN wird getrennt! |
wifi_station_set_config | Verändert sowohl die persistenten Konfigurationsdaten im Flash als auch die lokalen im
RAM. Achtung! Eine bestehende Verbindung zum WLAN wird getrennt! |
Die Datenstruktur, die zum Datenaustausch dient ist wie folgt definiert:
struct station_config {
uint8 ssid[32];
uint8 password[64];
uint8 bssid_set; // Note: If bssid_set is 1, station will just connect to the router
// with both ssid[] and bssid[] matched. Please check about this.
uint8 bssid[6];
wifi_fast_scan_threshold_t threshold;
};
Das SDK stellt zwei weitere Methoden zum Verbindungsauf- und -abbau zur Verfügung:
Methode | Wirkung |
---|---|
wifi_station_connect | Stellt anhand der lokalen Konfigurationsdaten aus dem RAM eine Verbindung zum Access Point her. |
wifi_station_disconnect | Unterbricht eine aktuell bestehende WLAN-Verbindung zum Access-Point. |
Diese Methoden werden nicht veröffentlicht, man kann also nicht ohne Weiteres in der Arduino-Umgebung darauf zugreifen. Sie werden aber in vielfältiger Weise in den ESP8266-Klassen den der Arduino-Umgebung verwandt. Außerdem verändern die ESPWiFi...-Methoden auch unter der Haube die Konfigurationsdaten. Wenn man die Kontrolle über das Verbindungverhalten des ESP8266 behalten will, man hier besonders wachsam sein. Über die Methode ESP8266WiFiGenericClass::persistent kann gesteuert werden, ob nur die lokalen Daten im RAM oder Flash und RAM gemeinsam betroffen sind.
Methode | Wirkung |
---|---|
ESP8266WiFiSTAClass::begin(<Verbindungsdaten>) | Die Übergebenen Verbindungsdaten werden gespeichert, entweder nur lokal (RAM) oder sowohl im Flash als auch im RAM. Mit den im RAM abgelegten Daten erfolgt dann der Verbindungsaufbau. |
SP8266WiFiSTAClass::begin() | Es wird versucht eine Verbindung mit den hinterlegten Daten aufzubauen (s.u. reconnect). |
ESP8266WiFiSTAClass::disconnect | Die gespeicherten Verbindungsdaten werden gelöscht, entweder nur lokal (RAM) oder sowohl im Flash als auch im RAM. Eine bestehende Verbindung zu einem Access Point wird getrennt. |
ESP8266WiFiSTAClass::reconnect | Eine bestehende Verbindung wird getrennt und dann wird versucht eine Verbindung mit den hinterlegten Daten aufzubauen (s.o. begin()). |
Es gibt zwei weitere Funktionalitäten zum automatischen Verbindungsaufbau:
Funktionalität | Wirkung |
---|---|
AutoConnect | Es wird versucht, beim Programmstart mit den (im Flash) hinterlegten Verbindungsdaten eine Verbindung zu einem Access Point herzustellen (Flash-Bereich wird bei Programmstart in den RAM-Bereich kopiert, s.o.). |
AutoReconnect | Es wird versucht, beim Verbindungsabbruch mit den (im RAM) hinterlegten Verbindungsdaten eine Verbindung zu einem Access Point herzustellen. |
Beide Funktionalitäten können über entsprechende Methoden aktiviert werden.
Nach dem Hochladen ein Programms via Bootloader (COM-Schnittstelle) ist der ESP8266 wir folgt konfiguriert:
Element | Nach dem Hochladen ein Programms via Bootloader (COM-Schnittstelle) | Nach Retsart |
---|---|---|
Konfiguration im Flash | leer bzw. Nullwerte. | behält zuletzt eingestellten Wert |
Konfiguration im RAM | leer bzw. Nullwerte. | Kopie aus dem Flash |
AutoConnect | aktiviert. Es kann aber wegen fehlender Verbindungsdaten keine Verbindung zu einem Access Point hergestellt werden. | behält zuletzt eingestellten Wert. |
AutoReconnect | aktiviert. | aktiviert |
Persistenz | aktiviert | aktiviert |
AutoConnect und Persistenz müssen zu Anfang eines jeden Programms passend gesetzt werden. Die Beibehaltung von AutoConnect stellt sicher, dass der ESP8266 nicht ungewollt eine Verbindung herstellt. Bei dem ersten Programmstart nach dem Programm-Upload gibt es keinen Verbindungsaufbau wegen fehlender Verbindungsdaten. Für alle weiteren Programmstarts kann dies durch Deaktivierung der AutoConnect-Funktion erfolgen.
Noch einmal der Hinweis: bei einem Aufruf von disconnect werden die abgelegten Verbindungsdaten gelöscht. Damit wird auch das AutoReconnect wirksam unterbunden. War vorher Persistenz aktiviert (dies ist die Voreinstellung) werden auch die Konfigurationsdaten im Flash gelöscht. Damit klappt dann auch das AutoConnect beim nächsten Programmstart nicht mehr.
Ein ähnliches Verhalten gibt es bei den Konfigurationsdaten für den Soft-Access-Point.
Zuletzt bleibt noch die Methode Esp.eraseConfig zu erwähnen. Hier wird der Konfigurationsbereich im Flash per flashEraseSector gelöscht. Dies hat nur Auswirkungen auf den Flash-Bereich. Der RAM-Bereich bleibt unberührt. Es wird aber der gesamte(!) Konfigurationsbereich gelöscht.