-
-
Save anecdata/fe35dc6a94069fc920edf61a64750b53 to your computer and use it in GitHub Desktop.
# SPDX-FileCopyrightText: 2023 anecdata | |
# | |
# SPDX-License-Identifier: MIT | |
import json | |
import microcontroller | |
import supervisor | |
from ⚙️ import * | |
# safemode.py is the entry point for SAFE MODE (hard fault, etc.) | |
# store supervisor.runtime.safe_mode_reason since it won't be available during boot.py or code.py | |
# NVM Safe Mode - to cross-check against safemode reason | |
if microcontroller.nvm[NVM_INDEX_SAFEMODE] != SAFEMODESET: | |
microcontroller.nvm[NVM_INDEX_SAFEMODE] = SAFEMODESET | |
# set up the safemode dict | |
safemode_dict = {} | |
safemode_dict["safemode_reason"] = str(supervisor.runtime.safe_mode_reason) | |
update_restart_dict_time(safemode_dict) # add timestamp | |
# write dict as JSON | |
precode_file_write("/safemode.json", json.dumps(safemode_dict)) # use storage.remount() | |
if False: # check for any safemode conditions where we shouldn't RESET | |
pass | |
else: | |
# RESET out of safe mode | |
microcontroller.reset() # or alarm.exit_and_deep_sleep() |
Original PR: adafruit/circuitpython#7577
NVM (non-volatile memory) is available on most ports, and persists until explicitly changed
Sleep Memory (alarm.sleep_memory
) is also available on some ports, and persists from boot.py
to code.py
, across reloads, and during light and deep sleep
What the heck is this from ⚙️ import *
madness? :)
I love emoji, maybe too much ;-) it's just where I parked the constants and functions that get reused
# SPDX-FileCopyrightText: 2023 anecdata
#
# SPDX-License-Identifier: MIT
import storage
# safemode.py & boot.py file write
def precode_file_write(file, data):
storage.remount("/", False) # writeable by CircuitPython
with open(file, "w") as fp:
fp.write(f"{data}\n")
fp.flush()
storage.remount("/", True) # writeable by USB host
I love emoji, maybe too much ;-) it's just where I parked the constants and functions that get reused
😆 👍
That's gonna throw a few people, using emoji imports, but I kind of love and hate it with equal measures.
I've changed the utility file from emoji to utility.py, code-wise the file open mode to a+ for appending (I want a boot/safemode reason history and have megs of space), and possible reading. Also made sure the calls to add the timestamp all are followed by adding traceback (rather have it than not even if null). And my timestamp is supervisor.ticks_ms() as that seemed consistent. In safemode I print the reason after writing the file and pause for 1second in case I'm watching the terminal on a tiny TFT and want to read the reason (similarly in code.py print run_dict with reason+traceback).
Thank you for this, it's made my weekend
Nice. Yeah, my intention was to highlight the features of safemode.py, and carrying data forward through into boot.py and code.py. Hopefully folks modify and extend it to better suit their needs.
print()
in safemode.py
goes nowhere, hence the file write and nvm. print()
in boot.py
also goes nowhere except for getting appended to boot_out.txt
, up to 512 bytes total (no serial console / REPL until [re]load / run time / code.py
).
I mostly work with espressif
and raspberrypi
ports, and there are some differences that may be beneficial in either case. For example, espressif
maintains the on-chip RTC time across soft resets, whereas raspberrypi
does not. Also, there is a new PR to add sleep_memory on the raspberrypi
port, and in that case sleep_memory
survives not only deep sleep and reloads, but also soft resets (microcontroller.reset()
).
Adafruit Learn Guide: "CircuitPython Safe Mode"
https://learn.adafruit.com/circuitpython-safe-mode
Adafruit Learn Guide: "CircuitPython Essentials: CircuitPython Storage" (writing to flash)
https://learn.adafruit.com/circuitpython-essentials/circuitpython-storage
nice!
Emoji's as imports. What is this chaos... I love it.
from 💣 import TotallySafeCodeNotA💥
from 📷import 🖼️ I could see some neat uses.
I wonder how do you test safemode.py
. The documentation mentions supervisor.SafeModeReason.PROGRAMMATIC
however there is not much about how to trigger it. I tried blowing the stack in code.py
however that ends up with RuntimeError
.
edit: I see now, the guide on https://learn.adafruit.com/circuitpython-safe-mode/safe-mode-reasons describes how to do it using microcontroller.on_next_reset(microcontroller.RunMode.SAFE_MODE)
Safe Mode will be re-entered and halt operation if there are exceptions in safemode.py
Manually entering Safe Mode using the device buttons will not execute safemode.py (or boot.py, or code.py)