[gelöst] ESP8266 - Telnet ruckelig

Hallöle!

Ich habe mal wieder ein Projekt, an dem ich seit gestern arbeite. Ich möchte eine “Brawl Stars”-Kopie für den ESP bzw. für mindestens 2 ESPs programmieren. Für die, die das Spiel nicht kennen: Es ist sowas wie Fortnite in 2D und in viel simpler. Die Datenübertragung soll über WLAN laufen, ein ESP stellt den AP her, die anderen loggen sich bei ihm ein.
Jetzt kommt mein Problem: Ich habe den Beispiel-Sketch ESP8266/WiFiTelnetToSerial auf einen ESP geladen und auf das andere Ding folgendes:

#include <ESP8266WiFi.h>

#define STASSID "MeinWLAN"
#define STAPSK  "Hey! Das ist geheim!"

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("---IP---", 23);
  digitalWrite(LED_BUILTIN, LOW);
}
byte i = 0;
void loop() {
  data.println(analogRead(0));
  delay(5);
}

Ich war überrascht, dass alles nach dem 3. Anlauf lief. Der eine, über einen Akku betriebene ESP sendete fleißig die Daten vom Poti und der andere µC schickte sie an den PC. Das Problem: Der Empfänger schickte nur etwa alle halbe Sekunde eine Schwung Daten. Ich schob es auf den Umweg über den Router und auf das lange Programm beim Empfänger. Daher habe ich ein neues Programm für den Empfänger geschrieben:

#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);
  while(!client){
    client = server.available();
  }
  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(){
  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 = -5;i <= 5;i++){
        Serial.print(in + i);
        Serial.write('\t');
      }
      Serial.println();
      input = "";
    }
  }
}

Das Programm ist für den seriellen Plotter optimiert, deswegen wird der Wert mehrfach leicht verändert versendet. Das Ergebnis könnt ihr auf dem sich im Anhang befindlichen und professionellem 4K-Video sehen. Ja, es funktioniert wesentlich besser, aber es ruckelt trotzdem sehr und hat eine Verzögerung von mindestens 1/10 Sekunde. Ein Baller-Spiel würde bei solchen Servern schrecklich aussehen. Oder bei solchen Sendern, keine Ahnung. Woran liegt das? Ich versende doch nur maximal 1200 B/s! Kann man die Übertragung beschleunigen? Nur, damit ihr das nicht falsch versteht: Ich habe ungeheuren Respekt vor solch einer 2€-µC-Leistung. Es ist unfassbar. Trotzdem …
Ich freue mich über Antworten.

video.zip (1.47 MB)

Ich würde sagen, dass Deine seriellen Ausgaben der bremsende Faktor sind. Rechne mal aus, wie lange 1200 Byte brauchen.

Gruß Tommy

Bei einer Rate von 115200 b/s ergibt das ca. 15000 B/s. Das ist kein Problem, selbst, wenn auch noch * 11 gerechnet wird. 1200 * 11 < 15000. Das ist der Grund, aus dem ich 115200 Baud nutze. Und das Problem habe ich auch bei weniger Spuren übereinander.

Da ich das gerade nicht testen kann, aber schon öfter "Ruckel Probleme", bzw. Verzögerungen hatte...
Denke ich mir, dass du unter den selben Symptomen leidest.

Meine magische Kugel sagt:
Der TCP Part der Wifi Lib wartet bis es ein ganzes Datenpaket zusammen hat und sendet erst dann.
Es gibt einen Timeout, nachdem trotzdem gesendet wird, auch wenn das Paket noch nicht komplett ist.

Abhilfe:
Um das senden zu erzwingen, data.flush(); aufrufen.

Die Glaskugel solltest du reklamieren: Das macht’s nur noch schlimmer! Jetzt kommt nur noch jede halbe Sekunde ein Schwung!

#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);
  digitalWrite(LED_BUILTIN, LOW);
}
void loop() {
  for(byte i = 0;i < 10;i++){
    data.println(analogRead(0));
    delay(5);
  }
  data.flush();
}

Oder ist das so falsch?

Oder ist das so falsch?

Je nach dem, was du erreichen willst, ist, oder kann, das falsch sein.
Oder auch richtig.

Edit:
https://www.arduino.cc/en/Reference/WiFiClientFlush

Verwerfen, war nicht mein Ziel.
Die Kugel hat sich geirrt!

Die Glaskugel solltest du reklamieren:

Nein.
Sie hat sich schon öfter als erstaunlich erfolgreich erwiesen.

Aber wie sagt man so schön...:

Nobody is perfect!
(und selbst der nicht zu 100%)

combie:
Je nach dem, was du erreichen willst, ist, oder kann, das falsch sein.
Oder auch richtig.

Ich möchte eine Übertragung mit konstant mindestens 20 Datenhaufen pro Sekunde à 15 Bytes verschicken.

Ich hätte auch nichts gegen eine Übertragung auf eine andere Variante als den WiFiClient und Server - es soll aber über WLAN laufen.

Hab’s! client.setNoDelay(true);hat’s gebracht! Ich habe jetzt eine flüssige Datenübertragung mit folgendem Programm:

#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);
}

Gibt es vielleicht auch den Befehl XYZ?

data.println("test");
data.println("test2 wird auch mitgesendet");
data.XYZ();

Schön, dann war die Glaskugel ja doch nicht in allen Punkten neben der Spur!

Gibt es vielleicht auch den Befehl XYZ?

Ich sehe Methodenaufrufe, keine Befehle.

Probiere es aus, wenn es kein WiFiClient::XYZ() gibt, wird dir der Compiler das schon sagen.

Wie kommst du auf XYZ?

Die Methode meine ich. Und dass diese Methode wahrscheinlich nicht existiert, ist mir schon klar. XYZ war als Platzhalter gedacht. Ich wollte wissen, ob es eine methode gibt, die bewirkt, dass alles gesendet wird. Sonst müsste ich immer alles in einem Buffer speichern. Aber die Frage hat sich in der Zwischenzeit erübrigt.