Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

sntp: fix the handling of the rtc counter wrapping #148

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
5 changes: 2 additions & 3 deletions extras/sntp/sntp.h
Original file line number Diff line number Diff line change
Expand Up @@ -61,10 +61,9 @@ int sntp_set_servers(char *server_url[], int num_servers);
void sntp_set_update_delay(uint32_t ms);

/*
* Returns the time read from RTC counter, in seconds from Epoch. If
* us is not null, it will be filled with the microseconds.
* Returns the time read from RTC counter, in micro seconds from Epoch.
*/
time_t sntp_get_rtc_time(int32_t *us);
uint64_t sntp_get_rtc_time();

/*
* Update RTC timer. This function is called by the SNTP module each time
Expand Down
128 changes: 66 additions & 62 deletions extras/sntp/sntp_fun.c
Original file line number Diff line number Diff line change
Expand Up @@ -11,18 +11,23 @@
#include <espressif/esp_common.h>
#include <esp/timer.h>
#include <esp/rtc_regs.h>
#include "FreeRTOS.h"
#include "semphr.h"
#include "sntp.h"

#define TIMER_COUNT RTC.COUNTER

// daylight settings
// Base calculated with value obtained from NTP server (64 bits)
#define sntp_base (*((uint64_t*)RTC.SCRATCH))
// Timer value when base was obtained
// Timer value when sntp_base was obtained
#define tim_ref (RTC.SCRATCH[2])
// Calibration value
#define cal (RTC.SCRATCH[3])

// To protect access to the above.
static xSemaphoreHandle sntp_sem = NULL;

// Timezone related data.
static struct timezone stz;

Expand All @@ -32,81 +37,80 @@ void sntp_init(void);
// Sets time zone.
// NOTE: Settings do not take effect until SNTP time is updated.
void sntp_set_timezone(const struct timezone *tz) {
if (tz) {
stz = *tz;
} else {
stz.tz_minuteswest = 0;
stz.tz_dsttime = 0;
}
if (tz) {
stz = *tz;
} else {
stz.tz_minuteswest = 0;
stz.tz_dsttime = 0;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yay, removing of tabs! :D

}

// Initialization
void sntp_initialize(const struct timezone *tz) {
if (tz) {
stz = *tz;
} else {
stz.tz_minuteswest = 0;
stz.tz_dsttime = 0;
}
sntp_base = 0;
// To avoid div by 0 exceptions if requesting time before SNTP config
cal = 1;
tim_ref = TIMER_COUNT;
sntp_init();
}

// Check if a timer wrap has occurred. Compensate sntp_base reference
// if affirmative.
// TODO: think about multitasking and race conditions
static inline void sntp_check_timer_wrap(uint32_t current_value) {
if (current_value < tim_ref) {
// Timer wrap has occurred, compensate by subtracting 2^32 to ref.
sntp_base -= 1LLU<<32;
// DEBUG
printf("\nTIMER WRAPPED!\n");
}
if (tz) {
stz = *tz;
} else {
stz.tz_minuteswest = 0;
stz.tz_dsttime = 0;
}
sntp_base = 0;
// To avoid div by 0 exceptions if requesting time before SNTP config
cal = 1;
tim_ref = TIMER_COUNT;
sntp_sem = xSemaphoreCreateMutex();
if (sntp_sem == NULL)
printf("sntp: mutex creation failed\n");
sntp_init();
}

// Return secs. If us is not a null pointer, fill it with usecs
inline time_t sntp_get_rtc_time(int32_t *us) {
time_t secs;
uint32_t tim;
uint64_t base;

tim = TIMER_COUNT;
// Check for timer wrap
sntp_check_timer_wrap(tim);
base = sntp_base + tim - tim_ref;
secs = base * cal / (1000000U<<12);
if (us) {
*us = base * cal % (1000000U<<12);
}
return secs;
// Return usecs.
inline uint64_t sntp_get_rtc_time() {
xSemaphoreTake(sntp_sem, portMAX_DELAY);
uint32_t tim = TIMER_COUNT;
// Assume the difference does not overflow in which case
// wrapping of the RTC timer still yields a good difference.
uint32_t diff = tim - tim_ref;
tim_ref = tim;
uint64_t diff_us = ((uint64_t)diff * cal) >> 12;
uint64_t base = sntp_base + diff_us;
sntp_base = base;
xSemaphoreGive(sntp_sem);

return base;
}

// Syscall implementation. doesn't seem to use tzp.
int _gettimeofday_r(struct _reent *r, struct timeval *tp, void *tzp) {
(void)r;
// Syscall defined by xtensa newlib defines tzp as void*
// So it looks like it is not used. Also check tp is not NULL
if (tzp || !tp) return EINVAL;
(void)r;
// Syscall defined by xtensa newlib defines tzp as void*
// So it looks like it is not used. Also check tp is not NULL
if (tzp || !tp) return EINVAL;

tp->tv_sec = sntp_get_rtc_time((int32_t*)&tp->tv_usec);
return 0;
uint64_t base = sntp_get_rtc_time();

tp->tv_sec = base / 1000000U;
tp->tv_usec = base % 1000000U;
return 0;
}

// Update RTC timer. Called by SNTP module each time it receives an update.
void sntp_update_rtc(time_t t, uint32_t us) {
// Apply daylight and timezone correction
t += (stz.tz_minuteswest + stz.tz_dsttime * 60) * 60;
// DEBUG: Compute and print drift
int64_t sntp_current = sntp_base + TIMER_COUNT - tim_ref;
int64_t sntp_correct = (((uint64_t)us + (uint64_t)t * 1000000U)<<12) / cal;
printf("\nRTC Adjust: drift = %ld ticks, cal = %d\n", (time_t)(sntp_correct - sntp_current), cal);

tim_ref = TIMER_COUNT;
cal = sdk_system_rtc_clock_cali_proc();

sntp_base = (((uint64_t)us + (uint64_t)t * 1000000U)<<12) / cal;
// Apply daylight and timezone correction
t += (stz.tz_minuteswest + stz.tz_dsttime * 60) * 60;
int64_t sntp_correct = (uint64_t)us + (uint64_t)t * 1000000U;

xSemaphoreTake(sntp_sem, portMAX_DELAY);
uint32_t tim = TIMER_COUNT;
// Assume the difference does not overflow in which case
// wrapping of the RTC timer still yields a good difference.
uint32_t diff = tim - tim_ref;
tim_ref = tim;
uint64_t diff_us = ((uint64_t)diff * cal) >> 12;
uint64_t sntp_current = sntp_base + diff_us;
sntp_base = sntp_correct;
cal = sdk_system_rtc_clock_cali_proc();
xSemaphoreGive(sntp_sem);

printf("\nRTC Adjust: drift = %d usec, cal = %d\n", (int)(sntp_correct - sntp_current), cal);
}