ESP8266 Arduino-Programm: setLED

Dieses Programm ist mein erster Versuch, das WLAN Modul ESP8266 über den Arduino Uno zu bändigen. Die Funktion des Programms erscheint recht simpel. Über Steuerbefehle lässt sich die L-Led (digital Pin13), die auf fast jedem Arduino Board vorhanden ist, Ein- und Ausschalten. Es gibt zwei Varianten. Die erste Variante erlaubt über den Browser direkt durch Eingabe einer URL die LED zu schalten. Die zweite Variante läuft über einen eigenen TCP-Client, wie ich ihn z.B. in dem Artikel über die ersten Schritte verwendet habe.

Verwendung

In dem Programm können über die Variablen

#define xSSID „Hier könnte Ihr Netzwerk stehen“

#define xPASSWORT “ Hier könnte Ihr Netzwerk stehen „

#define xPORT 80

passende Einstellungen zum Netzwerk, Passwort und Port des Servers vorgenommen werden.

Zum Aktiveren der LED über den Browser muss man die IP-Adresse gefolgt von Doppelpunkt dem Port und /Led1 eingeben.

Beispiel:

192.168.178.37:80/Led1

Zum Deaktivieren hängt man nichts oder beliebiges hinten an

192.168.178.37:80/Led0

192.168.178.37:80

192.168.178.37:80/Lalelu

Beim Senden über einen TCP-Client muss das Kommando

set 0

Oder

set 1

Lauten. Dass Leerzeichen nach set ist wichtig.

App: TCP-Client im Google PlayStore

Dies ist natürlich nur ein erster Test. Wenn das Modul irgendwo zum Einsatz kommt, muss sollte man eine praktischere Befehlsstruktur entwickeln

Das Programm

Das WLAN-Modul wird über die Software basierte serielle Schnittstelle angesprochen und hängt an den Pins 10 und 11. Ich musste die SoftwareSerial-Library etwas anpassen. Genauer gesagt habe ich den internen Buffer von 64 Bytes auf 256 Bytes erhöht. Dies war wichtig, da mir am Anfang zu viele Zeichen verloren gegangen sind. Evtl. erlaubt die aktuelle Version auch einen kleineren Buffer, ich habe dies noch nicht getestet. Damit es keine Verwechslungen gibt, ist die Library in dem Sketchordner mit dabei. Es handelt sich wie um die gleiche Library die auch mit der Arduino Software installiert wird allerdings ist an der Stelle

#define _SS_MAX_RX_BUFF 256 // RX buffer size auf

in der SoftwareSerial.h.-Datei die Größe angepasst worden.

Das Verwenden der Software-Seriellen hat hat zwei Vorteile. Zum einen kann das Modul den Programmupload nicht unerwartet stören. Zum anderen ist die Hardwareserielle noch frei fürs Debugging. Dies ist hier auch oft genutzt worden. Wenn man das Terminal nebenbei mitlaufen lässt, bekommt man einiges an Feedback. Man kann auch direkt AT-Befehle über das Terminal an das Modul verschicken.

Das Programm ist mit rund 311 Zeilen recht lang geworden. Deswegen werde ich den Quelltext nur Teilweise hier vorstellen. Der Quelltext lässt sich aber herunterladen und ist auch mit einigen, wenn auch sehr schlampig geschriebenen, Kommentaren versehen.

Ich habe versucht, möglichst viele Modul-Funktionen in eigene Routinen zu packen. Eine Liste dieser Funktionen mit Parametern:

  • boolean ESP_Test()                                    Testet die Bereitschaft des Moduls durch AT
  • boolean ESP_Reset()                                  Resetet das Modul
  • void ESP_TotalStart()                                 Resetet Modul, Verbindet WLAN, Startet Server
  • boolean WLAN_Connect(char SSID[], char PASSWORT[] )              Verbindet WLAN
  • boolean SERVER_Start(int Port)                 Startet Server
  • int SERVER_Status()                                   Server Status
  • void SERVER_IPD()                                     Behandelt reinkommende Befehle

Der Programmablauf ganz grob

  • Startet WLAN- Verbindet mit angegeben Netzwerk
  • Startet Server an Port 80
  • Wartet auf Kommando eingeleitet durch +IPD
  • Analyse des Kommandos in eigner Routine

Das Problem lag vor allem in der Auswertung unerwartet kommender Daten. Dies kann durch verschiedene Sachen hervorgerufen werden.

Ein besonders ungünstiger Fall wäre, dass das Modul ready meldet. Das würde bedeuten, dass sich das Modul resetet hat. Dies ist im Test bei mir vorgekommen, wenn auch nur selten. Um diese Fehlerquelle abzufangen wird die ESP_TotalStart in diesem Fall neu ausgeführt.

Ein andere Fall wäre eine Link oder Unlink Meldung. Das bedeutet, dass sich ein Client auf dem Server des Moduls an– bzw. abgemeldet hat. Dies erzeugt z.Z. nur eine Debugmeldung, wäre aber auch ein möglicher Ansatz für eine erweiterte Kommunikation.

Ein weiter Fall ist, dass Daten empfangen wurden. Empfangene Daten werden grundsätzlich in der Form

 +IPD,0,376:DATEN

Empfangen. +IPD kennzeichnet also reinkommende  Daten. Die erste Zahl, hier die Null, gibt an, von welchem der verbundenen Clients Daten gesendet wurden. Die zweite Zahl sagt aus, wie viele Zeichen gesendet wurden. Nach dem Doppelpunkt folgen schließlich die übermittelten Daten.

Besonderheiten im Programm

Variable NC:

In der Loop Routine werden Zeichen für Zeichen durchgereicht. Damit nicht jedes Zeichen auf den Anfang eines möglichen Kommandos analysiert werden muss, wird überprüft ob das vorherige Zeichen ein NewLine Zeichen war (Befehle fangen immer in neuer Zeile an). Ist dies der Fall wurde NC = true gesetzt und das Zeichen genauer betrachtet.

WLAN-Connect

In der Routine für die WLAN-Verbindung wird zunächst mittels AT+CWJAP? überprüft, ob das Modul bereits mit dem angegebenen Netzwerk verbunden ist. Dies kommt öfter vor da sich das Modul wohl nach einem Reset auch automatisch verbindet. Ein ERROR bedeutet keine Verbindung und ansonsten wird der Name des Verbunden Netzwerkes zurückgegeben und mit der vorgegeben SSID verglichen. Ist diese identisch wird die Routine vorzeitig verlassen.

Server-Status

Anhand des Server-Status wird überprüft, ob der Server bereits gestartet ist. Das kann sehr gut sein, das beim Neuaufspielen eines Programms zwar der Arduino-Controller resetet wird, das Modul aber nicht. Ich vermute das Status 4 und 5 auf Inaktivität hinweisen und dann führe ich die komplette Startroutine neu aus.

Loop-Routine

Quelltext

void loop() {

  while (ESPSerial.available()) {
    char inChar = ESPSerial.read();

    if (NC == true) //Nur wenn NL das letze Zeichen war wird dieses Zeichen auf +,r, L oder U untersucht!! dadruch beschleunigt //Wenn hier false in die If-Afrage einteragen wird, gehjt alles in Debug durch- keine interpretation
    {
      if (inChar  == '+' || inChar  == 'r' || inChar  == 'L' || inChar  == 'U') //Diese 4 sind der beginn eines evtl interessanten Kommandos
      {
        switch (inChar) {
          case '+':
            {
              //debug("Befehl/Request?");
              delay (1); //Delay scheont die Zuverlässigkeit von der Befehlserkennung zu erhhöhen
              char NextPeek = ESPSerial.peek();
              if (NextPeek == 'C') {
                break; //z.B. durch CWLAP möglichst schneller austieg damit keine DAten aus Buffer verloren gehen
              }

              if (NextPeek == 'I') {//Vermutung IPD = Einkommender Befehl
                if (ESPSerial.findUntil("IPD", " ")) { //Bestätigen
                  debug("IPD-Kommando");
                  SERVER_IPD(); //Unterfunktion zum analysieren des IPD-Kommandos
                  inChar = 0;
                }
              }
              break;
            }

          case 'r':  //ready vom Modul gesendet=?
            //debug("Ungeplanter Neustart??");
            if (ESPSerial.findUntil("eady", "/n")) {
              debug("Ungeplanter ModulReset -> Neustart");
              inChar = 0;
              ESP_TotalStart(); //Aufruf der Startroutine
            }
            break;

          case 'L':
            //debug("Link? ");  //Linked: eine Verbindung wurde aufgebaut... Evtl für später zu gerbauchen -> senden an Clients
            if (ESPSerial.findUntil("ink", " ")) {
              debug("Linked!");
              inChar = 0;
            }
            break;

          case 'U':  //UnLinked: eine Verbindung wurde getrennt... Evtl für später zu gerbauchen -> senden an Clients //ALLERDINGS: scheint selten ausgeläst zu werden
            debug("Unlink?"); //Hier kommen Daten gelegentlich weg die mit U beginnen in neuer Zeile ---  kommt eher bei Browser request vor.. sowieso abfangen in request befehl
            if (ESPSerial.findUntil("nlink", " ")) {
              debug("Unlinked");
              inChar = 0;
            }
            break;
        }
      }
    }
    //New Line ermöglicht einkommenden Befehl // damit beschleunigt
    if (inChar == '\n') {
      NC = true;
    }    else {
      NC = false;
    }

    //Debug-> alles lässt sich über die Konsole verfolgen
    Serial.write(inChar);
  }
}

[Einklappen]

Routine: Analyse der Befehle

Quelltext

void SERVER_IPD() {
  //Beispiel +IPD,0,376:GET / HTTP/1.1 = Typischer Browser request
  int Client;    //Client ID
  int NZeichen;   //Anzahl der gesendeten Zeichen

  char buffer[5];    //Buffer eig nur für Müll
  char Kommando1[10]; //Buffer für das erste Kommando direkt nach dem : bis Leerzeichen
  char Kommando2[10];  //Buffer für zweites Kommando... noch nicht ausgereift

  //Löscht alles aus Kommando1 und 2- Scheint wichtig sonst bleibt müll...memset schnellste möglichkeit
  memset(Kommando1, 0, sizeof(Kommando1));
  memset(Kommando2, 0, sizeof(Kommando2));

  Client = ESPSerial.parseInt();
  NZeichen = ESPSerial.parseInt();

  ESPSerial.readBytesUntil(':', buffer, 5);
  ESPSerial.readBytesUntil(' ', Kommando1, 10); //hier konvention für Kommandolänge oder Ähnliches


  //Vorrausslich Request über Server
  if (strcmp (Kommando1, "GET") == 0) {
    ESPSerial.readBytesUntil(' ', Kommando2, 10);
    debug ("kommando2: " + String(Kommando2));

    //Das aktivierende Kommando ist im browser IP:80/Led1 - Alles andere deaktivert die LED
    if (strcmp (Kommando2, "/Led1") == 0) {
      digitalWrite (LED, HIGH);
      debug("LED AN");
    }
    else {
      digitalWrite (LED, LOW);
      debug("LED AUS");
    }

    if ( ESPSerial.find("OK")) {
      debug ("OK Skipt"); //hier wird der ganze Quatsch vom Browser Request übersprungen... Dauet ne weile
    }
  }


  //Vorrausslich Request über TCP-Cient
  else if (strcmp (Kommando1, "set") == 0) {
    debug("set");
    //kommando über TCP client: set 1 oder set 0
    digitalWrite(13, ESPSerial.parseInt());
  }

  //Ausgabe zu debug zwecken
  debug("von Client: " + String(Client) + " Zeichenanzahl: " + String(NZeichen) + " Kommando1: " + Kommando1); //+ " Kommando2: "+ Kommando2
}

[Einklappen]

ESP8266_SetLED1