PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : NodeMCU V3 als Server stürzt nach ein paar Minuten ab.



Philippp
09.09.2020, 16:26
Moin,
ich arbeite nun schon seit längerem an einer recht einfach gehaltenen Datenübertragung. In der Summe nutze ich sechs Komponenten:

Vier Clients (Wemos D1 Mini)
Ein Server (NodeMCU V3)
Ein WLAN Router (Fritzbox)



Gewünschte Funktion:
Die Clients senden zu verschiedenen Zeitpunkten je eine Zahl, welche einen von zwei Status repräsentiert. Abhängig von der Zahl, wird eine LED (über einen Transistor) über den NodeMCU angesteuert. Der Staus bleibt erhalten (die LED leuchtet), bis eine andere Zahl übertragen wird und ein anderer Status eintritt. Nachdem ein Client eine Zahl übertragen hat, geht selbiger in den DeepSleep (10 Sekunden), um anschließend wieder eine Zahl zu senden.

Was passiert:
Am Anfang klappt alles super. Clients und Server verbinden sich mit der Fritzbox, und ich kann über die Clients munter die LEDs welche an dem NodeMCU angeschlossen sind steuern. Nach ein paar Minuten friert der NodeMCU dann ein, während die Clients motiviert weiter arbeiten. Ich bin mir recht sicher das dass Problem beim Server liegt.

Was ich bislang versucht habe:

Stromversorgung getauscht, von 5V, 2A Handy Netzteil auf Laptop und zurück.
Ping Befehl an die Fritzbox (gegenwärtig nicht im Programm).
"delay(500);" Befehl aus der "Verbinden mit Netzwerk Schleife" im Setup gelöscht (was verhindert hat das dass Programm startet).


Ich bin nun leider mit meinem Latein am Ende. Wäre super wenn mal jemand drüber schauen könnte. Entsprechend meiner bescheidenen Erfahrung ist das Programm sehr einfach und übersichtlich geschrieben.

Vielen Dank,
Philippp



/*
Lazy gardener V1 - Server

Der Server zeigt über die angeschlossenen LEDs an, wie der Status der Beete,
des Wassertankes und des Netzwerkes ist.
*/

#include <SPI.h>
#include <ESP8266WiFi.h>

/* Im folgenden werden die Ausgänge für die LEDs definiert.
Die Pinnummern unterscheiden sich von den Bezeichnungen auf dem Board*/

#define led_Tomaten_rot 16 //LED für Tomaten rot
#define led_Tomaten_gruen 5 //LED für Tomaten grün

#define led_Kraeuter_rot 4 //LED für Kraeuter rot
#define led_Kraeuter_gruen 0 //LED für Kraeuter grün

#define led_WIFI_rot 14 //LED für WIFI rot
#define led_WIFI_gruen 2 //LED für WIFI grün

#define led_Blumen_rot 12 //LED für Blumen rot
#define led_Blumen_gruen 13 //LED für Blumen grün

#define led_Wassertank_rot 15 //LED für Wassertank rot
#define led_Wassertank_gruen 3 //LED für Wassertank grün
/************************************************** **********************/

/* Im folgenden werden Netzwerkdaten definiert. DIese Daten müssen dem
Netzwerk entsprechend angepasst werden*/

char ssid[] = "Netzwerk"; // Name des Netzwerkes
char pass[] = "Passwort"; // Passwort des Netzwerkes
WiFiServer server(80);

/************************************************** **************************/

/*Alle Anweisungen der Funktion setup() werden nur einmal am Start des Programs
ausgeführt*/

void setup() {
Serial.begin(115200); // Startet die Serielle Schnittstelle
WiFi.begin(ssid, pass); // Baut eine Verbindung zum W-LAN Router auf.

pinMode(led_Tomaten_rot, OUTPUT); // LED zeigt an das Wasser knapp ist. LED wird als Ausgang definiert.
pinMode(led_Tomaten_gruen, OUTPUT); // LED zeigt an das Wasser vorhanden ist. LED wird als Ausgang definiert.
pinMode(led_Kraeuter_rot, OUTPUT); // LED zeigt an das Wasser knapp ist. LED wird als Ausgang definiert.
pinMode(led_Kraeuter_gruen , OUTPUT); // LED zeigt an das Wasser vorhanden ist. LED wird als Ausgang definiert.
pinMode(led_WIFI_rot, OUTPUT); // LED zeigt an das keine Verbindung zum W-LAN besteht. LED wird als Ausgang definiert.
pinMode(led_WIFI_gruen, OUTPUT); // LED zeigt an das eine Verbindung zum W-LAN besteht. LED wird als Ausgang definiert.
pinMode(led_Blumen_rot, OUTPUT); // LED zeigt an das Wasser knapp ist. LED wird als Ausgang definiert.
pinMode(led_Blumen_gruen, OUTPUT); // LED zeigt an das Wasser vorhanden ist. LED wird als Ausgang definiert.
pinMode(led_Wassertank_rot, OUTPUT); // LED zeigt an das Wasser knapp ist. LED wird als Ausgang definiert.
pinMode(led_Wassertank_gruen, OUTPUT); // LED zeigt an das Wasser vorhanden ist. LED wird als Ausgang definiert.

//Da die folgeden PINS beim booten HIGH sind, werden selbige hier zur Sicherheit noch einmal auf LOW gesetzt
digitalWrite(led_Tomaten_rot, LOW);
digitalWrite(led_WIFI_gruen, LOW);
digitalWrite(led_Wassertank_gruen, LOW);


while (WiFi.status() != WL_CONNECTED) // Schleife läuft so lange, bis eine Verbindung zum W-LAN aufgebaut ist.
{
Serial.print(".");
digitalWrite(led_WIFI_rot, HIGH); //Zeigt an das keine Netzwerkverbindung besteht
delay(500);
}

server.begin(); // Startet die Serverfunktion
Serial.println(" ");
Serial.println("Connected to wifi");
Serial.print("IP: "); Serial.println(WiFi.localIP()); // Zeigt die IP an
Serial.print("Subnet: "); Serial.println(WiFi.subnetMask()); // Zeigt die Subnet Mask an
Serial.print("Gateway: "); Serial.println(WiFi.gatewayIP()); // Zeigt die Gateway Adresse an
Serial.print("SSID: "); Serial.println(WiFi.SSID()); // Zeigt den Netzwerknamen an
Serial.print("Signal: "); Serial.println(WiFi.RSSI()); // Signalstärke in dBm
}


void loop () {
//Folgende Abfrage klärt ob noch immer eine Netzwerkverbindung besteht.
if (WiFi.status() != WL_CONNECTED)
{
//Zeigt an das keine Netzwerkverbindung besteht.
digitalWrite(led_WIFI_rot, HIGH);
digitalWrite(led_WIFI_gruen, LOW);
digitalWrite(led_Tomaten_gruen, LOW);
digitalWrite(led_Kraeuter_gruen, LOW);
digitalWrite(led_Blumen_gruen, LOW);
digitalWrite(led_Wassertank_gruen, LOW);
digitalWrite(led_Tomaten_rot, LOW);
digitalWrite(led_Kraeuter_rot, LOW);
digitalWrite(led_Blumen_rot, LOW);
digitalWrite(led_Wassertank_rot, LOW);
}

else {
//Zeigt an das eine Netzwerkverbindung aufgebaut wurde.
digitalWrite(led_WIFI_rot, LOW);
digitalWrite(led_WIFI_gruen, HIGH);
}

int a = -1; // speichert Übergabe des Clients
WiFiClient client = server.available();
if (client) {
if (client.connected()) {
Serial.println(".");

while(a == -1) //Schleife läuft so lange bis ein passendes Ergebnis übertragen wird.
{
a = client.read(); // Liest die Übergabe des Clients
}

Serial.println("Kontrollwert: "); // Gibt gelesenen Wert zur Kontrolle aus
Serial.print(a); // Gibt gelesenen Wert zur Kontrolle aus

switch(a) // Mehrfachauswahl Anfang
{
case 0: //Tomaten hat genug Wasser
digitalWrite(led_Tomaten_rot, LOW);
digitalWrite(led_Tomaten_gruen, HIGH);
break;

case 1: //Tomaten müssen bewaessert werden
digitalWrite(led_Tomaten_rot, HIGH);
digitalWrite(led_Tomaten_gruen, LOW);
break;

case 2: //Kraeuter hat genug Wasser
digitalWrite(led_Kraeuter_rot, LOW);
digitalWrite(led_Kraeuter_gruen, HIGH);
break;

case 3: //Kraeuter muss bewaessert werden
digitalWrite(led_Kraeuter_rot, HIGH);
digitalWrite(led_Kraeuter_gruen, LOW);
break;

case 4: //Blumen hat genug Wasser
digitalWrite(led_Blumen_rot, LOW);
digitalWrite(led_Blumen_gruen, HIGH);
break;

case 5: //Blumen muss bewaessert werden
digitalWrite(led_Blumen_rot, HIGH);
digitalWrite(led_Blumen_rot, LOW);
break;

case 6: //Wassertank hat genug Wasser
digitalWrite(led_Wassertank_rot, LOW);
digitalWrite(led_Wassertank_gruen, HIGH);
break;

case 7: //Wassertank muss nachgefuellt werden
digitalWrite(led_Wassertank_rot, HIGH);
digitalWrite(led_Wassertank_rot, LOW);
break;
}// Mehrfachauswahl Ende
}
client.stop(); // Beendet die Verbindung mit dem Client
}
}

HaWe
09.09.2020, 20:08
hallo,
deine Abfrage in der loop() ist etwas anders gestrickt als wie ich es kenne:


WiFiClient client = wifiserver.available();

while ( client.connected() ) {
if ( client.available() ) {
char c = client.read();
//...
//... z.B. switch-Abfrage
//...
client.stop();
}
//...
delay(1);
}


Bin aber auch kein Fachmann, vlt hilft es aber trotzdem ;-)

- - - Aktualisiert - - -

PS,
ich mache aber nur die Koomunikation mit websites über die wifiserver/client libs,
die Kommunikation mit mehreren ESP-Clients mache ich über die webserver lib.

Philippp
09.09.2020, 20:15
Danke,
werde ich mal ausprobieren. Wofür ist das delay von einer ms am Ende?
LG,
Philippp

HaWe
09.09.2020, 22:42
Wofür ist das delay von einer ms am Ende?

kA, ob das nötig ist, stand bei mir so im example-Code.

Philippp
10.09.2020, 21:26
Also ich habe ein delay(10) an das Ende des Programmes gesetzt. Seit dem hatte ich nur noch einen Absturz, und das nachdem das Programm schon lange lief. Das ist zwar noch immer einer zuviel, aber um Welten besser als das was ich vorher hatte. Ein ehemaliger Kollege von mir meinte das er in so ziemlich jede Schleife ein kleines delay() reinschreibt, weil sich die Chips sonst "tot rechnen". Werde ich mir wohl auch angewöhnen.
Als nächstes werde ich mir mal die Stromversorgung anschauen. Ich wollte die ganze Geschichte eigentlich mit USB 3.0 versorgen. Das müsste locker ausreichen. Frage ist nur wie sauber die Spannung ist. Ich habe mal gelesen das ESP8266 Chips da sehr empfindlich sind...
LG,

Philippp

Klebwax
11.09.2020, 10:46
Ein ehemaliger Kollege von mir meinte das er in so ziemlich jede Schleife ein kleines delay() reinschreibt, weil sich die Chips sonst "tot rechnen".

Selten einen größeren Blödsinn gelesen.


Ich habe mal gelesen das ESP8266 Chips da sehr empfindlich sind...

Was man im Internet so alles lesen kann. Der Chip braucht bis zu 380mA, das liefert jeder USB Anschluß selbst mit einem Linearregler für die 3,3V. Praktische Erfahrungen zeigen, daß der Chip auch noch mit rund 2,5V arbeitet. Das führt dann leicht zu der Annahme, daß die Versorgung ok ist, obwohl sie in Wirklichkeit zu schwach ist.

Übliche Probleme sind, daß der ESP an den internen 3,3V Regler eines USB-Chips angeschlossen wird. Die können typisch weniger als 100mA (Data Sheet not read Error). Bei einem großen Stützkondensator reichts dann manchmal gerade so, da die knapp 400mA nur kurz beim Senden gebraucht werde. Dafür verlangsamt dieser Kondensator die Anschaltflanke so stark, daß der ESP keinen sauberen Power-On Reset mehr bekommt und damit ab und an mal nicht anläuft. Sporadische Abstürze sind da aber nicht zu erwarten. Ich verwende 3-Bein Schaltregler von z.B. Recom. Mit der 500mA Variante laufen bei mit alle ESP zuverlässig.

Abstürze nach einigen Minuten Laufzeit haben typisch 2 Gründe: Temperatur oder Speicherüberlauf. Wäre es Temperatur, würde die Zeit bei schnell aufeinanderfolgenden Tests immer kürzer werden.

Für mich klingt das Problem nach einem Speicherüberlauf. Bei TCP Verbindungen ist es ein häufig auftretendes Problem, daß Verbindungen nicht sauber geschlossen werden und offen bleiben, obwohl die Übertragung eigentlich beendet ist. Die Verbindung belegt dabei Buffer und Kontrollstrukturen. Am Ende beendet dann eine Timer im TCP-Stack die ganze Sache und gibt den Speicher wieder frei. Da auf einem PC Gigabytes Speicher zur Verfügung stehen, fällt das selten auf und solche fehlerhaften Programme haben ein langes Leben. Auf einem System mit wenig Speicher wird das dann ein Problem. IMHO kann der TCP-Stack des ESP maximal 4 Verbindungen gleichzeitig, mehr Speicher steht nicht zur Verfügung.


Also ich habe ein delay(10)...

Ich werte das als Bestätigung für meine Vermutung. Das delay() ermöglicht (meistens) dem Timer im TCP-Stack die Verbindung zu schließen. Das ist natürlich keine Lösung des wirklichen Problems.

MfG Klebwax

HaWe
11.09.2020, 19:22
hmmh, ich habe 5 ESP8266 clients am ESP8266 Server verbunden, und auf dem Server wird auch noch eine website aufgebaut, neben anderen services, und da hängt nichts.
Man muss aber aufpassen, dass while Schleifen nicht länger als ca. 5 sec ohne Unterbrechung laufen, sonst wird das interne RTOS Multithreading blockiert und dann führt der ESP RTOS Scheduler einen Reboot aus. Ein delay(1) in der Schleife kann das verhindern. Evt ist das der Grund, warum es auch im obigen example code verwendet wird.

Klebwax
12.09.2020, 08:29
hmmh, ich habe 5 ESP8266 clients am ESP8266 Server verbunden, und auf dem Server wird auch noch eine website aufgebaut, neben anderen services, und da hängt nichts.

Das sind keine stehenden TCP Connections. Ein WEB-Server ist ein Socket im Zustand Listen. Wenn sich ein Client (Browser) verbindet, wird eine Verbindung aufgebaut, die Seite geliefert und die Verbindung sofort wieder geschlossen. Das dauert auf einem ESP einige hundert Millisekunden, manchmal auch weniger. Der Timeout beim Verbindungsaufbau ist im Bereich Sekunden. Selbst wenn deine 5 Clients gleichzeitig versuchen, eine Verbindung aufzubauen, können sie ohne Probleme nacheinander abgewickelt werden, ohne daß ein Timeout im Client ausgelöst und damit auffällig wird. Mit allen anderen Services ist es ähnlich. Voraussetzung ist aber, daß die Verbindung am Ende der Übertragung zügig beendet wird. Deine Konfiguration kann also mit einem Socket und daher einer TCP-Verbindung gleichzeit realisiert werden.


Man muss aber aufpassen, dass while Schleifen nicht länger als ca. 5 sec ohne Unterbrechung laufen, sonst wird das interne RTOS Multipthreading blockiert und dann führt der ESP RTOS Scheduler einen Reboot aus.

Ich hab da eher 50ms in Erinnerung. Das Verhalten ist schon richtig beschrieben, aber eine lange Schleife blockiert einfach das RTOS (es ist ein kooperatives Tasking System) und der HW-Watchdog löst den Reboot aus.

MfG Klebwax

Philippp
14.09.2020, 10:13
Also zu der Debatte um delay() gibt es sicherlich einige Meinungen, und viel zu lernen. Ich habe einige Hobby Programmierer in meinem Bekanntenkreis, und ja, einer sagte mir auch das delay() der Grund für den Absturz meines Programmes sei. Zu diesem Zeitpunkt hatte ich nur ein delay() im Programm, und das im setup() Teil, welcher ja nur ein mal durchlaufen wird...



while (WiFi.status() != WL_CONNECTED) // Schleife läuft so lange, bis eine Verbindung zum W-LAN aufgebaut ist.
{
Serial.print(".");
digitalWrite(led_WIFI_rot, HIGH); //Zeigt an das keine Netzwerkverbindung besteht
delay(500);
}


Nachdem ich dieses delay() raus genommen habe, hat das Programm nicht mehr funktioniert. Rein logisch hätte der Verbindungsaufbau und das drucken des Punktes ja auch ohne klappen müssen. Hat es aber nicht. Delays sind also anscheinend ab und an notwendig.
Ich hatte es bislang so gehandhabt, dass ich ein delay() rein genommen habe wenn ich Angst hatte das die Hardware nicht hinterher kommt. Der ESP ist unterm Strich auch ein Stück Hardware. Mir fehlt es noch an Erfahrung zu sagen wie schnell die einzelnen Bestandteile des Chips sind. Pauschal zu sagen das sich Chips nicht totrechnen können finde ich gewagt. In meiner einfachen Welt braucht jede Rechenoperation Energie. Dieser Energieverbrauch schlägt sich unter anderem in Wärme nieder. Und da wären wir dann beim Überhitzungsproblem. Auch denke ich wie angedeutet, das nicht alle Teile des ESP gleich schnell sind, was zu Problemen führen kann.
Wie erwähnt bin ich Anfänger, und hoffe das ich hier noch viel lerne. Ich hoffe das es niemanden stört das ich es mir trotzdem leiste Aussagen kritisch zu hinterfragen.

Gruß,
Philippp

Klebwax
14.09.2020, 13:09
Erstmal vorneweg, Software ist eine Ingenieurwissenschaft und nicht Magic oder Vodoo.


Also zu der Debatte um delay() gibt es sicherlich einige Meinungen ..

In einer Wissenschaft gibt es ganz selten "einige Meinungen", insbesondere bei Themen, die schon seit Jahrzehnten abgehakt sind. Wenn ein erfahrener Programmierer in einem Programm unmotivierte delays sieht, weiß er sofort, wie er den Verfasser diese Codes einzuschätzen hat. Und ein wenig spezifischer zu delays. Das kann einfach ein Stück Code sein, der die CPU eine gewisse Zeit mit Sinnlosem beschäftigt, damit halt die Zeit verstreicht. Es kann aber auch ein Call in ein Laufzeitsystem, ein OS oder RTOS sein, das neben der Verzögerung dem Scheduler die Chance gibt, einen Taskwechsel vorzunehmen. Diese könnte man natürlich mit den entsprechenden Calls direkt auslösen, dann wäre es für Anfänger leichter verständlich.


Pauschal zu sagen das sich Chips nicht totrechnen können finde ich gewagt. In meiner einfachen Welt braucht jede Rechenoperation Energie.

Richtig, sie braucht Energie. Und so ein Chip ist dafür gebaut ständig zu rechnen, immer, ohne Pause. Er rechnet sich nicht tot, kann er nicht. Das klassische embeded Programm besteht aus einer while(true) Schleife. Das ist auch bei Arduino nicht anders. Nach einem Call auf setup() kommt ein Call auf loop(), dann macht das Arduino Framework seine Hausaufgaben und called wieder loop(). Und das immer wieder.

Neuere Chips haben die Möglichkeit, den ganzen Chip oder Teile stillzulegen um Energie zu sparen, z.B. für den Batteriebetrieb. Das ändert aber nichts an der Tatsache, daß sie, wenn die Batterie nur groß genug ist, auch durchlaufen können. Es gibt aber auch Systeme, die mit 100% (thermischer) Überlast laufen können, solange sie es nicht dauernd tun und daher nicht überhitzen. Das wirst du im embeded Bereich aber kaum finden. Der ESP und alle gängigen System sind sicher nicht so aufgebaut.

Ein ESP ist aus der Sicht eines Programmierers beileibe nicht ein Stück Hardware, sondern hat zusätzlich eine Art Betriebssystem an Bord, daß WLAN und TCP/IP abhandelt. Das ist schon mal ne Menge Code. On Top kommt noch die Arduino Laufzeitumgebung. Für einen Programmierer ist es wichtiger die Software zu verstehen und richtig zu behandeln als die Hardware. Um die haben sich die Entwickler des Systems schon gekümmert.


Mir fehlt es noch an Erfahrung zu sagen wie schnell die einzelnen Bestandteile des Chips sind

Das ist auch unerheblich. Wie schnell z.B. ein UART ein Byte sendet muß man nicht wissen, der UART hat dafür Statusbits, die das anzeigen und die man gefälligst auswerten sollte. Das gleiche gilt auch für Systemfunktionen. Sie brauchen halt solange, wie es dauert. Sollte die Ausführung nebenläufig erfolgen, in einem Interrupthandler oder einer eigenen Task, gibt es pflichtgemäß Möglichkeiten festzustellen, wann der Vorgang abgeschlossen ist. Ein delay im embeded Bereich macht nur dann Sinn, etwas im Bereich Mikrosekunden zu verzögern, weil etwas an einem IO nicht schnell genug ist. Und wenn zuviele auch lange delays im Code auftauchen, siehe oben.

Und zu deinem Beispiel: Wie ich schon schrieb, darf das kooperative RTOS im ESP nicht zu lange angehalten werden. Die Funktion WiFi.status() löst offensichtlich keinen Taskwechsel aus und das Programm stürzt ab. Die Funktion delay(500) tut das scheinbar und das RTOS läuft weiter. Ich vermute das der Wert im delay unerheblich ist und jeder andere Systemcall, der einen Taskwechsel erzeugt ebenfalls funktioniert. Ich hab keine Ahnung, wie gut die Doku zur Arduino-Umgebung aus dem ESP ist, dort sollte sich aber alles finden. Zur Not kann man auch den Source Code lesen, für manche Programmierer ist das Doku genug. Die schreiben nicht gerne Texte.

MfG Klebwax

HaWe
14.09.2020, 13:45
Ich vemute auch, dass überall ein delay(1) grundsätzlich ausreichen müsste, weil dies dem Scheduler ermöglicht, auch noch die WiFi-Funktionen ständig(!) parallel abzuarbeiten (Multithreading auf 1 Kern!). Und die laufen tatsächlich ständig im Hintergrund nebenher, egal was in der selber geschriebenen "Hauptprogramm-loop()" passiert.
Bei der Einwahl anfangs kann es u.U. sinnvoll sein, etwas länger als nur 1ms zu warten, weil die entspr. WiFi-Routinen zusammen mit der Hardware einfach eine gewisse Zeit benötigen, das kannst du aber ganz einfach selber austesten, indem du statt delay(500) einen kleineren Wert einsetzt (ohne es komplett zu löschen) - allerdings macht dann das Schreiben von Punkten jede einzelne ms wenig Sinn, denn da kommt der Serial Monitor gar nicht hinterher, und was nützt es einem, wenn die Einwahlschleife hunderttausende Punkte per Serial Monitor auf den PC-Screen schreibt, bis endlich Wifi connected ist.
Was deine delay(10) bei der Client-Kommunikation besser bewirken als ein delay(1), kann ich aber nicht beurteilen, denn hier verstehe ich zu wenig von deiner Programm-Architektur (ich selber verwende ja wie gesagt eine andere Lib mit einer anderen Technik).