Reset and Boot Sequence ======================= A device running MicroPython follows a particular boot sequence to start up and initialise itself after a reset. .. note:: The ``_boot.py`` → ``boot.py`` → ``main.py`` → REPL sequence described below is what the firmware runs on **every reset**, regardless of how you connect — so it always applies. When you run a script from **OpenMV IDE**, the IDE interrupts the currently-running ``main.py`` and runs the script open in the editor in its place, over its own debug protocol. It does not use the on-device :doc:`REPL `, so the *REPL-specific* references on this page (the interactive prompt, ``Ctrl-D`` / ``Ctrl-C`` at a serial terminal, etc.) apply to standalone operation and direct serial-terminal sessions — but the boot sequence itself applies in all cases. .. _hard_reset: Hard reset ---------- Booting from hard reset is what happens when a board is first powered up, a cold boot. This is a complete reset of the MCU hardware. The MicroPython port code initialises all essential hardware (including embedded clocks and power regulators, internal serial UART, etc), and then starts the MicroPython environment. Existing :doc:`RTC ` configuration may be retained after a hard reset, but all other hardware state is cleared. The same hard reset boot sequence can be triggered by a number of events such as: - Python code executing :func:`machine.reset()`. - User presses a physical Reset button on the board (where applicable). - Waking from deep sleep (on most ports). - MCU hardware watchdog reset. - MCU hardware brown out detector. The details of hardware-specific reset triggers depend on the port and associated hardware. The :func:`machine.reset_cause()` function can be used to further determine the cause of a reset. .. _soft_reset: Soft Reset ---------- When MicroPython is already running, it's possible to trigger a soft reset by :ref:`typing Ctrl-D in the REPL ` or executing :func:`machine.soft_reset()`. A soft reset clears the Python interpreter, frees all Python memory, and starts the MicroPython environment again. State which is cleared by a soft reset includes: - All Python variables, objects, imported modules, etc. - Most peripherals configured using the :doc:`machine module `. There are very limited exceptions, for example :doc:`machine.Pin ` modes (i.e. if a pin is input or output, high or low) are not reset on most ports. More advanced configuration such as :func:`Pin.irq()` is always reset. - Bluetooth. - Network sockets. Open TCP sockets are closed cleanly with respect to the other party. - Open files. The filesystem is left in a valid state. Some system state remains the same after a soft reset, including: - Any existing network connections (Ethernet, Wi-Fi, etc) remain active at the IP Network layer. Querying the :doc:`network interface from code ` may indicate the network interface is still active with a configured IP address, etc. - An active :doc:`REPL ` appears continuous before and after soft reset, except in some unusual cases: * A serial UART REPL will restore its default hardware configuration (baud rate, etc). - CPU clock speed is usually not changed by a soft reset. - :doc:`RTC ` configuration (i.e. setting of the current time) is not changed by soft reset. .. _boot_sequence: Boot Sequence ------------- When MicroPython boots following either a hard or soft reset, it follows this boot sequence in order: _boot.py ^^^^^^^^ This is an internal script :doc:`frozen into the MicroPython firmware `. It is provided by MicroPython on many ports to do essential initialisation. For example, on most ports ``_boot.py`` will detect the first boot of a new device and format the :doc:`internal flash filesystem ` ready for use. Unless you're creating a custom MicroPython build or adding a new port then you probably don't need to worry about ``_boot.py``. It's best not to change the contents unless you really know what you're doing. .. _boot.py: boot.py ^^^^^^^ A file named ``boot.py`` can be copied to the board's internal :ref:`filesystem ` using :doc:`mpremote `. If ``boot.py`` is found then it is executed. You can add code in ``boot.py`` to perform custom one-off initialisation (for example, to configure the board's hardware). A common practice is to configure a board's network connection in ``boot.py`` so that it's always available after reset for use with the :doc:`REPL `, :doc:`mpremote `, etc. .. warning:: boot.py should always exit and not run indefinitely. Depending on the board, some hardware initialisation is delayed until after ``boot.py`` exits. This includes initialising USB on the STM32-based OpenMV Cams. On these boards, output printed from ``boot.py`` may not be visible on the built-in USB serial port until after ``boot.py`` finishes running. The purpose of this late initialisation is so that it's possible to pre-configure particular hardware in ``boot.py``, and then have it start with the correct configuration. .. note:: It is sometimes simpler to not have a ``boot.py`` file and place any initialisation code at the top of ``main.py`` instead. .. _main.py: main.py ^^^^^^^ Similar to ``boot.py``, a file named ``main.py`` can be copied to the board's internal :ref:`filesystem `. If found then it is executed next in the startup process. ``main.py`` is for any Python code that you want to run each time your device starts. Some tips for ``main.py`` usage: - ``main.py`` doesn't have to exit, feel free to put an infinite ``while True`` loop in there. - For complex Python applications you don't need to put all your code in ``main.py``. ``main.py`` can be a simple entry point that imports your application and starts execution:: import my_app my_app.main() This can help keep the structure of your application clear. It also makes it easy to install multiple applications on a board and switch among them. - It's good practice when writing robust apps to wrap code in ``main.py`` with an exception handler to take appropriate action if the code crashes. For example:: import machine, sys import my_app try: my_app.main() except Exception as e: print("Fatal error in main:") sys.print_exception(e) # Following a normal Exception or main() exiting, reset the board. # Following a non-Exception error such as KeyboardInterrupt (Ctrl-C), # this code will drop to a REPL. Place machine.reset() in a finally # block to always reset, instead. machine.reset() Otherwise MicroPython will drop to the REPL following any crash or if main exits (see below). - Any global variables that were set in ``boot.py`` will still be set in the global context of ``main.py``. - To fully optimise flash usage and memory consumption, you can copy :doc:`pre-compiled ` ``main.mpy`` and/or ``boot.mpy`` files to the filesystem, or even :doc:`freeze ` them into the firmware build instead. - ``main.py`` execution is skipped when a soft reset is initiated from :ref:`raw REPL mode ` (for example, when :doc:`mpremote ` or another program is interacting directly with MicroPython). Interactive Interpreter (REPL) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ If ``main.py`` is not found, or if ``main.py`` exits, then :doc:`repl` will start immediately. .. note:: Even if ``main.py`` contains an infinite loop, typing Ctrl-C on the REPL serial port will inject a `KeyboardInterrupt`. If no exception handler catches it then ``main.py`` will exit and the REPL will start. Any global variables that were set in ``boot.py`` and ``main.py`` will still be set in the global context of the REPL. The REPL continues executing until Python code triggers a hard or soft reset. .. _soft_bricking: Soft Bricking (failure to boot) --------------------------------- It is rare but possible for MicroPython to become unresponsive during startup, a state sometimes called "soft bricked". For example: - If ``boot.py`` execution gets stuck and the native USB serial port never initialises. - If Python code reconfigures the REPL interface, making it inaccessible. Rest assured, recovery is possible! If you use OpenMV IDE, simply connecting is often enough — the IDE stops the running ``main.py`` and takes over. If the camera won't connect at all, use the **Factory Reset** below. The ``Ctrl-C`` method described next is for direct serial-terminal sessions — it relies on the on-device REPL, which OpenMV IDE does not use. KeyboardInterrupt ^^^^^^^^^^^^^^^^^ In many cases, opening the REPL serial port and typing ``Ctrl-C`` will inject `KeyboardInterrupt` and may cause the running script to exit and a REPL to start. From the REPL, you can use :func:`os.remove()` to remove the misbehaving Python file:: import os os.remove('main.py') To confirm which files are still present in the internal filesystem:: import os os.listdir() Factory Reset ^^^^^^^^^^^^^ If you can't get to a REPL using the method above, the remaining option is a factory reset: erasing the entire contents of the internal flash filesystem. This is also the fix if the internal filesystem has become corrupted. OpenMV IDE has several built-in ways to do this. First put the camera into its recovery/bootloader mode — the method differs per board, so see the **Recovery and debug pins** section of your board's :doc:`quick reference ` for how to enter it. Then click the connect button in OpenMV IDE and follow the prompts to erase the filesystem and re-flash the firmware. .. warning:: Re-flashing the firmware *without* erasing the filesystem will usually not recover from soft bricking, as a normal firmware update preserves the contents of the filesystem. Be sure to choose the erase option when OpenMV IDE prompts for it. If you get stuck, ask on the `OpenMV forums `_.