Getting UTC-Time from NTP-Server

I found some code to get the utc time (maybe also the local time from another NTP-Server with some modification) I want to share with the community, so it’s not really a libary. (modified code from http://www.dl8ma.de/Arduino/web-utc-uhr/ , German language)

Unfortunately I realized while compiling in web IDE that the UPD libary in the Firmware is still not available. @zach : Is there a schedule for that? Is there another way to add UDP in the web IDE right now, or another way to poll that data ? How can I put the result to the onboard-RTC? (I’m an Arduino beginner, sorry!)

char string[ 17 ] = { "" };

int stunde, minute, sekunde;    //hour, minute, second
int led = D7; 

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

byte timeServer[] = { 192, 43, 244, 18}; // time.nist.gov NTP server

const int NTP_PACKET_SIZE= 48; // NTP time stamp is in the first 48 bytes of the message

byte packetBuffer[NTP_PACKET_SIZE]; //buffer to hold incoming and outgoing packets

void setup()
{
  pinMode(led, OUTPUT);
  Udp.begin(localPort);
  Serial.begin(9600);
  delay(1000);
  serial.printIn("NTP-Uhr");
}

void loop()
{
  sendNTPpacket(timeServer); // send an NTP packet to a time server

    // wait to see if a reply is available
  delay(1000); 
  if ( Udp.available() ) { 
    Udp.readPacket(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, esxtract 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; 

    // now convert NTP time into everyday 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; 

    stunde = (epoch % 86400L) / 3600;         
    minute = (epoch % 3600) / 60;
    sekunde = (epoch % 60);

    serial.print (stunde);
    serial.print (minute);
    serial.println (sekunde);

  }
  // wait ten seconds before asking for the time again
  digitalWrite(led, HIGH);   // Turn ON the LED
  delay(1000);               // Wait for 1000mS = 1 second
  digitalWrite(led, LOW);    // Turn OFF the LED
  delay(9000);               // Wait for 2 seconds
}

// send an NTP request to the time server at the given address
unsigned long sendNTPpacket(byte *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.sendPacket( packetBuffer,NTP_PACKET_SIZE,  address, 123); //NTP requests are to port 123
}  
2 Likes

Great, thanks for sharing! This might be related to: https://community.spark.io/t/feature-request-get-current-timestamp-over-cloud/1197

Working code (previous was not woking for me):

UDP UDP;

char string[ 17 ] = { "" };

int hour, minute, second;

unsigned int localPort = 123;
unsigned int timeZone = 4;

byte timeServer[] = { 192, 43, 244, 18}; // time.nist.gov NTP server

const int NTP_PACKET_SIZE= 48; // NTP time stamp is in the first 48 bytes of the message

byte packetBuffer[NTP_PACKET_SIZE]; //buffer to hold incoming and outgoing packets

void setup()
{
  UDP.begin(localPort);
  Serial.begin(9600);
  delay(1000);
  Serial.println("NTP-clock");
}

void loop()
{
    sendNTPpacket(timeServer); // send an NTP packet to a time server

    // wait to see if a reply is available
    delay(1000); 
    Serial.println("------");
    if ( UDP.parsePacket() ) {
        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, esxtract the two words:

    unsigned long highWord = (packetBuffer[40] << 8) + packetBuffer[41];
    unsigned long lowWord = (packetBuffer[42] << 8) + 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;
    secsSince1900 += timeZone*60*60;

    // now convert NTP time into everyday 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; 

    hour = (epoch % 86400L) / 3600;         
    minute = (epoch % 3600) / 60;
    second = (epoch % 60);

    Serial.print (hour);
    Serial.print (":");
    Serial.print (minute);
    Serial.print (":");
    Serial.println (second);

  }
    while ( UDP.parsePacket() ) { // clean-up buffer
        UDP.read(packetBuffer, NTP_PACKET_SIZE);  // read the packet into the buffer
    }

  // wait ten seconds before asking for the time again
  delay(10000);
}

// send an NTP request to the time server at the given address
unsigned long sendNTPpacket(byte *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);
  UDP.write(packetBuffer, NTP_PACKET_SIZE); //NTP requests are to port 123
  UDP.endPacket();

}
1 Like

Hello @nikbyte, when I copy your code in the IDE and compile it I get the following error:

the_user_app.cpp:69:1: error: expected declaration before '}' token
make: *** [the_user_app.o] Error 1

If I remove that curly bracket it compiles fine but running this app on the SparkCore results in a print out of the 6 dashes every 10 seconds, nothing more.

What am i doing wrong?

Br,
Henk

Sorry for bracket - I’ve corrected it in previous message.
If you see only dashes, you didn’t receive response from NTP server.
Try another NTP servers. For example:
0.rhel.pool.ntp.org has address 217.79.179.106
0.rhel.pool.ntp.org has address 81.171.44.131
0.rhel.pool.ntp.org has address 81.181.87.248
0.rhel.pool.ntp.org has address 193.238.80.20
1.rhel.pool.ntp.org has address 82.199.107.209
1.rhel.pool.ntp.org has address 178.18.228.131
1.rhel.pool.ntp.org has address 212.158.160.166
1.rhel.pool.ntp.org has address 188.254.50.181
2.rhel.pool.ntp.org has address 46.8.40.31
2.rhel.pool.ntp.org has address 93.180.6.3
2.rhel.pool.ntp.org has address 80.90.180.140
2.rhel.pool.ntp.org has address 31.135.95.60

1 Like

Ok, so I had to do my part. I added a number of extra time server IPs to the array along with a timer loop. Basically if the NTP server does not reply within 8 seconds, the next server IP is used for the next UDP call. Its not complicated and it works!

:smile:

UDP UDP;

char string[ 17 ] = { "" };

int hour, minute, second;

unsigned int localPort = 123;
unsigned int timeZone = -5;

unsigned int serverNum = 0;
unsigned long timeout;

byte timeServer[4][4] = { {31,135,95,60},{217,79,179,106},{81,171,44,131},{178,18,228,131} }; // time.nist.gov NTP server

const int NTP_PACKET_SIZE= 48; // NTP time stamp is in the first 48 bytes of the message

byte packetBuffer[NTP_PACKET_SIZE]; //buffer to hold incoming and outgoing packets

void setup()
{
	UDP.begin(localPort);
	Serial.begin(9600);
	delay(1000);
	Serial.println("NTP-clock");
}

void loop()
{
	sendNTPpacket(timeServer[serverNum]); // send an NTP packet to a time server

	// wait to see if a reply is available
	
	timeout = millis();

	// If no response in 8s then go to next time server IP address
    while ((millis() - timeout) < 8000L) {                     
	    delay(1000);
	    if ( UDP.parsePacket() ) {
	    	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, esxtract the two words:

		    unsigned long highWord = (packetBuffer[40] << 8) + packetBuffer[41];
		    unsigned long lowWord = (packetBuffer[42] << 8) + 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;
		    secsSince1900 += timeZone*60*60;

		    // now convert NTP time into everyday 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; 

		    hour = (epoch % 86400L) / 3600;         
		    minute = (epoch % 3600) / 60;
		    second = (epoch % 60);

		    Serial.print (hour);
		    Serial.print (":");
		    Serial.print (minute);
		    Serial.print (":");
		    Serial.println (second);
		    break;
	        }
        }
    if ((millis() - timeout) > 8000L) {
        if (serverNum++ > 3)
            serverNum = 0;
        Serial.print("timeout, going to server ");
        Serial.println(serverNum);
        }

	while ( UDP.parsePacket() ) {               // clean-up buffer
		UDP.read(packetBuffer, NTP_PACKET_SIZE);  // read the packet into the buffer
	}

	// wait ten seconds before asking for the time again
	delay(10000);
}


// send an NTP request to the time server at the given address
unsigned long sendNTPpacket(byte *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);
	UDP.write(packetBuffer, NTP_PACKET_SIZE); //NTP requests are to port 123
	UDP.endPacket();

}
2 Likes

I have been working on NTP as well and while I am not quite ready to share my code just yet, I do have a few notes:

  • UDP.beginPacket will also take a char string and do DNS lookups for you.
  • Using this, you can switch to the “host” pool.ntp.org which uses DNS tricks to share the load instead of hitting any one server. There are regional pools (North America, Europe, etc) so you can get hosts with lower latency for better results.
  • The NTP protocol now includes a “kiss-of-death” when the stratum is set to zero. This is supposed to force clients to stop asking or least back-off asking so quickly.

If lots of folks hit a single NTP server too hard and ignore the kiss-of-death, the server will be shut down. This has happened a few times already, once famously with a router maker and a University.

Just thought you would all want to be good Netizens!

1 Like

Great info! I was going to look into how the Core library did DNS lookups so thanks for sharing. I look forward to your contribution! :smile:

1 Like

Hi @nikbyte, thanks. I used the IP address 217.79.179.106 and that one works.
Br.,
Henk

So here is the same code but with the DNS lookup that nko pointed out. So for each loop, the pool.npt.org is used to fetch a new time server IP. Works well but on my Spark, it will go to a flashing green light of death after a random number of calls. Ideally, the onboard real time clock is synched with the NTP time so it only gets called once a day (or longer) but there is no code yet to handle the RTC and ideally, integrate it with the (arduino) TIME library. :smile:

UDP UDP;

char string[ 17 ] = { "" };

int hour, minute, second;

unsigned int localPort = 123;
unsigned int timeZone = -5;

unsigned int serverNum = 0;
unsigned long timeout;

const char timeServer[] = "pool.ntp.org";

const int NTP_PACKET_SIZE= 48; // NTP time stamp is in the first 48 bytes of the message

byte packetBuffer[NTP_PACKET_SIZE]; //buffer to hold incoming and outgoing packets

void setup()
{
	UDP.begin(localPort);
	Serial.begin(9600);
	delay(1000);
	Serial.println("NTP-clock");
}

void loop()
{
	sendNTPpacket(timeServer);  // send an NTP packet to a time server
	// wait to see if a reply is available

    delay(1000);
    if ( UDP.parsePacket() ) {
    	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, esxtract the two words:

	    unsigned long highWord = (packetBuffer[40] << 8) + packetBuffer[41];
	    unsigned long lowWord = (packetBuffer[42] << 8) + 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;
	    secsSince1900 += timeZone*60*60;

	    // now convert NTP time into everyday 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; 

	    hour = (epoch % 86400L) / 3600;         
	    minute = (epoch % 3600) / 60;
	    second = (epoch % 60);

	    Serial.print (hour);
	    Serial.print (":");
	    Serial.print (minute);
	    Serial.print (":");
	    Serial.println (second);
        }

	while ( UDP.parsePacket() ) {               // clean-up buffer
		UDP.read(packetBuffer, NTP_PACKET_SIZE);  // read the packet into the buffer
	}

	// wait ten seconds before asking for the time again
	delay(10000);
}


// send an NTP request to the time server at the given address
unsigned long sendNTPpacket(const char *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);
	UDP.write(packetBuffer, NTP_PACKET_SIZE); //NTP requests are to port 123
	UDP.endPacket();

}
2 Likes

Hi,

First the Cloud API now syncs the Core time on sync - so probably that functionality has been added since the start of this thread… However, I was was interested to play around with UDP and NTP is a nice protocol to experiment with.

Using the example of passing the FQDN to UDP.beginPacket I fail to connect to NTP. If I change the function prototype of sendNTPpacket to use a pointer to byte and pass in the IP array then it works as expected.

I dug into the core firmware and found that the overloaded function calls gethostbyname but on my core it does not resolve the hostname. Am I missing something, sure the core knows how to resolve hostname to IP because I’m communicating with it via the Spark cloud service.

Thanks,
Al

uint32_t ip_addr = 0;
gethostbyname((char*)address, strlen(address), &ip_addr);
sprintf(publishString, "inSendNTPpacket:beginPacket we looked up %s and found it to be %lu.%lu.%lu.%lu", address, BYTE_N(ip_addr, 3), BYTE_N(ip_addr, 2), BYTE_N(ip_addr, 1), BYTE_N(ip_addr, 0) );
Spark.publish("Log", publishString);

Hi @albal

The core has a variety of fall-back mechanisms to find the cloud connection, so finding the cloud host does not necessarily imply that DNS is working.

In your code snipping above, address should be something like “www.google.com”, right?

Can you also check your DNS settings on your router? Does you wireless router supply DNS service or do you have a more complicated setup with a separate DNS host?

[EDIT] I can see your log events in the public event stream and your sprintf above does not appear to be printing correctly due to the string length. There is a 63 character max for publish, so you should remove some of the text before the address to make room to print it.

Yes I was using the FQDN without any URI reference. Overflow, thanks for the tip. Indeed I was overflowing something and probably everything but… I eventually got my spark into the state where I needed to load the factory firmware back on. Even after a factory reset it wasn’t completing an initial setup with the iPhone app. My home network has a Windows 2012 Server as a DC/DNS server. I think I will look into setting up another SSID just for IOT type devices that don’t need all that ‘jazz’.

However, initial provision was fine on this network. I can’t test right now because I’m not near any Wi-Fi that supports the authentication methods Spark supports.

I now know that the flashing RED Leds mean different things depending how they flash.

I have two ideas for Spark Mobile App.

  1. The app could use the camera to sample the colour LED and tell me in a natural language what it means :smile: and maybe what else to do.
  2. It would be neat if I could connect to networks with using EAP and certificates.

[edit] back online :slight_smile: I figured I could use my iPhone for the Personal Hotspot - time for some more overflow!!

1 Like

@peekay123 @ScruffR I’m trying to use the UTC to normal Time code that you have listed here but I need some help :smile:

So I’m pulling weather data via a Webhook and it’s providing Sunrise & Sunset times in UTC time.

The Sunrise and Sunset times are parsed from a string into separate strings which is not compatible with your code.

When trying to compile I get this error:

I have no idea how to properly convert the UTC time that’s held in a string to a acceptable format for your code. I did do some searching but its all mainly C++ advice which does not make sense to me right now.

Here is the part of the code that causes the compile error:

I’m parsing the UTC Sunrise time from the webhook response via this code:

 String sunriseStr = tryExtractString(str, "<Sunrise>", "</Sunrise>");

Here is your UTC to normal time conversion code. I trying to use the sunriseStr that I extract from the webhook response to feed your code the UTC time it needs to calculate the real time.

 sunriseStr += timeZone*60*60;

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

	    hour = (epoch % 86400L) / 3600;         
	    minute = (epoch % 3600) / 60;
	    second = (epoch % 60);

	    Serial.print (hour);
	    Serial.print (":");
	    Serial.print (minute);
	    Serial.print (":");
	    Serial.println (second);

Here is my whole program:

unsigned int timeZone = -5;
int hour, minute, second;

// called once on startup
void setup() {
    // For simplicity, we'll format our weather data as text, and pipe it to serial.
    // but you could just as easily display it in a webpage or pass the data to another system.

    // Learn more about the serial commands at https://docs.particle.io/reference/firmware/photon/#serial
    //  for the Photon, or https://docs.particle.io/reference/firmware/core/#serial for the Core
    // You can also watch what's sent over serial with the particle cli with
    //  particle serial monitor
    Serial.begin(115200);

    // Lets listen for the hook response
    Particle.subscribe("hook-response/FishersWeather", gotWeatherData, MY_DEVICES);

    // Lets give ourselves 10 seconds before we actually start the program.
    // That will just give us a chance to open the serial monitor before the program sends the request
    for(int i=0;i<10;i++) {
        Serial.println("waiting " + String(10-i) + " seconds before we publish");
        delay(1000);
    }
}


// called forever really fast
void loop() {

    // Let's request the weather, but no more than once every 60 seconds.
    Serial.println("Requesting Weather!");

    // publish the event that will trigger our Webhook
    Particle.publish("FishersWeather");

    // and wait at least 60 seconds before doing it again
    delay(60000  );
}

// This function will get called when weather data comes in
void gotWeatherData(const char *name, const char *data) {
    // Important note!  -- Right now the response comes in 512 byte chunks.
    //  This code assumes we're getting the response in large chunks, and this
    //  assumption breaks down if a line happens to be split across response chunks.
    //
    // Sample data:
    //  <location>Minneapolis, Minneapolis-St. Paul International Airport, MN</location>
    //  <weather>Overcast</weather>
    //  <temperature_string>26.0 F (-3.3 C)</temperature_string>
    //  <temp_f>26.0</temp_f>


    String str = String(data);
    String weatheridStr = tryExtractString(str, "<WeatherID>", "</WeatherID>");
    String weatherstatusStr = tryExtractString(str, "<WeatherStatus>", "</WeatherStatus>");
    String weatherdiscriptionStr = tryExtractString(str, "<WeatherDescription>", "</WeatherDescription>");
    String tempStr = tryExtractString(str, "<Temp>", "</Temp>");
    String humidityStr = tryExtractString(str, "<Humidity>", "</Humidity>");
    String windspeedStr = tryExtractString(str, "<WindSpeed>", "</WindSpeed>");
    String winddirectionStr = tryExtractString(str, "<WindDirection>", "</WindDirection>");
    String cloudinessStr = tryExtractString(str, "<Cloudiness>", "</Cloudiness>");
    String sunriseStr = tryExtractString(str, "<Sunrise>", "</Sunrise>");
    String sunsetStr = tryExtractString(str, "<Sunset>", "</Sunset>");
    String cityStr = tryExtractString(str, "<City>", "</City>");
   
    

    if (weatheridStr != NULL) {
        Serial.println("Weather ID: " + weatheridStr);
    }

    if (weatherstatusStr != NULL) {
        Serial.println("Weather Status: " + weatherstatusStr);
    }

    if (weatherdiscriptionStr != NULL) {
        Serial.println("Weather Discription: " + weatherdiscriptionStr);
    }

    if (tempStr != NULL) {
        Serial.println("The Temp is: " + tempStr + String(" *F"));
    }
    
     if (humidityStr != NULL) {
        Serial.println("The Humidity is: " + humidityStr + String(" %"));
    }
    
     if (windspeedStr != NULL) {
        Serial.println("The Wind Speed is: " + windspeedStr + String(" MPH"));
    }
    
     if (winddirectionStr != NULL) {
        Serial.println("The Wind Direction is: " + winddirectionStr + String(" Degrees"));
    }
    
    if (cloudinessStr != NULL) {
        Serial.println("The Cloudcover is: " + cloudinessStr + String(" %"));
    }
    
     if (sunriseStr != NULL) {
        Serial.println("The Sun Will Rise at: " + sunriseStr + String(" UTC"));
    }
    
    if (sunsetStr != NULL) {
        Serial.println("The Sun Will Set at: " + sunsetStr + String(" UTC"));
    }
    
     if (cityStr != NULL) {
        Serial.println("Weather Data for: " + cityStr );
        
        
    
        
        
        
        sunriseStr += timeZone*60*60;

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

	    hour = (epoch % 86400L) / 3600;         
	    minute = (epoch % 3600) / 60;
	    second = (epoch % 60);

	    Serial.print (hour);
	    Serial.print (":");
	    Serial.print (minute);
	    Serial.print (":");
	    Serial.println (second);
    }
    
}

// Returns any text found between a start and end string inside 'str'
// example: startfooend  -> returns foo
String tryExtractString(String str, const char* start, const char* end) {
    if (str == NULL) {
        return NULL;
    }

    int idx = str.indexOf(start);
    if (idx < 0) {
        return NULL;
    }

    int endIdx = str.indexOf(end);
    if (endIdx < 0) {
        return NULL;
    }

    return str.substring(idx + strlen(start), endIdx);
}

How would experts like you go about converting the code so that it works? :doge:

The NIST “Daytime” protocol to get date/time is far simpler than NTP, for an embedded device.
Plain text. Just send a UDP packet to the right IP address on port 13. That’s it. Back comes ASCII text - and I’ve found that the format doesn’t change among servers you may choose.
https://tools.ietf.org/html/rfc867

For years, NIST keeps talking about shutting this off but not yet.

@RWB, there we are again :wink:
If you could post the actual string you want to parse it makes it a lot easier, otherwise we’d need to know all possible return formats of all possible sources or need to acquire them to see what we are talking about.

But generally speaking, I’d guess that we are back at strtok() or sscanf() again if we don’t want to rely on fix character positions (which would be option three :stuck_out_tongue_closed_eyes:)

@stevech, I guess the problem is, that the webhook returns a time string that needs to be parsed (which doesn’t quite fit this thread’s topic tho’)

@ScruffR Ahh your right :smile: I thought I provide everything important but I didn’t.

So here is the formatted webhook response I am parsing to get the Sunrise & Sunset times in UTC format:

{"data":"\"responseTemplate\": \"<WeatherID>800</WeatherID><WeatherStatus>Clear</WeatherStatus><WeatherDescription>clear sky</WeatherDescription><Temp>62.4</Temp><Humidity>62</Humidity><WindSpeed>2.71</WindSpeed><WindDirection>256.506</WindDirection><Cloudiness>1</Cloudiness><Sunrise>1461063560</Sunrise><Sunset>1461112036</Sunset><City>Fishers</City>\",","ttl":"60","published_at":"2016-04-19T04:50:39.010Z","coreid":"1a002d000347343339373536","name":"hook-response/FishersWeather/0"}  

I’m parsing the UTC Sunrise time using this code:

 String sunriseStr = tryExtractString(str, "<Sunrise>", "</Sunrise>");

And I end up serial printing all the data like this:

Now my logic tells me I should be able to just save the SunriseStr contents to another variable that is in the format that does not cause the following const char* to long unsigned int compile error.

I’m not 100% sure what is going on but I’m pretty sure that the Char points to a place in memory pointer and the unsigned int is pointing to actual data. Am I close on that concept?

I see, and you’re in luck an can dodge strtok() this time :wink:
You could do this

uint utcTime = sunriseStr.toInt();
Serial.print(Time.format(utcTime, TIME_FORMAT_ISO8601_FULL));
1 Like

@ScruffR

ScruffR your code works for getting the string data into a usable unsigned Int format. Great! You da Man :smile:

I looked into the Particle Firmware Docs Time section and I see how I can now format the UTC time output by using these functions: https://docs.particle.io/reference/firmware/photon/#time

So I changed the data output to this which is easier for me to read. Now that I know I can change the layout I’ll work with it some more to get the times to display in AM & PM formatting.

For now I have the times being printed like this:

The sunrise and set times are pretty accurate with other data services :wink:

Here is how I formatted the code if anybody else is interested.

    String sunriseStr = tryExtractString(str, "<Sunrise>", "</Sunrise>");
    String sunsetStr = tryExtractString(str, "<Sunset>", "</Sunset>");
    String cityStr = tryExtractString(str, "<City>", "</City>");
   
    uint SunriseTime = sunriseStr.toInt();
    uint SunsetTime = sunsetStr.toInt();
    Time.zone(-4);

And then I serial print using this code:

 if (sunriseStr != NULL) {
        Serial.println("The Sun Will Rise at: " + (Time.format(SunriseTime, TIME_FORMAT_DEFAULT)));
    }
    
    if (sunsetStr != NULL) {
        Serial.println("The Sun Will Set at: " + (Time.format(SunsetTime, TIME_FORMAT_DEFAULT)));
    }

I tried using @peekay123 's code above but it would only return the correct time if I set the time zone to -10 when my real time zone setting is -4 so I’m not sure what was going on with that.

Thanks again @ScruffR :+1:

1 Like