Teensy 4.1 seems to hang (NativeEthernet)

nlecaude

Well-known member
Hello,

I converted a sketch I had that was using Serial to NativeEthernet. I noticed that after a few hours, the Teensy seems to either reboot or hang. I was wondering if someone could have a look at my sketch and see if I made any errors that could lead to this ?

The program uses OSC to set timing values on pins (mostly using elapsedMillis) in order to strobe some lights.

Code:
#include <SPI.h>
#include <NativeEthernet.h>
#include <NativeEthernetUdp.h>
#include <elapsedMillis.h>
#include <OSCBundle.h>
#include <OSCBoards.h>

EthernetUDP Udp;

uint8_t mac[6];

const byte NUM_CHANNELS = 16;
byte PINS[NUM_CHANNELS] = { 2, 3, 4, 5, 6, 30, 31, 32, 33, 36, 37, 40, 41, 13, 22, 23 };
uint16_t OSC_INPUT_PORT = 3333;

typedef struct {
   elapsedMillis elapsedTime;
   uint32_t onTime;
   uint32_t offTime;
   byte pin;
   bool enable;
} channel;

channel channels[NUM_CHANNELS];

void teensyMAC(uint8_t *mac) {
  for(uint8_t by=0; by<2; by++) mac[by]=(HW_OCOTP_MAC1 >> ((1-by)*8)) & 0xFF;
  for(uint8_t by=0; by<4; by++) mac[by+2]=(HW_OCOTP_MAC0 >> ((3-by)*8)) & 0xFF;
  Serial.printf("MAC: %02x:%02x:%02x:%02x:%02x:%02x\n", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
}

void setup() {
  Serial.begin(9600);
  teensyMAC(mac);

  // Default IP address
  IPAddress ip(169, 254, mac[4], mac[5]);
  
  if (Ethernet.begin(mac) == 0) {
    Serial.println("Failed to configure Ethernet using DHCP");
    // DHCP failed, so use a fixed IP address:
    Ethernet.begin(mac, ip);
  }
  
  Udp.begin(OSC_INPUT_PORT);
  
  char deviceName[13];
  sprintf(deviceName, "Goutteur-%02x%02x", mac[4], mac[5]);
  
  MDNS.begin(deviceName, 1);
  MDNS.addService("_osc._udp", OSC_INPUT_PORT);
  
  Serial.println(Ethernet.localIP());
  
  for (byte c = 0 ; c < NUM_CHANNELS ; c++) {
    channels[c].onTime = 1000;
    channels[c].offTime = 2000;
    channels[c].enable = true;
    channels[c].pin = PINS[c];
    
    pinMode(channels[c].pin, OUTPUT);
  }
}

void processLed() {
  for (byte c = 0 ; c < NUM_CHANNELS ; c++)
  {
    if (channels[c].enable) {
      if (channels[c].elapsedTime <= channels[c].onTime) {
        digitalWrite(channels[c].pin, HIGH);
      }
      if (channels[c].elapsedTime > channels[c].onTime) {
        digitalWrite(channels[c].pin, LOW);
      }
      if (channels[c].elapsedTime > channels[c].onTime + channels[c].offTime) {
        channels[c].elapsedTime = 0;
      }
    }  
  }
}

void setInterval(OSCMessage &msg) {
  if (msg.isInt(0) && msg.isInt(1) && msg.isInt(2)) {
    int c = msg.getInt(0);
    // All channels
    if (c == 255) {
      for (byte i = 0 ; i < NUM_CHANNELS ; i++) {
        channels[i].enable = true;
        channels[i].onTime = msg.getInt(1);
        channels[i].offTime = msg.getInt(2);
        channels[i].elapsedTime = 0;
      }
    // Single channel
    } else if (c >= 0 && c < NUM_CHANNELS) {
      channels[c].enable = true;
      channels[c].onTime = msg.getInt(1);
      channels[c].offTime = msg.getInt(2);
      channels[c].elapsedTime = 0;
    }
  }
}

void on(OSCMessage &msg) {
  if (msg.isInt(0)) {
    int c = msg.getInt(0);
    // All channels
    if (c == 255) {
      for (byte i = 0 ; i < NUM_CHANNELS ; i++) {
        channels[i].enable = false;
        digitalWrite(channels[i].pin, HIGH);
      }
    // Single channel
    } else if (c >= 0 && c < NUM_CHANNELS) {
      channels[c].enable = false;
      digitalWrite(channels[c].pin, HIGH);
    }
  }
}

void off(OSCMessage &msg) {
  if (msg.isInt(0)) {
    int c = msg.getInt(0);
    // All channels
    if (c == 255) {
      for (byte i = 0 ; i < NUM_CHANNELS ; i++) {
        channels[i].enable = false;
        digitalWrite(channels[i].pin, LOW);
      }
    // Single channel
    } else if (c >= 0 && c < NUM_CHANNELS) {
      channels[c].enable = false;
      digitalWrite(channels[c].pin, LOW);
    }
  }
}

void reset(OSCMessage &msg) {
  if (msg.isInt(0)) {
    int c = msg.getInt(0);
    // All channels
    if (c == 255) {
      for (byte i = 0 ; i < NUM_CHANNELS ; i++) {
        channels[i].elapsedTime = 0;
      }
    // Single channel
    } else if (c >= 0 && c < NUM_CHANNELS) {
      channels[c].elapsedTime = 0;
    }
  }
}

void loop() {
  OSCBundle bundleIN;
  int size;

  processLed();
  if( (size = Udp.parsePacket())>0) {
     
     while(size--)
       bundleIN.fill(Udp.read());
   }
  
  if(!bundleIN.hasError()) {
    bundleIN.dispatch("/setInterval", setInterval);
    bundleIN.dispatch("/reset", reset);
    bundleIN.dispatch("/on", on);
    bundleIN.dispatch("/off", off);
  }
  Ethernet.maintain();
}
 
Would you be interested in trying QNEthernet? I use its MDNS class there without problems.

Here's approximately what the converted code should look like (I took the liberty of doing some small modifications):
Code:
#include <OSCBundle.h>
#include <OSCBoards.h>
#include <QNEthernet.h>

using namespace qindesign::network;

constexpr uint32_t kDHCPTimeout = 10000;  // 10 seconds
constexpr int NUM_CHANNELS = 16;
constexpr byte PINS[NUM_CHANNELS] = { 2, 3, 4, 5, 6, 30, 31, 32, 33, 36, 37, 40, 41, 13, 22, 23 };
constexpr uint16_t OSC_INPUT_PORT = 3333;

// Backup static IP configuration if DHCP doesn't get an address
IPAddress staticIP{169, 254, 0, 0};  // Randomly assign the last two bytes down below
IPAddress subnetMask{255, 255, 0, 0};
IPAddress gateway{0, 0, 0, 0};

EthernetUDP Udp;

struct Channel {
   elapsedMillis elapsedTime;
   uint32_t onTime;
   uint32_t offTime;
   byte pin;
   bool enable;
};

Channel channels[NUM_CHANNELS];

void setup() {
  Serial.begin(115200);
  while (!Serial && millis() < 4000) {
    // Wait for Serial
  }

  uint8_t mac[6];
  Ethernet.macAddress(mac);
  Serial.printf("MAC = %02x:%02x:%02x:%02x:%02x:%02x\n",
                mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);

  if (!Ethernet.begin()) {
    Serial.println("Failed to start Ethernet");
  } else if (!Ethernet.waitForLocalIP(kDHCPTimeout)) {
    Serial.println("Failed to configure Ethernet using DHCP");
    // DHCP failed, so use a fixed IP address:
    staticIP[2] = random(1, 255);  // Range 1-254
    staticIP[3] = random(0, 256);  // Range 0-255
    if (!Ethernet.begin(staticIP, subnetMask, gateway)) {
      Serial.println("Failed to configure Ethernet using a static IP");
    }
  }

  Serial.print("Local IP = ");
  Serial.println(Ethernet.localIP());
  
  Udp.begin(OSC_INPUT_PORT);
  
  char deviceName[13];
  sprintf(deviceName, "Goutteur-%02x%02x", mac[4], mac[5]);
  
  if (!MDNS.begin(deviceName)) {
    Serial.println("Error starting mDNS");
  } else {
    if (!MDNS.addService("_osc", "_udp", OSC_INPUT_PORT)) {
      Serial.println("Error adding OSC service");
    }
  }
    
  for (int c = 0; c < NUM_CHANNELS; c++) {
    channels[c].onTime = 1000;
    channels[c].offTime = 2000;
    channels[c].enable = true;
    channels[c].pin = PINS[c];
    
    pinMode(channels[c].pin, OUTPUT);
  }
}

void processLed() {
  for (int c = 0; c < NUM_CHANNELS; c++) {
    if (channels[c].enable) {
      if (channels[c].elapsedTime <= channels[c].onTime) {
        digitalWrite(channels[c].pin, HIGH);
      }
      if (channels[c].elapsedTime > channels[c].onTime) {
        digitalWrite(channels[c].pin, LOW);
      }
      if (channels[c].elapsedTime > channels[c].onTime + channels[c].offTime) {
        channels[c].elapsedTime = 0;
      }
    }  
  }
}

void setInterval(OSCMessage &msg) {
  if (msg.isInt(0) && msg.isInt(1) && msg.isInt(2)) {
    int c = msg.getInt(0);
    // All channels
    if (c == 255) {
      for (int i = 0; i < NUM_CHANNELS; i++) {
        channels[i].enable = true;
        channels[i].onTime = msg.getInt(1);
        channels[i].offTime = msg.getInt(2);
        channels[i].elapsedTime = 0;
      }
    // Single channel
    } else if (c >= 0 && c < NUM_CHANNELS) {
      channels[c].enable = true;
      channels[c].onTime = msg.getInt(1);
      channels[c].offTime = msg.getInt(2);
      channels[c].elapsedTime = 0;
    }
  }
}

void on(OSCMessage &msg) {
  if (msg.isInt(0)) {
    int c = msg.getInt(0);
    // All channels
    if (c == 255) {
      for (int i = 0; i < NUM_CHANNELS; i++) {
        channels[i].enable = false;
        digitalWrite(channels[i].pin, HIGH);
      }
    // Single channel
    } else if (c >= 0 && c < NUM_CHANNELS) {
      channels[c].enable = false;
      digitalWrite(channels[c].pin, HIGH);
    }
  }
}

void off(OSCMessage &msg) {
  if (msg.isInt(0)) {
    int c = msg.getInt(0);
    // All channels
    if (c == 255) {
      for (int i = 0; i < NUM_CHANNELS; i++) {
        channels[i].enable = false;
        digitalWrite(channels[i].pin, LOW);
      }
    // Single channel
    } else if (c >= 0 && c < NUM_CHANNELS) {
      channels[c].enable = false;
      digitalWrite(channels[c].pin, LOW);
    }
  }
}

void reset(OSCMessage &msg) {
  if (msg.isInt(0)) {
    int c = msg.getInt(0);
    // All channels
    if (c == 255) {
      for (int i = 0; i < NUM_CHANNELS; i++) {
        channels[i].elapsedTime = 0;
      }
    // Single channel
    } else if (c >= 0 && c < NUM_CHANNELS) {
      channels[c].elapsedTime = 0;
    }
  }
}

void loop() {
  OSCBundle bundleIN;
  int size;

  processLed();
  if ((size = Udp.parsePacket())>0) {
     while(size--) {
       bundleIN.fill(Udp.read());
     }
   }
  
  if (!bundleIN.hasError()) {
    bundleIN.dispatch("/setInterval", setInterval);
    bundleIN.dispatch("/reset", reset);
    bundleIN.dispatch("/on", on);
    bundleIN.dispatch("/off", off);
  }
}
 
Last edited:
Mostly, but there's a few more ways to do things. I modified my post to include your code (with other small modifications) modified to work with QNEthernet.
 
Seems to be working well with QNEthernet !
I'll leave it running for the weekend but so far no weird behaviours.

Thanks for your help !
 
Back
Top