From eaa8bdf47e79ca29d29dd4e148e42b86b7590a8a Mon Sep 17 00:00:00 2001 From: cyberman54 Date: Wed, 8 Mar 2023 15:53:30 +0100 Subject: [PATCH 1/7] wakeup sync (experimental) --- docs/configuration/index.md | 4 ++++ include/reset.h | 2 +- src/reset.cpp | 17 ++++++++++++++++- 3 files changed, 21 insertions(+), 2 deletions(-) diff --git a/docs/configuration/index.md b/docs/configuration/index.md index e1a22249..557a3e8a 100644 --- a/docs/configuration/index.md +++ b/docs/configuration/index.md @@ -88,6 +88,10 @@ Supported external time sources are GPS, LORAWAN network time and LORAWAN applic If your LORAWAN network does not support network time, you can run a Node-Red timeserver application using the enclosed [**Timeserver code**](https://github.com/cyberman54/ESP32-Paxcounter/blob/master/src/Node-RED/Timeserver.json). Configure the MQTT nodes in Node-Red for the LORAWAN application used by your paxocunter device. Time can also be set without precision liability, by simple remote command, see section remote control. +## Syncing multiple paxcounters + +A fleet of paxcounters can be synchronized to keep all devices wake up and start scanning at the same time. Synchronization is based on top-of-hour as common time point of reference. This feature requires time-of-day to be present on each device. Thus, `TIME_SYNC_INTERVAL` option, as explained above, must be enabled. Wake up syncing is enabled by setting `SYNCWAKEUP` in `paxcounter.conf` to a value X, greater than zero, and smaller than `SLEEPCYCLE`. This defines a time window, centered at top-of-hour, sized +/- X seconds. If a device, returning from sleep, would wakeup within this time windows, the wakeup will be adjusted to top-of-hour. + ## Wall clock controller Paxcounter can be used to sync a wall clock which has a DCF77 or IF482 time telegram input. Set `#define HAS_IF482` or `#define HAS_DCF77` in board's hal file to setup clock controller. Use case of this function is to integrate paxcounter and clock. Accurary of the synthetic DCF77 signal depends on accuracy of on board's time base, see above. diff --git a/include/reset.h b/include/reset.h index c82ede9f..f16c1c39 100644 --- a/include/reset.h +++ b/include/reset.h @@ -14,7 +14,7 @@ void reset_rtc_vars(void); void do_reset(bool warmstart); void do_after_reset(void); -void enter_deepsleep(const uint32_t wakeup_sec, const gpio_num_t wakeup_gpio); +void enter_deepsleep(uint32_t wakeup_sec, const gpio_num_t wakeup_gpio); unsigned long long uptime(void); enum runmode_t { diff --git a/src/reset.cpp b/src/reset.cpp index 0796b6db..9fda97fe 100644 --- a/src/reset.cpp +++ b/src/reset.cpp @@ -81,7 +81,7 @@ void do_after_reset(void) { } } -void enter_deepsleep(const uint32_t wakeup_sec, gpio_num_t wakeup_gpio) { +void enter_deepsleep(uint32_t wakeup_sec, gpio_num_t wakeup_gpio) { ESP_LOGI(TAG, "Preparing to sleep..."); RTC_runmode = RUNMODE_SLEEP; @@ -146,6 +146,21 @@ void enter_deepsleep(const uint32_t wakeup_sec, gpio_num_t wakeup_gpio) { // configure wakeup sources // https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/system/sleep_modes.html +#ifdef TIME_SYNC_INTERVAL +#if (SYNCWAKEUP) && (SLEEPCYCLE) + time_t now; + uint16_t shift_sec; + time(&now); + shift_sec = 3600 - (now + wakeup_sec) % + 3600; // 1..3600 remaining seconds between planned + // wakeup time and following top-of-hour + if (shift_sec <= SYNCWAKEUP) // delay wakeup to catch top-of-hour + wakeup_sec += shift_sec; + else if (shift_sec >= (3600 - SYNCWAKEUP)) + wakeup_sec = 3600 - shift_sec; // shorten wake up to next top-of-hour +#endif +#endif + // set up RTC wakeup timer, if we have if (wakeup_sec > 0) { esp_sleep_enable_timer_wakeup(wakeup_sec * uS_TO_S_FACTOR); From c703d99ec1c6cba1878a03b68f7a04167a3f1ec4 Mon Sep 17 00:00:00 2001 From: cyberman54 Date: Wed, 8 Mar 2023 16:03:39 +0100 Subject: [PATCH 2/7] correct typos --- docs/configuration/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/configuration/index.md b/docs/configuration/index.md index 557a3e8a..fa1e181e 100644 --- a/docs/configuration/index.md +++ b/docs/configuration/index.md @@ -90,7 +90,7 @@ Supported external time sources are GPS, LORAWAN network time and LORAWAN applic ## Syncing multiple paxcounters -A fleet of paxcounters can be synchronized to keep all devices wake up and start scanning at the same time. Synchronization is based on top-of-hour as common time point of reference. This feature requires time-of-day to be present on each device. Thus, `TIME_SYNC_INTERVAL` option, as explained above, must be enabled. Wake up syncing is enabled by setting `SYNCWAKEUP` in `paxcounter.conf` to a value X, greater than zero, and smaller than `SLEEPCYCLE`. This defines a time window, centered at top-of-hour, sized +/- X seconds. If a device, returning from sleep, would wakeup within this time windows, the wakeup will be adjusted to top-of-hour. +A fleet of paxcounters can be synchronized to keep all devices wake up and start scanning at the same time. Synchronization is based on top-of-hour as common time point of reference. This feature requires time-of-day to be present on each device. Thus, `TIME_SYNC_INTERVAL` option, as explained above, must be enabled. Wake up syncing is enabled by setting `SYNCWAKEUP` in `paxcounter.conf` to a value X, greater than zero, and smaller than `SLEEPCYCLE`. This defines a time window, centered at top-of-hour, sized +/- X seconds. If a device, returning from sleep, would wakeup within this time window, it's wakeup will be adjusted to top-of-hour. ## Wall clock controller From f488c5a3e7fa55529f673bca76612e2d5b8a5aaa Mon Sep 17 00:00:00 2001 From: cyberman54 Date: Wed, 8 Mar 2023 16:28:41 +0100 Subject: [PATCH 3/7] update paxcounter.conf --- src/paxcounter_orig.conf | 1 + 1 file changed, 1 insertion(+) diff --git a/src/paxcounter_orig.conf b/src/paxcounter_orig.conf index 93c12161..01d8e53a 100644 --- a/src/paxcounter_orig.conf +++ b/src/paxcounter_orig.conf @@ -20,6 +20,7 @@ #define SLEEPCYCLE 0 // sleep time after a send cycle [seconds/10], 0 .. 65535; 0 means no sleep [default = 0] #define PAYLOAD_ENCODER 2 // payload encoder: 1=Plain, 2=Packed, 3=Cayenne LPP dynamic, 4=Cayenne LPP packed #define COUNTERMODE 0 // 0=cyclic, 1=cumulative, 2=cyclic confirmed +#define SYNCWAKEUP 300 // shifts sleep wakeup to top-of-hour, when +/- X seconds off [0=off] // default settings for transmission of sensor data (first list = data on / second line = data off) #define PAYLOADMASK \ From cc03d504064ff91456c90d303ce4f75130b330c1 Mon Sep 17 00:00:00 2001 From: cyberman54 Date: Sat, 11 Mar 2023 15:13:19 +0100 Subject: [PATCH 4/7] bugfix newline in syslog after sleep --- src/sdcard.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sdcard.cpp b/src/sdcard.cpp index d21629b0..4cb17e4b 100644 --- a/src/sdcard.cpp +++ b/src/sdcard.cpp @@ -174,8 +174,8 @@ bool sdcard_init(bool create) { #if (HAS_SDS011) fprintf(data_file, "%s", SDCARD_FILE_HEADER_SDS011); #endif + fprintf(data_file, "\n"); } - fprintf(data_file, "\n"); } else { useSDCard = false; From d626f8675464383825d11238da1161969f6bb7ff Mon Sep 17 00:00:00 2001 From: cyberman54 Date: Sat, 11 Mar 2023 17:17:55 +0100 Subject: [PATCH 5/7] fix show sleep time & show syncwakeup --- include/timekeeper.h | 1 + src/reset.cpp | 31 +++++++++++++++++++------------ 2 files changed, 20 insertions(+), 12 deletions(-) diff --git a/include/timekeeper.h b/include/timekeeper.h index aa13d5ea..e4bbb614 100644 --- a/include/timekeeper.h +++ b/include/timekeeper.h @@ -12,6 +12,7 @@ #define HAS_LORA_TIME \ ((HAS_LORA) && ((TIME_SYNC_LORASERVER) || (TIME_SYNC_LORAWAN))) +#define HAS_TIME (TIME_SYNC_INTERVAL) && (HAS_LORA_TIME || HAS_GPS) #define SECS_YR_2000 (946684800UL) // the time at the start of y2k #define GPS_UTC_DIFF 315964800UL // seconds diff between gps and utc epoch diff --git a/src/reset.cpp b/src/reset.cpp index 9fda97fe..da405e61 100644 --- a/src/reset.cpp +++ b/src/reset.cpp @@ -66,7 +66,7 @@ void do_after_reset(void) { (sleep_stop_time.tv_sec - RTC_sleep_start_time.tv_sec) * 1000 + (sleep_stop_time.tv_usec - RTC_sleep_start_time.tv_usec) / 1000; RTC_millis += sleep_time_ms; // increment system monotonic time - ESP_LOGI(TAG, "Time spent in deep sleep: %d ms", sleep_time_ms); + ESP_LOGI(TAG, "Time spent in deep sleep: %llu ms", sleep_time_ms); // do we have a valid time? -> set global variable timeSource = timeIsValid(sleep_stop_time.tv_sec) ? _set : _unsynced; // set wakeup state, not if we have pending OTA update @@ -146,18 +146,25 @@ void enter_deepsleep(uint32_t wakeup_sec, gpio_num_t wakeup_gpio) { // configure wakeup sources // https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/system/sleep_modes.html -#ifdef TIME_SYNC_INTERVAL +#if (HAS_TIME) #if (SYNCWAKEUP) && (SLEEPCYCLE) - time_t now; - uint16_t shift_sec; - time(&now); - shift_sec = 3600 - (now + wakeup_sec) % - 3600; // 1..3600 remaining seconds between planned - // wakeup time and following top-of-hour - if (shift_sec <= SYNCWAKEUP) // delay wakeup to catch top-of-hour - wakeup_sec += shift_sec; - else if (shift_sec >= (3600 - SYNCWAKEUP)) - wakeup_sec = 3600 - shift_sec; // shorten wake up to next top-of-hour + if ((timeSource != _unsynced) && + (sntp_get_sync_status() != + SNTP_SYNC_STATUS_IN_PROGRESS)) { // only sync if we have a valid time + time_t now; + time(&now); + + // 1..3600 seconds between next wakeup time and following top-of-hour + uint16_t shift_sec = 3600 - (now + wakeup_sec) % 3600; + + if (shift_sec <= SYNCWAKEUP) { + wakeup_sec += shift_sec; // delay wakeup to catch top-of-hour + ESP_LOGI(TAG, "Syncwakeup: Wakeup %hu sec postponed", shift_sec); + } else if (shift_sec >= (3600 - SYNCWAKEUP)) { + wakeup_sec = 3600 - shift_sec; // shorten wake up to next top-of-hour + ESP_LOGI(TAG, "Syncwakeup: Wakeup %hu sec preponed", shift_sec); + } + } #endif #endif From 3b6ecf7b36ab7742f216e5fb59a37b0b05d2cd3f Mon Sep 17 00:00:00 2001 From: cyberman54 Date: Sun, 12 Mar 2023 00:31:55 +0100 Subject: [PATCH 6/7] reset.cpp/.h code sanitizations --- include/reset.h | 2 +- src/reset.cpp | 28 +++++++++++++++------------- 2 files changed, 16 insertions(+), 14 deletions(-) diff --git a/include/reset.h b/include/reset.h index f16c1c39..05d18ab3 100644 --- a/include/reset.h +++ b/include/reset.h @@ -15,7 +15,7 @@ void reset_rtc_vars(void); void do_reset(bool warmstart); void do_after_reset(void); void enter_deepsleep(uint32_t wakeup_sec, const gpio_num_t wakeup_gpio); -unsigned long long uptime(void); +uint64_t uptime(void); enum runmode_t { RUNMODE_POWERCYCLE, diff --git a/src/reset.cpp b/src/reset.cpp index da405e61..e889506b 100644 --- a/src/reset.cpp +++ b/src/reset.cpp @@ -5,14 +5,14 @@ // Conversion factor for micro seconds to seconds #define uS_TO_S_FACTOR 1000000ULL -// RTC_NOINIT_ATTR -> keep value after a software restart or system crash +// RTC_NOINIT_ATTR -> keeps value after a software restart or system crash RTC_NOINIT_ATTR runmode_t RTC_runmode; RTC_NOINIT_ATTR uint32_t RTC_restarts; -// RTC_DATA_ATTR -> keep values after a wakeup from sleep -RTC_DATA_ATTR struct timeval RTC_sleep_start_time; -RTC_DATA_ATTR unsigned long long RTC_millis = 0; +// RTC_DATA_ATTR -> keeps value after a wakeup from sleep +RTC_DATA_ATTR struct timeval sleep_start_time; +RTC_DATA_ATTR int64_t RTC_millis = 0; -timeval sleep_stop_time; +struct timeval sleep_stop_time; void reset_rtc_vars(void) { RTC_runmode = RUNMODE_POWERCYCLE; @@ -36,7 +36,7 @@ void do_reset(bool warmstart) { void do_after_reset(void) { struct timeval sleep_stop_time; - uint64_t sleep_time_ms; + int64_t sleep_time_ms; // read (and initialize on first run) runtime settings from NVRAM loadConfig(); @@ -62,9 +62,11 @@ void do_after_reset(void) { case RESET_REASON_CORE_DEEP_SLEEP: // calculate time spent in deep sleep gettimeofday(&sleep_stop_time, NULL); - sleep_time_ms = - (sleep_stop_time.tv_sec - RTC_sleep_start_time.tv_sec) * 1000 + - (sleep_stop_time.tv_usec - RTC_sleep_start_time.tv_usec) / 1000; + sleep_time_ms = ((int64_t)sleep_stop_time.tv_sec * 1000000L + + (int64_t)sleep_stop_time.tv_usec - + (int64_t)sleep_start_time.tv_sec * 1000000L - + (int64_t)sleep_start_time.tv_usec) / + 1000LL; RTC_millis += sleep_time_ms; // increment system monotonic time ESP_LOGI(TAG, "Time spent in deep sleep: %llu ms", sleep_time_ms); // do we have a valid time? -> set global variable @@ -180,8 +182,8 @@ void enter_deepsleep(uint32_t wakeup_sec, gpio_num_t wakeup_gpio) { } // time stamp sleep start time and save system monotonic time. Deep sleep. - gettimeofday(&RTC_sleep_start_time, NULL); - RTC_millis += esp_timer_get_time() / 1000; + gettimeofday(&sleep_start_time, NULL); + RTC_millis += esp_timer_get_time() / 1000LL; ESP_LOGI(TAG, "Going to sleep, good bye."); // flush & close sd card, if we have @@ -192,6 +194,6 @@ void enter_deepsleep(uint32_t wakeup_sec, gpio_num_t wakeup_gpio) { esp_deep_sleep_start(); } -unsigned long long uptime() { - return (RTC_millis + esp_timer_get_time() / 1000); +uint64_t uptime() { + return (uint64_t)(RTC_millis + esp_timer_get_time() / 1000LL); } \ No newline at end of file From e14f515cd5e77e1438f8ceb50695f441a6e8b53d Mon Sep 17 00:00:00 2001 From: cyberman54 Date: Sun, 12 Mar 2023 00:37:50 +0100 Subject: [PATCH 7/7] reset.cpp code sanitizations --- src/reset.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/reset.cpp b/src/reset.cpp index e889506b..13ec8afc 100644 --- a/src/reset.cpp +++ b/src/reset.cpp @@ -196,4 +196,4 @@ void enter_deepsleep(uint32_t wakeup_sec, gpio_num_t wakeup_gpio) { uint64_t uptime() { return (uint64_t)(RTC_millis + esp_timer_get_time() / 1000LL); -} \ No newline at end of file +}