Hi ZeroSix,
I have have had some similar challenges in my system operating on AWS IOT MQTT. I had thought that it was due to the modem not enjoying being put to sleep then woken up again and asked to do thing straight away. My system has two power states - one that sends every 1 minute simply using delay to wait between sends, then a low power mode running off of the battery, where the modem is shut down and the processor put to sleep, that sends every 20 minutes. My low power mode lasts 30 days on a 3400mAh 18650.
If you would like to increase your battery life, call nbAccess.shutdown() before sleep, and nbAccess.begin() after, here is the relevant portion in my code:
nbAccess.shutdown();
Serial.println("Modem Shutdown");
for (int i = 0; i < 12; i++) {
Watchdog.clear(); //reset watchdog before it loops
LowPower.deepSleep(30000); // wait 5 minutes
}
Serial.println("Awake!");
//boot and setup the modem
nbAccess.begin();
In the low power mode, after waking up from sleep and turning the modem back on I more often than not get a successful cellular connection, and then the same failure to retrieve time from the NTP server over UDP that you describe above.
I originally addressed this by just forcing a watchdog reset upon waking at the end of the 20 minutes, thereby restarting the program from scratch. This proved very reliable in connecting successfully every time. This approach may be something that works for you.
However, I require IOs to be persistently held at certain states, which is lost upon the reset, so I am trying to solve this problem directly.
My initial thoughts were that even though the modem is able to report a successful connection to the network, there is still some background tasks that prevent it from functioning properly. However in your code you never put the modem to sleep so I'm beginning to doubt that this is the case.
Just today, I've added Udp.stop() to the end of my epoch retrieval, that releases all resources. Then after my system awakes I begin the Udp.begin(localPort) again afresh. This tentatively appears to have removed the time retrieval failures and subsequent MQTT failures, but more testing is needed. Here are my Time functions:
unsigned long refreshTime() {
sendNTPpacket(timeServer); // send an NTP packet to a time server
// wait to see if a reply is available
delay(3000);
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, 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;
Serial.println("Time successfully retrieved!");
Serial.println("Time successfully retrieved!");
Serial.println("Time successfully retrieved!");
Serial.println(epoch);
Udp.stop();
Serial.println("UDP Closed");
return epoch;
} else {
unsigned long epoch = getTime();
Serial.println("Time Failure!");
Serial.println(epoch);
Udp.stop();
Serial.println("UDP Closed");
return epoch;
}
}
unsigned long sendNTPpacket(IPAddress& address) {
//Serial.println("1");
// 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)
//Serial.println("2");
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;
//Serial.println("3");
// 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
//Serial.println("4");
Udp.write(packetBuffer, NTP_PACKET_SIZE);
//Serial.println("5");
Udp.endPacket();
//Serial.println("6");
}
Hope that's all helpful to you and there's a couple of solutions for you to try.
Best,
Tom