mirror of
https://github.com/imjasonh/esp32
synced 2026-07-06 23:52:24 +00:00
- New `docs/observability.md`: descriptive (present-tense) write-up of the Cloud Logging + Cloud Monitoring pipelines as currently shipped. Replaces the historical `logs-plan.md` + `monitoring-plan.md` planning docs. - New `docs/setup.md`: prerequisites, first flash, day-to-day Make targets, and the optional GCP setup (lifted out of the README so the README can stay terse). Includes the Python 3.12-shim explanation from the old `notes.txt`. - Move `ota.md` → `docs/ota.md`. - Move `eink-plan.md` → `docs/eink-plan.md`. Per-feature plans still use the `<feature>-plan.md` name; once shipped they get rewritten in present tense alongside the other docs. - Delete `logs-plan.md`, `monitoring-plan.md`, `notes.txt`. Their user-facing content is now in `docs/setup.md`; their LLM-relevant bits (architectural rationale, partition-table CMake quirk, Python shim, no-LED, `make` conventions, NVS key length cap) are in `CLAUDE.md`. - Trim `README.md` to a top-level overview + links into `docs/`. - Update internal cross-references (Makefile, ota.md, eink-plan.md, tools/provision/src/main.rs doc-comment). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5.5 KiB
5.5 KiB
Project notes for Claude
Project layout
src/main.rs firmware entrypoint, Wi-Fi + boot orchestration
src/ota.rs OTA poll loop, manifest fetch, blob streaming
src/sig.rs cosign Sigstore Bundle verification
src/trust.rs NVS-loaded trust config (signer identities + Sigstore CAs)
src/cloud_log.rs tracing → Cloud Logging (queue + sender thread)
src/metrics.rs chip-health snapshots → Cloud Monitoring time series
src/gcp_auth.rs multi-scope JWT mint, shared TokenProvider, ShortHttpsLock,
OtaDownloadGuard, HTTPS helpers
trust/ Sigstore Fulcio root + intermediate certs (provisioned via NVS)
tools/publisher/ host-side tool that pushes signed OCI artifacts to GHCR
tools/provision/ host-side tool that builds + flashes NVS partition
.github/workflows/ ci.yml (PRs) + publish.yml (push to main)
Cargo.toml firmware deps
partitions.csv OTA-capable partition table (1.94 MB app slots)
sdkconfig.defaults.in ESP-IDF kconfig template (Makefile substitutes paths)
Makefile build / flash / monitor / provision / publish entrypoints
provisioning.toml.example template for per-device NVS values
docs/setup.md first-flash + day-to-day workflow + GCP setup
docs/ota.md full OTA + provisioning + signing system documentation
docs/observability.md Cloud Logging + Monitoring design (current state)
docs/eink-plan.md planned e-ink display work
README.md short top-level overview, links into docs/
Conventions established in this repo
docs/for descriptive docs and per-feature plans. Forward- looking work uses<feature>-plan.md; once shipped, the plan is rewritten in present tense and lives next to the other reference docs (e.g.logs-plan.md+monitoring-plan.mdcollapsed intodocs/observability.md).- Cargo.lock IS tracked for all three crates here — they're all binaries.
- Don't use "and" in Make target names — chain existing targets
instead (
make publish monitor, notmake publish-and-monitor). - Env-setup scripts (e.g. espup's
export-esp.sh) live in the project dir, not~. - GHCR auth uses classic PATs with
write:packages(fine-grained PATs don't expose a Packages permission for user-owned packages). - Use
maketargets, not rawespflash/cargocommands. The Makefile sets toolchain env, the Python 3.12 shim, and (critically) always passes--partition-table partitions.csvtoespflash flash— without that, espflash 4.x silently writes a default factory-only partition table over our OTA layout.
Architectural decisions worth knowing
- std Rust on top of ESP-IDF, not no_std with
esp-hal. We need easy Wi-Fi + HTTP + TLS viaesp-idf-svc, plus standard threading/heap. The cost is ESP-IDF's C SDK + CMake/Ninja/Python toolchain; the lighter no_std path is worth knowing for future projects. - Pure-Rust
rsacrate for JWT signing, not mbedtls FFI. Adds ~50 KB but keeps the code in safe Rust. - mbedtls heap is tight. With cloud_log + metrics + OTA each
potentially holding a TLS session, three concurrent handshakes
(~25–35 KB each) blow our heap. Mitigations stacked in
sdkconfig.defaults.in(MBEDTLS_DYNAMIC_BUFFER, free config/CA-cert,KEEP_PEER_CERTIFICATE=n) plus runtime serialization viagcp_auth::ShortHttpsLockand anOTA_DOWNLOAD_IN_PROGRESSgate. Seedocs/observability.mdfor the full chain. - OTA download is not serialized. Multi-second blob downloads
must not block per-5 s log flushes. cloud_log + metrics check
OTA_DOWNLOAD_IN_PROGRESSand skip-but-don't-drain while a download is in flight; the queue absorbs and flushes after.
Gotchas to remember
- Custom partition table path resolution. ESP-IDF resolves
CONFIG_PARTITION_TABLE_CUSTOM_FILENAMErelative to its top-level CMake project, which for embuild istarget/.../esp-idf-sys-<hash>/out/, not the repo root. Workaround:sdkconfig.defaults.inis the template (committed), with the placeholder@PROJECT_DIR@/partitions.csv; the Makefile substitutes the absolute path at build time and writes the resolvedsdkconfig.defaults(gitignored). Don't "fix" the placeholder. - Python 3.12 shim. embuild grabs whatever
python3is first on PATH. macOS's default is 3.9.6, which silently breaks ESP-IDF dependency resolution. The Makefile creates.embuild/python-shim/python3 → uv-managed 3.12and prepends it to PATH. If a dep-check error appears after a fresh clone:rm -rf .embuild && make build. - No user LED on this board. Inland ESP-WROOM-32 has only a
power LED; the DOIT/DEVKITC blue LED on GPIO 2 is not populated.
Don't suggest GPIO blink demos — use serial logging via
make monitorinstead. - NVS key names cap at 15 characters. When adding new keys to
tools/provision/, abbreviate (metric_intvl, notmetrics_interval). The.tomlfield can be long and readable; the NVS key is the constrained one. make flashafter a freshmake bootstrapcan boot the wrong partition: bootstrap clears otadata so the bootloader picks ota_0, but a subsequent OTA download installs to ota_1 and otadata starts pointing there. A baremake flashwrites to ota_0 (the first app partition in the table) but the device will keep booting ota_1 until youmake bootstrapagain or wait for OTA promotion.