Forum: Mikrocontroller und Digitale Elektronik ESP82266 UART-WIFI-UART Brücke


von Hans im Glück (Gast)


Lesenswert?

Hallo,
ich bin noch absoluter Neuling was das ESP8266 angeht… also bitte 
entschuldigt meine Unwissenheit.

Also mein Ziel ist es eine bestehende UART Schnittstelle zwischen zwei 
AVR µC durch eine Wlan-Brücke zu ersetzten. Diese soll also für die AVRs 
komplett transparent sein AVR-UART <> ESP8266-WIFI <> WIFI-ESP8266 
<>UART-AVR. Verwenden möchte ich zur Programmierung der ESP8266 die 
Arduino IDE. Das ist auch soweit eingerichtet und funktioniert. Ich kann 
mir vielleicht jemand einen Tipp geben ob es dafür vielleicht schon 
irgendwo ein Beispiel dazu gibt?

Ich habe das Ganze auch schon selbst mit Abwandlung und Erweiterung 
davon 
https://blog.thesen.eu/telnet2serial-telnet-zu-rs232seriell-bruecke-mit-dem-esp8266-microcontroller/#comment-2437 
probiert. Also ein ESP als Server und einen als Client. Leider ist es so 
dass die Latenzzeit für die Übertragung eines Befehls (7 Byte) von einem 
AVR zum anderen stark schwankt (100ms bis 600ms). Das führt dann zu 
einem Timeout in der Firmware der AVRs.
Also brauche ich eine andere Lösung. Habt ihr einen Tipp?

von Bastian W. (jackfrost)


Lesenswert?

Wie groß sind die Pakete zwischen den AVRs ?

Hast du Steuerzeichen in deinem Protokoll ?

Gruß JackFrost

von Hans im Glück (Gast)


Lesenswert?

Danke für die schnelle Antwort :)
Die Pakete sehen prinzipiell so aus ~LIGHT1x und als Antwort z.B. 
~ACKNOLx
wobei das x CRC8 der vorangegangen 7 Byte ist, also 8Byte in Summe…

von Hans im Glück (Gast)


Lesenswert?

ich hatte auch mal was gelesen das die Übertagung über Websockets und 
dem ESP eine geringere Latenz hat, ich kann es aber leider nicht mehr 
ergooglen und es scheiter hier auch an den Grundlagen der 
Netzwerktechnik.

von Bastian W. (jackfrost)


Lesenswert?

Wo tritt die Latenz auf , AVR1 -> AVR2 oder vom Start bis zum ACK ?

Gruß JackFrost

von holger (Gast)


Lesenswert?

>Leider ist es so
>dass die Latenzzeit für die Übertragung eines Befehls (7 Byte) von einem
>AVR zum anderen stark schwankt (100ms bis 600ms). Das führt dann zu
>einem Timeout in der Firmware der AVRs.
>Also brauche ich eine andere Lösung. Habt ihr einen Tipp?

Ja, mach das Timeout länger oder zieh ein Kabel. Bei WLAN gibt es
keine garantierten Latenzen. Was heute noch funktioniert könnte
morgen schon wieder vorbei sein weil in der Nachbarschaft ein neuer
Router dazugekommen ist.

von Marc S. (darkchaos)


Lesenswert?

Wie weit sind die ESP's denn von einander entfernt?

Ich hatte zB selbst bei TCP von einem Laptop (HP Elitebook) zum ESP WLAN 
Latenzen von 3ms inklusive Bearbeitung des Paketes auf dem ESP.

Dabei muss man allerdings sagen, dass ich ein NodeMCU Board hatte und 
dass es eben ein TCPServer auf dem ESP war, ebenfalls mit Arduino 
Firmware.

Finde auch noch herraus, ob es sich um eine Latenz zwischen ESP und ESP 
oder ESP und AVR handelt (zB indem du ein einfaches ECHO System 
implementierst und ein ESP dann mittels den Systemticks die vergangene 
Zeit misst)

von Hans im Glück (Gast)


Lesenswert?

Hallo und danke für eure Hilfe. Das Echo System ist naheliegend aber 
leider hatte ich wohl ein Brett vorm Kopf hüst. Ich hab es umgesetzt 
und es ergibt sich eine maximale Latenz von ~300ms und durchschnittlich 
180ms für den Weg AVR -> ESP -> ESP -> AVR und zurück. Das gleiche 
Echosystem ohne ESPs in der Leitung hat eine zu erwartende Latenz von 
unter 1ms. Die ESPs sind im Moment ~50cm voneinander entfernt.

Mit 3ms wäre ich sehr zufrieden... Störungen durch externe Funkwellen 
kann ich hier in meinem Keller ausschließen. Hier herrscht ein absolutes 
Funkloch.

von Hans im Glück (Gast)


Angehängte Dateien:

Lesenswert?

Also ich komme einfach nicht drauf. Ich hänge meinen Arduino Code mal an 
vielleicht könnt ihr euch mal ansehen ob ihr einen Fehler findet oder 
eventuell auch Testen ob es bei euch mit kürzerer Latenz funktioniert...

Über die Einstellung WiFiMode wifi_mode = "WIFI_AP" oder "WIFI_STA" kann 
gewählt werden ob das Programm als Access-Point oder Client läuft.

Besten Dank für eure Hilfe!

von Pete K. (pete77)


Lesenswert?

Warum nicht beide AVRs durch die ESP8266 ersetzen und die Daten auf 
einem gemeinsamen Server sammeln?

: Bearbeitet durch User
von Stefan F. (Gast)


Lesenswert?

Ein Problem könnte die verborgen verwendete String Klasse bei 
"Serial.write(client.read());" sein.

Zum Einen blockiert sie bis zu einem Timeout, was deine Latenzen 
erklärt. Zum Anderen kann sie (wenn du Pech hast) bis zu ca. 5 Kilobyte 
Daten am Stück liefern, was ratz fatz zu einem Stack überlauf führt. Der 
Stack ist nämlich nur irgendwo zwischen 4 und 5kB klein!

Das passiert vor allem, wenn sich WLAN mal Daten angestaut haben und 
dann alle zusammen "auf einmal" eintreffen.

Die Art, wie du den seriellen Port verwendest, macht es noch träger. 
Denn der serielle Port hat nur einen kleinen Puffer (meist 64 Bytes). 
Wenn du mehr zu senden versuchst, als da rein passt, muss das Programm 
lange warten.

Ändere deine Schleife so, dass sie niemals warten muss. Und zwar weder 
auf Netzwerk noch auf die serielle Schnittstelle.

read() kannst du daher schonmal vergessen. Es gibt aber eine andere 
nicht blockierende read Funktion, die in ein char-array einliest und dir 
zurück liefert, wie viele Zeichen sie geliefert hat.

Vor dem Schreiben kannst du abfragen, für wie viele Zeichen der 
Sende-Puffer frei hat. Wenn er nicht genug Platz hast, dann warte nicht, 
sondern sende nur so viel wie gerade geht und spare Dir den Rest für 
einen späteren Durchlauf der loop Schleife auf.

Um das zu ermöglichen brauchst du wiederum große Puffer in deinem 
Sketch. Der Puffer sorgt dafür, dass du weniger häufig überschüssige 
Daten verwenfen musst. Du kannst sie später senden - sofern dein Puffer 
nicht überläuft.

Das WLAN Netz hat im Gegensatz zur seriellen Schnittstelle eine sehr 
unregelmäßige Performance. Stell Dir vor, ein µC sendet mit 115200 Baud 
kontinuierlich so schnell er kann und dann stockt die Übertragung für 
eine halbe Sekunde (was durchaus normal ist). Dann stauen sich irgendwo 
etwa 6000 Bytes an. Nachdem die Übertragung wieder läuft, musst du nun 
diese 6000 Bytes seriell abliefern, aber gleichzeitig kommen genau so 
schnell neue Daten herein.

Das ist ein Dilemma und das ist das Hauptproblem sämlticher 
Seriell/Netzwerk Adapter, die es gibt. Man muss funktional Abstriche in 
Kauf nehmen, und zwar entweder:

a) Wenn über Netz mehr Daten rein kommen, als man seriell Senden kann, 
verwirft man Teile davon.

oder

b) Man verwendet sehr große Puffer und hofft, dass der Absender 
irgendwann mal eine Pause einlegt, in der man die angestauten Daten 
abarbeiten kann. Aber auc diese Puffer können überlaufen, dann musst du 
doch Daten verwerfen.

Dein jetziges Programm verwirft auch Daten, das ist Dir eventuell nur 
noch nicht aufgefallen, weil die WLAN Schnittstelle etwa 5kB empfangene 
Daten puffern kann und der Sender notfalls einige male Wiederholt, falls 
etwas verloren geht. Doch das geht eben massiv auf die Latenz Zeit und 
dennoch können die Puffer überlaufen.

Programme, die für serielle Kabel gedacht sind, erwarten jedoch eine 
kontinuierliche Lückenlose Übertargung ohne verlorene Teile. Deine WLAN 
Brücke kann nur dann zufriedenstellend funktionieren, wenn die seriellen 
Programme die stockende Übertragung im WLAN tolerieren und durch 
Soft-Handshake sicherstellen, dass sie die Puffer nicht überfüllen.

Bei einer Halb-Duplex Kommunikation ergibt sich das von ganz alleine. Im 
fokgenden Beispiel sind (A) und (B) die seriellen Programme:

(A) zu (B): Schalte mal das Licht an!
(B) zu (A): Ist erledigt
1 Sekunde Pause
(A) zu (B): Schalte das Licht wieder aus!
(B) zu (A): Ist erledigt

Hier haben wir ein Soft-Handshake. Der Befehlsgeber wartet immer auf die 
Rückmeldung, bevor er den nächsten Befehl sendet. Was könnte ohne 
Handshake passieren?:

(A) zu (B): Schalte mal das Licht an!
WLAN ist gestört, das Kommando kommt zunächst nicht an.
1 Sekunde Pause
WLAN geht wieder, jetzt kommt das Kommando bei (B) an.
(B) schaltet das Licht an.
gleichzeitig kommt schon der nächste Befehl herein:
(A) zu (B): Schalte das Licht wieder aus!
(B) schaltet das Licht aus.

Das Licht wurde ein und dann sofort wieder ausgeschaltet. Vermutlich 
flackert es nur kurz, falls man es überhaupt wahrnimmt. Dieses Ergebnis 
hatte A aber nicht gewollt. Gewollt war, dass das Licht eine Sekunde an 
ist.

Versteht du das Problem jetzt?

von Stefan F. (Gast)


Lesenswert?

> Mit 3ms wäre ich sehr zufrieden

Die bekommst du nur im Idealfall, quasi wenn du Netz gerade ganz für 
Dich alleine hast. Und mit Netz meine ich nicht nur deine Fritz Box, 
sondern die WLAN Funkfrequenzen, die Du Dir vermutlich mit einigen 
Nachbarn teilst.

Es sei denn, du hast eine einsame Hütte im Wald.

von Stefan F. (Gast)


Lesenswert?

Ich muss etwas korrigieren:

> Ein Problem könnte die verborgen verwendete String Klasse bei
> "Serial.write(client.read());" sein.

Hier habe ich read() mit readString() verwechselt. Read kann nicht zu 
einem Speicherüberlauf führen.

von Hans im Glück (Gast)


Lesenswert?

Hallo,
erstmal danke für eure umfängliche Hilfe und Mühe. Ich habe das Problem 
mittlerweile gelöst und möchte es euch (falls ihr so etwas auch 
benötigt) nicht vorenthalten. Die Latenz beträgt nun durchschnittlich 
3ms und maximal 7ms für die oben genannten 8 Byte Befehle. Im Test 
wurden 8Byte übertragen und die Zeit bis zum kompletten Empfang des 
Echos gemessen. Das Problem lag daran das zu viele kleine Pakete über 
Wlan gesendet wurden. Nun warten die ESP8266 solang bis entweder der 
Serielle Eingangspuffer fast voll ist oder keine neuen Daten mehr 
ankommen. Das Folgende muss daher im Quelltext geändert werden
1
.....
2
      /////////////////////
3
      //Client Serial --> Telnet
4
      /////////////////////
5
      int available_byts = Serial.available();
6
      if ((available_byts >= 50) || ((available_byts != 0) && (available_byts == Serial_couter)))
7
      {//nur ausführen wenn keine neuen Daten auflaufen -> ab 50 Byte muss gesendet werden (Serial Buffer 64Byte -> sonst SERIAL_BUFFER_SIZE in RingBuffer.h ändern)
8
        size_t len = Serial.available();
9
        Serial_couter = 0;
10
        uint8_t sbuf[len];
11
        
12
        Serial.readBytes(sbuf, len);
13
  
14
        if (client.connected())
15
        {
16
          client.write(sbuf, len);
17
        }
18
      }
19
      else
20
      {//Datenzähler neu setzen + Pause
21
        Serial_couter = available_byts;
22
        delayMicroseconds(500);
23
      }
24
25
....
26
27
28
    /////////////////////
29
    //Server Serial --> Telnet
30
    /////////////////////
31
    int available_byts = Serial.available();
32
    if ((available_byts >= 50) || ((available_byts != 0) && (available_byts == Serial_couter)))
33
    {//nur ausführen wenn keine neuen Daten auflaufen -> ab 50 Byte muss gesendet werden (Serial Buffer 64Byte -> sonst SERIAL_BUFFER_SIZE in RingBuffer.h ändern)
34
      size_t len = Serial.available();
35
      Serial_couter = 0;
36
      uint8_t sbuf[len];
37
      Serial.readBytes(sbuf, len);
38
39
      for (ii = 0; ii < MAX_NO_CLIENTS; ii++)
40
      {
41
        if (pClientList[ii] && pClientList[ii].connected())
42
        {
43
          pClientList[ii].write(sbuf, len);
44
        }
45
      }
46
    }
47
    else
48
    {//Datenzähler neu setzen + Pause
49
      Serial_couter = available_byts;
50
      delayMicroseconds(500);
51
    }

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.