# 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 - `/.env` on 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. - `.bak` rollback files plus a stable `boot.py` protect 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 ```text 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: ```text / ├─ 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: ```python ctx["activity"].touch() ``` Bracket anything that must never be interrupted by OTA: ```python 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 ```env 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: ```env 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.