Teensy 4.1 UDP crash with more then 24 bytes?

halentech

New member
First my code and out put and then my questions and observations:

code running on Teensy:
Code:
#include <NativeEthernet.h>
#include <NativeEthernetUdp.h>

#include <FastLED.h>

#define UDP_TX_PACKET_MAX_SIZE 1480

// How many leds in your strip?
#define NUM_LEDS 14


#define DATA_PIN 3
#define CLOCK_PIN 2

// 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(10, 10, 10, 10);

// Define the array of leds
CRGB leds[NUM_LEDS];

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

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

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

void setup() {
  // You can use Ethernet.init(pin) to configure the CS pin
  //Ethernet.init(10);  // Most Arduino shields
  //Ethernet.init(5);   // MKR ETH shield
  //Ethernet.init(0);   // Teensy 2.0
  //Ethernet.init(20);  // Teensy++ 2.0
  //Ethernet.init(15);  // ESP8266 with Adafruit Featherwing Ethernet
  //Ethernet.init(33);  // ESP32 with Adafruit Featherwing Ethernet

  // start the Ethernet
  Ethernet.begin(mac, ip);

  // Open serial communications and wait for port to open:
  Serial.begin(9600);
  while (!Serial) {
    ; // wait for serial port to connect. Needed for native USB port only
  }

  // 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);
  
  FastLED.addLeds<APA102, DATA_PIN, CLOCK_PIN, BGR>(leds, NUM_LEDS);  // BGR ordering is typical
 
  Serial.println(UDP_TX_PACKET_MAX_SIZE);
  Serial.println("end of setup");
}

void loop() {
  // if there's data available, read a packet
  uint32_t packetSize;
  if (packetSize = Udp.parsePacket()) Serial.println("gotone");
  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_TX_PACKET_MAX_SIZE);
    Serial.println("Contents:");
    Serial.printf(packetBuffer);
    Serial.println();

    FastLED.clear();
    FastLED.show();
    uint32_t dataCounter = 0;
      for (uint32_t i = 0; i < packetSize; i++)
      {
        leds[i].r = packetBuffer[dataCounter+2];
        leds[i].g = packetBuffer[dataCounter+1];
        leds[i].b = packetBuffer[dataCounter  ];
        dataCounter += 3;
        Serial.println(dataCounter);
        
      }
    
   // leds = packetBuffer;
    FastLED.show();

    // send a reply to the IP address and port that sent us the packet we received
    Udp.beginPacket(Udp.remoteIP(), Udp.remotePort());
    Serial.println("out");
    Udp.write(ReplyBuffer);
    Serial.println("out2");
    Udp.endPacket();
    Serial.println("out3");
  }
  delay(1);
  
}

Python code running from my computer:

Code:
import socket
import time

with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as sock:
    sock.connect(("10.10.10.10", 8887))
    
    while True:
    
        sock.send(bytes.fromhex("220000002200000022220000002200000022220000002200"))
        print(sock.recvfrom(1500))
        time.sleep(1)
        sock.send(bytes.fromhex("002200220022220000222200000022002200222222000022"))
        print(sock.recvfrom(1500))
        time.sleep(1)
        sock.send(bytes.fromhex("000022002200220022002222002200220022002222002200"))
        print(sock.recvfrom(1500))
        time.sleep(1)
        print('done')

when running this Python code from my computer everything works as expected and if we change the Python code to:

Code:
import socket
import time

with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as sock:
    sock.connect(("10.10.10.10", 8887))
    
    while True:
    
        sock.send(bytes.fromhex("220000002200000022220000002200000022220000002200"))
        print(sock.recvfrom(1500))
        time.sleep(1)
        sock.send(bytes.fromhex("002200220022220000222200000022002200222222000022"))
        print(sock.recvfrom(1500))
        time.sleep(1)
        sock.send(bytes.fromhex("000022002200220022002222002200220022002222002200444444"))
        print(sock.recvfrom(1500))
        time.sleep(1)
        print('done')

Now things start to go off the rails... I added 3 more bytes to the last message and here is the output I get from the Python:

Code:
(b'acknowledged', ('10.10.10.10', 8887))
(b'acknowledged', ('10.10.10.10', 8887))
(b'acknowledged$fDt\x8e\xbbC\xa9\x01\xec\xb3\xad', ('10.10.10.10', 8887))
done

The serial out from the teensy looks good and the LEDs all update correctly but the last acknowledged statement from my Python looks to have some garbage in it.

If I change the Python again:

Code:
import socket
import time

with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as sock:
    sock.connect(("10.10.10.10", 8887))
    
    while True:
    
        sock.send(bytes.fromhex("220000002200000022220000002200000022220000002200"))
        print(sock.recvfrom(1500))
        time.sleep(1)
        sock.send(bytes.fromhex("002200220022220000222200000022002200222222000022"))
        print(sock.recvfrom(1500))
        time.sleep(1)
        sock.send(bytes.fromhex("000022002200220022002222002200220022002222002200444444444444444444444444444444"))
        print(sock.recvfrom(1500))
        time.sleep(1)
        print('done')

Adding more bytes to the last massage again the teensy crashes. It will no longer respond to pings. Here is the out from the Python:

Code:
(b'acknowledged', ('10.10.10.10', 8887))
(b'acknowledged', ('10.10.10.10', 8887))

And here is the output from the serial of the teensy:

Code:
1480

end of setup

gotone

Received packet of size 24

From 10.10.10.1, port 57503

Contents:

"

3

6

9

12

15

18

21

24

27

30

33

36

39

42

45

48

51

54

57

60

63

66

69

72

out

out2

out3

gotone

Received packet of size 24

From 10.10.10.1, port 57503

Contents:



3

6

9

12

15

18

21

24

27

30

33

36

39

42

45

48

51

54

57

60

63

66

69

72

out

out2

out3

So this looks to me like a buffer or an array somewhere is being overrun but the odd thing is that serial output seems to die long before the teensy does because I get the last FastLED.show() as the LED strip is displaying the third packet as expected. I don't seem to get anything from the serial output about processing the third packet so it makes it difficult to figure out where things are jumping off the rails. I know the last packet was processed because the LED strip is displaying it. Also, the fact the the Teensy stops responding to the network is bad (it's just dead). I see that the UDP_TX_PACKET_MAX_SIZE default size is 24 bytes and that seems to be the biggest size packet that the teensy is fine with handling increasing UDP_TX_PACKET_MAX_SIZE doesn't seem to buy you anything. What good is UDP_TX_PACKET_MAX_SIZE? Why does it exists and why is it set so low? I'm not sure UDP_TX_PACKET_MAX_SIZE is even the problem but it seems suspicious. I don't think my code is bad but my programing skills aren't the best. I think something is getting messed up in teensy NativeEthernet.

I'm using a fresh install of Arduino 1.8.13, 1.53 Teensyduino, and I grab a new version of NativeEthernet 1.0.5

I think version of NativeEthernet 1.0.1 came with Teensyduino, and that version seems to handle less bytes then 1.0.5 before crashing.

Please any help will be grateful I have six teensy 4.1 I want to use for a big project... I'm trying to get them to replace 10 esp32 boards because the wireless isn't reliable.
 
I noticed you are running outside of the leds array, the length is 14 and you are accessing up to packetSize which is 24,24,27. It could be causing your problem. The garbage data in your third acknowledge message may be from your code overwriting the null character at the end of your char array and causing it to continue printing until it hits the next one. I assume you wanted something like i < packetSize/3 but you could still run into an issue if you don't check whether it is greater than the size of the leds array and handle it accordingly.

Code:
    uint32_t dataCounter = 0;
      for (uint32_t i = 0; i < packetSize; i++)
      {
        leds[i].r = packetBuffer[dataCounter+2];
        leds[i].g = packetBuffer[dataCounter+1];
        leds[i].b = packetBuffer[dataCounter  ];
        dataCounter += 3;
        Serial.println(dataCounter);
        
      }
 
Yeah, that's defiantly a problem. I think the UDP_TX_PACKET_MAX_SIZE sent me down the wrong rabbit hole because it fit so well... I couldn't catch my mistake because the serial output was dyeing before I could see the problem.

Thank you

more testing I go.
 
No problem, hopefully that's the issue. I'm not even sure if UDP_TX_PACKET_MAX_SIZE does anything inside of the library, I quickly searched for it but I didn't get any hits. I would assume it's just a standard way of declaring the buffer array size and using it across your program in that case but I could be wrong. Good luck with your project!
 
I don't think that #define is actually used anywhere in the Ethernet library. Maybe is cruft left over from an ATmega version of the lib where RAM is much more constrained?
 
I took the liberty of writing an alternative Teensy program in a more QNEthernet style. Here it is, for illustration.

Code:
// This is a redo of the code at:
// https://forum.pjrc.com/threads/66591-Teensy-4-1-UDP-crash-with-more-then-24-bytes
// This is a long-winded "basic" example written in a more QNEthernet-style.

#include <algorithm>
    // For std::min()

#include <FastLED.h>
#include <QNEthernet.h>

using namespace qindesign::network;

// -------------------------------------------------------------------
//  LED Configuration
// -------------------------------------------------------------------

// LED count
constexpr size_t kNumLEDs = 14;

// LED communication
constexpr auto kLEDChipset = APA102;
constexpr auto kLEDRGBOrder = BGR;  // BGR ordering is typical
constexpr uint8_t kDataPin = 3;
constexpr uint8_t kClockPin = 2;

constexpr uint32_t kFPS = 60;  // Frames per second

// Define the array of leds.
CRGB leds[kNumLEDs];

// Timer to effect a specific frame rate.
elapsedMillis ledShowTimer;

// -------------------------------------------------------------------
//  Network
// -------------------------------------------------------------------

constexpr uint16_t kListenPort = 8887;

// Address --- Set to valid values to use a static IP instead of DHCP
const IPAddress staticIP{0, 0, 0, 0};
const IPAddress netmask{0, 0, 0, 0};
const IPAddress gateway{0, 0, 0, 0};

// Use for Options 1 & 2 below
// constexpr size_t kMaxUDPSize = Ethernet.mtu() - 8 - 20;
//     // Note: This doesn't take into account packet fragmentation
// uint8_t packetBuf[kMaxUDPSize];  // Buffer to hold incoming packets

constexpr char kReplyStr[]{"acknowledged"};  // a string to send back
constexpr size_t kReplyStrLen = sizeof(kReplyStr) - 1;  // Exclude the NUL

// Object for sending and receiving packets over UDP.
EthernetUDP udp;

// -------------------------------------------------------------------
//  Main Program
// -------------------------------------------------------------------

// Main program setup.
void setup() {
  // Initialize Serial output
  Serial.begin(115200);
  while (!Serial && millis() < 4000) {
    // Wait for Serial or a timeout
  }
  stdPrint = &Serial;  // Make printf work, a QNEthernet feature

  printf("Starting...\r\n");

  // Print the Ethernet MAC address
  // With QNEthernet, there's no need to choose one
  uint8_t mac[6];
  Ethernet.macAddress(mac);
  printf("MAC = %02x:%02x:%02x:%02x:%02x:%02x\r\n",
         mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);

  // Use listeners that monitor state
  Ethernet.onLinkState([](bool state) {
    printf("[Ethernet] Link %s\r\n", state ? "ON" : "OFF");
    // Optional: Call things that need to know there's a link
  });

  Ethernet.onAddressChanged([]() {
    IPAddress ip = Ethernet.localIP();
    if (ip == INADDR_NONE) {
      printf("[Ethernet] Address changed: No IP address\r\n");
      return;
    }

    IPAddress subnet = Ethernet.subnetMask();
    IPAddress gw = Ethernet.gatewayIP();
    IPAddress dns = Ethernet.dnsServerIP();
    printf("[Ethernet] Address changed:\r\n"
            "    Local IP = %u.%u.%u.%u\r\n"
            "    Subnet   = %u.%u.%u.%u\r\n"
            "    Gateway  = %u.%u.%u.%u\r\n"
            "    DNS      = %u.%u.%u.%u\r\n",
            ip[0], ip[1], ip[2], ip[3],
            subnet[0], subnet[1], subnet[2], subnet[3],
            gw[0], gw[1], gw[2], gw[3],
            dns[0], dns[1], dns[2], dns[3]);

    // Optional: Call things that need to know there's an IP address
  });

  // Depending on what's in `staticIP`, start DHCP or not
  printf("Starting Ethernet...");
  bool initResult;
  if (staticIP == INADDR_NONE) {
    // Start DHCP
    initResult = Ethernet.begin();
  } else {
    // Use the gateway as the DNS server
    initResult = Ethernet.begin(staticIP, netmask, gateway, gateway);
  }

  if (!initResult) {
    printf("Error!\r\n");
  } else {
    printf("done.\r\n");

    // Start UDP
    printf("Starting UDP listen...");
    if (!udp.begin(kListenPort)) {
      printf("Error!\r\n");
    } else {
      printf("done.\r\n");
    }
  }

  FastLED.addLeds<kLEDChipset, kDataPin, kClockPin, kLEDRGBOrder>(leds, kNumLEDs);

  printf("Setup done.\r\n");
}

// Main program loop.
void loop() {
  // Check for packet data
  // Note: Checking `if (packetSize)` won't work if there's no packet data
  //       because in QNEthernet, parsePacket() returns -1 for no data, and
  //       -1 implicitly gets converted to a bool `true`
  int packetSize = udp.parsePacket();
  if (packetSize >= 0) {
    IPAddress remoteIP = udp.remoteIP();
    printf("Received packet: size=%d from=%u.%u.%u.%u:%u\r\n",
           packetSize,
           remoteIP[0], remoteIP[1], remoteIP[2], remoteIP[3],
           udp.remotePort());
    
    // ---------------------------------------------------------------
    //  Option 1 --- Ignore packet fragmentation, assume max. size
    // ---------------------------------------------------------------
    // Note: Due to packet fragmentation, the packet, in theory, can
    //       be larger than the buffer size; restrict to the maximum
    //       size for this demo
    // size_t toRead = packetSize;
    // toRead = std::min(toRead, sizeof(packetBuf));
    // udp.read(packetBuf, toRead);
    // printf("Contents:");
    // for (size_t i = 0; i < toRead; i++) {
    //   printf(" %02xh", packetBuf[i]);
    // }
    // printf("\r\n");

    // ---------------------------------------------------------------
    //  Option 2 --- Traditional loop until all data is read
    // ---------------------------------------------------------------
    // Note: Due to packet fragmentation, the packet, in theory, can
    //       be larger than the buffer size
    // Note 2: Tracking the data so that there's always a multiple of
    //         3 is a little trickier for this option
    // printf("Contents:");
    // size_t toRead = udp.available();
    // while (toRead > 0) {
    //   toRead = std::min(toRead, sizeof(packetBuf));
    //   udp.read(packetBuf, toRead);
    //   for (size_t i = 0; i < toRead; i++) {
    //     printf(" %02xh", packetBuf[i]);
    //   }
    //   toRead = udp.available();
    // }
    // printf("\r\n");

    // ---------------------------------------------------------------
    //  Option 3 --- Use QNEthernet's EthernetUDP.data() and size()
    // ---------------------------------------------------------------
    // Note: Due to packet fragmentation, the packet, in theory, can
    //       be larger than the buffer size
    printf("Contents:");
    size_t size = udp.size();
    const uint8_t *data = udp.data();
    for (size_t i = 0; i < size; i++) {
      printf(" %02xh", data[i]);
    }
    printf("\r\n");

    // Update the pixels
    // Also ensure we're only using a value divisible by 3 and that
    // we're within the pixel bounds
    size_t pixelCount = std::min(size / 3, kNumLEDs);
    size_t index = 0;    
    for (size_t i = 0; i < pixelCount; i++) {
      // Assume received data is in RGB order
      leds[i].setRGB(data[index], data[index + 1], data[index + 2]);
      index += 3;
    }

    // Reply with an acknowledgement
    // Note: EthernetUDP::send() is a QNEthernet feature to avoid
    //       having to always do beginPacket()/write()/endPacket()
    udp.send(remoteIP, udp.remotePort(),
             reinterpret_cast<const uint8_t *>(kReplyStr),
             kReplyStrLen);
  }

  // Don't use either delay() or FastLED.delay() because then we might
  // miss UDP packets; instead, see if a timer has expired

  if (ledShowTimer >= 1000/kFPS) {
    FastLED.show();
    ledShowTimer = 0;
  }
}
 
Last edited:
Back
Top