Skip to content

Instantly share code, notes, and snippets.

@anecdata
Last active April 16, 2024 19:44
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save anecdata/fe35dc6a94069fc920edf61a64750b53 to your computer and use it in GitHub Desktop.
Save anecdata/fe35dc6a94069fc920edf61a64750b53 to your computer and use it in GitHub Desktop.
CircuitPython 8 safemode.py
# 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()
@anecdata
Copy link
Author

anecdata commented Feb 19, 2023

boot.py

# SPDX-FileCopyrightText: 2023 anecdata
#
# SPDX-License-Identifier: MIT

import json
import microcontroller

from ⚙️ import *

# boot.py is the entry point for RESET (software, reset button, or power cycle)
# read and process safemode.json if desired

# NVM Safe Mode - clear it for the next Safe Mode
if microcontroller.nvm[NVM_INDEX_SAFEMODE] != SAFEMODECLEAR:
    microcontroller.nvm[NVM_INDEX_SAFEMODE] = SAFEMODECLEAR

# set up the boot dict
boot_dict = {}
boot_dict["reset_reason"] = str(microcontroller.cpu.reset_reason)
update_restart_dict_time(boot_dict)  # add timestamp

# write dict as JSON
precode_file_write("/boot.json", json.dumps(boot_dict))  # use storage.remount()

@anecdata
Copy link
Author

anecdata commented Feb 19, 2023

code.py

# SPDX-FileCopyrightText: 2023 anecdata
#
# SPDX-License-Identifier: MIT

import supervisor

from ⚙️ import *

# code.py is the entry point for RELOAD (software, REPL control-D, auto-reload, or run after reset)
# read and process safemode.json and boot.json if desired
# read and process boot_out.txt if desired (should have any traceback from boot.py, and any print() from boot.py, up to ~512 bytes)

# set up the run dict
run_dict = {}
run_dict["run_reason"] = str(supervisor.runtime.run_reason)
update_restart_dict_time(run_dict)  # add timestamp
update_restart_dict_traceback(run_dict)  # supervisor.get_previous_traceback

@anecdata
Copy link
Author

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)

@anecdata
Copy link
Author

@anecdata
Copy link
Author

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

@todbot
Copy link

todbot commented Feb 20, 2023

What the heck is this from ⚙️ import * madness? :)

@anecdata
Copy link
Author

anecdata commented Feb 20, 2023

I love emoji, maybe too much ;-) it's just where I parked the constants and functions that get reused

@anecdata
Copy link
Author

anecdata commented Mar 19, 2023

# 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

@tyeth
Copy link

tyeth commented Mar 19, 2023

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.

@tyeth
Copy link

tyeth commented May 21, 2023

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
image

@anecdata
Copy link
Author

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()).

@anecdata
Copy link
Author

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

@todbot
Copy link

todbot commented Jun 25, 2023

nice!

@DJDevon3
Copy link

Emoji's as imports. What is this chaos... I love it.

@todbot
Copy link

todbot commented Jan 30, 2024

from 💣 import TotallySafeCodeNotA💥

@DJDevon3
Copy link

from 📷import 🖼️ I could see some neat uses.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment