Skip to content

Commit

Permalink
Reverted to using NTP for network time. The mobile network time is no…
Browse files Browse the repository at this point in the history
…t reliable.
  • Loading branch information
dajtxx committed Oct 24, 2023
1 parent 37de365 commit 7b5b8c0
Show file tree
Hide file tree
Showing 4 changed files with 28 additions and 127 deletions.
2 changes: 1 addition & 1 deletion firmware/wombat/src/CAT_M1.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ bool CAT_M1::make_ready() {
r5.autoTimeZoneForBegin(true);

r5.enableAtDebugging();
r5.enableDebugging();
//r5.enableDebugging();

// This is relatively benign - it enables the network indicator GPIO pin, set error message format, etc.
// It does close all open sockets, but there should not be any open sockets at this point so that is ok.
Expand Down
129 changes: 13 additions & 116 deletions firmware/wombat/src/Utils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

#define TAG "utils"

bool getNTPTime(SARA_R5 &r5);

/**
* @brief Returns a pointer to a string representation of value, with trailing zeros removed.
Expand Down Expand Up @@ -348,86 +349,10 @@ bool wait_for_at(void) {
return false;
}

/**
* Attempt to get the current time from the modem. Times reported before 2023 from the modem are assumed to be in
* error because this firmware went into production in 2023.
*
* @param tv on return, tv.tv_sec contains a UNIX style epoch value calculated from the local time provided by the modem.
* @return true if the time from the modem is valid, otherwise false.
*/
bool get_network_time(struct timeval & tv) {
int retries = 3;
constexpr size_t time_buffer_sz = 20;
char time_buffer[time_buffer_sz + 1];
// Try a few times with a pause. The modem doesn't always get the network time quickly enough for an immediate
// query.
while (retries > 0) {
// Get the time from the R5 modem. This is given in local time with the
// timezone given as the number of 15 minute offsets from UTC, eg +40 for AEST
// and +44 for AEDT.
// Example return from r5.clock(): 23/02/13,08:07:57+44
memset(time_buffer, 0, sizeof(time_buffer_sz));
String time_str = r5.clock();
delay(20);
strncpy(time_buffer, time_str.c_str(), time_buffer_sz);
time_buffer[time_buffer_sz] = 0;

ESP_LOGI(TAG, "Time from modem: %s", time_buffer);
log_to_sdcardf("Time from modem: %s", time_buffer);

int tz;
int year;
struct tm tm{};

int field_count = sscanf(time_buffer, "%2d/%2d/%2d,%d:%d:%d%d", &year, &tm.tm_mon, &tm.tm_mday, &tm.tm_hour, &tm.tm_min, &tm.tm_sec, &tz);
// The firmware was put into production in 2023, so earlier times must be invalid. This is to capture the modem
// default value of '15/01/01,00:00:09+00'.
if (field_count == 7 && year > 22) {
// Parse the response into a tm structure which will be used to convert to an epoch value.
// Do not take account of the timezone or daylight savings. This means we'll be creating an
// epoch time using the local time, but pretending it is UTC. The timezone offset is calculated
// and applied later to make it true UTC.
tm.tm_year = year + 100; // tm.tm_year is years since 1900, and modem gives 2-digit year since 2000.
tm.tm_isdst = 0; // No daylight savings.

ESP_LOGI(TAG, "Network time: %02d/%02d/%04d %02d:%02d:%02d %02d", tm.tm_mday, tm.tm_mon, tm.tm_year + 1900,
tm.tm_hour, tm.tm_min, tm.tm_sec, tz);

tm.tm_mon -= 1; // tm.tm_month is months since January, so from 0 - 11.

// No microseconds in the calculated time.
tv.tv_usec = 0;

// Get the epoch value by converting the values in the tm structure. This will make tv.tv_sec equal to
// the current local time, but assume it is UTC. So giving it the tm generated from the response 23/02/13,08:07:57+44
// means tv.tv_sec epoch will represent 23/02/13,08:07:57 UTC.
tv.tv_sec = mktime(&tm);

// Calculate an adjustment for the epoch value based upon the timezone information supplied
// by the modem. The modem reports timezone offsets in units of 15 minutes.
int dst_offset_secs = tz * 15 * -1 * 60;
tv.tv_sec += dst_offset_secs;
ESP_LOGI(TAG, "1970 epoch calculated from modem time: %ld", tv.tv_sec);

return true;
}

ESP_LOGE(TAG, "Time too early or could not parse, wait and retry");
log_to_sdcard("[E] Time too early or could not parse, wait and retry");

delay(1000);
retries--;
}

ESP_LOGE(TAG, "Failed to parse time from modem");
log_to_sdcard("[E] Failed to parse time from modem");
return false;
}

/**
* @brief Get the R5 modem connected to the internet.
*
* This function sets the ESP32 RTC from the network time.
* This function sets the ESP32 and modem RTCs from the network time using NTP.
*
* If this function returns true, the modem has registered to the network, brought up a
* packet switched profile, and obtained an IP address. MQTT, FTP, etc can be used.
Expand Down Expand Up @@ -510,36 +435,8 @@ bool connect_to_internet(void) {
setenv("TZ", "UTC", 1);
tzset();

ESP_LOGI(TAG, "RTC time now: %s", iso8601());
log_to_sdcardf("RTC time now: %s", iso8601_buf); // Look out, this is a side-effect of calling iso8601.

bool time_set = false;
struct timeval tv{};

if (get_network_time(tv)) {
// tv.tv_sec is a time_t so difftime can be used to compare it with 'now' and get the difference in seconds.
// If the time from the ESP32 RTC is later than the time from the modem then timedelta will be
// negative.
//
// If the modem appears to be up to 10 seconds behind the RTC, assume clock drift is making the RTC run fast
// and use the modem time. If the modem appears to be in the future, also use the modem time assuming either
// the RTC is still in 1970 after booting or it is running slow.
time_t now = time(nullptr);
double timedelta = difftime(tv.tv_sec, now);
ESP_LOGI(TAG, "timedelta = %f", timedelta);
if (timedelta > -10.0) {
// Now set the system clock.
settimeofday(&tv, nullptr);
time_set = true;
} else {
ESP_LOGW(TAG, "modem time greater than 10 seconds in the past");
}
}

if ( ! time_set) {
ESP_LOGI(TAG, "Assuming bad time from modem, not setting RTC");
log_to_sdcard("Assuming bad time from modem, not setting RTC");
}
ESP_LOGI(TAG, "RTC time before ntp: %s", iso8601());
log_to_sdcardf("RTC time before ntp: %s", iso8601_buf); // Look out, this is a side-effect of calling iso8601.

log_to_sdcard("Starting PDP");

Expand All @@ -556,17 +453,17 @@ bool connect_to_internet(void) {
delay(20);
r5.bufferedPoll();

ESP_LOGI(TAG, "Attempting NTP query");
if ( ! getNTPTime(r5)) {
ESP_LOGW(TAG, "NTP query failed");
log_to_sdcard("NTP query failed");
}

ESP_LOGI(TAG, "RTC time after ntp: %s", iso8601());
log_to_sdcardf("RTC time after ntp: %s", iso8601_buf); // Look out, this is a side-effect of calling iso8601.

log_to_sdcard("cti returning");

already_called = true;
return true;
}

int get_version_string(char *buffer, size_t length) {
if (buffer == nullptr) {
return -1;
}

int i = snprintf(buffer, length, "%u.%u.%u", ver_major, ver_minor, ver_update);
return i;
}
12 changes: 12 additions & 0 deletions firmware/wombat/src/cli/peripherals/cat-m1.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
#include "globals.h"
#include "cli/CLI.h"

bool getNTPTime(SARA_R5 &r5);

//! Sparkfun SARA-R5 library instance
extern SARA_R5 r5;
//! Holds responses from the modem
Expand Down Expand Up @@ -228,6 +230,16 @@ static int get_response(uint32_t timeout = 500) {
return pdFALSE;
}

if (!strncmp("ntp", param, paramLen)) {
bool rc = false;
if (connect_to_internet()) {
rc = getNTPTime(r5);
}

snprintf(pcWriteBuffer, xWriteBufferLen-1, "\r\n%s\r\n", rc ? "OK" : "ERROR");
return pdFALSE;
}

if (!strncmp("ok", param, paramLen)) {
LTE_Serial.print("ATI\r");
int ch = waitForChar(LTE_Serial, 250);
Expand Down
12 changes: 2 additions & 10 deletions firmware/wombat/src/ntp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,20 +30,12 @@ bool getNTPTime(SARA_R5 &r5) {
// set all bytes in the buffer to 0
memset(packetBuffer, 0, NTP_PACKET_SIZE);

// Initialize values needed to form NTP request
// Initialize values needed to form NTP request. The client request only needs to set version numbers & mode.
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;

//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=

//Allocate a UDP socket to talk to the NTP server
// Allocate a UDP socket to talk to the NTP server

int socketNum = r5.socketOpen(SARA_R5_UDP);
if (socketNum == -1) {
Expand Down

0 comments on commit 7b5b8c0

Please sign in to comment.