Teensy4.1 Simple EthernetUDP Send before Receive crashes

save_jeff

New member
Hi,

I'm working on sending UDP Datagrams to a server over the Ethernet on the Teensy 4.1 and i get crashes as soon as i send a Package without receiving one first

here is the example code:

Code:
#include <NativeEthernet.h>
#include <NativeEthernetUdp.h>

#define EXECUTE_EVERY(ms) { \
  static ulong _last_time = 0; \
  if (millis() - _last_time > (ms)) \
  { \
    _last_time = millis();\

#define EXECUTE_EVERY_END }}


// Enter a MAC address and IP address for your controller below.
// The IP address will be dependent on your local network:
byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };

IPAddress ip(192, 168, 178, 150);

unsigned int localPort = 8888;  // local port to listen on

// buffers for receiving and sending data
#define UDP_RX_BUFF_SIZE 1024
char packetBuffer[UDP_RX_BUFF_SIZE];  // buffer to hold incoming packet,
char ReplyBuffer[] = "acknowledged abcdefghijklmnopqrstuvwxyz  abcdefghijklmnopqrstuvwxyz  abcdefghijklmnopqrstuvwxyz ";        // a string to send back

// An EthernetUDP instance to let us send and receive packets over UDP
EthernetUDP Udp;


void setup() {
  // Open serial communications and wait for port to open:
  Serial.begin(115200);
  while (!Serial) {
    ; // wait for serial port to connect. Needed for native USB port only
  }
  
  // start the Ethernet
  Ethernet.begin(mac, ip);

  
  // Check for Ethernet hardware present
  if (Ethernet.hardwareStatus() == EthernetNoHardware) {
    Serial.println("Ethernet shield was not found.  Sorry, can't run without hardware. :(");
    while (true) {
      delay(1); // do nothing, no point running without Ethernet hardware
    }
  }
  if (Ethernet.linkStatus() == LinkOFF) {
    Serial.println("Ethernet cable is not connected.");
  }

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







void loop() {
  EXECUTE_EVERY(1000)

    static int loop_count = 0;

    Serial.printf("loop: count=%d\n", loop_count++);    

  EXECUTE_EVERY_END
  
#if 1
  EXECUTE_EVERY(10)

    // if there's data available, read a packet
    int packetSize = Udp.parsePacket();
    if (packetSize) 
    {
      Serial.print("Received packet of size ");
      Serial.println(packetSize);
      Serial.print("From ");
      IPAddress remote = Udp.remoteIP();
      for (int i=0; i < 4; i++) {
        Serial.print(remote[i], DEC);
        if (i < 3) {
          Serial.print(".");
        }
      }
      Serial.print(", port ");
      Serial.println(Udp.remotePort());

      // read the packet into packetBufffer
      Udp.read(packetBuffer, UDP_RX_BUFF_SIZE);
      Serial.println("Contents:");
      Serial.println(packetBuffer);

      // send a reply to the IP address and port that sent us the packet we received
      //Udp.beginPacket(Udp.remoteIP(), Udp.remotePort());
      //Udp.write(ReplyBuffer);
      //Udp.endPacket();
    }

  EXECUTE_EVERY_END
  
#endif 

#if 1
  EXECUTE_EVERY(2500)

    // send a reply to the IP address and port that sent us the packet we received
    Udp.beginPacket("192.168.178.100", 5100);
    Udp.write(ReplyBuffer);
    Udp.endPacket();

  EXECUTE_EVERY_END
  
#endif 
}

this is a mostly unchanged example UDPSendReceiveString.ino with the send part removed from after the reception and put into an extra segment that sends a UDP Datagram every 2.5s.

it just stops doing anything after the first send, while conforming to the Arduino convention. The same code works fine on ESP32 Wifi.
I have a UDP Echo Server running in 192.168.178.100:5100 and it received the Datagram. the response is also received on the Teensy but the freezes.

here is the Serial printout that shows that the message is received and then the code freezes

Code:
loop: count=0
Received packet of size 96
From 192.168.178.100, port 5100
Contents:
acknowledged abcdefghijklmnopqrstuvwxyz  abcdefghijklmnopqrstuvwxyz  abcdefghijklmnopqrstuvwxyz


I tested this in the Arduino IDE as well as on PlatformIO (both up to date versions)
 
I found a solution

The UDP Echo Server i set up answers with the echo as well as a second empty UDP Datagram. this second empty datagram seems to crash the Teensy.
Might be a bug where the package size returned from parsePacket is zero because the msg is len 0 and not because no msg was received.

i think this should be fixed in the firmware, as it is possible to intentionally crash all teensy's with open UDP ports by sending a zero-length UDP datagram to them.
 
QNEthernet supports zero-length UDP packets. The NativeEthernet library and likely most Arduino-style Ethernet libraries don’t support them. (Note: Arduino’s text description of EthernetUDP.parsePacket() is technically correct, but the example is wrong (here). The example shows “zero means no packet”.)

Short rant: I don’t agree that libraries should always copy a very old way of doing things because that amplifies any bad design decisions, such as in that Arduino example linked above. My goal with QNEthernet (and my other libraries) is to provide great functionality for without forcing the user into “only one way of doing something.” Example: my library’s `begin()` functions don’t block. If calling code wants to block on some condition, it can. In other words, I tried to support both styles. Other libraries’ `begin()` behaviour, on the other hand, likely blocks, so it’s not possible to write non-blocking-style code. This particular design decision is probably specifically because past libraries (including for other boards) do it. While there’s nothing technically wrong with that design, it’s limiting. The only “downside” with my approach is that existing code without changes won’t work exactly the same. But I don’t think this is actually a downside here.

And that’s why I decided to support zero-length packets. Because it’s the right thing to do and because the reason for not doing so is based on an incorrect and very old Arduino example (even though the text description is technically correct).

See also: https://github.com/ssilverman/QNEth...4a54d5147/README.md#parsepacket-return-values

Latest version: 0.17.0
Note: if you do a search for QNEthernet in PlatformIO or the Arduino IDE, you’ll encounter lots of noise. There’s a bunch of libraries that use it and that have “QNEthernet” in their tags and description. If you wade through that noise, you’ll find my library. :)
 
Last edited:
First: sorry for the late replay. i was quite busy the last week.

Thanks for responding. I have to agree with you. The "Arduino Convention" is often easy to understand but some things have been defined in a rather short sited manner. The Project I'm working on is cross platform and supports a lot of processors (ESP32, ESP8266, Teensy, RP2040, SAMD, etc..) and i started to define Arduino inspired standard interfaces for I2C, SPI, Serial, Files, Memory etc.
So I'm quite familiar with the differences in implementation on different Arduino cores. it's quite common to adjust the "Arduino Convention" to the target system. In the end you still have to do #ifdef to work around these small differences in behavior.

Maybe ill publish the Standartized Arduino Inferfaces at some point.
Especially with Network classes there should be a standard super class for a TCP connection that can be given to libraries. so that you can use for example the same websocket library with Ethernet on Teensy, Wifi on ESP and RP2040 and Cellular over AT Commands using the TinyGSM library. currently i have to implement multiple variants for basically the same task.


Ill look into the QNEthernet library and replace the NativeEthernet lib.
i use PlatformIO and for most libraries i download them locally as it never takes too long until i start modifying the lib to my needs and standards

Again Thanks for the detailed info



EDIT: I'm also not a fan with the parsePacket thing. they not have a Receive() function that gives you true/false back if there was something received and then you can check the data length and content via available() or something like that
 
Back
Top