[Meinungen gesucht] ESP8266 - Telnet-Server erkennt keine Verbindungs-Trennung

Zack - nächstes Problem!
Folgendes Programm soll sich einen Client suchen, die versendeten Daten auslesen und bei Verbindungstrennung sich einen neuen Client suchen. Ziel ist es, dass zwei ESPs über WLAN in Verbindung stehen. Zu Testzwecken soll auch noch über D7 ein Ton ausgegeben werden, besser gesagt über den Buzzer an dem Pin. Aber das ist egal, das läuft bzw. piept.

#include <ESP8266WiFi.h>

#include <algorithm>

#ifndef APSSID
  #define APSSID "ESPAP"
  #define APPSK  "ThisPasswortIsShitty"
#endif

const int port = 23;

String input;

WiFiServer server(port);
WiFiClient client;

IPAddress apIP(192,168,4,1);

void setup(){
  Serial.begin(115200);
  Serial.setRxBufferSize(1024);
  
  Serial.println("\n\nUsing Serial for logging");
  Serial.println(ESP.getFullVersion());

  WiFi.mode(WIFI_AP);
  
  WiFi.softAPConfig(apIP, apIP, IPAddress(255, 255, 255, 0));
  WiFi.softAP(APSSID, APPSK);
  Serial.println(APSSID);
  Serial.println();
  Serial.print("connected, address=");
  Serial.println(WiFi.softAPIP());
  
  server.begin();
  server.setNoDelay(true);

  Serial.print("Ready! Use 'telnet ");
  Serial.print(WiFi.softAPIP());
  Serial.printf(" %d' to connect\n", port);
  Serial.println("Okay...");
}
uint16_t getFreq(uint8_t note){
  float freq[] = {65.4, 69.3, 73.4, 77.7, 82.4, 87.3, 92.5, 98.0, 103.8, 110.0, 116.5, 123.4};
  return freq[note%12] * (1 << uint8_t(note / 12)) * std::min(1, int(note));
}
void loop(){
  if(!client.connected() && server.hasClient()){
    client = server.available();
  }
  while(client.available()){
    char ch = client.read();
    input += ch;
    if(ch == '\n'){
      int in = input.toInt();
      tone(13, int(getFreq(floor(map(float(in), 0, 1024, 0, 12) + 24))));
      for(int8_t i = -2;i <= 2;i++){
        Serial.print(in + i);
        Serial.write('\t');
      }
      Serial.println();
      input = "";
    }
  }
}

Das Sendeprogramm sieht so aus:

#include <ESP8266WiFi.h>

#define STASSID "ESPAP"
#define STAPSK  "ThisPasswortIsShitty"

WiFiClient data;
void setup(){
  pinMode(LED_BUILTIN, OUTPUT);
  digitalWrite(LED_BUILTIN, LOW);
  delay(250);
  digitalWrite(LED_BUILTIN, HIGH);
  WiFi.begin(STASSID, STAPSK);
  while(WiFi.status() != WL_CONNECTED){
    delay(250);
  }
  data.connect("192.168.4.1", 23);
  data.setNoDelay(true);
  digitalWrite(LED_BUILTIN, LOW);
}
void loop(){
  String temp;
  for(byte i = 0;i < 10;i++){
    temp += analogRead(0);
    temp += '\n';
    delay(5);
  }
  data.print(temp);
}

Dank eurer Hilfe ist inzwischen die Datenübertragung flüssig. Was aber nicht funktioniert, ist, dass nach einem Reset vom Sender der Client vom Server als nicht mehr verbunden betrachtet wird. !client bzw. !client.connected() ist immer false. Das hat folgender Versuch ergeben:

#include <ESP8266WiFi.h>

#include <algorithm>

#ifndef APSSID
  #define APSSID "ESPAP"
  #define APPSK  "ThisPasswortIsShitty"
#endif

#define STACK_PROTECTOR  512

#define MAX_SRV_CLIENTS 1

const int port = 23;

String input;

WiFiServer server(port);
WiFiClient serverClients[MAX_SRV_CLIENTS];

IPAddress apIP(192,168,4,1);

void setup(){
  Serial.begin(115200);
  Serial.setRxBufferSize(1024);
  
  Serial.println("\n\nUsing Serial for logging");
  Serial.println(ESP.getFullVersion());

  WiFi.mode(WIFI_AP);
  
  WiFi.softAPConfig(apIP, apIP, IPAddress(255, 255, 255, 0));
  WiFi.softAP(APSSID, APPSK);
  Serial.println(APSSID);
  Serial.println();
  Serial.print("connected, address=");
  Serial.println(WiFi.softAPIP());

  //start server
  server.begin();
  server.setNoDelay(true);

  Serial.print("Ready! Use 'telnet ");
  Serial.print(WiFi.softAPIP());
  Serial.printf(" %d' to connect\n", port);
}
uint16_t getFreq(uint8_t note){
  float freq[] = {65.4, 69.3, 73.4, 77.7, 82.4, 87.3, 92.5, 98.0, 103.8, 110.0, 116.5, 123.4};
  return freq[note%12] * (1 << uint8_t(note / 12)) * std::min(1, int(note));
}
void loop() {
  //check if there are any new clients
  if (server.hasClient()) {
    //find free/disconnected spot
    int i;
    for (i = 0; i < MAX_SRV_CLIENTS; i++)
      if (!serverClients[i]) { // equivalent to !serverClients[i].connected()
        serverClients[i] = server.available();
        Serial.print("New client: index ");
        Serial.print(i);
        break;
      }

    //no free/disconnected spot so reject
    if (i == MAX_SRV_CLIENTS) {
      server.available().println("busy");
      // hints: server.available() is a WiFiClient with short-term scope
      // when out of scope, a WiFiClient will
      // - flush() - all data will be sent
      // - stop() - automatically too
      Serial.printf("server is busy with %d active connections\n", MAX_SRV_CLIENTS);
    }
  }
  for(int i = 0; i < MAX_SRV_CLIENTS; i++){
    while(serverClients[i].available()){
      // working char by char is not very efficient
      char ch = serverClients[i].read();
      input += ch;
      if(ch == '\n'){
        tone(13, int(getFreq(floor(map(float(input.toInt()), 0, 1024, 0, 12) + 24))));
        Serial.println(input);
        input = "";
      }
    }
  }
  size_t maxToTcp = 0;
  for (int i = 0; i < MAX_SRV_CLIENTS; i++)
    if (serverClients[i]) {
      size_t afw = serverClients[i].availableForWrite();
      if (afw) {
        if (!maxToTcp) {
          maxToTcp = afw;
        } else {
          maxToTcp = std::min(maxToTcp, afw);
        }
      } else {
        // warn but ignore congested clients
        Serial.println("one client is congested");
      }
    }

  //check UART for data
  size_t len = std::min((size_t)Serial.available(), maxToTcp);
  len = std::min(len, (size_t)STACK_PROTECTOR);
  if (len) {
    uint8_t sbuf[len];
    size_t serial_got = Serial.readBytes(sbuf, len);
    for (int i = 0; i < MAX_SRV_CLIENTS; i++)
      if (serverClients[i].availableForWrite() >= serial_got) {
        size_t tcp_sent = serverClients[i].write(sbuf, serial_got);
        if (tcp_sent != len) {
          Serial.printf("len mismatch: available:%zd serial-read:%zd tcp-write:%zd\n", len, serial_got, tcp_sent);
        }
      }
  }
}

Mir wird, wenn der Sender resettet wird,

server is busy with 1 active connections

angezeigt. Das ist also das Problem: Der Server erkennt nicht, dass die Verbindung zum anderen ESP getrennt ist. Vermutlich, weil nirgendwo im Sendeprogramm data.stop() aufgerufen wird. Kann auf eine andere Weise erkannt werden, wenn der ESP nicht mehr verbunden ist? Ich möchte nicht alle halbe Sekunde ein “Ich bin noch da!” mit dem Sender verschicken.

HTML-Fan:
Ich möchte nicht alle halbe Sekunde ein "Ich bin noch da!" mit dem Sender verschicken.

Dann sende doch von Deinem Sender ein definitives "Ich bin dann mal weg".
Diesen Weg bin ich bei meinem Debugger über Telnet gegangen.

Gruß Tommy

Beim Reset kann meines Wissens nach nichts noch versendet werden, oder bin ich da falsch informiert?

Reset ist ja auch nicht der normale Betriebsfall.

Gruß Tommy

Man soll aber für das Spiel hinterher einfach den Strom abstellen können - das ist ja sowas wie ein Reset auf höhst unsanfte Art.

Dann wirst Du wohl mit Heartbeat arbeiten müssen. Telnet hält keinen "Verbindungsstatus".

Gruß Tommy

Mist.
Trotzdem danke.

Hi

Wenn Du den Stecker ziehst - kannst Du Das vom Arduino erkennen lassen.
Wenn Du jetzt schnell genug 'so ziemlich Alles abstellst', Was Du nicht mehr brauchst, hast Du ggf. genug Zeit, Dich ordentlich abzumelden.
Ggf. einen Puffer-Elko an den Arduino geschnallt und die Zuleitung per Diode entkoppelt - so kannst Du das Wegbrechen der Spannung der Zuleitung erfassen, wo der Arduino noch 'Strom ohne Ende' hat.

MfG

Ja, an so 'ne Elko-Lösung habe ich schon gedacht, nur schützt das auch nicht vor einem "Absturz" bei einem Reset. Ich habe jetzt, da konstant die analogen Werte übertragen werden, einfach ein Timeout von 100ms festgelegt, das sollte so auch hinterher möglich sein.

Außerdem: Kann es sein, dass ESPs im AP-Mode totale Störsender sind? Einmal der ESP an und schon wird mein WLAN über den Router instabil.

Hi

Wenn Dein Projekt im Produktiv-Einsatz ist - muß der Reset-Knopf ja nicht noch per blinkender LED zu einer Berührung animieren - im Produktiv-Einsatz gibt's also kein Reset.
Bestenfalls ein 'So, Schluß jetzt, Emma' - und dann kann der Arduino sich noch abmelden, die Zipfelmütze anziehen und langsam zum Bettchen schlürfen, bis das Licht wirklich ausgeht.

Wenn zwischenzeitlich eine Boing auf Dein Haus abstürzt, ist die fehlende Abmeldung eines der geringsten Probleme - wir gehen also davon aus, daß der Kram 'so wie Er soll' läuft und Sich eben auch 'ganz normal abmeldet'.
Wenn Schlimmeres passiert ... die Katze hat drauf gepi??t ... der Enkel mit dem Hammer untersucht, was man da drin so Alles finden kann ... ist die fehlende Abmeldung 'nicht mehr ganz so wichtig'.

MfG

Ich glaube, ich mach's anders: Ich habe soeben mit einem Test herausgefunden, dass sich die Übertragungsrate so im Raum 75 - 90 kB/s liegt und da ein Display-Buffer exakt 1kB verbraucht, lassen sich damit problemlos 30 Bilder pro Sekunde übertragen. Wenn der ESP dann nicht mit den gedrückten Tastern antwortet, ist er weg. Haltet ihr es für eine gute oder für eine hirnrissige Idee, dass das eigentliche Programm auf dem Server läuft und die anderen ESPs nur die Daten vom Server direkt anzeigen?

postmaster-ino:
der Enkel mit dem Hammer untersucht, was man da drin so Alles finden kann ...

Ich bin zwar schon zwölf, aber da ich ein etwas seltsamer besonderer Mensch bin, habe ich noch keine Enkel. Aber konntest du ja nicht wissen, nur für's nächste mal. :wink: