Wifi Telnet Server auf dem ESP8266

Mit dem Over The Air Update kann man einen neuen Sketch über eine WLAN-Verbindung auf ein ESP8266 Modul wie z. B. dem Wemos D1 hochladen. Ein kleiner Nachteil, der Serial Monitor funktioniert leider nicht ohne direkte USB-Verbindung. Probiert man es dennoch den Serial Monitor zu starten, erscheint die Fehlermeldung “Serial monitor is not supported on network ports such as X.X.X.X for the null in this release” und es passiert nichts. Ein Wifi Telnet Server kann Abhilfe schaffen und erlaubt ein Debugging über das Telnet Protokoll im gesamten Netzwerk. Es wird dafür keine Arduino IDE benötigt, ein einfacher Telnet Client wie PuTTY reicht aus.
Wifi Telnet Server auf dem ESP8266 - Telnet Client
Der Wifi Telnet Server ist so aufgebaut, dass er mehrere Telnet Verbindungen annehmen kann. Das ist gerade in komplexen Projekten sehr praktisch, denn so können auch mehrere Personen gleichzeitig die Meldungen lesen. Wurde die im Sketch definierte maximale Anzahl von Verbindungen (MAX_TELNET_CLIENTS) erreicht, werden weitere Verbindung abgelehnt. Wird eine Verbindung geschlossen, kann die Sitzung wieder für eine neue Verbindung verwendet werden. Wenn ihr noch mit dem ESP8266 verbunden seid, seht ihr die einzelnen Telnet Verbindungen im Serial Monitor.
Wifi Telnet Server auf dem ESP8266 - Serial Monitor
Als Telnet Client auf einem Windows Endgerät kann ich PuTTY empfehlen oder ihr installiert den Windows Telnet Client. Für Android oder iOS gibt es genügend (freie) Apps. Das Telnet nicht gerade das sicherste Protokoll ist muss ich hier hoffentlich nicht erwähnen?!

Telnet Nachricht senden

Um eine Nachricht an jeden verbundenen Telnet Client zu senden, habe ich eine kleine Funktion (TelnetMsg) erstellt. Ihr könnt diese anstelle oder zusätzlich zu Serial.print in den Sketch einbauen.

Mit dem folgenden Beispiel kann z. B. ein Text (ledStateMsg) mit einem Status (ledState) an die Funktion übergeben werden.

String ledStateMsg = "LED State = ";
ledStateMsg += ledState;
TelnetMsg(ledStateMsg);

Ihr könnt natürlich auch die einfache Variante wählen und die Funktion direkt aufrufen.

TelnetMsg("Dies ist ein Test");

Sketch

Ihr müsst im Sketch mindestens die folgenden Zeilen anpassen damit auch eine WLAN Verbindung aufgebaut wird! Die IP Adresse findet ihr dann im Serial Monitor oder auf dem DHCP Server / Router.

const char* ssid = "SSID";
const char* password = "PASSWORD";

Im Sketch ist das bereits bekannte Over The Air Update und der Wifi Telnet Server mit allem was dazugehört enthalten. Zum Test habe ich noch eine LED Blink Funktion mit Telnet Ausgabe eingebaut. Der Wifi Telnet Server funktioniert natürlich auch ohne die Over The Air Update Funktion! Der Sketch ist so aber eine gute Grundlage für ein Projekt mit einem ESP8266 Modul.

In diesem Sketch geht es nur um die Ausgabe auf dem Telnet Client. Eingaben imTelnet Client werden aktuell nur auf der seriellen Konsole ausgegeben und nicht weiter verarbeitet.

#include <ESP8266WiFi.h>
#include <ESP8266mDNS.h>
#include <WiFiUdp.h>
#include <ArduinoOTA.h>

const char* ssid = "SSID";
const char* password = "PASSWORD";

uint8_t i;
bool ConnectionEstablished; // Flag for successfully handled connection
  
#define MAX_TELNET_CLIENTS 2

WiFiServer TelnetServer(23);
WiFiClient TelnetClient[MAX_TELNET_CLIENTS];

void setup()
{
  Serial.begin(115200);
  Serial.println("Over The Air and Telnet Example");

  Serial.printf("Sketch size: %u\n", ESP.getSketchSize());
  Serial.printf("Free size: %u\n", ESP.getFreeSketchSpace());

  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, password);

  // ... Give ESP 10 seconds to connect to station.
  unsigned long startTime = millis();
  Serial.print("Waiting for wireless connection ");
  while (WiFi.status() != WL_CONNECTED && millis() - startTime < 10000)
  {
    delay(200);
    Serial.print(".");
  }
  Serial.println();
  
  while (WiFi.status() != WL_CONNECTED)
  {
    Serial.println("Connection Failed! Rebooting...");
    delay(3000);
    ESP.restart();
  }

  Serial.print("IP address: ");
  Serial.println(WiFi.localIP());

  Serial.println("Starting Telnet server");
  TelnetServer.begin();
  TelnetServer.setNoDelay(true);

  pinMode(BUILTIN_LED, OUTPUT);  // initialize onboard LED as output

  // OTA

  // Port defaults to 8266
  // ArduinoOTA.setPort(8266);

  // Hostname defaults to esp8266-[ChipID]
  // ArduinoOTA.setHostname("myesp8266");

  // No authentication by default
  ArduinoOTA.setPassword((const char *)"1234");
  
  ArduinoOTA.onStart([]() {
    Serial.println("Start");
  });
  
  ArduinoOTA.onEnd([]() {
    Serial.println("\nEnd");
  });
  
  ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) {
    Serial.printf("Progress: %u%%\r", (progress / (total / 100)));
  });
  ArduinoOTA.onError([](ota_error_t error) {
    Serial.printf("Error[%u]: ", error);
    if (error == OTA_AUTH_ERROR) Serial.println("Auth Failed");
    else if (error == OTA_BEGIN_ERROR) Serial.println("Begin Failed");
    else if (error == OTA_CONNECT_ERROR) Serial.println("Connect Failed");
    else if (error == OTA_RECEIVE_ERROR) Serial.println("Receive Failed");
    else if (error == OTA_END_ERROR) Serial.println("End Failed");
  });
  
  ArduinoOTA.begin();
}

void loop() {
  ArduinoOTA.handle(); // Wait for OTA connection
  blinkLED();  // Blink LED
  Telnet();  // Handle telnet connections
}

void TelnetMsg(String text)
{
  for(i = 0; i < MAX_TELNET_CLIENTS; i++)
  {
    if (TelnetClient[i] || TelnetClient[i].connected())
    {
      TelnetClient[i].println(text);
    }
  }
  delay(10);  // to avoid strange characters left in buffer
}
      
void Telnet()
{
  // Cleanup disconnected session
  for(i = 0; i < MAX_TELNET_CLIENTS; i++)
  {
    if (TelnetClient[i] && !TelnetClient[i].connected())
    {
      Serial.print("Client disconnected ... terminate session "); Serial.println(i+1); 
      TelnetClient[i].stop();
    }
  }
  
  // Check new client connections
  if (TelnetServer.hasClient())
  {
    ConnectionEstablished = false; // Set to false
    
    for(i = 0; i < MAX_TELNET_CLIENTS; i++)
    {
      // Serial.print("Checking telnet session "); Serial.println(i+1);
      
      // find free socket
      if (!TelnetClient[i])
      {
        TelnetClient[i] = TelnetServer.available(); 
        
        Serial.print("New Telnet client connected to session "); Serial.println(i+1);
        
        TelnetClient[i].flush();  // clear input buffer, else you get strange characters
        TelnetClient[i].println("Welcome!");
        
        TelnetClient[i].print("Millis since start: ");
        TelnetClient[i].println(millis());
        
        TelnetClient[i].print("Free Heap RAM: ");
        TelnetClient[i].println(ESP.getFreeHeap());
  
        TelnetClient[i].println("----------------------------------------------------------------");
        
        ConnectionEstablished = true; 
        
        break;
      }
      else
      {
        // Serial.println("Session is in use");
      }
    }

    if (ConnectionEstablished == false)
    {
      Serial.println("No free sessions ... drop connection");
      TelnetServer.available().stop();
      // TelnetMsg("An other user cannot connect ... MAX_TELNET_CLIENTS limit is reached!");
    }
  }

  for(i = 0; i < MAX_TELNET_CLIENTS; i++)
  {
    if (TelnetClient[i] && TelnetClient[i].connected())
    {
      if(TelnetClient[i].available())
      { 
        //get data from the telnet client
        while(TelnetClient[i].available())
        {
          Serial.write(TelnetClient[i].read());
        }
      }
    }
  }
}

////////////////////////////////////////////////////////////////////////////////////////
// Blink function with telnet output

const long interval = 2000;
int ledState = LOW;
unsigned long previousMillis = 0;

void blinkLED()
{
  unsigned long currentMillis = millis();

  // if enough millis have elapsed
  if (currentMillis - previousMillis >= interval)
  {
    previousMillis = currentMillis;

    // toggle the LED
    ledState = !ledState;
    digitalWrite(BUILTIN_LED, ledState);

    String ledStateMsg = "LED State = ";
    ledStateMsg += ledState;
    TelnetMsg(ledStateMsg);
  }
}

5 Gedanken zu „Wifi Telnet Server auf dem ESP8266“

  1. Hallo,
    das mit dem Telnet- Server ist soweit klar und funktioniert auch. Was passiert aber mit den Meldungen aus z.B. ArduinoOTA.onError? die benötigen doch aber trotzdem eine serielle Verbindung, oder? Aber die ist bei der OTA- Übertragung nicht vorhanden.
    Mein Ziel war den Status während der OTA- Übertragung anzusehen, da der esp8266 öfters mal nach der Übertragung hängt. Dann hilft nur noch ein flashen via USB.

    Gruß, Klaus

    1. Hallo Klaus,

      das klappt nicht bzw. ich weiß leider nicht wie! Ich habe es versucht, aber sobald das OTA Update anfängt ist Telnet nicht mehr nutzbar.

      Viele Grüße
      Stefan

      1. Hallo zusammen,

        der OTA Prozess stoppt alle UDF Prozesse und somit auch alle TCP/IP Verbindungen. Ich würde empfehlen die Meldungen in einer Datei mithilfe von LittleFS zu speichern und nach dem Neustart zu übertragen. Das geht mit einem einfachen Webserver, mit dem die Datei heruntergeladen werden kann.

        Viele Grüße,
        Ladi

  2. Hallo Kalus,
    habe hier ein captive portas (AZ-Vertrieb), das sowohl AP als auch STA sein kann.
    AP und STA haben natürlich verschiedene IP Adressen.
    Über die WLAN Sta ist der Telnet server erreichbar.
    Wie kann ich es anstellen, daß er über beide Wege / IPs erreichbar ist ?

  3. Hallo,
    tolle Idee. So habe ich jetzt in meinem Projekt es geschafft Meldung zum Handy(APP. Mobile Telnet) zu schicken.
    Danke.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert