NodeMCU- wdt reset nach Empfangen von Udp Nachricht

Hallo,

bei meinem aktuellen NodeMCU(ESp8266-12E)-Projekt habe ich immer wieder Fehlermeldungen beim Senden von UDP-Paketen bekommen. Also habe ich den Code so lange reduziert, bis ich den Fehler ausfindig machen konnte. Ich habe allerdings festgestellt, dass bereits beim Empfangen Fehler kommen.

Es passiert das Folgende:
Nachdem ich eine Nachricht zum Board gesendet habe, scheint erst einmal alles zu funktionieren, aber ca. 8 Sekunden danach schmeißt er einen wdt-reset:

ets Jan  8 2013,rst cause:4, boot mode:(3,6)

wdt reset
load 0x4010f000, len 3656, room 16 
tail 8
chksum 0x0c
csum 0x0c
v9c56ed1f
~ld

Hier ist der zugehörige Code:

/*
 * WIFI UDP communication based on a script by noiasca:
 * https://forum.arduino.cc/index.php?topic=676411.15
 * 
 * remaining parts by
 * CodeCrafter1 2020-08-19
 */

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

IPAddress myIP(192,168,137,101);
IPAddress mySubnet(255,255,255,0);
IPAddress myGateway(192,168,137,200);
const uint32_t localPort = 1337;

const char* WiFissid = "xxxxxxx";
const char* WiFipass = "yyyyyyyy";

WiFiUDP Udp; //object for UDP communication

void startWiFi(){
  WiFi.disconnect();
  delay(20);
  WiFi.config(myIP,myGateway,mySubnet);
  WiFi.mode(WIFI_STA);
  Serial.print(F("init WiFi:")); //DEBUG
  WiFi.begin(WiFissid, WiFipass);
  while (WiFi.status() != WL_CONNECTED) {
    Serial.print('.'); //DBEUG
    delay(500);
  }
  Serial.print(F("Connected! IP address: ")); //DEBUG
  Serial.println(WiFi.localIP()); //DEBUG
  Serial.printf("UDP server on port %d\n", localPort); //DEBUG

  Udp.begin(localPort); //start UDP-Server
}


/*
 * RETURNS:
 * -1 ... nothing arrived
 * 0 ... default answer
 */
int handleUDPrx(){
  // if there's data available, read a packet
  int packetSize = Udp.parsePacket();
  if (packetSize) {
    // buffers for receiving and sending data
    char packetBuffer[UDP_TX_PACKET_MAX_SIZE + 1];             // buffer to hold incoming packet,
    char ReplyBuffer[] = "OK";                            // a string to send back
    Serial.printf("Received packet of size %d from %s:%d\n    (to %s:%d, free heap = %d B)\n",
                  packetSize,
                  Udp.remoteIP().toString().c_str(), Udp.remotePort(),
                  Udp.destinationIP().toString().c_str(), Udp.localPort(),
                  ESP.getFreeHeap()); //DEBUG

    // read the packet into packetBufffer
    int n = Udp.read(packetBuffer, UDP_TX_PACKET_MAX_SIZE);
    packetBuffer[n] = 0;

    Serial.println("Contents:"); //DEBUG
    Serial.println(packetBuffer); //DEBUG
    
    // send a reply, to the IP address and port that sent us the packet we received
    Serial.printf("send answer to %s:%d\n",Udp.remoteIP().toString().c_str(), Udp.remotePort()); //DEBUG
    Udp.beginPacket(Udp.remoteIP(), Udp.remotePort());
    Udp.write(ReplyBuffer);
    Udp.endPacket();
    return 0;
  }
  return -1;
}


void setup() {
  Serial.begin(115200);
  Serial.println(); 

  //wifi config
  startWiFi();
}

void loop() {
  handleUDPrx();
}

Beste Grüße
CodeCrafter1

Da Du bisher noch nichts als Ansatz bekommen hast, versuche ich mal zwei Dinge. A) noiasca liest ggfls. mit B) Dir fehlen noch eine Handvoll serieller Ausgaben, mit denen Du das runterbrechen kannst, an welcher Stelle das ganze System steht, damit es zum WDT kommt.

codecrafter1: int handleUDPrx(){  // if there's data available, read a packet  int packetSize = Udp.parsePacket();  if (packetSize) {

Ich denke hier hängt es. Aber:

    return 0;
  }
  return -1;
}

void loop() {
  handleUDPrx();
}

Warum generierst Du einen Rückgabewert, wenn Du den nicht nutzt? Da wäre mein erstes Serial.print(ln) drin - damit ich sehe, wann Schluß ist mit Lustig!

Das nur aus der Logik heraus - ich mache kein ESP.

Dank Deines Tipps hab ich jetzt schon mal genauere Anhaltspunkte. Ich habe die Loop jetzt wie folgt geändert:

void loop() {
  Serial.println("START");
  int stat = handleUDPrx();
  if(stat == 0){
    Serial.println("mssg successful arrived");
  }else{
    Serial.println("NONE");
  }
  delay(200);
  Serial.println("END");
}

Wenn ich den Udp.begin Udp.write und Udp.end Teil in handleUDPrx drin lasse (siehe Quellcode) hängt es sich dort auf und es kommt zum wdt-reset (sprich die Funktion erreicht nie das return 0 ; return -1 kommt immer erfolgreich)

Nehme ich die 3 besagten Zeilen aus handleUDPrx raus, so gibt die Funktion erfolgreich ein returnvalue, aber beim delay in der Loop hängt es sich auf und es kommt zum wdt-reset.

Beste Grüße CodeCrafter1

codecrafter1:
Dank Deines Tipps hab ich jetzt schon mal genauere Anhaltspunkte. Ich habe die Loop jetzt wie folgt geändert:

  delay(200);

Ersetz mal die delay()-Zeile durch folgende Zeilen:

  unsigned long jetzt=millis();
  while millis()-jetzt<200{
    Serial.println();}

Schau mal, ob der immer an der gleichen Stelle hängen bleibt, indem Du die LeerZeilen der seriellen Ausgabe zählst.

Wenn ja, füge mal nach

int stat = handleUDPrx();

ein

wdt_reset();

Das Board macht immernoch einen wdt-reset allerdings nach dem “Serial.println(“bevor end”);” Allerdings erst nachdem eine Nachricht eingetroffen ist. Vorher läuft alles wunderbar.

void loop() {
  Serial.println("START");
  int stat = handleUDPrx();
  wdt_reset();
  if(stat == 0){
    Serial.println("function successful");
  }else{
    Serial.println("NONE");
  }
  unsigned long now = millis();
  Serial.println("bevor end");
  while (millis()-now<200){
    Serial.print(" ");
  }
  Serial.println("END");
}

Beste Grüße
CodeCrafter1

Was willst Du mit diesem Konstrukt erreichen (außer den Ausgabepuffer vollballern)?

while (millis()-now<200){
    Serial.print(" ");
  }

Wenn Du meinst, das unbedingt zu brauchen, schreibe mal ein yield(); in die Schleife.
Besser wäre es aber auf das Konstrukt zu verzichten.

Gruß Tommy

codecrafter1:
Das Board macht immernoch einen wdt-reset allerdings nach dem “Serial.println(“bevor end”);” Allerdings erst nachdem eine Nachricht eingetroffen ist. Vorher läuft alles wunderbar.

Also hier:

  unsigned long now = millis();
  Serial.println("bevor end");
// Hier ein Reste OHN das eine Leerzeile ausgegeben wird?

  while (millis()-now<200){
    Serial.print(" ");
[/quote]

Dann mach mal das mit dem einfügen des von mir vorgeschlagenen
[code]
wdt_reset();

Damit wird der 8-sekundentimer wieder zurückgesetzt.
Ich denke, das Timing von empfangen UND senden st zu lang und schiesst Dir das System ab.

Alternativ könntest Du auch den wdt_reset(); an den Anfang von

int handleUDPrx(){
wdt_reset();
  // if there's data available, read a packet

stellen.
Dann stände Dir eine längere zeit zur Verfügung.[/code]

@my_xy_project Ich habe mal mir den wdt_reset() an den von dir vorgeschlagenen Stellen probiert, und es ändert nichts.

ob delay oder dein while-Konstrukt es macht an der gleichen Stelle einen wdt-reset. Wenn ich das delay(200) an den Anfang der loop mache und eine Nachricht empfange scheint er sich zwischen den Loop Durchgängen aufzuhängen (?) und einen wdt-reset zu machen. Kann es sein dass der wdt-reset beim yielding ausgelöst wird?

Zusammenfassung: Es kommt ein wdt-reset wenn ich nach dem ankommen einer Nachricht eine Antwort senden will. ODER: Es kommt ein wdt-reset beim nächsten yielding nach einer eintreffenden Nachricht (wenn ich keine Antwort schicke)?

Tommy56: Was willst Du mit diesem Konstrukt erreichen

Das war ein Vorschlag von my_xy_project um den Fehler zu lokalisieren

Beste Grüße CodeCrafter1

Tommy56:
Was willst Du mit diesem Konstrukt erreichen (außer den Ausgabepuffer vollballern)?

while (millis()-now<200){

Serial.print(" ");
 }

Das kam von mir.
Da aus dem Thread bisher nur bekannt war, das bei eintreffen einer Nachricht - und nur da - ein wdt reset ausgelöst wird, unzwar genau nach der Rückkehr aus der Funktion, wollte ich wissen, wieviel Zeit nach der Rückkehr bis zum reset verbleibt.

Wie sich das darstellt, ist weder das delay() noch das Konstrukt daran beteiligt, da der Reset schon ausgelöst wird, ohne da eine Aktion auszuführen.

Für mich heisst das, das mit dem:

 // send a reply, to the IP address and port that sent us the packet we received
    Serial.printf("send answer to %s:%d\n",Udp.remoteIP().toString().c_str(), Udp.remotePort()); //DEBUG
    Udp.beginPacket(Udp.remoteIP(), Udp.remotePort());
    Udp.write(ReplyBuffer);
    Udp.endPacket();
    return 0;

die Restzeit der 8Sekunden vor dem reset() aufgebraucht werden.

Eigentlich wäre es jetzt angebracht die vergangene Zeit zwischen den Ausgaben:
Serial.printf("Received packet of size %d from %s:%d\n […]
und
Serial.printf(“send answer to %s:%d\n”,Udp.remoteIP().toString().c_str(), […]
mal anzusehen.

Also das was im SerMon als Zeiten vor der Ausgabe steht.
Die Differenz dürfte dann für die Lösung hilfreich sein.

Alternativ könnte man auch die millis dazwischen messen und ausgeben.

@TO: Ich habe vor einiger Zeit mal mit UDP und dem NodeMCU gespielt und keinerlei derartige Probleme gehabt. Evtl. kannst Du Dir da ein paar Anregungen holen. Ob man den ESP als AP betreibt oder beide in einem bestehenden Netz ist dabei egal.

Gruuß Tommy

Ich habe jetzt mal millis() ausgaben reingemacht und es vergehen 3ms zwischen den beiden Prints. Allerdings kommt nun eine sehr interessante Exception (die bis zum letzten Flashen nicht kam)

Am Code nur die zwei prints und die loop leicht anders als vorher:

int handleUDPrx(){
  // if there's data available, read a packet
  int packetSize = Udp.parsePacket();
  if (packetSize) {
    // buffers for receiving and sending data
    char packetBuffer[UDP_TX_PACKET_MAX_SIZE + 1];             // buffer to hold incoming packet,
    char ReplyBuffer[] = "OK";                            // a string to send back
    Serial.printf("Received packet of size %d from %s:%d\n    (to %s:%d, free heap = %d B)\n",
                  packetSize,
                  Udp.remoteIP().toString().c_str(), Udp.remotePort(),
                  Udp.destinationIP().toString().c_str(), Udp.localPort(),
                  ESP.getFreeHeap()); //DEBUG
    Serial.print("time: ");
    Serial.println(millis());
    
    // read the packet into packetBufffer
    int n = Udp.read(packetBuffer, UDP_TX_PACKET_MAX_SIZE);
    packetBuffer[n] = 0;

    Serial.println("Contents:"); //DEBUG
    Serial.println(packetBuffer); //DEBUG
    

    
    // send a reply, to the IP address and port that sent us the packet we received
    Serial.printf("send answer to %s:%d\n",Udp.remoteIP().toString().c_str(), Udp.remotePort()); //DEBUG
    Serial.println(millis());
    Udp.beginPacket(Udp.remoteIP(), Udp.remotePort());
    Udp.write(ReplyBuffer);
    Udp.endPacket();
    return 0;
  }
  return -1;
}

void loop() {
  Serial.println("START");
  delay(200);
  int stat = handleUDPrx();
  wdt_reset();
  if(stat == 0){
    Serial.println("function successful");
  }else{
    Serial.println("NONE");
  }
  Serial.println("END");
}

Dann kam der folgende output aufm SerialMonitor:

...
START
NONE
END
START
Received packet of size 2 from 192.168.137.1:50378
    (to 192.168.137.101:1337, free heap = 51872 B)
time: 10273
Contents:
OK
send answer to 192.168.137.1:50378
10276

---HIER KOMMT DIE EXCEPTION

es handelt sich hierbei um eine "Exception 9: LoadStoreAlignmentCause: Load or store to an unaligned address". Aus dem Stack (Exception decoder) lässt sich schließen, dass es von den 3 besagten udp send Funktionen kommt. Und nen yielding ist im Stack auch drin. Da es wichtig sein könnte, hier das ergebnis vom exception decoder (ich musste etwas kürzen):

Exception 9: LoadStoreAlignmentCause: Load or store to an unaligned address
Decoding 163 results
pp_attach at ?? line ?
ets_post at core_esp8266_main.cpp line 177
pp_post at ?? line ?
ppTxPkt at ?? line ?
ieee80211_output_pbuf at ?? line ?
wdt_feed at ?? line ?
_printf_i at nano-vfprintf_i.c line 246
glue2esp_linkoutput at lwip-esp.c line 301
malloc at umm_malloc.cpp line 511
ethernet_output at ethernet.c line 312
uart_write_char_d at core_esp8266_postmortem.cpp line 221
(inlined by) uart_write_char_d at core_esp8266_postmortem.cpp line 216
etharp_raw at etharp.c line 1161
etharp_request at etharp.c line 1202
etharp_query at etharp.c line 987
_vsnprintf_r at vsnprintf.c line 73
etharp_output_LWIP2 at etharp.c line 890
ip4_output_if_opt_src at ip4.c line 1764
uart_write at uart.cpp line 509
ip4_output_if_src at ip4.c line 1590
ip_chksum_pseudo at inet_chksum.c line 395
HardwareSerial::write(unsigned char const*, unsigned int) at HardwareSerial.h line 164
udp_sendto_if_src at udp.c line 893 (discriminator 3)
umm_free_core at umm_malloc.cpp line 316
udp_sendto_if at udp.c line 692
udp_sendto at udp.c line 599
HardwareSerial::write(unsigned char const*, unsigned int) at HardwareSerial.h line 164
WiFiUDP::endPacket() at UdpContext.h line 458
(inlined by) WiFiUDP::endPacket() at WiFiUdp.cpp line 176
Print::println() at Print.cpp line 186
WiFiUDP::write(unsigned char const*, unsigned int) at WiFiUdp.cpp line 185
handleUDPrx() at Testserver.ino line 76---------------------------------------------das ist die return 0 Zeile 
wifi_station_set_default_hostname at ?? line ?
pvPortMalloc at heap.cpp line 271
glue2esp_linkoutput at lwip-esp.c line 301
malloc at umm_malloc.cpp line 511
sntp_request at sntp.c line 604
malloc at umm_malloc.cpp line 511
umm_free_core at umm_malloc.cpp line 316
free at umm_malloc.cpp line 362
calloc at umm_malloc.cpp line 826
calloc at umm_malloc.cpp line 820
vPortFree at heap.cpp line 308
ets_post at core_esp8266_main.cpp line 177
ets_timer_disarm at ?? line ?
sta_input at ?? line ?
delay_end at core_esp8266_wiring.cpp line 41
do_memp_malloc_pool at memp.c line 255
ets_timer_arm_new at ?? line ?
sys_timeout_abs at timeouts.c line 189
pp_enable_noise_timer at ?? line ?
ets_post at core_esp8266_main.cpp line 177
pp_noise_test at ?? line ?
esp_schedule at core_esp8266_main.cpp line 125
wdt_feed at ?? line ?
delay_end at core_esp8266_wiring.cpp line 44
ets_timer_handler_isr at ?? line ?
ets_timer_handler_isr at ?? line ?
loop_task(ETSEventTag*) at core_esp8266_main.cpp line 205
call_user_start_local at ?? line ?
call_user_start_local at ?? line ?
call_user_start at ?? line ?
cont_ret at cont.S line 142
ets_post at core_esp8266_main.cpp line 177
ets_timer_disarm at ?? line ?
lmacTxFrame at ?? line ?
wDev_ProcessFiq at ?? line ?
__ssputs_r at nano-vfprintf.c line 233
pp_attach at ?? line ?
_printf_i at nano-vfprintf_i.c line 194 (discriminator 1)
system_param_save_with_protect at ?? line ?
_vsnprintf_r at vsnprintf.c line 73
uart_write_char at uart.cpp line 487
HardwareSerial::write(unsigned char) at esp8266/HardwareSerial.h line 160
uart_write at uart.cpp line 509
uart_write at uart.cpp line 509
vsnprintf at vsnprintf.c line 42
HardwareSerial::write(unsigned char const*, unsigned int) at HardwareSerial.h line 165
malloc at umm_malloc.cpp line 511
uart_write_char at uart.cpp line 492
mem_malloc at mem.c line 210
do_memp_malloc_pool at memp.c line 255
WiFiUDP::begin(unsigned short) at WiFiUdp.cpp line 83
memp_malloc at memp.c line 356
HardwareSerial::write(unsigned char const*, unsigned int) at HardwareSerial.h line 164
udp_new at udp.c line 1224
uart_write at uart.cpp line 509
__esp_yield at core_esp8266_main.cpp line 119
__delay at core_esp8266_wiring.cpp line 57
loop at Testserver.ino line 93
loop_wrapper() at core_esp8266_main.cpp line 197

Tommy56: Evtl. kannst Du Dir da ein paar Anregungen holen.

Das werde ich gleich mal machen

Beste Grüße CodeCrafter1

codecrafter1: Ich habe jetzt mal millis() ausgaben reingemacht und es vergehen 3ms zwischen den beiden Prints. Allerdings kommt nun eine sehr interessante Exception (die bis zum letzten Flashen nicht kam)

Das wird vermutlich mein letzter werden - da fehlt mir einiges an Wissen. Aber: Der Einsatz der Ausgabe war an der falschen Stelle. Ich hab das mal umgeändert, was ich wollte:

int handleUDPrx(){
  // if there's data available, read a packet
  int packetSize = Udp.parsePacket();
  if (packetSize) {
    // buffers for receiving and sending data
    char packetBuffer[UDP_TX_PACKET_MAX_SIZE + 1];             // buffer to hold incoming packet,
    char ReplyBuffer[] = "OK";                            // a string to send back
    Serial.printf("Received packet of size %d from %s:%d\n    (to %s:%d, free heap = %d B)\n",
                  packetSize,
                  Udp.remoteIP().toString().c_str(), Udp.remotePort(),
                  Udp.destinationIP().toString().c_str(), Udp.localPort(),
                  ESP.getFreeHeap()); //DEBUG
    Serial.print("time: ");
    Serial.println(millis());
    
    // read the packet into packetBufffer
    int n = Udp.read(packetBuffer, UDP_TX_PACKET_MAX_SIZE);
    packetBuffer[n] = 0;

    Serial.println("Contents:"); //DEBUG
    Serial.println(packetBuffer); //DEBUG
    
    // send a reply, to the IP address and port that sent us the packet we received
    Serial.printf("send answer to %s:%d\n",Udp.remoteIP().toString().c_str(), Udp.remotePort()); //DEBUG
    Udp.beginPacket(Udp.remoteIP(), Udp.remotePort());
    Udp.write(ReplyBuffer);
    Udp.endPacket();

    Serial.println(millis());  // Ich will wissen, wieviel Zeit hier drauf geht

    return 0;
  }
  return -1;
}
[code]

Ansonsten denke ich, wird der Eine oder Andere auf den Thread aufmerksam geworden sein ;)
Viel Erfolg!

Mein Vorschlag: Nimm das printf-Konstrukt raus und gib die Sachen normal über println aus.

Serial.printf("Received packet of size %d from %s:%d\n    (to %s:%d, free heap = %d B)\n",
                  packetSize,
                  Udp.remoteIP().toString().c_str(), Udp.remotePort(),
                  Udp.destinationIP().toString().c_str(), Udp.localPort(),
                  ESP.getFreeHeap()); //DEBUG

Der Compiler scheint da Alignmentprobleme zu haben.

Gruß Tommy

Tommy56: @TO: Ich habe vor einiger Zeit mal mit UDP und dem NodeMCU gespielt und keinerlei derartige Probleme gehabt. Evtl. kannst Du Dir da ein paar Anregungen holen. Ob man den ESP als AP betreibt oder beide in einem bestehenden Netz ist dabei egal.

Nachdem ich das mal grob überflogen hatte, kam ich auf die Idee mal nen älteren Boardtreiber fürs NodeMCU zu installieren und ich hab mal die Versionen durchgetestet.

Version 2.2.0; 2.3.0; 2.4.0 das Programm funktioniert ohne Exception bzw. wdt-reset Ab Version 2.5.0 klappts nicht mehr. Scheint als sollte man den NodeMCU nicht in der neusten Version programmieren...

@Tommy56 ich hatte in der 2.7.4 gebuilded und ich schau mal obs das Printf war...

Beste Grüße CodeCrafter1

2.7.4 habe ich noch nicht ausgiebig getestet. Die ist mir noch zu neu ;)

Gruß Tommy

Ich kann da Tommys Aussage bestätigen, es gibt mit UDP keine Probleme am NodeMCU.

Mein Tipp, baue ein einfaches Projekt mit UDP auf. Wobei du auf einer Seite nur sendest, auf der anderen nur empfängst.

Das sollte problemlos funktionieren. Dann kannst du das ja erweitern.

Als es im April beim TO schon einmal um UDP ging.

Derfips: Wahrscheinlich wäre es angebracht mal einen Blick in die Beispiele der IDE zu werfen.

Diese Empfehlung ist aktueller den je!

char packetBuffer[UDP_TX_PACKET_MAX_SIZE + 1];

Diese Zeile verursacht die Exception.

    Serial.printf("Received packet of size %d from %s:\n    (to %s:%d, free heap = %d B)\n",
                  packetSize,
                  Udp.remoteIP().toString().c_str(),
                  Udp.destinationIP().toString().c_str(), Udp.localPort(),
                  ESP.getFreeHeap());

Und tritt hier ans Tageslicht.

Gruß Fips