diff --git a/Catena4430_Sensor.ino b/Catena4430_Sensor.ino index e325592..1e03fec 100644 --- a/Catena4430_Sensor.ino +++ b/Catena4430_Sensor.ino @@ -129,6 +129,7 @@ static const cCommandStream::cEntry sMyExtraCommmands[] = { "tree", cmdDir }, { "info", cmdInfo }, { "interval", cmdInterval }, + { "hang", cmdHang }, // other commands go here.... }; @@ -164,6 +165,7 @@ void setup() setup_gpio(); setup_rtc(); setup_commands(); + setup_watchdog(); setup_start(); } @@ -411,6 +413,7 @@ void setup_rtc() uint8_t nBlink = 0; while (nBlink < 5) { + gpMeasurementLoopConcrete->refreshWatchdog(); gpio.setRed(true); delay(100); gpio.setRed(false); @@ -503,6 +506,11 @@ void setup_commands() ); } +void setup_watchdog() + { + gpMeasurementLoopConcrete->setupWatchdog(); + } + void setup_start() { gpMeasurementLoopConcrete->requestActive(true); @@ -516,6 +524,7 @@ void setup_start() void loop() { + gpMeasurementLoopConcrete->refreshWatchdog(); gCatena.poll(); if (gpMeasurementLoopConcrete->fDisableLED) diff --git a/Catena4430_cMeasurementLoop.cpp b/Catena4430_cMeasurementLoop.cpp index 305e4b5..9c2481d 100644 --- a/Catena4430_cMeasurementLoop.cpp +++ b/Catena4430_cMeasurementLoop.cpp @@ -136,6 +136,7 @@ cMeasurementLoop::fsmDispatch( bool fEntry ) { + this->refreshWatchdog(); State newState = State::stNoChange; if (fEntry && this->isTraceEnabled(this->DebugFlags::kTrace)) @@ -244,6 +245,7 @@ cMeasurementLoop::fsmDispatch( while (true) { std::uint32_t lmicCheckTime; + this->refreshWatchdog(); os_runloop_once(); lmicCheckTime = this->m_UplinkTimer.getRemaining(); @@ -340,14 +342,15 @@ cMeasurementLoop::fsmDispatch( uint8_t nBlink = 0; while (nBlink < 5) { + this->refreshWatchdog(); gpio.setBlue(true); - delay(100); + safeDelay(100); gpio.setBlue(false); - delay(100); + safeDelay(100); gpio.setBlue(true); - delay(100); + safeDelay(100); gpio.setBlue(false); - delay(500); + safeDelay(500); nBlink += 1; } } @@ -392,6 +395,13 @@ void cMeasurementLoop::resetMeasurements() gpMeasurementLoopConcrete->clearMeasurements(); } +void cMeasurementLoop::safeDelay(uint32_t millis) + { + this->refreshWatchdog(); + delay(millis); + this->refreshWatchdog(); + } + void cMeasurementLoop::updateScd30Measurements() { if (this->m_fScd30) @@ -1015,6 +1025,7 @@ void cMeasurementLoop::doSleepAlert(bool fDeepSleep) while (uint32_t(millis() - tNow) < 1000) { + this->refreshWatchdog(); gCatena.poll(); yield(); } @@ -1024,6 +1035,7 @@ void cMeasurementLoop::doSleepAlert(bool fDeepSleep) uint32_t tNow = millis(); while (uint32_t(millis() - tNow) < 100) { + this->refreshWatchdog(); gCatena.poll(); yield(); } @@ -1046,7 +1058,8 @@ void cMeasurementLoop::doDeepSleep() this->deepSleepPrepare(); /* sleep */ - gCatena.Sleep(sleepInterval); + // gCatena.Sleep(sleepInterval); + this->fitfulSleep(sleepInterval); /* recover from sleep */ this->deepSleepRecovery(); @@ -1055,6 +1068,69 @@ void cMeasurementLoop::doDeepSleep() this->m_fsm.eval(); } +// sleep, broken up into intervals of 10 seconds or so. +void cMeasurementLoop::fitfulSleep(uint32_t seconds) + { + /* we need to sleep for not too long each time, so we can have the watchdog enabled */ + while (seconds > 0) + { + uint32_t nSecondsThisTime = seconds; + if (nSecondsThisTime > this->kNumSecondsFitfulSleepMax) + nSecondsThisTime = kNumSecondsFitfulSleepMax; + + gCatena.Sleep(nSecondsThisTime); + seconds -= nSecondsThisTime; + + this->refreshWatchdog(); + } + } + +#define FUNCTION "cMeasurementLoop::setupWatchdog" + +void cMeasurementLoop::setupWatchdog() + { + constexpr uint32_t IWDG_KEY_ENABLE = 0xCCCC; + constexpr uint32_t IWDG_KEY_WRITE_ACCESS_ENABLE = 0x5555; + constexpr uint32_t IWDG_KEY_WRITE_ACCESS_DISABLE = 0; + + // compute number of ticks to set in the reload register. + // 40000 is the rough RC frequency of the watchdog, 256 is the pre-scaler. + constexpr uint32_t knTicks = (this->kWatchdogSeconds * 40000) / 256; + static_assert(knTicks <= 0xFFF, "knTicks must fit in a 12-bit field"); + + // enable the IWDG + IWDG->KR = IWDG_KEY_ENABLE; + // allow write access. + IWDG->KR = IWDG_KEY_WRITE_ACCESS_ENABLE; + // set prescaler to 7, i.e., divide by 256. So one tick == 40000 Hz/256 == 156.25 Hz == 6.4ms + IWDG->PR = 7; + // set reload register. + IWDG->RLR = knTicks; + // wait for the register to update. Since we're initializing, we don't + // really care very much. + for (uint32_t tNow = millis(); millis() - tNow < 48;) + { + if (IWDG->SR == 0) + break; + } + if (IWDG->SR != 0) + { + gCatena.SafePrintf("?" FUNCTION ": watchdog setup failed!: %x\n", IWDG->SR); + } + + // refresh the watchdog + this->refreshWatchdog(); + } + +#undef FUNCTION + +void cMeasurementLoop::refreshWatchdog() + { + constexpr uint32_t IWDG_KEY_REFRESH = 0xAAAA; + + IWDG->KR = IWDG_KEY_REFRESH; + } + // // call this after waking up from a long (> 15 minute) sleep to correct for LMIC sleep defect // This should be done after updating micros() and updating LMIC's idea of time based on diff --git a/Catena4430_cMeasurementLoop.h b/Catena4430_cMeasurementLoop.h index 220dfa1..bdb02ae 100644 --- a/Catena4430_cMeasurementLoop.h +++ b/Catena4430_cMeasurementLoop.h @@ -157,6 +157,9 @@ class cMeasurementLoop : public McciCatena::cPollableObject static constexpr std::uint8_t kSdCardCSpin = D5; using Flash_t = McciCatena::FlashParamsStm32L0_t; using ParamBoard_t = Flash_t::ParamBoard_t; + static constexpr std::uint32_t kNumSecondsFitfulSleepMax = 10; + static constexpr std::uint32_t kWatchdogSeconds = 26; + static_assert(kNumSecondsFitfulSleepMax < kWatchdogSeconds, "Wake up often enough to refresh the IWDG watchdog"); void deepSleepPrepare(); void deepSleepRecovery(); @@ -409,6 +412,15 @@ class cMeasurementLoop : public McciCatena::cPollableObject /// tear down the SD card. void sdFinish(); + /// @brief setup STM32Lxx IWDG to require 20 second updates. + void setupWatchdog(); + /// @brief get another 20 seconds before the watchdog triggers + void refreshWatchdog(); + + /// @brief delay for a while, and refresh the watchdog. + /// @param millis number of milliseconds, as for \c ::delay(). + void safeDelay(uint32_t millis); + // timeout handling // set the timer @@ -424,6 +436,7 @@ class cMeasurementLoop : public McciCatena::cPollableObject bool checkDeepSleep(); void doSleepAlert(bool fDeepSleep); void doDeepSleep(); + void fitfulSleep(uint32_t nSeconds); // read data void updateSynchronousMeasurements(); diff --git a/Catena4430_cMeasurementLoopV1.cpp b/Catena4430_cMeasurementLoopV1.cpp index 2630837..8124ea7 100644 --- a/Catena4430_cMeasurementLoopV1.cpp +++ b/Catena4430_cMeasurementLoopV1.cpp @@ -78,6 +78,7 @@ bool cMeasurementLoopV1::takeMeasurements(void) while (fSi1133Data) { + this->refreshWatchdog(); if (this->m_si1133.isOneTimeReady()) { fSi1133Data = false; diff --git a/Catena4430_cMeasurementLoopV2.cpp b/Catena4430_cMeasurementLoopV2.cpp index 11c4dc0..c623bf6 100644 --- a/Catena4430_cMeasurementLoopV2.cpp +++ b/Catena4430_cMeasurementLoopV2.cpp @@ -81,6 +81,7 @@ bool cMeasurementLoopV2::takeMeasurements(void) while (! this->m_Ltr.queryReady(fHardError)) { + this->refreshWatchdog(); if (fHardError) break; } diff --git a/Catena4430_cmd.h b/Catena4430_cmd.h index 6432b18..f0ee3bd 100644 --- a/Catena4430_cmd.h +++ b/Catena4430_cmd.h @@ -25,5 +25,6 @@ McciCatena::cCommandStream::CommandFn cmdInfo; McciCatena::cCommandStream::CommandFn cmdDate; McciCatena::cCommandStream::CommandFn cmdLog; McciCatena::cCommandStream::CommandFn cmdDir; +McciCatena::cCommandStream::CommandFn cmdHang; #endif /* _Catena4430_cmd_h_ */ diff --git a/cmdHang.cpp b/cmdHang.cpp new file mode 100644 index 0000000..85f2e65 --- /dev/null +++ b/cmdHang.cpp @@ -0,0 +1,72 @@ +/* + +Module: cmdHang.cpp + +Function: + Process the "hang" command + +Copyright and License: + See accompanying LICENSE file for copyright and license information. + +Author: + Pranau R, MCCI Corporation April 2024 + +*/ + +#include "Catena4430_cmd.h" + +#include + +using namespace McciCatena; + +/* + +Name: ::cmdHang() + +Function: + Command dispatcher for "hang" command. + +Definition: + McciCatena::cCommandStream::CommandFn cmdHang; + + McciCatena::cCommandStream::CommandStatus cmdHang( + cCommandStream *pThis, + void *pContext, + int argc, + char **argv + ); + +Description: + The "hang" command has the following syntax: + + hang + enter a tight loop waiting for the watchdog to fire. + +Returns: + cCommandStream::CommandStatus::kSuccess if successful. + Some other value for failure. + +*/ + +// argv[0] is "hang" +// argv[1] is new log flag mask; if omitted, mask is printed +cCommandStream::CommandStatus cmdHang( + cCommandStream *pThis, + void *pContext, + int argc, + char **argv + ) + { + if (argc > 1) + return cCommandStream::CommandStatus::kInvalidParameter; + + pThis->printf("looping... watchdog should get us out before we exit\n"); + + uint32_t now = millis(); + + while (millis() - now < 60 * 1000) + ; + + pThis->printf("watchdog did not fire.\n"); + return cCommandStream::CommandStatus::kError; + } \ No newline at end of file