Skip to content

Commit

Permalink
fix #26: added watchdog to perform reset on hang
Browse files Browse the repository at this point in the history
  • Loading branch information
Pranau-R committed Apr 8, 2024
1 parent 6e803d3 commit d0a1af3
Show file tree
Hide file tree
Showing 7 changed files with 178 additions and 5 deletions.
9 changes: 9 additions & 0 deletions Catena4430_Sensor.ino
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@ static const cCommandStream::cEntry sMyExtraCommmands[] =
{ "tree", cmdDir },
{ "info", cmdInfo },
{ "interval", cmdInterval },
{ "hang", cmdHang },
// other commands go here....
};

Expand Down Expand Up @@ -164,6 +165,7 @@ void setup()
setup_gpio();
setup_rtc();
setup_commands();
setup_watchdog();
setup_start();
}

Expand Down Expand Up @@ -411,6 +413,7 @@ void setup_rtc()
uint8_t nBlink = 0;
while (nBlink < 5)
{
gpMeasurementLoopConcrete->refreshWatchdog();
gpio.setRed(true);
delay(100);
gpio.setRed(false);
Expand Down Expand Up @@ -503,6 +506,11 @@ void setup_commands()
);
}

void setup_watchdog()
{
gpMeasurementLoopConcrete->setupWatchdog();
}

void setup_start()
{
gpMeasurementLoopConcrete->requestActive(true);
Expand All @@ -516,6 +524,7 @@ void setup_start()

void loop()
{
gpMeasurementLoopConcrete->refreshWatchdog();
gCatena.poll();

if (gpMeasurementLoopConcrete->fDisableLED)
Expand Down
86 changes: 81 additions & 5 deletions Catena4430_cMeasurementLoop.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,7 @@ cMeasurementLoop::fsmDispatch(
bool fEntry
)
{
this->refreshWatchdog();
State newState = State::stNoChange;

if (fEntry && this->isTraceEnabled(this->DebugFlags::kTrace))
Expand Down Expand Up @@ -244,6 +245,7 @@ cMeasurementLoop::fsmDispatch(
while (true)
{
std::uint32_t lmicCheckTime;
this->refreshWatchdog();
os_runloop_once();
lmicCheckTime = this->m_UplinkTimer.getRemaining();

Expand Down Expand Up @@ -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;
}
}
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -1015,6 +1025,7 @@ void cMeasurementLoop::doSleepAlert(bool fDeepSleep)

while (uint32_t(millis() - tNow) < 1000)
{
this->refreshWatchdog();
gCatena.poll();
yield();
}
Expand All @@ -1024,6 +1035,7 @@ void cMeasurementLoop::doSleepAlert(bool fDeepSleep)
uint32_t tNow = millis();
while (uint32_t(millis() - tNow) < 100)
{
this->refreshWatchdog();
gCatena.poll();
yield();
}
Expand All @@ -1046,7 +1058,8 @@ void cMeasurementLoop::doDeepSleep()
this->deepSleepPrepare();

/* sleep */
gCatena.Sleep(sleepInterval);
// gCatena.Sleep(sleepInterval);
this->fitfulSleep(sleepInterval);

/* recover from sleep */
this->deepSleepRecovery();
Expand All @@ -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
Expand Down
13 changes: 13 additions & 0 deletions Catena4430_cMeasurementLoop.h
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down Expand Up @@ -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
Expand All @@ -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();
Expand Down
1 change: 1 addition & 0 deletions Catena4430_cMeasurementLoopV1.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ bool cMeasurementLoopV1::takeMeasurements(void)

while (fSi1133Data)
{
this->refreshWatchdog();
if (this->m_si1133.isOneTimeReady())
{
fSi1133Data = false;
Expand Down
1 change: 1 addition & 0 deletions Catena4430_cMeasurementLoopV2.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ bool cMeasurementLoopV2::takeMeasurements(void)

while (! this->m_Ltr.queryReady(fHardError))
{
this->refreshWatchdog();
if (fHardError)
break;
}
Expand Down
1 change: 1 addition & 0 deletions Catena4430_cmd.h
Original file line number Diff line number Diff line change
Expand Up @@ -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_ */
72 changes: 72 additions & 0 deletions cmdHang.cpp
Original file line number Diff line number Diff line change
@@ -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 <Arduino.h>

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;
}

0 comments on commit d0a1af3

Please sign in to comment.