You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: docs/architecture/boot-sequence.md
+13Lines changed: 13 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -7,6 +7,7 @@ MicroPythonOS consists of several core components that initialize and manage the
7
7
-**internal_filesystem/main.py**: Hands off execution to /lib/mpos/main.py by importing it
8
8
9
9
-**/lib/mpos/main.py**:
10
+
- Disables the Ctrl-C interrupt character for the duration of the boot
10
11
- Detects the hardware board
11
12
- Initializes the filesystem driver
12
13
- Mounts the freezefs into /builtin/
@@ -25,3 +26,15 @@ MicroPythonOS consists of several core components that initialize and manage the
25
26
See [Filesystem Layout](filesystem.md) for where apps and data are stored.
26
27
27
28
See [Service](../frameworks/service.md) for details on writing boot services.
29
+
30
+
## Connecting over serial during boot
31
+
32
+
Tools that open the serial port send Ctrl-C to interrupt whatever is running — `mpremote` does this whenever it enters the raw REPL. Arriving mid-boot, that would abort the boot scripts and leave the device at a bare REPL shell with the OS only half-started, which is easily mistaken for a broken build: the prompt looks normal, but apps are missing and files placed in `/lib` appear to have no effect.
33
+
34
+
To prevent this, `/lib/mpos/main.py` disables the interrupt character (`micropython.kbd_intr(-1)`) as its first action, and the asyncio REPL manages it from then on. It is restored on the paths that fall back to the REPL shell, so a failed boot still gives you an interruptible prompt.
35
+
36
+
Practical consequences:
37
+
38
+
- Connecting while the device boots is safe; the command waits for the boot to finish instead of corrupting it.
39
+
- Wait for `Starting asyncio REPL...` on the serial console to know the boot completed.
40
+
- Ctrl-C can no longer break into a boot that hangs — use the board's reset button in that case.
Create a background task that is automatically restarted if it dies unexpectedly. Used by the OS for long-lived services that must never stay down, such as the asyncio REPL console.
197
+
198
+
**Parameters:**
199
+
200
+
-`coroutine_factory` - A **callable returning a fresh coroutine** (not a coroutine object), because a new one is needed for every restart
201
+
-`restart_delay_ms` (int) - Delay before each restart, in milliseconds
202
+
203
+
**Returns:**
204
+
205
+
- Task object for the supervisor (cancel it to stop supervision)
206
+
207
+
**Restart behavior:**
208
+
209
+
- Task raises `Exception` or `KeyboardInterrupt` → logged and restarted after `restart_delay_ms`
210
+
- Task returns normally → supervision ends (a clean exit is treated as intentional)
211
+
- Supervisor task is cancelled → supervision ends, no restart
212
+
213
+
**Example:**
214
+
```python
215
+
defmake_listener():
216
+
return my_service.listen_forever()
217
+
218
+
TaskManager.create_supervised_task(make_listener)
219
+
```
220
+
221
+
Note the factory: passing `create_supervised_task(my_service.listen_forever())` would hand over a single coroutine object that cannot be re-run after it dies.
222
+
194
223
### Sleep Operations
195
224
196
225
#### `TaskManager.sleep(seconds)`
@@ -473,10 +502,15 @@ async def safe_task(self):
473
502
474
503
**Key features:**
475
504
-`create_task()` - Wraps `asyncio.create_task()`
505
+
-`create_supervised_task()` - Restarts a task that dies from an exception or KeyboardInterrupt
MicroPython's `run_until_complete()` only catches `CancelledError` and `Exception`, so a `KeyboardInterrupt` raised inside any task (for example from a stray Ctrl-C sent by a host connecting over serial) escapes the event loop and would otherwise terminate every running task while the LVGL UI keeps running on its hardware timer. `TaskManager.start()` catches this and re-enters `asyncio.run()`; the task queue survives the unwind, so the remaining tasks resume.
513
+
480
514
**Thread model:**
481
515
- All async tasks run on main asyncio event loop
482
516
- No separate threads created (unless using `_thread` module separately)
0 commit comments