UIPEthernet: UDP unresponsive to incoming data, still transmits outgoing

Hi all,

First off many thanks for ntruchsess for the UIPEthernet library, it is wonderful.

I’m posting b/c I am having a very difficult time debugging an occasional freezing issue, and I wonder if anyone on the forum has any tips. FYI I’ve updated to UIPEthernet v1.09, and read all I can find on the forums here and the project github page. Details below.

I’ve built a system with 21 nodes (arduino nano + ENC28J60, as well as Maxbotix MB1260 ultrasonic range finder, relay module, and limit switch) controlled from a central computer via OSC messaging from SuperCollider. For the curious, it controls a large mechatronic sculpture.

After some length of time (1-3 hrs) individual nodes become unresponsive to incoming UDP packets. They still sending outgoing packets to the computer (for instance if I trigger the limit switch) but do not respond to incoming messages to ping the ultrasound or switch the relay.

There is no pattern to which nodes freeze. As far as I can tell it may relate to the density of control messages sent: with all 21 online, sending lots of control messages back and forth, I get more freezes. But I am unable to replicate the problem on a home setup with only two nodes. They run fine for hours with lots of relay on/off and pinging control messages.

The code I am running on the arduinos is relatively simple (copied below).

I wonder if anyone sees any obvious problems such as my use of Udp.flush()/Udp.stop()/Udp.begin(), Ethernet.begin(), or PulseIn().

I’d really love to track this down.

Many thanks in advance!

Robert

#include <UIPEthernet.h>
#include <OSCMessage.h>
#include <OSCBundle.h>

//#define DEBUG

// update this id for each control unit between 1 - 21
#define UNIT_ID 2

// string / osc address voodoo using preprocessor
#define xstr(s) str(s)
#define str(s) #s

#define MAKE_MAC_PAIR(value) value + 16

// OSC ADDRESSES
#define RANGE_ADDR "/br/" xstr(UNIT_ID) "/range"
#define PONG_ADDR "/br/" xstr(UNIT_ID) "/pong"
#define LIMIT_ADDR "/br/" xstr(UNIT_ID) "/limit" 
#define RELAY_ADDR "/br/" xstr(UNIT_ID) "/relay"

// Networking / UDP Setup
EthernetUDP Udp;

// arduino address
IPAddress ip(192,168,3,UNIT_ID);
const unsigned int inPort = 8888;
byte mac[] = { 
  0xDE, 0xAD, 0xBE, 0xEF, 0xFE, MAKE_MAC_PAIR(UNIT_ID) }; // different mac for each arduino

// destination address
IPAddress targetIP(192, 168, 3, 100);
const unsigned int targetPort = 57120;

// reset network module, which occasionally freezes
// the reset takes next to no time
long lastReset = -50000;
long resetInterval = 10000;//10000;


// Arduino I/O pins
const int relayPin = 6; // relay control
const int limitPin = 7; // limit switch 
const int triggerPin = 9; // trigger ultrasound ping
#if UNIT_ID == 10
const int echoPin = 2; // pulse-in from ultrasonic rangefinder
#else
const int echoPin = 8; // pulse-in from ultrasonic rangefinder
#endif

// Range-finder calibration
// rangefinder
const int echoTimeout = 90000; //#define MAX_DISTANCE 400 // Maximum distance we want to ping for (in centimeters). Maximum sensor distance is rated at 400-500cm.
#define US_ROUNDTRIP_IN 146     // Microseconds (uS) it takes sound to travel round-trip 1 inch (2 inches total), uses integer to save compiled code space.

// Notifications
// periodic transmission of relay state and limit switch
const int transferInterval = 200;
long lastTransfer = 0;

// Arduino state
byte limitState = HIGH;
byte relayState = HIGH;

//--------------------------------//
//            SETUP               //
//--------------------------------//

void setup() {
#ifdef DEBUG
  Serial.begin(9600);
#endif

  Ethernet.begin(mac,ip);
  Udp.begin(inPort);

  pinMode(relayPin, OUTPUT);
  pinMode(limitPin, INPUT);
  pinMode(triggerPin, OUTPUT);
  pinMode(echoPin, INPUT);

  // disable maxbotix 
  digitalWrite(triggerPin, LOW);
  executeRelayState();
  
}


//--------------------------------//
//            MAIN LOOP           //
//--------------------------------//

void loop(){

  // read incoming udp packets
  OSCMessage msgIn;
  int size;
  int success;

//  while (Udp.parsePacket() > 0) ; // discard any previously received packets

  if( (size = Udp.parsePacket())>0)
  {

    //while((size = Udp.available()) > 0)
    while(size--)
      msgIn.fill(Udp.read());

    // route messages
    if(!msgIn.hasError()) {
      msgIn.route("/ping", pingOSCHandler);
      msgIn.route("/relay", relayOSCHandler);
    } 
    
    //finish reading this packet:
    Udp.flush();

    //restart UDP connection to receive packets from other clients
    Udp.stop();
    success = Udp.begin(inPort);

  }

  // read limit switch
  byte newLimitState = digitalRead(limitPin);
  
  // shut off motor at top no matter what
  if(newLimitState == LOW) {
      byte lastRelayState = relayState;
      relayState = HIGH; // off
      executeRelayState();
  }

  if(newLimitState != limitState) {
    // store new state
    limitState = newLimitState;

    // notify host
    sendAndRestart(LIMIT_ADDR, (byte)limitState);
  };
  
  // periodically send limit state
  if((millis() - lastTransfer) > transferInterval) {
    executeRelayState();

    // notify host
    sendAndRestart(LIMIT_ADDR, (byte)limitState);

    lastTransfer = millis();

  }
 
  // reset ethernet chip periodically
  if(millis() - lastReset > resetInterval) {
    Ethernet.begin(mac,ip);
    lastReset = millis();
  }
  
  // restart with new connection
  Udp.stop();
  Udp.begin(inPort);
}

//--------------------------------//

void sendAndRestart(char * addr, byte val) {
  OSCMessage msg(addr);

  msg.add((byte)val);
  Udp.beginPacket(targetIP, targetPort);
  msg.send(Udp); // send the bytes to the SLIP stream
  Udp.endPacket(); // mark the end of the OSC Packet
  msg.empty(); // free space occupied by message

  // restart UDP connection so we are ready to accept incoming ports
  Udp.stop();
  Udp.begin(inPort);
}


void sendAndRestart(char * addr, unsigned long val) {
  OSCMessage msg(addr);

  msg.add((int32_t)val);
  Udp.beginPacket(targetIP, targetPort);
  msg.send(Udp); // send the bytes to the SLIP stream
  Udp.endPacket(); // mark the end of the OSC Packet
  msg.empty(); // free space occupied by message

  // reset UDP connection so we are ready to accept incoming ports
  Udp.stop();
  Udp.begin(inPort);
}

//--------------------------------//

void executeRelayState() {
  // digital write changed value
  digitalWrite(relayPin, relayState);
  
  // notify control with new state
  sendAndRestart(RELAY_ADDR, relayState);
}

void relayOSCHandler(OSCMessage &msg, int addrOffset){

  int inValue = msg.getInt(0);

#ifdef DEBUG
  Serial.print("rx ");
  Serial.println(inValue);
#endif

  byte lastRelayState = relayState;
  relayState = (inValue == 1);

  if(lastRelayState != relayState)
    executeRelayState();
}

void pingOSCHandler(OSCMessage &msg, int addrOffset) {
  unsigned long duration; 
  unsigned long range;

#ifdef DEBUG
  Serial.print("rx - ping: ");
  Serial.println(" * )))))");
#endif

  // acknowledge to host
  sendAndRestart(PONG_ADDR, (byte)0);

  // send out the ping
  digitalWrite(triggerPin, HIGH); 
  delayMicroseconds(20); // held high for a minimum of 20uS 

  duration = pulseIn(echoPin, HIGH);//, echoTimeout); // optional timeout in microseconds, 95000 uS is 95ms
  range = duration / US_ROUNDTRIP_IN; // range calculated from echo time, more accurate

  // disable ranging
  digitalWrite(triggerPin, LOW);

  // 2cm / analogInUnit * 0.393701 in/cm = inches/analogInUnit
//  if(range == 0) range = analogRead(A0) * 0.393701 * 2.0; // range from analog

  //range=analogRead(A0);
  
#ifdef DEBUG
  Serial.println(" * (((((");
  Serial.println(range);
#endif

  sendAndRestart(RANGE_ADDR, (unsigned long)range);
}


//--------------------------------//
/*       Configuration via OSC    */
//--------------------------------//

// removed

Don’t know too much about the enc28j60, but the library is supposed to be compatible with the w5100 library. That being the case, you should call Ethernet.begin() and Udp.begin() only once in setup. No need to stop and start the ethernet or udp service in the loop function.

Thanks for your thoughts on this. Hmm... yeah maybe I have excessive Udp.stop()/Udp.begin() calls.

The UIPEthernet UdpServer example calls stop()/begin() every time they finish parsing an incoming packet. I am doing this in the if statement at the top of my loop. I will try eliminating the redundant calls at the end of loop, called every cycle (!). Maybe that will fix something.

The Ethernet.begin() I took from here, a soft-reset of the chip. Adding this periodic reset increased the length of time the project runs without freezing, but has not fixed it.

Hi all,

Can I yuse Protocole I2C without code slave?

Thanks in advance.
Regards