Calling machine.reset from timer interrupt routine
-
Is there any reason to believe that we should not call machine.reset() from a alarm/timer interrupt routine?
We are having GPy units lock up very rarely when the machine.reset() is called from a alarm interrupt routine. I have not been able to catch the problem occur with a terminal connected to see what might be displayed. What I know is the unit does not reset. Our LCD display shows the message that occurs immediately before the reset() is called, but the unit does not reset. We are calling the alarm.cancel() function before the reset.
-
I think we have discovered the problem with our code!
I was initializing a global variable by calling the constructor but the constructor would throw an exception (very rarely).
lte = LTE()
Since there was no try/except around the global code and since the watchdog had not been initialized yet, the exception would break the startup code from running and freeze the system.
The lesson learned is NEVER put any code in the global variable declarations that might throw an exception!
lte = None ##################################################################### # Reset Modem ##################################################################### def reset_modem(): global lte g.modem_state = g.MS_RESET_MODEM log("reset modem...") if lte == None: try: lte = LTE() except Exception as ex: sys.print_exception(ex) util.reset(27, "LTE exception") sleep_ms(0) lte.reset() sleep_ms(0)
-
@tlanier I have not implemented that yet. For the ESP32, reset is sufficient. Internally the Reset (= EN) pin is a power switch. For the GSM modem, in the absence of circuit diagrams I would switch the power, even it that means that the chip looses the attach state and re-attaching takes longer.
-
@robert-hh are you switching the power to the GPy somehow or are you driving the reset pin?
-
@tlanier Initially I looked at the MAX 6469-6374 series. But these are comparable expensive, with low volume prices at about 2€. The other option is to use a ATTiny45 (< 1 €) and code that according to the needs. I used that already in a few set-ups and found them very reliable, with standby currents ~2 µA, and runtime currents at 1 MHz of ~1 mA. For this task it can run from an internal clock an needs not any external components.
The advantage of the MAX chip is, it's ready to use, but the standby current is larger (8µA typ.)
-
@robert-hh Yes we are using the software watchdog. Apparently the machine.reset() command simply sets the software watchdog to 1 msec and waits for the watchdog to reset the computer.
machine_reset source code on github
mp_obj_t NORETURN machine_reset(void) { machtimer_deinit(); machine_wdt_start(1); for ( ; ; );
Do you know of an example hardware reset circuit compatible with the GPy that would work?
Would it help to detach from the LTE network and reset the LTE modem before calling the machine.reset()?
lte.detach(reset=True) machine.reset()
-
@tlanier If you are talking about watchdog, are you using the software watchdog or an external hardware watchdog?
My view on that is, that the only reliable way to reset the device is either a power cycle of pulling the reset line low. For the LTE and LoRa modems only a power cycle ensures a clean restart.
-
I've now discovered that the machine.reset() lockup occurs even if the routine is called outside of an alarm/timer interrupt. Running tests on 24 GPy units, only one unit had the problem.
I've also added a call to machine.disable_irq() before calling reset() but that did not help.
Here's the code that sometimes locks up at the call to machine.reset().
Does anyone know a reliable way to reset the processor in all situations? Is it a good idea to disable irq's before calling the reset function. I'm just trying everything I can think of to make this reliable. Anybody have any other ideas?
Note that the watchdog is also not resetting the processor in this situation.
Thanks
##################################################################### # Reset System ##################################################################### def reset(err_code=0, lcd0=""): global wdt try: if g.reset_flag: return # prevent recursive calls to reset g.reset_flag = True g.timer1.cancel() # disable timer interrupts g.oNvram.err_code = err_code g.oNvram.reset = True g.oNvram.reset_count = g.oNvram.reset_count + 1 g.oNvram.save() print("***** RESETING SYSTEM *****") wdt = None # force watchdog to trigger reset if code below does not work i2c.lcd_disp(0, lcd0) i2c.lcd_disp(1, "reset code: {0}".format(err_code)) except Exception as ex: sys.print_exception(ex) while True: try: state = machine.disable_irq() machine.reset() except Exception as ex: machine.enable_irq(state) sys.print_exception(ex)