PaulStoffregen / Ethernet

Ethernet library for Teensy (Wiznet W5100 / W5200 / W5500)
http://www.pjrc.com/teensy/td_libs_Ethernet.html
130 stars 83 forks source link

Using NTP + UDP + Webserver == bad DNS.cpp behaviour #31

Closed pierre83 closed 4 years ago

pierre83 commented 5 years ago

First of all thanks for your very good work on this library !

Having problems with my own skecth, I agregated 3 sketches from examples: NTP client, UDPSendReceive and Web Server, all are running simultaneously, this example exhibits the problem.

The mechanism is as follow: the HTTP part receive and send messages while the UDP part is also receiving messages (no send) and every 13 seconds the Arduino sends an NTP request to pool.ntp.org.

Most of the time the NTP request abort because the DNS request stays stuck in getHostByName() with a timeout but while this happens, HTTP and UDP processes continue to work correctly only the DNS requests don't work. Other information: Resetting the HTTP stack ( http_server.begin() ) resolve the DNS problem (i added a command in the sketch) for one or maybe some requests. The Retry mechanism in getHostByName cause an arduino reboot (if suppressed no reboot) Using an IP address directly instead of a hostname never shows the problem.

Example is joined to this request. Thank you Pierre

/* Web Server

A simple web server that shows the value of the analog input pins. using an Arduino Wiznet Ethernet shield.

Udp NTP Client

Get the time from a Network Time Protocol (NTP) time server Demonstrates use of UDP sendPacket and ReceivePacket

UDPSendReceiveString

This sketch receives UDP message strings, prints them to the serial port and sends an "acknowledge" string back to the sender

*/

include

include

include

// Ethernet part byte MAC[] = { 0x90, 0xA2, 0xDA, 0x0D, 0xAB, 0xAF}; IPAddress IP( 192, 168, 0, 56 ); IPAddress FREEBOX( 192, 168, 0, 254 ); IPAddress SUBNET( 255, 255, 255, 0 );

// HTTP part EthernetServer server(80); EthernetClient client;

// UDP part unsigned int localPort = 8888; // local port to listen on // buffers for receiving and sending data char packetBuffer2[UDP_TX_PACKET_MAX_SIZE]; // buffer to hold incoming packet, // An EthernetUDP instance to let us send and receive packets over UDP EthernetUDP Udp;

// NTP part const char timeServer[] = "time.nist.gov"; // time.nist.gov NTP server const int NTP_PACKET_SIZE = 48; // NTP time stamp is in the first 48 bytes of the message uint32_t intervalle = 0; const uint16_t INTERVALLE = 11325; char packetBuffer[NTP_PACKET_SIZE]; // buffer to hold incoming packet,

void setup() { // You can use Ethernet.init(pin) to configure the CS pin //Ethernet.init(10); // Most Arduino shields //Ethernet.init(5); // MKR ETH shield //Ethernet.init(0); // Teensy 2.0 //Ethernet.init(20); // Teensy++ 2.0 //Ethernet.init(15); // ESP8266 with Adafruit Featherwing Ethernet //Ethernet.init(33); // ESP32 with Adafruit Featherwing Ethernet

// Open serial communications and wait for port to open: Serial.begin(115200); while (!Serial) { ; // wait for serial port to connect. Needed for native USB port only } Serial.println("\nEthernet WebServer Example");

// start the Ethernet connection and the server: Ethernet.begin( MAC, IP, FREEBOX, FREEBOX, SUBNET);

// Check for Ethernet hardware present if (Ethernet.hardwareStatus() == EthernetNoHardware) { Serial.println("Ethernet shield was not found. Sorry, can't run without hardware. :("); while (true) { delay(1); // do nothing, no point running without Ethernet hardware } } if (Ethernet.linkStatus() == LinkOFF) { Serial.println("Ethernet cable is not connected."); }

// start HTTP server server.begin(); Serial.print("server is at "); Serial.println(Ethernet.localIP());

// start UDP Udp.begin(localPort);

// NTP intervalle = millis() + INTERVALLE ; // Proof the NTP part is Ok ntp_loop(); }

void loop() {

// listen for incoming clients client = server.available(); if (client) { http_loop(); } // if there's data available, read a packet int packetSize = Udp.parsePacket(); if (packetSize) { udp_loop(packetSize); }

// NTP if ( millis() > intervalle ) { ntp_loop(); } }

void udp_loop(int packetSize) {

Serial.print("Received packet of size "); Serial.println(packetSize); Serial.print("From "); IPAddress remote = Udp.remoteIP(); for (int i = 0; i < 4; i++) { Serial.print(remote[i], DEC); if (i < 3) { Serial.print("."); } } Serial.print(", port "); Serial.println(Udp.remotePort());

// read the packet into packetBufffer Udp.read(packetBuffer2, UDP_TX_PACKET_MAX_SIZE); Serial.println("Contents:"); Serial.println(packetBuffer2); / // send a reply to the IP address and port that sent us the packet we received Udp.beginPacket(Udp.remoteIP(), Udp.remotePort()); Udp.write(ReplyBuffer); Udp.endPacket(); / //delay(10); }

void http_loop() {

Serial.println("new client"); // an http request ends with a blank line boolean currentLineIsBlank = true; while (client.connected()) { if (client.available()) { char c = client.read(); Serial.write(c); // if you've gotten to the end of the line (received a newline // character) and the line is blank, the http request has ended, // so you can send a reply if (c == '\n' && currentLineIsBlank) { // send a standard http response header client.println("HTTP/1.1 200 OK"); client.println("Content-Type: text/html"); client.println("Connection: close"); // the connection will be closed after completion of the response client.println("Refresh: 20"); // refresh the page automatically every 5 sec client.println(); client.println("<!DOCTYPE HTML>"); client.println(""); // output the value of each analog input pin for (int analogChannel = 0; analogChannel < 6; analogChannel++) { int sensorReading = analogRead(analogChannel); client.print("analog input "); client.print(analogChannel); client.print(" is "); client.print(sensorReading); client.println("
"); } client.println(""); break; } if (c == '\n') { // you're starting a new line currentLineIsBlank = true; } else if (c != '\r') { // you've gotten a character on the current line currentLineIsBlank = false; } } } // give the web browser time to receive the data delay(1); // close the connection: client.stop(); Serial.println("client disconnected"); }

void ntp_loop() {

Serial.println("NTP time !"); intervalle = millis() + INTERVALLE ; sendNTPpacket(timeServer); // send an NTP packet to a time server

// wait to see if a reply is available delay(2000); if (Udp.parsePacket()) { // We've received a packet, read the data from it Udp.read(packetBuffer, NTP_PACKET_SIZE); // read the packet into the buffer

// the timestamp starts at byte 40 of the received packet and is four bytes,
// or two words, long. First, extract the two words:

unsigned long highWord = word(packetBuffer[40], packetBuffer[41]);
unsigned long lowWord = word(packetBuffer[42], packetBuffer[43]);
// combine the four bytes (two words) into a long integer
// this is NTP time (seconds since Jan 1 1900):
unsigned long secsSince1900 = highWord << 16 | lowWord;
Serial.print("Seconds since Jan 1 1900 = ");
Serial.println(secsSince1900);

// now convert NTP time into everyday time:
Serial.print("Unix time = ");
// Unix time starts on Jan 1 1970. In seconds, that's 2208988800:
const unsigned long seventyYears = 2208988800UL;
// subtract seventy years:
unsigned long epoch = secsSince1900 - seventyYears;
// print Unix time:
Serial.println(epoch);

// print the hour, minute and second:
Serial.print("The UTC time is ");       // UTC is the time at Greenwich Meridian (GMT)
Serial.print((epoch  % 86400L) / 3600); // print the hour (86400 equals secs per day)
Serial.print(':');
if (((epoch % 3600) / 60) < 10) {
  // In the first 10 minutes of each hour, we'll want a leading '0'
  Serial.print('0');
}
Serial.print((epoch  % 3600) / 60); // print the minute (3600 equals secs per minute)
Serial.print(':');
if ((epoch % 60) < 10) {
  // In the first 10 seconds of each minute, we'll want a leading '0'
  Serial.print('0');
}
Serial.println(epoch % 60); // print the second

} // wait ten seconds before asking for the time again

//Ethernet.maintain(); }

void sendNTPpacket(const char * address) {

// send an NTP request to the time server at the given address Serial.println("Send NTP packet"); // 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: int ret = Udp.beginPacket(address, 123); // NTP requests are to port 123 //int ret = Udp.beginPacket(FREEBOX, 123); // NTP requests for my internet box if ( ret != 1 ) { Serial.print("beginPacket: "); Serial.println(ret); } else { Serial.println("beginPacket sent"); } Udp.write(packetBuffer, NTP_PACKET_SIZE); ret = Udp.endPacket(); if ( ret != 1 ) { Serial.print("endPacket: "); Serial.println(ret); } else { Serial.println("endPacket sent"); } }

pierre83 commented 4 years ago

I rewrote DNS into my sketch because using it (DNS library) with WTD lead sometimes to very long request causing the watchdog to reset the