lowPowerHandler(); & TimerLowPowerHandler()

Hello guys,
in core>asr650x>lora>system>low_power.c inside the TimerLowPowerHandler(void) i noticed you are looping at least five times HasLoopedThroughMain before actually calling the lower power mode and wanted to ask what’s the reasoning behind this please?

voidTimerLowPowerHandler(void)
{
     if (HasLoopedThroughMain < 5)
     {
         HasLoopedThroughMain++;
     }
     else
     {
        HasLoopedThroughMain = 0;
        lowPowerHandler();
     }
}

as of now the code after LoRaWAN.sleep(); in

case DEVICE_STATE_SLEEP:
  {
    Serial.printf("\r\n going to sleep now...\r\n");
    LoRaWAN.sleep();
    Serial.printf("\r\nwaking up from sleep and continuing work:\r\n");
}

the above code will demonstrate the waking up from sleep gets executed five times after calling the .sleep() method which makes it hard to detect that the processor really woke …

can you please explain if this was done on purpose or can we remove HasLoopedThroughMain ?

Hi jayjay,

Looks the delay can be removed safely:

https://github.com/Lora-net/LoRaMac-node/issues/259

This works fine for me:

52

Hello peterm,
Thank you for the link, after reading what was discussed it seems that Heltec is doing that to make sure that all events are being processed before going to sleep… they must have a reason for implementing this… so to keep the same behavior and give back the proper behavior when using lowpowerHandler() which continues to execute code right where it slept… i propose the below solution, tell me what you all think?

void TimerLowPowerHandler(bool *wokeUp) 
{
    if (HasLoopedThroughMain < 5)
    {
        HasLoopedThroughMain++;
        *wokeUp = false; 
    }
    else
    {
        *wokeUp = true;
        HasLoopedThroughMain = 0;
        lowPowerHandler();
    }
}

and then we would call the function as follow:

bool wokeUp;
LoRaWAN.sleep(&wokeUp);
if (wokeUp)
{
 printf("\n Woke up from sleep let's continue the work");
}

update the sleep function in LoRaWan_APP.c as well and it’s corresponding header to:

void LoRaWanClass::sleep(bool *wokeUp) 
{
    // return TimerLowPowerHandler();
    return TimerLowPowerHandler(wokeUp);
}

someone might ask why do we need to know that we have woken after a sleep. well consider the fact that if one decides to use the watchdog, which should be disabled before going to sleep and enabled while the system is awake… in that situation we could do this:

void setup(){
/* Enable the WDT, autofeed set to false we will feed it in the loop, if this is set to true the board will appear to neve go to sleep since the WDT will wake the board up every 2.4 sec i think to feed the WDT not a power friendly at all*/
  innerWdtEnable(false);
//your code here...
}

void loop(){
....
      case DEVICE_STATE_SLEEP:
      {
          CySysWdtDisable(); //disable the watchdog before going to sleep
          bool wokeUp;
          LoRaWAN.sleep(&wokeUp); //go to sleep and watch the wokeUp variable, should be true after we are woken up.
          if (wokeUp)
          {
               CySysWdtEnable(); //enable the watchdog since we have woken up.
               printf("\n Woke up from sleep let's continue the work");
          }
       break;
      }

    feedInnerWdt();// feed the WDT at the end of the loop so it the code locks up this won't trigger and the program resets.
}

What do you all think?

Hi jayjay,

The title of your thread already gave a good hint where to look: why not put the CySysWdtDisable/Enable calls inside the lowPowerHandler(), i.e. before & after CySysPmDeepSleep()?
Looks cleaner to me then trying to solve it at the App level.
Also, the watchdog would be consistently managed no matter how lowPowerHandler() is called.

/* Includes ------------------------------------------------------------------*/
#include "hw.h"
#include "low_power.h"

/* Private function prototypes -----------------------------------------------*/
extern bool wakeByUart;
extern uint32_t systime;
extern uint8 UART_1_initVar;
uint8_t HasLoopedThroughMain = 0;


void TimerLowPowerHandler( void )
{
	if( HasLoopedThroughMain < 5 )
	{
	    HasLoopedThroughMain++;
	}
	else
	{
	    HasLoopedThroughMain = 0;
	    lowPowerHandler( );
	}
}

void lowPowerHandler( void )
{
	if ( wakeByUart == false)
	{
		pinMode(P4_1,ANALOG);// SPI0  MISO;
		if(UART_1_initVar)
			pinMode(P3_1,ANALOG);//UART_TX
		CySysPmDeepSleep();
		systime = (uint32_t)RtcGetTimerValue();
		pinMode(P4_1,INPUT);
		if(UART_1_initVar)
			pinMode(P3_1,OUTPUT_PULLUP);
	}
}
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/

hi peterm,
good call. so in that case i would propose the following solution, where it would only disable the watch dog if it was enabled by the user and re-enable it when back from sleep.

void TimerLowPowerHandler(bool *wokeUp) 
{
    if (HasLoopedThroughMain < 5)
    {
        HasLoopedThroughMain++;
        *wokeUp = false;
    }
    else
    {
        *wokeUp = true;
        HasLoopedThroughMain = 0;
        lowPowerHandler();
    }
}

void lowPowerHandler()
{
    bool wdtState; // keep track of whether the watchdog was actually enabled by the user. 
    if (wakeByUart == false)
    {
        pinMode(P4_1, ANALOG); // SPI0  MISO;
        if (UART_1_initVar)
            pinMode(P3_1, ANALOG);             // UART_TX
        wdtState = CySysWdtGetEnabledStatus(); // get the status of the wdt whether it is enabled by the user or not
        if (wdtState)
            CySysWdtDisable(); // if it was enabled then disable it
        CySysPmDeepSleep();    // go to deep sleep
        if (wdtState)
        {
            CySysWdtEnable(); // restore the status of the wdt back to enabled only if was enabled by the user
        }
        systime = (uint32_t)RtcGetTimerValue();
        pinMode(P4_1, INPUT);
        if (UART_1_initVar)
            pinMode(P3_1, OUTPUT_PULLUP);
    }
}
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/

Hi jayjay,

Looks good.
Is the ‘wokeup’ modification in the TimerLowPowerHandler still needed?

Hi @peterm,
yes i left that intentionally there as it will come in handy in the sketch level, where one would want to need to know when to resume executing his code after the sleep command…

    printf("going to sleep for a while");
    bool wokeUp;
    LoRaWAN.sleep(&wokeUp);
    if (wokeUp)
    { 
        printf("enough sleep back to work, continue doing what i was doing  .....");
    }

hope this helps…
Jay

Hi Jay,
Got it.

Have you had a chance to test things yet?
I’m curious to your findings…
Thanks, Peter

yes i have and everything seems to work as expected :slight_smile:
no surprises and the WDT if enabled resumes it’s work after waking up, it resets the board if not fed properly and doesn’t kicking in when a sleep …

i wonder if the Heltec team can have these pushed into their main repo…

cheers,
Jay

1 Like

Good job!:+1:

Cheers,
Peter

Hi jayjay,

On 2nd thought, turning off the WDT before going to sleep is generally not a good idea:

A runaway inside an ISR may not be caught because the WDT is disabled when interrupts are being serviced. The WDT is enabled by lowPowerHandler() afterwards.
Enabling the WDT at the beginning of each ISR does not solve this for 100.0% either.

So, an interrupt driven sensor app that goes to sleep after it has put its sensor(s) to work should keep the WDT enabled. => Full runaway coverage while handling sensor interrupt tasks.
Then, after all sensor tasks are done and data transmitted, the WDT isn’t needed anymore until the next cycle – minutes/hours later – and can be disabled.

What do you think?

Hello @peterm

OK after digging a bit deeper i found the following solution using the innerWdtEnable(true) instead of innerWdtEnable(false) which will auto feed the WDT when enabled and awake. but in order to make it work and have the WDT disabled while asleep we would need to stop the WDT interrupt when not required since that’s what keeps waking up the board from sleep to feed the wdt. using the following code:

int count=0;
void setup()
{
  /* Enable the WDT, autofeed */
  innerWdtEnable(true);
  pinMode(PULSE_PIN, INPUT_PULLUP);
  attachInterrupt(PULSE_PIN, ISR_Pulse, FALLING);
..
}

void loop(){
.....
    case DEVICE_STATE_SLEEP:
      {
        bool wokeUp;
        wdt_isr_Stop(); //stop the WDT interrupt 
        LoRaWAN.sleep(&wokeUp);
        if (wokeUp)
        {
          innerWdtEnable(true); //re-enable the watchdog with auto-feed on.
          wokeUp = false;
        }
     break;
    }

    if (interrupted)
        {
        //do your thing here. process whatever needed from the interrupt handler
       printf("Total Count%d", count);
    }
}

my simplified interrupt handler

bool interrupted = false;
 void ISR_Pulse()
{
    count++;
    interrupted = true;
}

and lowPowerHandler() in low_power.c

void lowPowerHandler()
{
    bool wdtState; // keep track of wheter the watchdog was enabled.
    if (wakeByUart == false)
    {
        pinMode(P4_1, ANALOG); // SPI0  MISO;
        if (UART_1_initVar)
            pinMode(P3_1, ANALOG);             // UART_TX
        wdtState = CySysWdtGetEnabledStatus(); // get the status of the wdt whether it is enabled by the user or not
        if (wdtState)
            CySysWdtDisable(); // if it was enabled then disable it
        CySysPmDeepSleep();    // go to deep sleep
        if (wdtState)
            CySysWdtEnable(); // restore the status of the wdt to enabled only if was enabled by the user
        systime = (uint32_t)RtcGetTimerValue();
        pinMode(P4_1, INPUT);
        if (UART_1_initVar)
            pinMode(P3_1, OUTPUT_PULLUP);
    }
}

btw i moved all my code and data processing from interrupt to the loop and kept the interrupt as simple as possible in my case it increments a number and sets a flag to true that gets processed in the loop()
What do you think…?

1 Like

Hi jayjay,

Yes, I’m using a similar flow - works well!