TimeNTP_ESP8266WiFi - breaks if IP address is fixed

I made a ?MDE? by opening the example and checking it worked - which it did.

I then added code to fix the IP address.

ESP-8266 WiFi.config(ip, gateway, subnet); · Issue #2371 · esp8266/Arduino · GitHub & I tied several different suggestions.

Its now reluctant to connect to Wifi, although when it does DHCP gets the right address; but connection to the NTP server fails. Code follows - grateful for any ideas

/*
 * TimeNTP_ESP8266WiFi.ino
 * Example showing time sync to NTP time source
 *
 * This sketch uses the ESP8266WiFi library
 */


#include <TimeLib.h>
#include <ESP8266WiFi.h>
#include <WiFiUdp.h>

const char* ssid     = "myssid";
const char* pass = "mypass";

// NTP Servers:
static const char ntpServerName[] = "us.pool.ntp.org";
//static const char ntpServerName[] = "time.nist.gov";
//static const char ntpServerName[] = "time-a.timefreq.bldrdoc.gov";
//static const char ntpServerName[] = "time-b.timefreq.bldrdoc.gov";
//static const char ntpServerName[] = "time-c.timefreq.bldrdoc.gov";

const int timeZone = 1;     // Central European Time
//const int timeZone = -5;  // Eastern Standard Time (USA)
//const int timeZone = -4;  // Eastern Daylight Time (USA)
//const int timeZone = -8;  // Pacific Standard Time (USA)
//const int timeZone = -7;  // Pacific Daylight Time (USA)


WiFiUDP Udp;
unsigned int localPort = 8888;  // local port to listen for UDP packets

time_t getNtpTime();
void digitalClockDisplay();
void printDigits(int digits);
void sendNTPpacket(IPAddress &address);

// Fix the IP address for the shield:
IPAddress ip(192, 168, 1, 32); // https://github.com/esp8266/Arduino/issues/1959
IPAddress gateway(192, 168, 1, 1);   //IP Address of your WiFi Router (Gateway)
IPAddress subnet(255, 255, 255, 0);  //Subnet mask
IPAddress dns(8, 8, 8, 8);  //DNS
IPAddress dns2(8, 8, 4, 4); //google open dns
const char* deviceName = "Ping Monitor";

void setup()
{
  Serial.begin(74880);
  while (!Serial) ; // Needed for Leonardo only
  delay(250);
  Serial.println("TimeNTP Example");
  Serial.print("Connecting to ");
  Serial.println(ssid);  
  WiFi.disconnect(); //Prevent connecting to wifi based on previous configuration
    //try for fixed IP address
  WiFi.mode(WIFI_STA);
  WiFi.hostname(deviceName);      // DHCP Hostname (useful for finding device for static lease)
  WiFi.config(ip, gateway, subnet);

  
  WiFi.begin(ssid, pass);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.print("IP number assigned by DHCP is ");
  Serial.println(WiFi.localIP());

 
 
  Serial.println("Starting UDP");
  Udp.begin(localPort);
  Serial.print("Local port: ");
  Serial.println(Udp.localPort());
  Serial.println("waiting for sync");
  setSyncProvider(getNtpTime);
  setSyncInterval(3600); //seconds 
}

time_t prevDisplay = 0; // when the digital clock was displayed

void loop()
{
  if (timeStatus() != timeNotSet) {
    if (now() != prevDisplay) { //update the display only if time has changed
      prevDisplay = now();
      digitalClockDisplay();
    }
  }
}

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);
}

/*-------- 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()
{
  IPAddress ntpServerIP; // NTP server's ip address

  while (Udp.parsePacket() > 0) ; // discard any previously received packets
  Serial.println("Transmit NTP Request");
  // get a random server from the pool
  WiFi.hostByName(ntpServerName, ntpServerIP);
  Serial.print(ntpServerName);
  Serial.print(": ");
  Serial.println(ntpServerIP);
  sendNTPpacket(ntpServerIP);
  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();
}

Connecting to myssid
.......IP number assigned by DHCP is 192.168.1.32
Starting UDP
Local port: 8888
waiting for sync
Transmit NTP Request
us.pool.ntp.org: 255.255.255.255
No NTP Response :frowning:

Presume that you have checked 192.168.1.x is the correct subnet for your router?
And that nothing else on your network is using address 192.168.1.32?

Can you ping the Arduino when you manually configure IP?
Can you ping when you use DHCP?

here is how to get NTP time on esp8266

 configTime(TIME_ZONE, "pool.ntp.org");
 time_t now = time(nullptr);
 while (now < SECS_YR_2000) {
   delay(100);
   now = time(nullptr);
 }
 setTime(now);

a sketch TelnetStream/TelnetStreamEsp8266Test.ino at b1a24a033f84a9811a99d02389cc6b64c02c69c0 · JAndrassy/TelnetStream · GitHub

If you want a static (not 'fixed') IP address don't set it on the device. Go into the router configuration and somewhere in there you should find* an option to lock an IP address to the MAC address of the device you want to have the static IP address.

Advantages:

  • One place, the router, to change which IP address belongs to which device.
  • You can put the same code on multiple devices and each will have its own, different IP address without having to change it in the code every time you program a different device.
  • The router knows which IP addresses are static so it won't give them to some other device.

*Cheap, nasty routers excepted, get a decent router if yours does not allow this.

Juraj:
here is how to get NTP time on esp8266

 configTime(TIME_ZONE, "pool.ntp.org");

time_t now = time(nullptr);
while (now < SECS_YR_2000) {
  delay(100);
  now = time(nullptr);
}
setTime(now);



a sketch https://github.com/jandrassy/TelnetStream/blob/b1a24a033f84a9811a99d02389cc6b64c02c69c0/examples/TelnetStreamEsp8266Test/TelnetStreamEsp8266Test.ino#L27

That's cool. How would you incorporate DST? I've been using the time.h functions in AVR GCC, which lacks a configTime(). The functions that work for me are:

  set_dst(usa_dst);
  set_zone(-5 * ONE_HOUR);

usa_dst() is a callback function that returns a DST offset. Now I have an ESP32 and I want to port to it. I need local time.

aarg:
That's cool. How would you incorporate DST? I've been using the time.h functions in AVR GCC, which lacks a configTime(). The functions that work for me are:

  set_dst(usa_dst);

set_zone(-5 * ONE_HOUR);



usa_dst() is a callback function that returns a DST offset. Now I have an ESP32 and I want to port to it. I need local time.

the TIME_ZONE thing on esp8266 has problems. on esp32 things are simpler

 configTime(gmtOffset_sec, daylightOffset_sec, "pool.ntp.org");
 time_t now = time(nullptr);
 while (now < SECS_YR_2000) {
   delay(100);
   now = time(nullptr);
 }
 setTime(now);

the use of configTime on esp is important for secure network connection (a.k.a. SSL)

That is a little strange. Using this:

configTime(gmtOffset_sec, daylightOffset_sec, "pool.ntp.org");

Seems to configure daylight time statically. Is configTime() not usually run at boot time? If so, if I booted and it was daylight savings time, the next day DST was not in effect, unless there was a reboot, wouldn't the local time be incorrect? I found configTime() in esp32-hal-time.c, the last line in that is just

    setTimeZone(-gmtOffset_sec, daylightOffset_sec);

That looks similar to the 'setZone' in the AVR implemention, but where is the logic that adjusts the DST dynamically? I need to know because I try to support 43 global time zone definitions. In the AVR case, 'usa_dst' is actually a C program that I can edit to create different TZ definitions. It provides the time system with a DST offset based on the current time, thus auto adjusts for DST.

I realize this is a bit of a hijack because I'm really only interested in ESP32.

aarg:
That is a little strange. Using this:

configTime(gmtOffset_sec, daylightOffset_sec, "pool.ntp.org");

Seems to configure daylight time statically. Is configTime() not usually run at boot time? If so, if I booted and it was daylight savings time, the next day DST was not in effect, unless there was a reboot, wouldn't the local time be incorrect? I found configTime() in esp32-hal-time.c, the last line in that is just

    setTimeZone(-gmtOffset_sec, daylightOffset_sec);

That looks similar to the 'setZone' in the AVR implemention, but where is the logic that adjusts the DST dynamically? I need to know because I try to support 43 global time zone definitions. In the AVR case, 'usa_dst' is actually a C program that I can edit to create different TZ definitions. It provides the time system with a DST offset based on the current time, thus auto adjusts for DST.

I realize this is a bit of a hijack because I'm really only interested in ESP32.

sorry I am not an expert on this. I modified last week the examples for the TelnetStream library to use time retrieved by SDK, but I didn't dig deeper.

pcbbc: yes the settings are all correct; I have getting on for 20 devices that may access my network - some ethernet some wifi - and getting more depending on how bored I am as I've just bought a couple of ESP32's.

The router is a Huawei DG8041W and I'm pleased to note its quite configurable. (Less pleased it only has a single feeble LED which is why I'm building a ping monitor to display & log my connection; however its been fun doing so)
So thanks Perry, I'll take your advice and give everything fixed addresses in bands according to thier function.
Then the router can share out whats left to odd devices that connect intermittently.

Otherwise this discussion seems to have taken a strange and puzzling turn. I dont just need Unix time, I do need to use the time library or waste time replicating it.

I think the essence of reply #2, is that you might not have a problem if you use the "native" methods for getting NTP time, vs. the custom code you posted. It looks like it may have been cut and pasted from the Ethernet library NTP sketch... it looks awfully familiar. Sometimes using different tools can solve a problem even when you're not sure why.