jamesb wrote:sleepTime() is a function that puts the device to deepsleep. But the device executes the callback and goes into deepsleep as soon as it starts.
[Sorry if some of this strikes you as basic, but I try to reply in a way that less experienced folks can also benefit from...this is what really helped me when reading others posts...]
It's a while since I researched setting up timers. In cases like this where the docs are sparse, I tend to get something working and simply re-use it in different settings.
The code for my WDT is below, first a couple of comments about my coding style.
1) It is very useful during development to be able to print stuff out at different points. But it slows things down and it can be a pain to strip print statements out, only to discover you need them again later. If space is
not an issue, then I leave them in but make them conditional using one of the parameters passed to the function. In my code below, the print enable parameter is labelled "p_en". If that is anything other than "" then the function will print where instructed. Its a quirky approach that has proved invaluable in developing code when you aren't sure what you are doing and want to check state when calling your routines.
2) Configuration file. There are lots of times when you will use a variable in a global way - you want to access it from different parts in different modules. I have found it tremendously helpful to have a single config file. In my case it is called 'config.py'. If you define a bunch of variables in config.py, then simply import this in other modules, all the variables in config are available to the other modules.
For example in config.py I have around 40 different variables defined. Regarding WDT I have:
WDTcon=0 # Cyciling counter for WDT
WDTtrigger=15 # Threshold for machine resets
WDTobj=0 # Container variable for timer object
WDT_enable=0 # Set to 1 to enable WDT
The advantage of this approach is that all your config variables are available in
one place. I tend to roll out ESP systems with similar but not identical functionality. Almost all of the differences occur within the config file - this approach makes it much easier to customise and reuse code.
Say I want to reference the "WDTcon" variable in a different module. First you simply "import" the config module, and at the came sime give it a much shorter name:
After that, you can reference and use the WDTcon variable as "c.WDTcon". You haven't 'polluted the global namespace' but you have one very convenient place to check and modify your config for all the functionality that may have tweakable options.
Having explained that, here is my WDT code:
Code: Select all
from machine import Timer
import machine
import config as c
import time
tim = Timer(-1) # Create Timer instance
c.WDTobj=tim # Give it a 'c. reference'
def WDTtick(p_en=""):
c.WDTcon+=1
if p_en!="": print("WDT: "+str(c.WDTcon)+" WDTtrigger: "+str(c.WDTtrigger))
if c.WDTcon>(c.WDTtrigger-1):
import machine
WDTkill(p_en)
if p_en!="": print("############ WDT RESET! #############")
time.sleep(1) # So you have time to see on screen why it is resetting...
machine.reset()
def WDTfeed(p_en=""):
if c.WDT_enable==0:
c.WDTobj.deinit()
return
if p_en!="": print("WDT fed!")
c.WDTobj.deinit()
c.WDTcon=0
c.WDTobj.init(period=c.WDTtimval, mode=Timer.PERIODIC, callback=lambda x: WDTtick(p_en))
def WDTkill(p_en=""):
c.WDTobj.deinit()
c.WDT_enable=0
if p_en!="": print ("WDT dead!!")
There are three separate routines:
WDTkill() disables the WDT.
WDTfeed() actually sets up a timer and starts it.
WDTtick() is the interrupt service routine that the timer calls when it times out.
So if you want to use the WDT, simply include a call to WDTfeed() in your main loop.
There may be times when you want to
dynamically change the WDT time-to-reset parameter. It may be that in your main loop, a WDTthreshold of 5 is suitable. But when doing extended network operations you may want to try a longer timeout. The code above allows you to dynamically change the threshold. You would simply issue this:
WDTkill()
c.WDTtrigger=<new value>
WDTfeed() # To restart the WDT
Note on printing from an interrupt service routine. Generally you should keep an ISR extremely simple to avoid memory issues. So make sure when you are deploying the final version that you don't call WDTtick() with printing enabled - it is only a convenience during development. Having said that, I have never actually seen any issues with it printing inside an ISR...
Hope that helps