Ethernet UDP hang

I wasted many, many hours searching the reason why my Arduino freeze and variables were corrupted after two minutes running.
I first suspected my code, and then a bug in the library I use.

I found your post and others http://arduino.cc/forum/index.php?topic=96651.0 and http://code.google.com/p/arduino/issues/detail?id=605 which talk about an UDP bug.
After applying the patch, my arduino still running after 10 minutes ! I was so happy but the dream stopped too fast, the arduino froze again :astonished:

I was still searching a solution and found the windows arduino 1.0.1 RC 2 http://arduino.cc/forum/index.php?topic=100503.0 but new compilation with this release (which also include the UDP patch) solved nothing more.

I ask for your help and your skills to help me.
Below the code, it has two functions:

Thank you.

As my message with source code exceed the maximum allowed, my code is available here http://dl.dropbox.com/u/2577678/Viald-source-code.ino

I had problems with UDP if I did not disable the SD SPI interface. It would trash up the SPI and cause the w5100 SPI to fail. Having a microSD card in the slot seems to increase the odds of the failure.

I recommend trying this. Place it before your Ethernet.begin() call.

pinMode(4,OUTPUT);
digitalWrite(4,HIGH);

If that is not it, you night want to be more specific about the failure. Where in the code does it fail?

@SurferTim Thank you. I'm going to test and be more precise if needed.

I’m becoming completely silly :roll_eyes: After 5 hours of tests, here is the result.
As my code has two independent function (IR command and Iphone check) I tried to remove the code for each of one to run the program only with one at once.
Like this, after 30min of running, it seems to work like a charm :grin: So it seems that the freeze has nothing to do with another UDP bug.

So I started from the complete code and try to remove piece by piece, testing each time the result, hoping that I can easily put my finger on the wrong piece of code.
Unfortunately, it’s not so easy but I find a line code which do fast memory corruption after few seconds and multiple IR remote actions. It doesn’t prove that this line is the faulty code but it makes me think that String class used corrupts the memory.

Below is serial output

To avoid this corruption, I change line n° 225

client.println("GET /cgi-bin/domo.cgi?cmd=LM"+scriptname+" HTTP/1.0"); // Send the URL to Zibase API

by

client.println("GET /cgi-bin/domo.cgi HTTP/1.0");

Here is the remaining code

#include <Time.h>

#include <IRremote.h>
#include <IRremoteInt.h>

#include <Dhcp.h>
#include <Dns.h>
#include <Ethernet.h>
#include <EthernetClient.h>
#include <EthernetServer.h>
#include <EthernetUdp.h>
#include <util.h>
#include <ICMPPing.h>
#include <SPI.h>

#define DEBUG // Debug mode
//#undef DEBUG

#define ALARMPIN 2 // Status LED

#define IPHONE1IP 192,168,1,152
#define IPHONE1ID "v3Fxxxxxxxxxxxx"

#define ZIBASE 192,168,1,153
#define ZIBASEPT 80

#define PUSHINGBOX "api.pushingbox.com"
#define PUSHINGBOXPT 80

#define TIMEZONEH +2 //Enter Your Actual Time Zone EDT

//Network Init
byte mac[] = {
  0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
IPAddress ZIBASEIP (ZIBASE);
EthernetClient client;

//IR Init
#define RECVPIN 5  // IR receiver PIN
IRrecv irrecv(RECVPIN);
decode_results irresults;

//NTP
unsigned int localPort = 8888;      // local port to listen for UDP packets
IPAddress TIMESERVER(129,6,15,28); // time-a.nist.gouv NTP server 
EthernetUDP Udp; // A UDP instance to let us send and receive packets over UDP
long timeZoneOffset = (TIMEZONEH * -1) * 60 * 60 ;

// iPhone heartbeat interval
//#define HEARTBEATD 900 // Heartbeat delay in sec
#define HEARTBEATD 30
time_t heartbeat;


// ICMP
SOCKET pingSocket = 0; // Try socket 3 if errors
ICMPPing ping(pingSocket);
char icmpbuffer [256];

byte pingaddr1[] = {
  IPHONE1IP};
char device1[] = IPHONE1ID;

void setup()
{
#ifdef DEBUG
  Serial.begin(9600);// start the serial library 
#endif 

  // Initialize Alarm output
  pinMode(ALARMPIN, OUTPUT);
  digitalWrite(ALARMPIN,LOW);

  // Disable SDcard peripheral
  pinMode(4,OUTPUT);
  digitalWrite(4,HIGH);

  // Enable W5100 peripheral
  pinMode(10,OUTPUT);
  digitalWrite(10,LOW);

  if(Ethernet.begin(mac) == 0)  // start the Ethernet connection
  {
#ifdef DEBUG
    Serial.println("Failed to configure Ethernet using DHCP");
#endif
    digitalWrite(ALARMPIN,HIGH);
    for(;;);
  }
  delay(1000); // give the Ethernet shield a second to initialize

  digitalWrite(ALARMPIN,LOW);
  Udp.begin(localPort);
  irrecv.enableIRIn(); // Start the IR receiver

#ifdef DEBUG
  Serial.println("Check time set...");
#endif
  while(timeStatus()== timeNotSet)  
  { 
#ifdef DEBUG
    Serial.println("Waiting for time");
#endif
    digitalWrite(ALARMPIN,HIGH);
    setTime(getNTPTime());  
  }
#ifdef DEBUG
  Serial.println("Time is set");
#endif
  digitalWrite(ALARMPIN,LOW); 
  heartbeat = now() - HEARTBEATD;
}

void loop()
{ 
  time_t stime = now();
  String scriptname; 

  if ((heartbeat + HEARTBEATD) <= stime )  // check if it's time to send iPhone heartbeat
  {
    heartbeat = stime;
#ifdef DEBUG
    Serial.print("Heartbeat check at:");
    Serial.print(hour(stime));
    Serial.print(":");
    Serial.print(minute(stime));
    Serial.print(":");
    Serial.println(second(stime));
#endif

#ifdef DEBUG
    Serial.println("Heartbeat check...");
#endif

    if(ping(2, pingaddr1, icmpbuffer))   //  ICMPPing ping(pingSocket);
    {
#ifdef DEBUG
      Serial.print("Device ");
      Serial.print(device1);
      Serial.println(" is alive");
#endif
    }
    else
    {
#ifdef DEBUG
      Serial.print("Device ");
      Serial.print(device1);
      Serial.println(" seems to be off, try to wakeup");
#endif

      if (client.connect(PUSHINGBOX, PUSHINGBOXPT))
      { 
#ifdef DEBUG
        Serial.println("connected to PushingBox server"); 
#endif
        digitalWrite(ALARMPIN,LOW);
        client.print("GET /pushingbox?devid=");
        client.print(device1);
        client.println(" HTTP/1.1");  // Send the deviceID to Pushingbox API
        client.println("Host: api.pushingbox.com");
        client.println();

#ifdef DEBUG
        while (client.connected()) {
          while(client.available())
          {
            char c=client.read();
            Serial.print(c);
          }
        }
#endif  
        client.stop();

        if(ping(2, pingaddr1, icmpbuffer))
        {
#ifdef DEBUG
          Serial.print("Device ");
          Serial.print(device1);
          Serial.println("  is wakeup and alive");
#endif
        }
        else
        {
#ifdef DEBUG
          Serial.print("Device ");
          Serial.print(device1);
          Serial.println(" is off");
#endif
        }
      }
      else
      {
#ifdef DEBUG
        Serial.println("connection to PushingBox failed");
#endif
        digitalWrite(ALARMPIN,HIGH);
      }
    }
  }

  if (irrecv.decode(&irresults))
  {
#ifdef DEBUG  
    Serial.print("IR code received: "); 
    Serial.println(irresults.value);
#endif
    scriptname = String("none");
    switch (irresults.value)
    {   
    case 284127375:
      scriptname = String("[AC%20HC%20Light%20OFFxxxxxxxx]"); // for debug purpose          
    }

    if (scriptname != "none")
    {
#ifdef DEBUG
      Serial.println(scriptname);
#endif
      if (client.connect(ZIBASEIP, ZIBASEPT))
      { 
        digitalWrite(ALARMPIN,LOW);
#ifdef DEBUG
        Serial.println("connected to Zibase");
#endif
        client.println("GET /cgi-bin/domo.cgi?cmd=LM"+scriptname+" HTTP/1.0"); // Send the URL to Zibase API
  //    client.println("GET /cgi-bin/domo.cgi HTTP/1.0"); 
        client.println();
#ifdef DEBUG
        while (client.connected()) {
          while(client.available())
          {
            char c=client.read();
            Serial.print(c);
          }
        }
#endif          
        client.stop();
      }

      else
      {
#ifdef DEBUG
        Serial.println("connection to Zibase failed"); // if you didn't get a connection to the server
#endif
        digitalWrite(ALARMPIN,HIGH);
      }

    }
#ifdef DEBUG
    else
    {
      Serial.print("IR code not defined: "); // the IR code received is not defined
      Serial.println(irresults.value); 
    }    
#endif  
    delay(300); 
    irrecv.resume();
  }

}

unsigned long getNTPTime()
{
  int rpacket=0;
  byte packetBuffer[48]; //buffer to hold incoming packets
  memset(packetBuffer, 0, 48); // set all bytes in the buffer to 0

#ifdef DEBUG
  Serial.println("Get NTP time");
#endif

  while(!rpacket)
  {
    sendNTPpacket(TIMESERVER);
    delay(200); 
    if ( rpacket = Udp.parsePacket() ) 
    {  
#ifdef DEBUG
      Serial.println("Got Time Packet");
#endif
      digitalWrite(ALARMPIN,LOW); 
      Udp.read(packetBuffer,48);

      unsigned long highWord = word(packetBuffer[40], packetBuffer[41]);
      unsigned long lowWord = word(packetBuffer[42], packetBuffer[43]);  
      unsigned long secsSince1900 = highWord << 16 | lowWord;  

      const unsigned long seventyYears = 2208988800UL + timeZoneOffset;      
      unsigned long epoch = secsSince1900 - seventyYears;  

      return epoch;    
    }
#ifdef DEBUG
    Serial.println("No Time Packet Found");
#endif
    digitalWrite(ALARMPIN,HIGH);
  }
}


// send an NTP request to the time server at the given address 
void sendNTPpacket(IPAddress& address)
{
  byte packetBuffer[48]; //buffer to hold outgoing packets
  memset(packetBuffer, 0, 48); // set all bytes in the buffer to 0

  // Initialize values needed to form NTP request
  // (see URL above for details on the packets)
  packetBuffer[0] = 0b11100011;   // LI, Version, Mode
  packetBuffer[1] = 0;     // Stratum, or type of clock
  packetBuffer[2] = 6;     // Polling Interval
  packetBuffer[3] = 0xEC;  // Peer Clock Precision
  // 8 bytes of zero for Root Delay & Root Dispersion
  packetBuffer[12]  = 49; 
  packetBuffer[13]  = 0x4E;
  packetBuffer[14]  = 49;
  packetBuffer[15]  = 52;

  // all NTP fields have been given values, now
  // you can send a packet requesting a timestamp: 		   
  Udp.beginPacket(address, 123); //NTP requests are to port 123
  Udp.write(packetBuffer,48);
  Udp.endPacket(); 
#ifdef DEBUG
  Serial.println("NTP Packet sent");
#endif
}

I don't use the String stuff. I use character arrays.

char outBuf[64];

sprintf(outBuf,"GET /cgi-bin/domo.cgi?cmd=LM%s HTTP/1.0\r\nHost: api.pushingbox.com\r\n\r\n",scriptname);
client.write(outBuf);

Hi I have modified my code avoiding String Class and I have tried to simplify it as much as possible. And then nothing is working =( Even, I can't see the first message in the setup function: Serial.println("In setup...") That means that my Arduino freeze immediately. So, I suspect that my arduino hardware is damaged, can someone try my code with his arduino (with Ethernet field) to confirm my assumption ?

You need this libraries: ICMPPing http://dl.dropbox.com/u/2577678/Arduino/ICMPPing.zip IRemote http://dl.dropbox.com/u/2577678/Arduino/IRremote.zip Time http://dl.dropbox.com/u/2577678/Arduino/Time.zip

And the source file: http://dl.dropbox.com/u/2577678/Arduino/HA_0_5.ino

Thanks again for your help.

You could have run out of memory (SRAM, not program). The outbuffer array could have pushed it over the limit.

Have you tried an example sketch to see if the Arduino still works?

You are right again ;) I optimise as much as possible all variables, and I found something strange.

Here is the new code http://dl.dropbox.com/u/2577678/Arduino/HA_0_6.ino

If I set obuffer array (line 66) greater than 8 char, the arduino hangs just at ethernet.begin :~ Less or equal than 8 seems ok, but not enough during execution and of course the Arduino reset during execution due to memory corruption.

I don't understand, is it possible to have a faulty SDRAM ?

You are using a lot of SRAM with all the strings. They are duplicated in program memory and SRAM. You can avoid the constant strings being duplicated in SRAM with the F function. Just a quick scan of your code, and that looks like it could save you a lot of SRAM.

Serial.println("This string is in SRAM and FLASH");
Serial.println(F("This string is not in SRAM, just FLASH."));

Hi SurferTim, Thank you for this trick, it's absolutely useful. Yes all my strings use a lot of RAM, but they are only used in DEBUG mode. Sure, this can cause freeze only in DEBUG mode. So I changed again my code, avoiding using the sprintf function as it needs a big buffer (char array) to store the string and the variables. In fact, it's better to use serial.print with the F trick. I read that the arduino UNO as only 2KB of RAM :astonished: ohhh it's very very small, each line of code have to be thought and optimised. I found a useful function to know the amount of free memory. Here it is:

int freeRam ()
{
  extern int __heap_start, *__brkval;
  int v;
  return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval);
}

Call it at different place in the code to see if you have enough memory. Lack of memory was one of the reason for hanging the Arduino. Using all this tips help me to solve the issue. For example before using the print(F()) trick I had 449 bytes available after I had 965 free bytes. 8)

The second issue was caused by the Time library, precisely the function setSyncProvider(getTimeFunction). The second time this function is called, after the delay defined by the setSyncInterval(interval) function, the arduino instantly hangs. I didn't find the reason, so I avoided it and simply used the setTime(t) function instead.

Now, everything is fine, and the code is stable. I want to say special thanks to SurferTim who showed me the way :)

The following might be helpful:

http://arduino.cc/playground/Main/Printf

There's stuff in there about getting printf to write directly to the serial port, no need for buffers (sprintf). There are also notes on storing your strings in flash (like F() with the regular Serial.print functions).

Hope that helps,

Dylan

Thank you. Very interesting, again I'm able to optimize a bit more my code ;)

I'm trying to webserver programs, and i got many errors. something wrong with my udp? please help me to solve this.

C:\Users\acer\Documents\Arduino\libraries\Ethernet\Udp.cpp:35: error: 'UdpClass' has not been declared
C:\Users\acer\Documents\Arduino\libraries\Ethernet\Udp.cpp: In function 'void begin(uint16_t)':
C:\Users\acer\Documents\Arduino\libraries\Ethernet\Udp.cpp:36: error: '_port' was not declared in this scope
C:\Users\acer\Documents\Arduino\libraries\Ethernet\Udp.cpp:37: error: '_sock' was not declared in this scope
C:\Users\acer\Documents\Arduino\libraries\Ethernet\Udp.cpp: At global scope:
C:\Users\acer\Documents\Arduino\libraries\Ethernet\Udp.cpp:44: error: 'UdpClass' has not been declared
C:\Users\acer\Documents\Arduino\libraries\Ethernet\Udp.cpp: In function 'uint16_t sendPacket(uint8_t*, uint16_t, uint8_t*, uint16_t)':
C:\Users\acer\Documents\Arduino\libraries\Ethernet\Udp.cpp:45: error: '_sock' was not declared in this scope
C:\Users\acer\Documents\Arduino\libraries\Ethernet\Udp.cpp: At global scope:
C:\Users\acer\Documents\Arduino\libraries\Ethernet\Udp.cpp:50: error: 'UdpClass' has not been declared
C:\Users\acer\Documents\Arduino\libraries\Ethernet\Udp.cpp: In function 'uint16_t sendPacket(const char*, uint8_t*, uint16_t)':
C:\Users\acer\Documents\Arduino\libraries\Ethernet\Udp.cpp:56: error: '_sock' was not declared in this scope
C:\Users\acer\Documents\Arduino\libraries\Ethernet\Udp.cpp: At global scope:
C:\Users\acer\Documents\Arduino\libraries\Ethernet\Udp.cpp:60: error: 'UdpClass' has not been declared
C:\Users\acer\Documents\Arduino\libraries\Ethernet\Udp.cpp: In function 'int available()':
C:\Users\acer\Documents\Arduino\libraries\Ethernet\Udp.cpp:61: error: '_sock' was not declared in this scope
C:\Users\acer\Documents\Arduino\libraries\Ethernet\Udp.cpp: At global scope:
C:\Users\acer\Documents\Arduino\libraries\Ethernet\Udp.cpp:69: error: 'UdpClass' has not been declared
C:\Users\acer\Documents\Arduino\libraries\Ethernet\Udp.cpp: In function 'int readPacket(uint8_t*, uint16_t, uint8_t*, uint16_t*)':
C:\Users\acer\Documents\Arduino\libraries\Ethernet\Udp.cpp:70: error: '_sock' was not declared in this scope
C:\Users\acer\Documents\Arduino\libraries\Ethernet\Udp.cpp: At global scope:
C:\Users\acer\Documents\Arduino\libraries\Ethernet\Udp.cpp:74: error: 'UdpClass' has not been declared
C:\Users\acer\Documents\Arduino\libraries\Ethernet\Udp.cpp: In function 'int readPacket(uint8_t*, uint16_t)':
C:\Users\acer\Documents\Arduino\libraries\Ethernet\Udp.cpp:77: error: '_sock' was not declared in this scope
C:\Users\acer\Documents\Arduino\libraries\Ethernet\Udp.cpp: At global scope:
C:\Users\acer\Documents\Arduino\libraries\Ethernet\Udp.cpp:80: error: 'UdpClass' has not been declared
C:\Users\acer\Documents\Arduino\libraries\Ethernet\Udp.cpp:88: error: 'UdpClass' does not name a type

@Lugination: Looks like you did not add the include files required to compile the sketch. UDP requires these includes.

#include <SPI.h>
#include <Ethernet.h>
#include <EthernetUdp.h>

I know this is an old thread, but I have some related information.

There definitively is a still existing UDP vulnerability related to the W5100, W5200 and W5500. I have an example that reproduces a permanent hang within minutes.

See my ticket at: https://github.com/arduino-libraries/Ethernet/issues/78

Code for reproduction is included in the ticket, plus additional observations and an ad-hoc modification of the Ethernet library that fixes the problem for me. Without my fix, nodes in my UDP based setup will freeze at random after days or weeks, very annoying. Watchdog timer or manual Arduino reboot are ineffective, and the Ethernet shield has to be powered off.

Basically it never exits the send loop in the library because it waits for SEND_OK or TIMEOUT, but sometimes gets a RECEIVE instead, and that seems to cancel return of SEND_OK or TIMEOUT so it never exits the loop. I added a loop counter to exit the loop, and that helps but leaves one more socket occupied at each occurrence (verified by counting free sockets), so after a few times it stops permanently and requires a power-off.

My guess is that an incoming packet is received while trying to send, and that aborts the sending and leaves it in an undefined state.