102 Commits

Author SHA1 Message Date
Julien Calixte
b899286639 feat(sd): reformat a fresh card on mount failure in the spike
Flip format_if_mount_failed to true so a fresh exFAT/unformatted bench card is
reformatted to FAT on the device instead of failing — no Mac-side prep for the
spike. Fires only on a filesystem mount failure, not the CMD59 protocol
rejection. The real persistence module must keep this false.
2026-07-11 11:14:15 +02:00
Julien Calixte
698c10c8c0 fix(sd): unlink target before f_rename to overwrite on FAT
FatFS's f_rename returns FR_EXIST on an existing destination — unlike POSIX
rename(2) it won't replace it, so re-running the round-trip failed at rename.
Unlink the target first (tolerating a missing one for the first save). This
opens a crash window the real persistence module must close with *.tmp boot
recovery (ADR-007).
2026-07-11 11:14:04 +02:00
Julien Calixte
bef9587a34 chore(sd): drop Spike 3 CMD59 diagnostic logging
The per-command sdmmc/sdspi DEBUG logs only existed to diagnose the CMD59
init failure, now resolved (verified 2026-07-11). Remove the build-time log
ceiling and the runtime esp_log_level_set block.
2026-07-11 11:13:42 +02:00
Julien Calixte
78958d9de6 feat(justfile): split SD provisioning into init/load/provision
Replace the single seed-sd recipe with three entry points sharing
private helpers: init (repo + config), load (repo copy only), provision
(config only). Each excludes gitignored paths via git's own resolution
(so node_modules and firmware/.env never reach the card), writes
/sd/typoena.conf from firmware/.env (Wi-Fi + PAT + git identity, PAT
never printed), and ejects the card so it goes straight into Typoena.
2026-07-11 01:44:38 +02:00
Julien Calixte
9ea8e74394 chore(justfile): add seed-sd recipe to pre-seed the notes repo
The device never cold-clones the 566 MB notes repo over Wi-Fi + mbedTLS;
a laptop copies the clone onto the SD card via a reader so the device
only ever takes the open + fast-forward path. Excludes are driven off
git's own ignore resolution (collapsing node_modules, .env, etc.), and
the target card is auto-detected but refused on 0 or >1 match so rsync
--delete can't wipe the wrong disk.
2026-07-11 01:24:56 +02:00
Julien Calixte
da23b95cfd feat(editor): add :w/:sync commands as host save/publish effects
The pure, no-IO editor core can't persist or push, so `handle()` now
returns an `Effect` (None/Save/Publish) that the firmware actions. `:w`
signals Save, `:sync` signals Publish (save then git push); `:wq`/`:x`
alias Save with vim's "quit" half dropped, and the `:q` family stays
absent — an always-on appliance has nothing to quit to. `:fmt` remains
in-core and yields no effect.

The main loop keeps the batch's last effect and matches on it. Actual
SD-write and git-push are the v0.1 gate and still stubbed as log lines.
2026-07-11 01:24:46 +02:00
Julien Calixte
bc13c5b065 feat(usb-kbd): compose dead-key accents before enqueuing keys
Route decoded keys through keymap::Composer, so on device `'`+e yields é
(grave/acute/circumflex/diaeresis/tilde, plus ç), and reset it on
keyboard detach. Enabling it is now safe because the editor buffer is
UTF-8-correct. Not verified on the xtensa build in this environment.
2026-07-11 00:30:38 +02:00
Julien Calixte
e8063a2b13 refactor(editor): extract editor + display into host-testable crates
Move the editor core out of the firmware crate (pinned to the xtensa
target, so it can't run `cargo test`) into a standalone `editor` crate,
with the panel framebuffer + geometry split into a `display` crate. Both
depend only on embedded-graphics + keymap, so the editor is now
host-buildable and unit-tested (8 characterization tests). Firmware links
them and re-exports the geometry from epd.rs; behaviour is unchanged.

The xtensa firmware build was not verified in this environment.
2026-07-11 00:28:37 +02:00
Julien Calixte
875658a661 feat(wifi): retry association with backoff in shared connect helper
The single-shot connect_wifi aborted boot on any transient association or
DHCP miss — common right after a reset, when the AP still holds the stale
pre-reset association. Add a bounded retry (5 attempts, 500ms→4s backoff,
disconnect between tries) so those transient failures recover instead of
failing the boot.

Extract the logic into firmware::net (new lib target) and rewire the
wifi_tls / git_push / git_sync spikes to it, removing three duplicated
copies. main.rs reuses this when Wi-Fi lands there instead of adding a
fourth copy.
2026-07-10 23:44:02 +02:00
Julien Calixte
8edd3badfc fix(usb-kbd): quiesce in-flight transfer before free; guard hot-plug leaks
Adopt the keymap crate (drops the PREV_KEYS/CAPS_USED statics for one
owned Decoder) and harden device teardown:

- #1 UAF: track REPORT_INFLIGHT (set on submit, cleared first thing in
  report_cb). Teardown moves to close_device, which pumps client events
  until the transfer quiesces before freeing it, and leaks rather than
  frees if it never does — a leak is recoverable, a use-after-free is
  not. usb_host_transfer_free's return is now checked.
- #3 leaks: a new attach while a device is still open tears the old one
  down first; control_request and start_report_polling free the transfer
  they allocated on a submit error.

Decode correctness is covered by the keymap tests; the teardown paths
are FFI and still need an on-device hot-plug run to confirm.
2026-07-10 10:36:47 +01:00
Julien Calixte
f197df6dbe style(editor): drop the mode line to the panel's bottom edge
Move the mode indicator (and the NO KBD flag above it) down so the
mode sits flush at the bottom-left with a 2 px margin, reclaiming the
dead space that was left below it.
2026-07-08 22:01:10 +02:00
Julien Calixte
ba4a8bfad8 feat(editor): put the mode indicator at the side-panel bottom-left
Move the mode + pending-command echo from the panel's top to its
bottom-left (vim `-- INSERT --` style); word count takes the top slot
and the NO KBD flag sits just above the mode. Also drop the
words-this-session field (not wanted) and its session_base_words state.
2026-07-08 21:48:21 +02:00
Julien Calixte
50f650fef6 feat(editor): split display into writing column and side panel
Carve the panel into a ~60-col writing column and a right-hand side
panel (per CONTEXT.md screen regions); the divider sits right of the
x=396 driver seam. The mode indicator and pending-command echo move
into the panel, while the ':' command line stays in the bottom strip.

The panel also shows word count and words-this-session (a snapshot
refreshed on a typing pause / non-Insert action, never per keystroke,
so ordinary typing keeps the fast windowed refresh) and a "NO KBD"
flag while the keyboard is dropped.
2026-07-08 21:33:41 +02:00
Julien Calixte
ddd9c387d3 feat(usb): track keyboard connection state
Add a persistent KBD_PRESENT atomic (set on setup, cleared on unplug)
and a keyboard_present() accessor. DEV_GONE is a one-shot detach event
the client loop consumes, not a state the panel can read.
2026-07-08 21:33:32 +02:00
Julien Calixte
b746d6e3f9 feat(editor): add Markdown affordances and a :fmt formatter
v0.6 affordances plus the v0.4 command-line mechanism, all in editor.rs:

- soft-wrap display lines at word boundaries (layout() no longer cuts
  mid-word; a single word wider than the panel still hard-breaks)
- Enter continues Markdown lists: -/*/+ repeat, N. increments, an empty
  item exits the list; only at end-of-line, indentation preserved
- heading lines (# .. ######) render faux-bold via a 1px double-strike
  (no bold Latin-9 font); checks the logical line so wrapped headings stay bold
- new `:` command mode (Command variant + status-strip echo, Esc/Backspace
  cancel) with one command, `:fmt`: align pipe tables (honoring :--: colons),
  collapse runs of blank lines to one, strip trailing whitespace; caret lands
  on roughly the same line afterwards
2026-07-07 23:44:46 +02:00
Julien Calixte
8dece735d1 fix(firmware): delete repo dir via path-based recursion, not remove_dir_all
std::fs::remove_dir_all deletes race-free via the openat/unlinkat/fdopendir
family; esp-idf's path-based FATFS VFS doesn't implement those, so it fails
with EACCES on the first entry. That looked like a read-only-attribute
problem but wasn't. A plain read_dir -> remove_file/remove_dir walk
(remove_tree) uses only what FATFS supports.

Hardware-verified: a diagnostic recursive delete removed the whole
/spiflash/repo (every entry reported writable, ro=false) then re-cloned +
pushed, twice. Wire remove_tree into both the RECLONE_EACH_BOOT reset and
the stale-clone recovery.

The p_open/p_creat shim (39e1155) stays: it keeps libgit2's objects writable
(ro=false), so neither this delete nor libgit2's own gc/repack/fetch-prune
is ever blocked by a read-only file on FAT.
2026-07-07 22:48:17 +02:00
Julien Calixte
8e848894fa feat(firmware): add RECLONE_EACH_BOOT toggle to git_sync
Debug/recovery knob (ships false): when true, wipe /spiflash/repo and re-clone
every boot instead of opening the persistent clone. Doubles as the validation
harness for the FAT read-only-delete fix — the wipe deletes libgit2-created
objects, which only succeeds once they are writable.
2026-07-07 09:33:08 +02:00
Julien Calixte
39e1155bf9 fix(libgit2): create objects writable so FAT can delete them
libgit2 creates loose objects and packfiles with mode 0444 (read-only). FATFS
turns that into the AM_RDO attribute and then refuses to f_unlink them (EACCES),
and esp-idf's FATFS VFS chmod cannot clear AM_RDO — so a written object can
never be deleted, blocking re-clone recovery and (later) fetch/repack.

Shim p_open/p_creat in esp_stubs.c to force owner-write into every create mode
(compile posix.c's originals under throwaway names, same mechanism as p_rename),
so nothing is ever created read-only. Immutability is only a safety hint on an
appliance where nothing but libgit2 touches these files.

Non-regressive on hardware: git_push, which links the same component, still
commits + pushes cleanly with the shim. The delete path itself is validated via
git_sync's RECLONE_EACH_BOOT knob (pending a dedicated flash).
2026-07-07 09:33:08 +02:00
Julien Calixte
afa61deaa6 fix(firmware): recover git_sync from a leftover clone dir
859c478 aborted if /spiflash/repo already existed — libgit2 refuses to
clone into a non-empty dir (code=Exists). Remove a writable stale/partial
clone and re-clone.

Milestone #2A now hardware-verified end to end: clone (boot 1) + persistent
open + fast-forward push (boot 2) against origin/main, on the 96 KB git
thread, min heap 6.8 MB — clone/checkout fit the stack, no bump needed.

Known limit: the remove only works on a *writable* leftover. libgit2 marks
objects/packs read-only and FATFS won't f_unlink a read-only file (EACCES);
POSIX chmod does not clear AM_RDO on esp-idf's FATFS VFS (verified). So
re-cloning over a run that wrote objects needs an AM_RDO-clearing unlink
shim in esp_stubs.c (increment B, also needed for fetch/repack); until then
the fallback is a one-time `espflash erase-region 0x310000 0x400000`.
2026-07-07 08:34:09 +02:00
Julien Calixte
859c4787cf feat(firmware): add persistent-clone git sync spike (git_sync)
Milestone #2 increment A: the product's real publish flow, distinct from
git_push's fresh-init-per-boot throwaway branch. open-or-clone a persistent
/spiflash/repo, append to a tracked notes.md, commit on top of the branch,
and fast-forward push over mbedTLS HTTPS+PAT. Reconcile handles up-to-date
and fast-forward; a true divergence (merge commit on FATFS) is deferred to
increment B. Runs on the dedicated 96 KB git thread; gated behind `git`.

Compiles clean; on-device clone/open+fast-forward not yet hardware-verified
(clone/checkout is deeper than the proven init path — the flash may need a
GIT_STACK bump).
2026-07-07 00:06:13 +02:00
Julien Calixte
15955a13f4 refactor(firmware): run git on a dedicated large-stack thread
libgit2's init→push chain is deeply stack-hungry (~67 KB measured for a
trivial config write; the push is deeper). Spike 7 sized the shared main
task stack at 96 KB for it, which forced the editor build to over-reserve
~80 KB.

Move git_publish onto its own std::thread (GIT_STACK = 96 KB via
Builder::stack_size) and join it from main. This lets
CONFIG_ESP_MAIN_TASK_STACK_SIZE drop back to 12288 — the Spike-6 value
proven to run the editor plus a TLS-on-main handshake — so the editor no
longer pays for git's stack.

Hardware-verified: the push ran off-main (mbedTLS + FATFS) with no panic,
no stack overflow, and no ENOMEM on the 96 KB spawn. This retires the
earlier "time() only works on the main task" misdiagnosis for good — it
was always the default 4 KB pthread stack overflowing, never thread-vs-main.
2026-07-06 23:46:55 +02:00
Julien Calixte
2519ed8650 feat(firmware): verify git-push TLS chain against embedded GitHub CAs
Retire the cert-check bypass. Embed GitHub's root CAs (github_roots.pem:
USERTrust ECC/RSA + DigiCert G2/Global Root CA), write them to /spiflash/ca.pem
at boot, and point libgit2's mbedTLS stream at them via
GIT_OPT_SET_SSL_CERT_LOCATIONS (CONFIG_MBEDTLS_FS_IO is on). The push callback
now returns CertificatePassthrough instead of CertificateOk, so the http
transport maps it to `is_valid ? 0 : -1` (httpclient.c:805) -- an untrusted or
MITM cert fails the push (fail-closed), no blanket-accept.

Hardware-verified 2026-07-06: `TLS trust store installed`, chain validated
against the embedded USERTrust ECC root, push accepted (branch
device/1783372683 on jcalixte/typoena-test). Roots must be refreshed if GitHub
rotates CAs; a product would prefer esp-idf's bundle via a custom subtransport.
2026-07-06 23:19:24 +02:00
Julien Calixte
a15789a1b4 feat(firmware): add on-device git push spike (git_push)
Wi-Fi + SNTP + flash-FAT + libgit2 in one bench binary: init a fresh working
copy, commit, and push a per-boot device/<unix> branch over mbedTLS HTTPS with
PAT auth. Gated behind the `git` feature; built/flashed via `just flash-git-push`.

Status: local git init verified on hardware; the ODB-write fix and the full
push are not yet confirmed end-to-end on device. Cert verification is bypassed
in the push callback (spike shortcut) -- real trust-store wiring must land
before this leaves the bench.
2026-07-06 00:18:30 +02:00
Julien Calixte
63ffd7e37a feat(firmware): bake and document git push env vars
build.rs embeds TW_REMOTE_URL / TW_GH_USER / TW_PAT / TW_AUTHOR_* via env!()
so only the git_push binary carries them (the editor references none), and
.env.example documents them. NOTE: TW_PAT lands in the git_push flash image
-- a bench-only shortcut (ADR-005); a product must not embed the PAT.
2026-07-06 00:18:22 +02:00
Julien Calixte
8d80168bda fix(libgit2): make the loose ODB persist on FATFS via POSIX shims
The loose ODB silently dropped every write on device: utimes() returned 0
unconditionally, so libgit2's freshen probe (git_futils_touch -> p_utimes)
always reported "object exists" and git_odb_write skipped the write entirely
-- blobs/trees/commits never hit disk and write_tree failed with "invalid
object specified". Gate utimes on file existence (present -> 0, absent ->
ENOENT). Also add a remove-then-rename p_rename (FATFS f_rename can't replace
an existing target, no hardlinks), with posix.c's original scoped out in the
component CMakeLists, plus symlink/gai_strerror link stubs git_push pulls in.

p_rename was verified on hardware (cleared the rename error); the utimes fix
is diagnosed from the failure + libgit2 source and build-verified, on-device
confirmation still pending.
2026-07-06 00:18:16 +02:00
Julien Calixte
a0e58e029a fix(firmware): raise main task stack to 96 KB for libgit2 depth
libgit2's repository_init -> config-write -> FATFS -> wear-leveling chain
nests ~10 GIT_PATH_MAX (4 KB) stack buffers deep; a trivial config write
measured ~67 KB on hardware and overflowed the previous 48 KB, corrupting a
newlib lock handle (LoadProhibited in xQueueGenericSend). Shared with the
editor build, so this is temporary -- git should move to a dedicated
large-stack task and this can drop back to ~16 KB.
2026-07-06 00:18:07 +02:00
Julien Calixte
4441618d0f feat(firmware): add flash-FAT storage partition table
16 MB layout adding a `storage` FAT data partition for the on-device git
working copy. Applied only by `just flash-git-push` (espflash
--partition-table); the editor flash keeps its default single-app layout.
2026-07-06 00:18:00 +02:00
Julien Calixte
b216a67c3b chore(firmware): add git2 build/flash/monitor just recipes 2026-07-05 21:28:47 +02:00
Julien Calixte
2b268fe168 feat(firmware): wire libgit2 component + bind git2 in system mode
Adds the extra_components metadata, git2 as an optional dep behind the
`git` feature (default-features off -> no openssl-sys/libssh2-sys), and a
git_smoke bin gated on it. libgit2-sys/libz-sys run in system mode against
fake pkg-config files (empty Libs) so they emit no link flags -- symbols
come from the esp-idf component. Proven on device: git2 version + a blob
SHA1 through the mbedTLS backend.
2026-07-05 21:28:47 +02:00
Julien Calixte
bea3058004 feat(firmware): add libgit2 esp-idf component (CMake + mbedTLS shims)
Builds vendored libgit2 as an esp-idf component so it inherits the
lwip/mbedtls/pthread/vfs/newlib include+link graph -- the include cascade
that sank CFLAGS injection never appears. Configured for mbedTLS via a
hand-written git2_features.h. Small port surface, libgit2 sources
untouched: poll.h shim, lstat==stat (esp_port.h), p_mmap via malloc+read
(esp_map.c), and identity/symlink stubs (esp_stubs.c). Registers empty
when LIBGIT2_SRC is unset so the editor build is unaffected.
2026-07-05 21:28:47 +02:00
Julien Calixte
8883a55df6 feat(firmware): vendor libgit2 v1.9.4 as a git submodule
Pins the exact version libgit2-sys 0.18.5 expects, so git2's safe API can
bind it in system mode. Sits under the esp-idf component that builds it;
clone with --recurse-submodules to fetch the source.
2026-07-05 21:28:47 +02:00
Julien Calixte
51c8f461c0 feat(firmware): enable octal PSRAM
Adds the 8 MB octal PSRAM to the heap allocator (verified on the N16R8:
detected, memory-tested, 8192K pooled). OCT mode is required or init
fails; speed left at 40 MHz for a safe first enable. Prerequisite for
the git working set and the rope buffer.
2026-07-05 20:04:11 +02:00
Julien Calixte
9fc21568e7 feat(firmware): add SD/FAT spike (Spike 3)
Standalone bench program (src/bin/sd_fat.rs, `just flash-sd`) that mounts
FAT over the EPD's shared SPI2 bus and proves the persistence module's
atomic save: write .tmp, fsync, rename, read back and compare.

Runs SD-only: the EPD's SpiBusDriver holds an exclusive bus lock for its
lifetime, so an arbitrated SD device can't be live alongside it yet. Keeps
CRC required and maps a card that rejects CMD59 to a clear "use a genuine
card" message rather than running the user's notes over an unchecked bus.

sdkconfig gains CONFIG_FATFS_LFN_HEAP (the atomic-save .tmp name isn't valid
8.3) and, temporarily, CONFIG_LOG_MAXIMUM_LEVEL_DEBUG to read the drivers'
per-command R1 bytes during bring-up.
2026-07-05 18:16:42 +02:00
Julien Calixte
de14b8418f feat(editor): use ISO-8859-15 font for accented glyphs
Swap the mono font from the ascii subset to iso_8859_15 (Latin-9) so
à é ê ç … œ € have glyphs. Cell size is identical, so ASCII rendering is
byte-for-byte unchanged; groundwork for v0.2.5 international input.
2026-07-05 17:57:29 +02:00
Julien Calixte
c12539d933 docs(firmware): record Spike 6 Wi-Fi + TLS verification 2026-07-05 09:22:36 +02:00
Julien Calixte
f8a4d53851 feat(firmware): add Wi-Fi + TLS spike (Spike 6)
Standalone `wifi_tls` binary: station assoc, SNTP, then a validated
HTTPS GET to api.github.com against the esp-idf cert bundle. Gates
Spike 7 (gitoxide push). Creds come from firmware/.env via build.rs.
2026-07-05 09:22:31 +02:00
Julien Calixte
a1f58b7953 docs(firmware): document the vim modal editor 2026-07-05 01:41:34 +02:00
Julien Calixte
0665f2d5fb feat(firmware): add vim text objects and the change operator
Generalise the normal-mode command state machine into d/c operators that
compose with motions (dw, d$, …), doubled forms (dd, cc), and text
objects: iw/aw plus nesting-aware bracket pairs (i(, a{, …) and quotes
(i", i'). ciw/daw/di( and friends now work; pending op/object shows in
the status strip.
2026-07-05 01:41:23 +02:00
Julien Calixte
c88389b623 chore: update package meta data 2026-07-05 01:31:33 +02:00
Julien Calixte
00b57bac55 perf(firmware): window partial refresh to the edited rows
Per keystroke, diff the new frame against the last shown one to find the
changed row band and partial-refresh only those rows instead of all 272.
E-paper update time scales with the gate rows driven, so a single edited
text line refreshes far faster. Windowed in Y only (full width, both
controllers), so the seam/mirroring logic is unchanged. Also drop the
periodic full refresh to every 64 updates — the panel stays visually
clean, so it's now mainly for longevity.
2026-07-05 00:08:14 +02:00
Julien Calixte
17fd663c85 docs(firmware): record Spike 5 partial refresh + typing 2026-07-05 00:00:16 +02:00
Julien Calixte
25665561cd feat(firmware): type on the e-paper panel with partial refresh
Spike 5 — first spike where keyboard input and panel output run
together. usb_kbd changes from a blocking run() into start(): it brings
the USB host stack up on background threads and pushes edge-detected,
US-layout-translated key-downs onto a queue drained via next_key().
main.rs owns the panel, maintains a wrapped/scrolling text buffer, and
partial-refreshes per keystroke batch with a periodic full refresh to
clear ghosting. Logs per-refresh latency.
2026-07-05 00:00:11 +02:00
Julien Calixte
784dcb7666 feat(firmware): add partial refresh to the EPD driver
display_frame_partial writes the new image to bank 0x24, runs GxEPD2's
partial waveform (_Update_Part: 0x3C/0x80, 0x21/0x00,0x10, 0x22/0xFF,
0x20), then syncs bank 0x26 so the next partial has a correct baseline.
Like GxEPD2 for this dual-controller panel, the update covers the full
panel with the partial waveform — no windowing, since the waveform time
dominates. Much faster than a full refresh and without the flashing.
2026-07-05 00:00:02 +02:00
Julien Calixte
9bbcf6d4ed docs(firmware): record Spike 4 USB host keyboard verification 2026-07-04 19:28:59 +02:00
Julien Calixte
8b71e0d9ec feat(firmware): read keycodes from a USB HID boot keyboard
Spike 4. Drive the ESP-IDF USB Host Library directly through the raw
esp-idf-sys bindings (the convenience HID class driver is a managed
component not vendored in mainline, and a boot keyboard doesn't need
it). On attach, src/usb_kbd.rs enumerates the device and dumps its
descriptors, claims the boot-keyboard interface, sends SET_PROTOCOL(boot)
and SET_IDLE(0), then polls the interrupt-IN endpoint and decodes each
8-byte report into modifiers + keycodes logged over UART.

main.rs becomes the Spike 4 harness; the epd module is kept compiled
(allow(dead_code)) so it doesn't bit-rot. Verified on hardware with a
19f5:3255 keyboard: letters, digits, modifiers, and rollover all decode.
2026-07-04 19:28:53 +02:00
Julien Calixte
00a852b8c8 docs(firmware): record Spike 2 EPD verification and build stamping 2026-07-04 18:58:47 +02:00
Julien Calixte
6145d27405 feat(firmware): drive GDEY0579T93 EPD via dual-SSD1683 driver
Add a thin dual-SSD1683 driver (src/epd.rs, ported from GxEPD2's
GxEPD2_579_GDEY0579T93) for the 792x272 e-paper panel: reset/init,
RAM addressing, the split-and-mirror full-frame blit across the
master/slave seam at x=396, and full-refresh waveform. Frame
implements embedded-graphics' DrawTarget.

Replace the Spike 1 blink harness in main.rs with the Spike 2 test:
SPI at 4 MHz over SCK 12 / DIN 11 / CS 7 / DC 6 / RST 5 / BUSY 4,
alternating normal/inverted frames with a seam-straddling circle,
"Typoena", and the build tag. Verified on hardware 2026-07-04.
2026-07-04 18:58:42 +02:00
Julien Calixte
d971f36f10 chore(firmware): stamp builds with UTC time and git hash
Emit BUILD_TIME and BUILD_GIT (git describe --always --dirty) as
rustc env vars so the running firmware can identify itself on serial
and on-panel. A rerun-if-changed on a nonexistent file forces the
script every build, keeping the timestamp fresh. Bring-up lesson:
know which build you're diagnosing.
2026-07-04 18:58:33 +02:00
Julien Calixte
dd63c574f5 docs(firmware): add v1.0 pinout diagram and record verified blink
The bench board is a DevKitC-1 v1.0 (RGB LED on GPIO 48), not the v1.1
layout (GPIO 38). Vendor the official v1.0 pinout diagram under docs/ and
reference it from the README so the pinout is pinned offline, and fold in
the 2026-07-04 on-hardware blink verification note.
2026-07-04 17:30:18 +02:00
Julien Calixte
e91f3be26e feat(firmware): blink on-board WS2812 alongside GPIO 2
The DevKitC-1 has no discrete LED on GPIO 2, so the Spike 1 blink was
invisible without external wiring. Drive the on-board addressable LED
(WS2812 on GPIO 48) each cycle for visible confirmation on the bench.
2026-07-04 16:49:14 +02:00