Pages: [1]   Go Down
Author Topic: Web client + NTS reboots system  (Read 454 times)
0 Members and 1 Guest are viewing this topic.
Offline Offline
Newbie
*
Karma: 0
Posts: 17
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

I was working on a sketch where my Arduino is both a server, a web client and reads time from a NTP-server. It worked, but only for a while. After a couple of hours either the server stopped responding or the client did not get the data. I searched the forums and found some interesting stuff from SurferTim where he implements a timeout-function in case something goes wrong. So I started with the web-client from the playground, added my own addresses and let it get data (a simple xml file containing some setup data) from my (Apache) server for a couple of hours without crashes. Then I added the NTP code from my other sketch. This works fine on startup, but once in loop the Arduino resets itself every time the NTP code is called. This did not happen in my other (much larger) sketch. I understand that auto-rebooting is usually caused by a buffer overflow or memory shortage. So I also added code to check the amount of free memory. This reports 1280 bytes free and never changes.
When I out-comment the 'getPage' line from loop I get a new NTP-IP every 5 seconds. So the NTP code on its own works, the client code also works but together they cause havoc.
Note that I poll an NTP server very 5 seconds only for testing. In my first try I had not set this interval which means it polls every 5 minutes. The Arduino would reboot after 86 loops (with the loop set to 3 seconds that's about 5 minutes, also the syncInterval time when not set with setSyncInterval() if you call now() often enough). I set it to 5 seconds to check if polling the NTP server was what caused the crash. In real life my sketch can do with a poll once every 6 hours or longer.
Also note I xxx-ed out my server address. Also not I use port 6960 for my client. That is not a mistake, my Apache server is configured to also listen on that port.

Code:
/*
   Web client sketch for IDE v1.0.1 and w5100/w5200
   Uses GET method.
   Posted October 2012 by SurferTim
*/

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


// this must be unique
byte mac[] = {  0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };

// change to your network settings
IPAddress ip(192,168,0,177);
IPAddress dnsIP(8,8,8,8); // google dns
IPAddress gateway(192, 168, 0, 1);
IPAddress subnet(255, 255, 255, 0);
IPAddress server(1xx,1xx,xx,1xx); // server IP-number

// change to your server's port
int serverPort = 6960;

EthernetClient client;
int totalCount = 0;
int loopCount = 0;
char pageAdd[32];

/* Timeserver setups */

IPAddress timeServer; //
EthernetUDP Udp;
unsigned int localPort = 8888;  // local port to listen for UDP packets
const int timeZone = 2;     // Central European Time


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

  // disable SD SPI
  pinMode(4,OUTPUT);
  digitalWrite(4,HIGH);

  // Start ethernet
  Serial.println(F("Starting ethernet..."));
  Ethernet.begin(mac, ip, dnsIP, gateway, subnet);

  Serial.println(Ethernet.localIP());

  delay(2000);
 
  setSyncProvider(getNtpTime);
   
  setSyncInterval(5);
   
  //digitalClockDisplay();

  Serial.println(F("Ready"));
}

void loop()
{
  if(loopCount < 4)
  {
    // if loopCount is less than 30, just delay a second
    delay(1000);
  }
  else
  {
    // every thirty seconds this runs
    loopCount = 0;

    // Modify next line to load different page
    // or pass values to server
    //sprintf(pageAdd,"/",totalCount);

    sprintf(pageAdd,"http://www.xxx.xxx/xxx.xml",totalCount);

    if(!getPage(server,serverPort,pageAdd)) Serial.print(F("Fail "));
    else Serial.print(F("Pass "));
    totalCount++;
    Serial.println(totalCount,DEC);
    printRAM();
  }   
  loopCount++;
}

byte getPage(IPAddress ipBuf,int thisPort, char *page)
{
  int inChar;
  char outBuf[128];

  Serial.print(F("connecting..."));

  if(client.connect(ipBuf,thisPort))
  {
    Serial.println(F("connected"));

    sprintf(outBuf,"GET %s HTTP/1.0\r\n\r\n",page);
    client.write(outBuf);
  }
  else
  {
    Serial.println(F("failed"));
    return 0;
  }

  // connectLoop controls the hardware fail timeout
  int connectLoop = 0;

  while(client.connected())
  {
    while(client.available())
    {
      inChar = client.read();
      Serial.write(inChar);
      // set connectLoop to zero if a packet arrives
      connectLoop = 0;
    }

    connectLoop++;

    // if more than 10000 milliseconds since the last packet
    if(connectLoop > 10000)
    {
      // then close the connection from this end.
      Serial.println();
      Serial.println(F("Timeout"));
      client.stop();
    }
    // this is a delay for the connectLoop timing
    delay(1);
  }

  Serial.println();

  Serial.println(F("disconnecting."));
  // close client end
  client.stop();

  return 1;
}


/*-------- NTP code ----------*/

const int NTP_PACKET_SIZE = 48; // NTP time is in the first 48 bytes of message
byte packetBuffer[NTP_PACKET_SIZE]; //buffer to hold incoming & outgoing packets

time_t getNtpTime()
{
    Udp.begin(localPort);
   
    DNSClient dns;
    dns.begin(Ethernet.dnsServerIP());
   
    if(dns.getHostByName("pool.ntp.org",timeServer)) {
        Serial.print(F("NTP server ip: "));
        Serial.println(timeServer);
    }
    else Serial.print(F("dns lookup failed"));
   
    while (Udp.parsePacket() > 0) ; // discard any previously received packets
    //Serial.println("Transmit NTP Request");
    sendNTPpacket(timeServer);
    uint32_t beginWait = millis();
    while (millis() - beginWait < 1500) {
        int size = Udp.parsePacket();
        if (size >= NTP_PACKET_SIZE) {
            //Serial.println("Receive NTP Response");
            Udp.read(packetBuffer, NTP_PACKET_SIZE);  // read packet into the buffer
            unsigned long secsSince1900;
            // convert four bytes starting at location 40 to a long integer
            secsSince1900 =  (unsigned long)packetBuffer[40] << 24;
            secsSince1900 |= (unsigned long)packetBuffer[41] << 16;
            secsSince1900 |= (unsigned long)packetBuffer[42] << 8;
            secsSince1900 |= (unsigned long)packetBuffer[43];
            return secsSince1900 - 2208988800UL + timeZone * SECS_PER_HOUR;
        }
    }
    Serial.println("No NTP Response :-(");
    return 0; // return 0 if unable to get the time
}

// send an NTP request to the time server at the given address
void sendNTPpacket(IPAddress &address)
{
    // set all bytes in the buffer to 0
    memset(packetBuffer, 0, NTP_PACKET_SIZE);
    // 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, NTP_PACKET_SIZE);
    Udp.endPacket();
}
void digitalClockDisplay(){
    // digital clock display of the time
    Serial.print(hour());
    printDigits(minute());
    printDigits(second());
    Serial.print(" ");
    Serial.print(day());
    Serial.print(" ");
    Serial.print(month());
    Serial.print(" ");
    Serial.print(year());
    Serial.println();
}

void printDigits(int digits){
    // utility for digital clock display: prints preceding colon and leading 0
    Serial.print(":");
    if(digits < 10)
        Serial.print('0');
    Serial.print(digits);
}

void printRAM()
{
    Serial.print("Free RAM = ");
    Serial.println(freeRam());
    digitalClockDisplay();
}

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

What could be wrong with this code?
Logged

0
Offline Offline
God Member
*****
Karma: 39
Posts: 988
Get Bitlash: http://bitlash.net
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

You've obscured the actual address that gets sprintf'd into pageAdd, but if it is longer than 32 bytes you will overrun the end of pageAdd and write over other data in memory.

Does the problem persist if you make pageAdd 80 bytes?  You have plenty to spare…


-br
Logged

0
Offline Offline
God Member
*****
Karma: 39
Posts: 988
Get Bitlash: http://bitlash.net
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

May I also add, please stop abusing that NTP server!  Five seconds is an unfriendly retry interval in NTP-server-land.  Thump it a bit less, willya?

Cheers, and good luck with your project,

-br
Logged

Offline Offline
Newbie
*
Karma: 0
Posts: 17
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Well, bumping up pageAdd seems to help. Strange because the obscured url is in reality only 26 characters.
Also strange that the sketch worked for hours without the NTP code. Or maybe not, because overrunning the allocated space for pageAdd simply ended up in unused space with no code claiming this space. Or something like that.
Of course the 5 sec interval was only to test if it was indeed the start of NTP that caused the reset. Like I said, in real life the interval is set to 6 hours or longer. As you will have noticed, I also use pool.ntp.org to lessen the strain on one particular server.

Thanks for your suggestion, I'll leave the system running for a while to see if it is stable.
Logged

0
Offline Offline
God Member
*****
Karma: 39
Posts: 988
Get Bitlash: http://bitlash.net
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Glad it's working and I hope it stays fixed.

-br
Logged

Pages: [1]   Go Up
Jump to: