ntruchsess / arduino_uip

UIPEthernet: A plugin-replacement of the stock Arduino Ethernet library for ENC28J60 shields and breakout boards. Full support for persistent (streaming) TCP-connections and UDP (Client and Server each), ARP, ICMP, DHCP and DNS. Build around Adam Dunkels uIP Stack. Further developed version can be found on https://github.com/UIPEthernet/UIPEthernet
489 stars 211 forks source link

UDP unresponsive to incoming packets, still sending outgoing data #107

Open roberttwomey opened 9 years ago

roberttwomey commented 9 years ago

Hi,

Thank you for this library. It is wonderful. I'm having a hard time debugging an issue.

I have 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. 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.

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 on the arduinos is relatively simple. I wonder if you see any obvious problems such as my use of Udp.flush()/Udp.stop()/Udp.begin() and Ethernet.begin(). Or if anything leaps out to you as a reason for periodic unresponsiveness:

#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

Many thanks in advance,

Robert

nibelungen commented 9 years ago

Hey,

for me nearly the same here - controlling a LED Strip with an arduino nano and ENC28J60. First connect with android Controll app (https://play.google.com/store/apps/details?id=com.charlieroberts.Control&hl=en) works like a charm. I see the network traffic at the rj45 jack when changing color via the app - and i see the RX/TX led on the nano when changing the color. When i close the app and restart it - select the destination ip - i can see the rj45 jack led responses to the color change but it looks like the nano did not receive any packet (the RX/TX led stays off).

So i can reproduce it with a second connect - after reset the arduino everything works until the second connection...

Malte-D commented 9 years ago

I have exactly the same problem like nibelungen, are there any news about this?

sakugava commented 9 years ago

Try this: https://github.com/TMRh20/arduino_uip The same library with some improvements