adafruit / circuitpython

CircuitPython - a Python implementation for teaching coding with microcontrollers
https://circuitpython.org
Other
4.11k stars 1.22k forks source link

Force boards to start in safe mode #990

Closed dhalbert closed 5 years ago

dhalbert commented 6 years ago

Sometimes bad code in boot.py or code.py can leave the board in a stuck state with no access to CIRCUITPY or the REPL.

Some ways to get out of this state:

  1. Erase SPI flash: we have some .uf2's that do this. Have to build separately for many different boards, and not sure about QSPI support.
  2. Load a special CircuitPython .uf2 that never runs boot.py or code.py. Again, need to build these, but perhaps friendlier to use, and allows repair of the bad files.
  3. Could we detect triple or quadruple reset button push? I think this would require a UF2 bootloader change.
  4. Force board to start in safe mode. Only the CPX currently has this feature: if you hold down both buttons on boot, CircuitPython will go directly to safe mode.

It would be nice to be able to do 4. on regular boards without extra buttons. We could look for a particular set of pins pulled low or high, but there's always the possibility the user has wired the board in such a way that it might trigger that pattern. But we do have "spare" pins on SWDIO and SWCLK.

SWCLK is not suitable, because if it's held low the board goes into "extended reset", and it's pulled up internally during reset. But perhaps we could detect SWDIO being pulled high or low (as opposed to just floating). But we have to be careful not to interfere with debugger access.

sommersoft commented 6 years ago

Started sheet-reading since this is an interesting proposition. I like 4 the most as well, since it reduces user effort.

But perhaps we could detect SWDIO being pulled high or low (as opposed to just floating). But we have to be careful not to interfere with debugger access.

Since SWDIO is on the PORT, could easily establish it as an EXTINT prior to boot.py/code.py loading. To guard against debugger interference, seems that wrapping it in a conditional checking for DSU->STATUSB.DBGPRES (Debugger Present) would work. _EDIT: This approach in my head is only available at startup, and using DBGPRES works for it since it would catch a "cold-plugged" debugger. If no softboot request is detected, then SWDIO is set to its default and we proceed as normal.

Option 2 would get my next vote, since it is similar to the current approach, but is also non-volatile unlike current/1.

notro commented 6 years ago

How about we use a special baudrate to reset into safe mode like with the 1200 baud and reset_to_bootloader()?

tannewt commented 6 years ago

How can code.py do it?

Could we give boot.py a time limit and safe mode if its exceeded?

tannewt commented 5 years ago

I'm doing this. I'm doing a slower double tap than the bootloader. If there is a status neopixel it'll be yellow briefly. The single status LED will blink three times. The wait time is currently 700ms.