| docs | ||
| firmware | ||
| tools | ||
| .env.example | ||
| .gitignore | ||
| README.md | ||
ESP32-C3 Universal MicroPython Launcher
A fault-tolerant launcher intended for the Magnifica ESP32-C3 project, but kept generic enough to reuse elsewhere.
Core behavior
/.envon the device holds all secrets and per-device options.- Wi-Fi, BLE and UART are independent startup options.
- One Git repository can hold both the launcher and main application.
- The ESP32 does not need Git. It downloads a tiny JSON manifest and only changed raw files.
/launcher.py,/app/main.py, and other manifest-listed files can update in one staged transaction.- SHA-256 is verified before any file is committed.
.bakrollback files plus a stableboot.pyprotect against interrupted or broken OTA.- Secrets are never updated and are never in the manifest.
Energy / update behavior
There is no 5-second repository polling loop.
Defaults are:
- remote manifest check: once every 24 hours
- boot delay before a possible check: 3 minutes
- minimum inactivity before OTA may begin: 5 minutes
- if busy when due: reconsider locally after 15 minutes
- network failure: wait 1 hour before another remote attempt
- BLE disabled by default
- Wi-Fi power saving enabled best-effort
If Wi-Fi exists only for OTA, set WIFI_KEEP_CONNECTED=false. If the app itself needs Wi-Fi for remote control, leave it true.
Repository layout
repo/
├─ README.md
├─ .gitignore
├─ .env.example
├─ firmware/
│ ├─ boot.py # stable recovery stub; install manually
│ ├─ launcher.py # OTA-updatable launcher
│ ├─ manifest.json # generated SHA-256 manifest
│ └─ app/
│ ├─ __init__.py
│ └─ main.py # actual Magnifica logic
├─ tools/
│ └─ build_manifest.py
└─ docs/
├─ DIGITAL_PROBE.md
├─ SETUP.md
└─ OTA_PROTOCOL.md
On the ESP32:
/
├─ boot.py
├─ launcher.py
├─ .env
├─ update_state.json # generated automatically
└─ app/
├─ __init__.py
└─ main.py
Safe inactivity-aware updates
The application receives ctx["activity"].
Call this whenever a real interaction or machine-state change occurs:
ctx["activity"].touch()
Bracket anything that must never be interrupted by OTA:
ctx["activity"].begin()
try:
await brew_or_move_mechanism()
finally:
ctx["activity"].end()
OTA can start only after the long check interval is due and the device has been idle for UPDATE_MIN_IDLE_SECONDS and no busy lock is active.
Gitea example
UPDATE_PROVIDER=gitea
UPDATE_REPO_URL=https://git.example.com/user/repo
UPDATE_BRANCH=main
UPDATE_AUTH_MODE=auto
UPDATE_TOKEN=your_token
A public Gitea repo uses:
UPDATE_AUTH_MODE=none
Other platforms
Built-in URL/auth handling exists for Gitea/Forgejo/Codeberg, GitLab, GitHub and Bitbucket. For unusual self-hosted layouts, use UPDATE_MANIFEST_URL or UPDATE_RAW_BASE_URL and optionally a custom auth header.
See docs/SETUP.md and docs/OTA_PROTOCOL.md.
Development probe
The first Magnifica S development tool is an input-only digital probe for
front-panel signal discovery. See docs/DIGITAL_PROBE.md; do not connect an
unknown machine wire directly to an ESP32 GPIO.