diff --git a/BresserWeatherSensorTTN.ino b/BresserWeatherSensorTTN.ino index d7b4cb7..eb3e88e 100644 --- a/BresserWeatherSensorTTN.ino +++ b/BresserWeatherSensorTTN.ino @@ -160,11 +160,8 @@ #include "logging.h" #ifdef ARDUINO_ARCH_RP2040 - //#include "pico.h" #include "src/pico_rtc/pico_rtc_utils.h" - #include "hardware/rtc.h" - #include "hardware/structs/vreg_and_chip_reset.h" // for reset reason detection - #include "hardware/regs/vreg_and_chip_reset.h" // for reset reason detection + #include #endif #ifdef RAINDATA_EN @@ -618,11 +615,18 @@ const cMyLoRaWAN::lmic_pinmap myPinMap = { RTC_DATA_ATTR uint8_t rtcSavedExtraInfo[EXTRA_INFO_MEM_SIZE]; //!< extra Session Info data #endif -// TODO: -// RP2040: move to Preferences -RTC_DATA_ATTR bool runtimeExpired = false; //!< flag indicating if runtime has expired at least once -RTC_DATA_ATTR bool longSleep; //!< last sleep interval; 0 - normal / 1 - long -RTC_DATA_ATTR time_t rtcLastClockSync = 0; //!< timestamp of last RTC synchonization to network time +// Variables which must retain their values after deep sleep +#if defined(ESP32) + // Stored in RTC RAM + RTC_DATA_ATTR bool runtimeExpired = false; //!< flag indicating if runtime has expired at least once + RTC_DATA_ATTR bool longSleep; //!< last sleep interval; 0 - normal / 1 - long + RTC_DATA_ATTR time_t rtcLastClockSync = 0; //!< timestamp of last RTC synchonization to network time +#else + // Save to/restored from Watchdog SCRATCH registers + bool runtimeExpired; //!< flag indicating if runtime has expired at least once + bool longSleep; //!< last sleep interval; 0 - normal / 1 - long + time_t rtcLastClockSync; //!< timestamp of last RTC synchonization to network time +#endif #ifdef ESP32 #ifdef ADC_EN @@ -764,8 +768,23 @@ ESP32Time rtc; /// Arduino setup void setup() { #if defined(ARDUINO_ARCH_RP2040) - // see pico-sdk/src/rp2_common/hardware_rtc/rtc.c - rtc_init(); + // see pico-sdk/src/rp2_common/hardware_rtc/rtc.c + rtc_init(); + + // Restore variables and RTC after reset + time_t time_saved = watchdog_hw->scratch[0]; + datetime_t dt; + epoch_to_datetime(&time_saved, &dt); + + // Set HW clock (only used in sleep mode) + rtc_set_datetime(&dt); + + // Set SW clock + rtc.setTime(time_saved); + + runtimeExpired = ((watchdog_hw->scratch[1] & 1) == 1); + longSleep = ((watchdog_hw->scratch[1] & 2) == 2); + rtcLastClockSync = watchdog_hw->scratch[2]; #endif // set baud rate @@ -776,6 +795,9 @@ void setup() { // wait for serial to be ready - or timeout if USB is not connected delay(500); + #if defined(ARDUINO_ARCH_RP2040) + log_i("Time saved: %lu", time_saved); + #endif preferences.begin("BWS-TTN", false); prefs.ws_timeout = preferences.getUChar("ws_timeout", WEATHERSENSOR_TIMEOUT); log_d("Preferences: weathersensor_timeout: %u s", prefs.ws_timeout); @@ -1263,11 +1285,10 @@ cMyLoRaWAN::GetAbpProvisioningInfo(AbpProvisioningInfo *pAbpInfo) { return false; } #if defined(ARDUINO_ARCH_RP2040) - if ((vreg_and_chip_reset_hw->chip_reset & - (VREG_AND_CHIP_RESET_CHIP_RESET_HAD_RUN_BITS | - VREG_AND_CHIP_RESET_CHIP_RESET_HAD_POR_BITS)) != 0) { - // Last reset was power-on/brown-out detection or RUN pin reset; - // we assume that stored session info is no longer valid and clear it. + if (!watchdog_caused_reboot()) { + // Last reset was not caused by the watchdog, i.e. SW reset via restart(). + // Consequently, a power-on/brown-out detection or RUN pin reset occurred. + // We assume that stored session info is no longer valid and clear it. // A new join will be faster than trying with stale session info and // running into a timeout. log_d("HW reset detected, deleting session info."); @@ -1332,16 +1353,8 @@ void prepareSleep(void) { // to next non-fractional multiple of sleep_interval past the hour if (rtcLastClockSync) { struct tm timeinfo; - //#ifdef ESP32 - time_t t_now = rtc.getLocalEpoch(); - localtime_r(&t_now, &timeinfo); - //#else - // FIXME Is this needed? - // datetime_t t_now; - // rtc_get_datetime(&t_now); - // datetime_to_tm(t_now, timeinfo); - //#endif - + time_t t_now = rtc.getLocalEpoch(); + localtime_r(&t_now, &timeinfo); sleep_interval = sleep_interval - ((timeinfo.tm_min * 60) % sleep_interval + timeinfo.tm_sec); } @@ -1351,19 +1364,33 @@ void prepareSleep(void) { sleep_interval += 20; // Added extra 20-secs of sleep to allow for slow ESP32 RTC timers ESP.deepSleep(sleep_interval * 1000000LL); #else - // Set RTC to an arbitrary, but valid time - datetime_t dt = { - .year = 2023, - .month = 10, - .day = 06, - .dotw = 5, // 0 is Sunday, so 5 is Friday - .hour = 17, - .min = 15, - .sec = 00 - }; + time_t t_now = rtc.getLocalEpoch(); + datetime_t dt; + epoch_to_datetime(&t_now, &dt); rtc_set_datetime(&dt); sleep_us(64); pico_sleep(sleep_interval); + + // Save variables to be retained after reset + watchdog_hw->scratch[2] = rtcLastClockSync; + + if (runtimeExpired) { + watchdog_hw->scratch[1] |= 1; + } else { + watchdog_hw->scratch[1] &= ~1; + } + if (longSleep) { + watchdog_hw->scratch[1] |= 2; + } else { + watchdog_hw->scratch[1] &= ~2; + } + + // Save the current time, because RTC will be reset (SIC!) + rtc_get_datetime(&dt); + time_t now = datetime_to_epoch(&dt, NULL); + watchdog_hw->scratch[0] = now; + log_i("Now: %lu", now); + rp2040.restart(); #endif } diff --git a/src/pico_rtc/pico_rtc_utils.cpp b/src/pico_rtc/pico_rtc_utils.cpp index 48e339d..2f3be2c 100644 --- a/src/pico_rtc/pico_rtc_utils.cpp +++ b/src/pico_rtc/pico_rtc_utils.cpp @@ -1,3 +1,52 @@ +/////////////////////////////////////////////////////////////////////////////// +// pico_rtc_utils.cpp +// +// RTC utility functions for RP2040 +// +// Sleep/wakeup scheme based on +// https://github.com/lyusupov/SoftRF/tree/master/software/firmware/source/libraries/RP2040_Sleep +// by Linar Yusupov +// +// Using code from pico-extras: +// https://github.com/raspberrypi/pico-extras/blob/master/src/rp2_common/pico_sleep/include/pico/sleep.h +// https://github.com/raspberrypi/pico-extras/blob/master/src/rp2_common/pico_sleep/sleep.c +// https://github.com/raspberrypi/pico-extras/blob/master/src/rp2_common/hardware_rosc/include/hardware/rosc.h +// https://github.com/raspberrypi/pico-extras/blob/master/src/rp2_common/hardware_rosc/rosc.c +// +// created: 10/2023 +// +// +// MIT License +// +// Copyright (c) 2023 Matthias Prinke +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// +// +// History: +// +// 20231006 Created +// +// ToDo: +// - +// +/////////////////////////////////////////////////////////////////////////////// #if defined(ARDUINO_ARCH_RP2040) #include "pico_rtc_utils.h" @@ -29,34 +78,61 @@ datetime_t *tm_to_datetime(struct tm *ti, datetime_t *dt) } void print_dt(datetime_t dt) { - printf("%4d-%02d-%02d %02d:%02d:%02d\n", dt.year, dt.month, dt.day, dt.hour, dt.min, dt.sec); + log_i("%4d-%02d-%02d %02d:%02d:%02d", dt.year, dt.month, dt.day, dt.hour, dt.min, dt.sec); } void print_tm(struct tm ti) { - printf("%4d-%02d-%02d %02d:%02d:%02d\n", ti.tm_year+1900, ti.tm_mon+1, ti.tm_mday, ti.tm_hour, ti.tm_min, ti.tm_sec); + log_i("%4d-%02d-%02d %02d:%02d:%02d", ti.tm_year+1900, ti.tm_mon+1, ti.tm_mday, ti.tm_hour, ti.tm_min, ti.tm_sec); } +time_t datetime_to_epoch(datetime_t *dt, time_t *epoch) { + struct tm ti; + datetime_to_tm(dt, &ti); + + // Apply daylight saving time according to timezone and date + ti.tm_isdst = -1; + + // Convert to epoch + time_t _epoch = mktime(&ti); + + if (epoch) { + *epoch = _epoch; + } + + return _epoch; +} + +datetime_t *epoch_to_datetime(time_t *epoch, datetime_t *dt) { + struct tm ti; + + // Apply daylight saving time according to timezone and date + ti.tm_isdst = -1; + + // Convert epoch to struct tm + localtime_r(epoch, &ti); + + // Convert struct tm to datetime_t + tm_to_datetime(&ti, dt); + + return dt; +} + +// Sleep for seconds void pico_sleep(unsigned duration) { datetime_t dt; rtc_get_datetime(&dt); - printf("RTC time:\n"); + log_i("RTC time:"); print_dt(dt); - struct tm ti; - datetime_to_tm(&dt, &ti); - - // Convert to epoch - time_t now = mktime(&ti); + time_t now; + datetime_to_epoch(&dt, &now); // Add sleep_duration time_t wakeup = now + duration; - // Convert epoch to struct tm - localtime_r(&wakeup, &ti); + epoch_to_datetime(&wakeup, &dt); - // Convert struct tm to datetime_t - tm_to_datetime(&ti, &dt); - printf("Wakeup time:\n"); + log_i("Wakeup time:"); print_dt(dt); Serial.flush(); diff --git a/src/pico_rtc/pico_rtc_utils.h b/src/pico_rtc/pico_rtc_utils.h index daf9d2e..d43ba01 100644 --- a/src/pico_rtc/pico_rtc_utils.h +++ b/src/pico_rtc/pico_rtc_utils.h @@ -1,14 +1,68 @@ +/////////////////////////////////////////////////////////////////////////////// +// pico_rtc_utils.h +// +// RTC utility functions for RP2040 +// +// Sleep/wakeup scheme based on +// https://github.com/lyusupov/SoftRF/tree/master/software/firmware/source/libraries/RP2040_Sleep +// by Linar Yusupov +// +// Using code from pico-extras: +// https://github.com/raspberrypi/pico-extras/blob/master/src/rp2_common/pico_sleep/include/pico/sleep.h +// https://github.com/raspberrypi/pico-extras/blob/master/src/rp2_common/pico_sleep/sleep.c +// https://github.com/raspberrypi/pico-extras/blob/master/src/rp2_common/hardware_rosc/include/hardware/rosc.h +// https://github.com/raspberrypi/pico-extras/blob/master/src/rp2_common/hardware_rosc/rosc.c +// +// created: 10/2023 +// +// +// MIT License +// +// Copyright (c) 2023 Matthias Prinke +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// +// +// History: +// +// 20231006 Created +// +// ToDo: +// - +// +/////////////////////////////////////////////////////////////////////////////// #if defined(ARDUINO_ARCH_RP2040) #include #include +#include +#include #include "pico_sleep.h" #include "pico_rosc.h" +#include "../../logging.h" #ifndef PICO_RTC_UTILS_H #define PICO_RTC_UTILS_H struct tm *datetime_to_tm(datetime_t *dt, struct tm *ti); datetime_t *tm_to_datetime(struct tm *ti, datetime_t *dt); +time_t datetime_to_epoch(datetime_t *dt, time_t *epoch); +datetime_t *epoch_to_datetime(time_t *epoch, datetime_t *dt); void print_dt(datetime_t dt); void print_tm(struct tm ti);