Page 1 of 1


Posted: Sun Dec 18, 2016 2:23 am
by jamesb
Intermittently, a call to urequests to do an http post causes a hang in my setup. I'd like to set a timeout, or a watchdog to reset or deepsleep the device if this happens. Looks like there is a WDT: ... hlight=wdt
But WDT(timeout=xx) doesn't work, apparently the function doesn't take any arguments. Is there another way to set a timeout, or any documentation on how to use the WDT?


Posted: Wed Dec 21, 2016 5:38 am
by warren
jamesb wrote:Is there another way to set a timeout, or any documentation on how to use the WDT?
I decided to use a custom WDT using interrupts. I got more flexibility for my specific application. It is not as robust as a machine based WDT, but in my experience with several different projects, it is perfectly adequate. I long ago concluded that maximum reliability in WDT functionality is only achievable with an offboard device which physically power-cycles the device. I was going to try inexpensive voltage regulator chips which have a built in timer function, but in the end this home-coded WDT has been perfectly fine.

(And to be honest, it just is the case with ESP type systems that you should regularly reset them anyway)

A home-coded WDT is like this:

1) Set up a regular interrupt - say 1 per second.
2) Have a variable as a counter, initially set to zero.
3) The interrupt service routine regularly increments the counter.
4) The same routine checks if the counter has crossed a threshold.
5) If its reached a pre-set value, it uses machine.reset() to reboot.

Then to use it, you simply have a statement in your main loop which resets the counter to zero.

One advantage is that you can start and stop this WDT should you need to. (The uPy WDT class says that the official WDT cannot be stoped or reconfigured once it is started - not always ideal..)

There are some kinds of machine-lock up that this approach will not trap. But I have never yet had a machine that is just 'sitting there' having locked up and NOT being reset by this simple kind of WDT.

I also have a small routine which I can quickly invoke from the REPL which kills the WDT by disabling the interrupt. This is useful when you use CTRL-C to halt your loop and don't want the machine to reset until you have checked states of variables etc...

Hope that helps


Posted: Thu Dec 22, 2016 5:25 am
by jamesb
Thanks @warren!

Is this the idea?

Code: Select all

def countAndSleep(arg):
    count = count + 1;
    print ("Current count is " + str(count))
    if (count >= timeout/5): #30 seconds
        sleepTime() #go to deep sleep

from machine import Timer
timer = Timer(-1)
tim.init(period=5000, mode=Timer.ONE_SHOT, callback=countAndSleep) #sleeping if still awake after 30 seconds
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.

The documentation ( ... ight=timer) doesn't mention the callback parameter, bit hard to know what the syntax is meant to be (ie is the callback meant to have arguments, and if so, what are they?).


Posted: Thu Dec 22, 2016 3:24 pm
by warren
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 ''. If you define a bunch of variables in, then simply import this in other modules, all the variables in config are available to the other modules.

For example in 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:

Code: Select all

import config as c
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=""):
    if p_en!="": print("WDT: "+str(c.WDTcon)+" WDTtrigger: "+str(c.WDTtrigger))
    if c.WDTcon>(c.WDTtrigger-1):
        import machine
        if p_en!="": print("############ WDT RESET! #############")
        time.sleep(1) # So you have time to see on screen why it is resetting...

def WDTfeed(p_en=""):
    if c.WDT_enable==0:
    if p_en!="": print("WDT fed!")
    c.WDTobj.init(period=c.WDTtimval, mode=Timer.PERIODIC, callback=lambda x: WDTtick(p_en))

def WDTkill(p_en=""):
    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:

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


Posted: Thu Dec 22, 2016 10:24 pm
by jamesb
Very helpful, thanks @warren! I like the way you structure your code.
I'm about to give some devices as Christmas presents, so won't have time to get the timers in and tested for them, but I'll play around with the techniques you've suggested for my next deployment. Thanks!