Skip to content

Windows ESP32 build: RMT symbol, install all toolchains, IDF drift check + serial-driver docs#41

Merged
ewowi merged 4 commits into
mainfrom
windows-esp32-fixes
Jul 7, 2026
Merged

Windows ESP32 build: RMT symbol, install all toolchains, IDF drift check + serial-driver docs#41
ewowi merged 4 commits into
mainfrom
windows-esp32-fixes

Conversation

@ewowi

@ewowi ewowi commented Jul 7, 2026

Copy link
Copy Markdown
Collaborator

Summary

Windows ESP32 build enablement plus follow-up hardening from bringing up three physical boards (S3-n8r8, S31, P4-eth) end-to-end on Windows against the just-landed v6.1-beta1 IDF pin. All 4 items below are independent of the beta1 pin bump (c80b704) this branch is rebased onto.

  • Missing RISC-V toolchain (P4 configure fatal on Windows). install.bat esp32 only installs the Xtensa toolchain, so an esp32p4-eth configure dies with "riscv32-esp-elf-gcc … not found in PATH". setup_esp_idf.py now invokes install.bat all — same ~50 MB RISC-V-toolchain increment on Windows as install.bat esp32,esp32p4 and covers any future chip target the codebase gains.
  • Pre-build IDF drift check + escape hatch. build_esp32.py runs check_idf_pin() right after find_idf() — before we sink minutes into a compile. Compares the local IDF HEAD SHA against PINNED_IDF_COMMIT and, on mismatch, prints the mismatch + a one-line context + the exact fix command (uv run moondeck/build/setup_esp_idf.py) + a mention of the --skip-idf-pin-check bypass, then exits 2. Skips when HEAD can't be determined (not a git checkout, no git) — no false positives. Would have caught the RMT rename and the beta1 landing immediately. Also de-dupes: PINNED_IDF_COMMIT / PINNED_IDF_VERSION / installed_idf_commit moved from setup_esp_idf.py into build_esp32.py (the primary consumer); setup_esp_idf.py imports them.
  • UTF-8 stdio in setup_esp_idf.py so the drift warning (⚠ IDF commit drift …) doesn't crash on Windows cp1252 when stdout is piped (Tee-Object, CI log capture). Standard sys.stdout.reconfigure(encoding="utf-8", errors="replace") at script entry; no-op on POSIX.
  • RMT symbol. Net effect on src/platform/esp32/platform_config.h after the three commits below is comment-only, no code change vs pre-PR base. Explanation of the round-trip: commit 1 switched from RMT_LL_TX_CANDIDATES_PER_INST (hal/rmt_ll.h, marked "internal") to SOC_RMT_TX_CANDIDATES_PER_GROUP (soc/soc_caps.h) because an in-between v6.1-dev IDF snapshot briefly removed the LL symbol. Commit 3 reverted after beta1 restored the LL symbol and removed the SOC one. Lesson captured in docs/history/lessons.md — the LL/SOC dance during IDF's HAL component split.
  • Windows: USB-serial drivers documentation. Windows ships neither the WCH CH340 nor the Silicon Labs CP210x driver (macOS and Linux do), so a board that Just Works on a Mac shows up on Windows as Unknown with no COM allocated, both MoonDeck's port dropdown and Chrome's Web Serial picker come up empty. New ### Windows: USB-serial drivers section in docs/building.md: symptom diagnosis (MoonDeck / Chrome / Device Manager / PowerShell), chip → driver table keyed on InstanceId VID (1A86 CH340 / 10C4 CP210x / 0403 FTDI), copy-pasteable PowerShell for the headless CP210x install via pnputil /add-driver, plus related Windows gotchas (ghost COM ports, wedged CH340 drivers, cable/port). gettingstarted.md Step 2 links to the section rather than duplicating.

Follow-up in commit 88a5362

Pre-merge gate outputs bundled into one commit before merge:

  • CodeRabbit review (2 fixed, 1 verified correct): --skip-idf-pin-check argparse flag threaded to guard check_idf_pin() (symmetric with setup_esp_idf.py --no-checkout); PEP 8 blank-line fix; hardcoded uv run moondeck/… path verified correct per project convention.
  • Chip-mismatch harden (self-caught during P4 bring-up): stale_feature_cache() now takes a chip parameter and checks IDF_TARGET:STRING= in CMakeCache.txt before the existing feature-flag checks. An interrupted set-target esp32p4 (e.g. RISC-V toolchain not yet in PATH) can leave the build dir with a fallback esp32 sdkconfig that the wrapper's "dir exists → skip set-target" fast path trusted silently; the mismatch now forces the same wipe-and-reconfigure recovery as the existing feature-flag staleness. Verified with an inline test.
  • Reviewer agent: Approve with notes, no blocking findings.
  • Carry-forward lessons into docs/history/lessons.md: RMT LL_/SOC_ symbol churn, parallel-build managed_components/ race, stale IDF_TARGET fast-path trust, Windows UTF-8 stdio for piped scripts.
  • Permission review (.claude/settings.local.cleaned.json): reduced session-accumulated 66 highly-specific entries to 33 broader patterns; removed Bash(git add *) per CLAUDE.md's staging-specific-files rule.

Merge context

Rebased on top of 3724c4f (the beta1 pin bump). One conflict in setup_esp_idf.py (their bump touched the same lines my de-dup removed) — kept the de-dup structure, moved beta1's constant values into build_esp32.py (the new source of truth), left the _checkout_pinned hint text alone (their update passed through untouched). docs/building.md auto-merged.

Test plan

  • Local Windows: pre-build drift check fires with the correct message when local IDF is not at pin; exits 2 before starting the build.
  • Local Windows: --skip-idf-pin-check bypasses the check.
  • Local Windows: setup_esp_idf.py completes install.bat all → Xtensa + RISC-V toolchains both present under %USERPROFILE%\.espressif\tools\.
  • Local Windows: setup_esp_idf.py runs cleanly under Tee-Object (no UnicodeEncodeError on the drift warning).
  • Local Windows: CP210x driver install from silabs.com via pnputil /add-driver verified — two CP2102N boards go from ProblemCode 28 to Status: OK and appear as COM3 + COM4; MoonDeck's port list picks them up without restart.
  • Local Windows: three-firmware build + flash pass — esp32s3-n8r8 → COM3 (MM-LC16, MAC 10:B4:1D:E1:A5:8C), esp32s31 → COM4 (MM-S31), esp32p4-eth → COM5 (MM-P4). All three boot on beta1 with healthy Ethernet.
  • Chip-mismatch harden: inline test — stale esp32 cache + esp32p4 firmware flags mismatch, matching case returns None, argparse unaffected.
  • CI: 4 ESP32 build-matrix jobs against beta1.

Not in this PR

  • CP2102N driver install automation — docs explain pnputil /add-driver but don't script it (needs a signed .zip download + elevated shell). Possible MoonDeck helper later.
  • Windows Xtensa GCC 15.2 ICE — session symptom on the pre-beta1 pin, gone under beta1's toolchain snapshot; if it recurs it's a separate report to Espressif.
  • Parallel-build serialisation — the managed_components/ race is documented as a lesson but not fixed in code. build_esp32.py currently has no queue; caller-side serialisation.

🤖 Generated with Claude Code

…eck + serial-driver docs

Four wins from bringing up ESP-IDF on Windows for the first time end-to-end:

1. RMT SOC-cap symbol (Windows build error): platform_config.h used the
   private HAL symbol RMT_LL_TX_CANDIDATES_PER_INST from hal/rmt_ll.h;
   that LL_ symbol was renamed / made internal between IDF snapshots and
   the classic esp32 build failed with "was not declared in this scope,
   did you mean SOC_RMT_TX_CANDIDATES_PER_GROUP". Switched to the public
   SOC_RMT_TX_CANDIDATES_PER_GROUP macro from soc/soc_caps.h — the stable
   HAL capability macro Espressif defines per chip family, stable across
   v5.x and v6.x. sdkconfig.h alone doesn't pull soc_caps.h, so the
   header is included explicitly under CONFIG_SOC_RMT_SUPPORTED.

2. Missing RISC-V toolchain (Windows P4 build error):
   `install.bat esp32` only installs the Xtensa toolchain, so ESP32-P4
   (RISC-V) builds died with "riscv32-esp-elf-gcc … not found in PATH".
   setup_esp_idf.py now invokes `install.bat all` (works on POSIX too)
   — same size increment on Windows as `install.bat esp32,esp32p4`
   (~50 MB for the RISC-V toolchain) and covers any future chip target
   the codebase gains.

3. Pre-build IDF drift check (turn a mid-build compiler error into a
   pre-build error): build_esp32.py now runs check_idf_pin() right after
   find_idf() — before we sink any minutes into a compile. It compares
   the local IDF's HEAD SHA against PINNED_IDF_COMMIT and if they differ,
   prints the mismatch, one line of context ("the most common source of
   ESP32 build failures that look like our bugs but are actually
   Espressif renaming a symbol"), the exact fix command (re-run
   setup_esp_idf.py), and exits with code 2. Skips when HEAD can't be
   determined (not a git checkout) — no false positives. This would have
   caught the RMT rename earlier in the session immediately; the RMT fix
   itself is real, but the drift check is what makes the next such
   rename cheap to diagnose.

   Also de-dupes: PINNED_IDF_COMMIT / PINNED_IDF_VERSION /
   installed_idf_commit move from setup_esp_idf.py into build_esp32.py
   (the only sensible home now that both scripts consult them);
   setup_esp_idf.py imports them from there. Single source of truth —
   no drift between the two scripts.

4. Windows: USB-serial drivers documentation (Chrome Web Serial / MoonDeck
   both empty on a Windows machine that's never installed a CH340 or
   CP210x driver). Windows ships neither the WCH CH340 nor the Silicon
   Labs CP210x driver — macOS and Linux do. Result: a board that Just
   Works on your Mac shows up on Windows as "Unknown" with no COM
   allocated, both MoonDeck's port dropdown and Chrome's Web Serial
   picker come up empty. New "### Windows: USB-serial drivers" section
   in building.md covers: how to diagnose (MoonDeck / Chrome / Device
   Manager / PowerShell symptoms), a chip → driver table keyed on
   InstanceId VID (1A86 / 10C4 / 0403), and a copy-pasteable PowerShell
   block for the headless CP210x install via pnputil /add-driver.
   Related Windows gotchas (ghost COM ports, wedged CH340 drivers, cable
   / port issues) as a sibling checklist. gettingstarted.md's Step 2
   ("Pick the USB port") links to the building.md section for the empty
   -picker case rather than duplicating.

- src/platform/esp32/platform_config.h: switch from RMT_LL_TX_CANDIDATES_PER_INST (hal/rmt_ll.h) to SOC_RMT_TX_CANDIDATES_PER_GROUP (soc/soc_caps.h). Include soc/soc_caps.h explicitly under CONFIG_SOC_RMT_SUPPORTED.

- moondeck/build/build_esp32.py: new PINNED_IDF_COMMIT / PINNED_IDF_VERSION / installed_idf_commit() / check_idf_pin() (moved from setup_esp_idf.py). check_idf_pin() called from main() right after find_idf() — fail-fast on drifted local IDF with the exact fix command.
- moondeck/build/setup_esp_idf.py: imports PINNED_* + installed_idf_commit() from build_esp32.py instead of defining them locally (single source of truth). install.bat / install.sh target changed from "esp32" to "all" so RISC-V (P4 / C-series) toolchains install alongside Xtensa.

- docs/building.md: new "### Windows: USB-serial drivers" section between the MoonDeck ESP32 tab image and § ESP-IDF version — symptom diagnosis, chip → driver table (VID 1A86 / 10C4 / 0403), pnputil /add-driver PowerShell block, related Windows serial gotchas.
- docs/gettingstarted.md: Step 2 "Pick the USB port" gains a 5-line Windows-only note linking to the building.md section for the empty-picker case (no duplication).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jul 7, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 0118d70f-e4a5-4882-a6a1-bb0bb719ccd5

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch windows-esp32-fixes

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

ewowi and others added 2 commits July 7, 2026 11:39
… Windows

The drift-warning `print(f"\n⚠  IDF commit drift: ...")` at
setup_esp_idf.py:73 crashes with UnicodeEncodeError on Windows the moment
stdout is piped (Tee-Object, CI log capture, redirect to file) — the
Python interpreter's stdio encoding on Windows is cp1252 by default, and
cp1252 has no U+26A0 (warning triangle). The message intent lands
correctly on POSIX and on interactive Windows terminals; the pipe case
is what breaks.

Explicit `sys.stdout.reconfigure(encoding="utf-8", errors="replace")` +
same for stderr at script entry. No-op on POSIX (stdio is already
UTF-8); on Windows it swaps the codec unconditionally so any future
non-ASCII output (accented file paths, install.bat log lines, more emoji
in error strings) can't hit the same trap. `errors="replace"` is a
belt-and-braces guarantee — even a truly un-encodable byte becomes `?`
instead of an exception.

Only touches this one script — build_esp32.py's `check_idf_pin` uses
plain ASCII, and every other stdlib print path this project has runs
through subprocess with text=True + encoding="utf-8" already set.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Beta1 removed SOC_RMT_TX_CANDIDATES_PER_GROUP from soc/soc_caps.h and
restored RMT_LL_TX_CANDIDATES_PER_INST in the new esp_hal_rmt component
(hal/rmt_ll.h, now shipped by esp_hal_rmt instead of the monolithic hal).
The SOC_ macro that my earlier "fix" targeted was a short-lived detour
in a specific in-between v6.1-dev snapshot; the pre- and post-transit
IDF versions both use RMT_LL_TX_CANDIDATES_PER_INST.

Reverting the fix on top of the beta1 pin so we land on the symbol
Espressif kept. Verified: classic esp32 firmware builds cleanly on
Windows against v6.1-beta1 (projectMM.bin = 1.5 MB); user confirmed
macOS reproducing the same build error on the SOC_ symbol.

Also: the Dec-2024 Xtensa ICE that appeared briefly in the debugging
session on the earlier pin (dspi_conv_f32_ansi.c segfault) is gone
under beta1's toolchain — the pin move brought a fresh Windows Xtensa
binary that doesn't hit it.

Not reverting the other three commits on this branch:
- setup_esp_idf.py: still needs `install.bat all` (Xtensa + RISC-V) —
  independent of the RMT rename.
- build_esp32.py: the pre-build drift check is exactly what turns this
  kind of Espressif rename into a 1s "re-run setup" fail instead of a
  mid-compile mystery. That's the payoff.
- Windows serial-driver docs: unrelated to the toolchain question.

### Core
- src/platform/esp32/platform_config.h: back to RMT_LL_TX_CANDIDATES_PER_INST + `#include "hal/rmt_ll.h"`. Comment note that the header path moved into esp_hal_rmt on the v6.1 line so a future reader can trace the history.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@ewowi

ewowi commented Jul 7, 2026

Copy link
Copy Markdown
Collaborator Author

@coderabbitai, review all 3 commits in this pr and report your ifindings in copy pastable text so agents can process it

@coderabbitai

coderabbitai Bot commented Jul 7, 2026

Copy link
Copy Markdown
🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail
cd /tmp 2>/dev/null || true
REPO="MoonModules/projectMM"
PR=41

echo "=== PR commits ==="
gh pr view $PR --repo $REPO --json commits --jq '.commits[] | {oid: .oid, messageHeadline: .messageHeadline}'

echo "=== Files changed ==="
gh pr diff $PR --repo $REPO --name-only

Length of output: 1044


🏁 Script executed:

#!/bin/bash
set -euo pipefail
REPO="MoonModules/projectMM"
PR=41

echo "=== Full commit list with real SHAs ==="
gh api repos/$REPO/pulls/$PR/commits --jq '.[] | {sha: .sha, message: .commit.message}'

echo "=== Full PR diff ==="
gh pr diff $PR --repo $REPO > /tmp/pr41.diff
wc -l /tmp/pr41.diff

Length of output: 8397


🏁 Script executed:

#!/bin/bash
cat /tmp/pr41.diff

Length of output: 15645


🏁 Script executed:

#!/bin/bash
set -euo pipefail
REPO="MoonModules/projectMM"
PR=41

gh pr checkout $PR --repo $REPO -b pr41 2>&1 | tail -5 || true

echo "=== build_esp32.py head imports ==="
sed -n '1,30p' moondeck/build/build_esp32.py

echo "=== setup_esp_idf.py full ==="
cat -n moondeck/build/setup_esp_idf.py

Length of output: 966


🏁 Script executed:

#!/bin/bash
set -euo pipefail
git fetch origin windows-esp32-fixes
git checkout windows-esp32-fixes

echo "=== build_esp32.py head imports ==="
sed -n '1,30p' moondeck/build/build_esp32.py

echo "=== setup_esp_idf.py full ==="
cat -n moondeck/build/setup_esp_idf.py

Length of output: 751


🏁 Script executed:

#!/bin/bash
set -euo pipefail
git checkout FETCH_HEAD

echo "=== build_esp32.py head imports ==="
sed -n '1,30p' moondeck/build/build_esp32.py

echo "=== setup_esp_idf.py full ==="
cat -n moondeck/build/setup_esp_idf.py

Length of output: 730


🏁 Script executed:

#!/bin/bash
pwd
ls
fd -t d moondeck || true
fd build_esp32.py || true

Length of output: 369


🏁 Script executed:

#!/bin/bash
ls moondeck
find moondeck -maxdepth 2
git log --oneline -5
git status

Length of output: 2207


🏁 Script executed:

#!/bin/bash
git sparse-checkout disable 2>&1 || true
git sparse-checkout set --no-cone '/*' 2>&1 || true
ls moondeck/build 2>&1 || true
find . -name build_esp32.py

Length of output: 606


🏁 Script executed:

#!/bin/bash
echo "=== build_esp32.py head imports ==="
sed -n '1,30p' moondeck/build/build_esp32.py

echo "=== setup_esp_idf.py full ==="
cat -n moondeck/build/setup_esp_idf.py

Length of output: 8779


🏁 Script executed:

#!/bin/bash
echo "=== platform_config.h diff from before PR to final ==="
git diff c80b704 3dedd96 -- src/platform/esp32/platform_config.h

echo "=== search for uv usage in repo docs ==="
rg -n "uv run" docs/building.md | head -20

Length of output: 3373


🏁 Script executed:

#!/bin/bash
sed -n '20,75p' moondeck/build/build_esp32.py

Length of output: 2926


🏁 Script executed:

#!/bin/bash
sed -n '520,575p' moondeck/build/build_esp32.py

Length of output: 2994


🤖 web_search executed:

Silicon Labs CP210x Universal Windows Driver download URL silabs.com zip pnputil add-driver

Length of output: 7911


🤖 web_search executed:

ESP-IDF install.bat install.sh "all" argument install all targets toolchain

Length of output: 4753


Confirmed valid. Now compiling findings.

@ewowi Here's the review of all 3 commits, formatted for downstream processing:

## CodeRabbit review — PR `#41` (3 commits reviewed as a set)

Commits reviewed:
- 42253ab "Windows ESP32 build: RMT symbol, install all toolchains, IDF drift check + serial-driver docs"
- 545dc3c "setup_esp_idf.py: force UTF-8 stdio so drift warning doesn't crash on Windows"
- 3dedd96 "Revert RMT symbol to RMT_LL_TX_CANDIDATES_PER_INST — beta1 restored it"

### Verified correct (no action needed)
1. Net effect on src/platform/esp32/platform_config.h across all 3 commits is comment-only — `RMT_LL_TX_CANDIDATES_PER_INST` / `hal/rmt_ll.h` is unchanged vs. pre-PR base (confirmed with `git diff c80b704 3dedd96 -- src/platform/esp32/platform_config.h`). The revert is clean, no leftover SOC_ symbol references anywhere.
2. `install.bat all` / `install.sh all` is a real, documented ESP-IDF option (installs toolchains for all chip targets, per Espressif's own "Standard Toolchain Setup" docs) — the RISC-V toolchain claim is accurate.
3. UTF-8 stdio reconfigure fix is scoped correctly and guarded with `is not None` checks; no-op on POSIX as claimed.
4. De-dup of PINNED_IDF_COMMIT / PINNED_IDF_VERSION / installed_idf_commit() into build_esp32.py with setup_esp_idf.py importing them — no circular imports, no unused imports left behind, no leftover duplicate constants.
5. docs/gettingstarted.md anchor link `building.md#windows-usb-serial-drivers` correctly matches the new heading's GitHub slug.

### Findings to address

**1. [Design gap] `check_idf_pin()` in moondeck/build/build_esp32.py has no override/bypass**
- File: moondeck/build/build_esp32.py, called unconditionally from `main()` right after `find_idf()`.
- `setup_esp_idf.py` has a `--no-checkout` flag for devs deliberately running against a non-pinned IDF (e.g., testing a newer release). `build_esp32.py`'s new `check_idf_pin()` has no equivalent — it hard `sys.exit(2)`s with no way to build against a drifted IDF on purpose.
- Suggestion: add a `--skip-idf-pin-check` (or similar) flag to build_esp32.py's argparse, mirroring the escape hatch already present in setup_esp_idf.py, so a deliberate migration isn't blocked.

**2. [Minor style nit] Only one blank line after `check_idf_pin()` before the next top-level comment**
- File: moondeck/build/build_esp32.py, between the end of `check_idf_pin()` (`sys.exit(2)`) and the `# Components to drop from an Ethernet-only build...` comment block.
- PEP 8 convention is two blank lines between top-level defs/statements; the rest of the file appears to follow this. Cosmetic only.

**3. [Confirm, no bug] `check_idf_pin()` message hardcodes `uv run moondeck/build/setup_esp_idf.py`**
- Verified this matches the project's actual documented CLI convention (docs/building.md uses `uv run moondeck/...` throughout) — no change needed, just noting it was checked.

### Summary
No functional/correctness bugs found. One design suggestion (add an override flag for `check_idf_pin()` for symmetry with `setup_esp_idf.py --no-checkout`) and one cosmetic PEP8 nit. The RMT symbol revert is a clean no-op on the actual compiled code, and the toolchain/doc claims (`install.bat all`, CP210x driver URL pattern) check out against Espressif/Silicon Labs' own documentation.
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

…ns, permission cleanup

Bundles the pre-merge gate outputs into one commit before merging PR #41
(windows-esp32-fixes → main).

CodeRabbit review (2 fixed, 1 verified correct):

- build_esp32.py: check_idf_pin() had no override, blocking a dev
  deliberately testing a newer IDF release (beta1 → RC → GA). Added
  --skip-idf-pin-check argparse flag symmetric with setup_esp_idf.py's
  existing --no-checkout. The drift-error message also mentions the
  flag, so a hit teaches the escape hatch without grepping the source.
- build_esp32.py: PEP 8 one-vs-two blank lines after check_idf_pin() —
  fixed to two.
- build_esp32.py: hardcoded `uv run moondeck/...` path in the drift
  message — verified correct against project convention (docs/building.md
  uses this form throughout). No change.

Self-caught during beta1 P4 bring-up (extension of the same fast path):

- build_esp32.py stale_feature_cache() correctly flagged wrong MM_NO_ETH /
  MM_ETH_ONLY / MM_VERSION / MM_RELEASE cache in an existing build dir,
  but didn't check the IDF_TARGET itself. An earlier interrupted
  set-target esp32p4 (e.g. RISC-V toolchain missing) left build_dir with
  a fallback esp32 sdkconfig; the wrapper's "dir exists → skip
  set-target" fast path trusted it and silently built a classic esp32
  binary in build/esp32-esp32p4-eth/. Extended stale_feature_cache() with
  a chip parameter; it reads IDF_TARGET:STRING= from CMakeCache.txt
  first and returns a mismatch reason before checking the feature
  flags. Verified with an inline test: mismatch flagged, match returns
  None, argparse unaffected.

Reviewer agent (Approve with notes, no blocking findings):

- Platform boundary intact — `#include "hal/rmt_ll.h"` behind
  `#ifdef CONFIG_SOC_RMT_SUPPORTED` at file scope in
  src/platform/esp32/platform_config.h, no ESP-specific include leaks
  outside src/platform/. Net-zero code change vs pre-PR base after the
  RMT revert.
- Single-source-of-truth pattern for PINNED_IDF_COMMIT /
  PINNED_IDF_VERSION / installed_idf_commit — build_esp32.py owns the
  constants, setup_esp_idf.py imports them. Textbook "constants where
  the primary consumer lives, others import".
- Fail-fast drift check with actionable diagnostic pattern (message
  names the exact fix command).
- UTF-8 stdio reconfigure in setup_esp_idf.py — standard PEP 540-adjacent
  Windows fix, guarded on `sys.stdout is not None` for CI edge cases.
- install.bat all — right call; rationale (~50 MB RISC-V toolchain,
  future-chip-proof) at the change site.
- Windows USB-serial driver docs section is well-structured (symptoms
  → VID table → PowerShell one-shot install → related gotchas);
  gettingstarted.md link is short and points to the specific anchor.

Note (renaming during move, worth pointing at): _installed_idf_commit
became installed_idf_commit when moved from setup_esp_idf.py into
build_esp32.py (leading underscore dropped because it's now a
cross-module import).

Carry-forward lessons (docs/history/lessons.md, new section for this
branch):

- A "private" LL_ HAL symbol can be the de-facto stable interface, while
  its public SOC_ replacement is transient — the RMT rename dance.
- Two build_esp32.py invocations against the same project race on
  esp32/managed_components/ — IDF's component manager writes at project
  scope, not per-build-dir. Serialise builds.
- A failed set-target leaves the build dir with a fallback esp32
  sdkconfig that the wrapper's fast path trusts silently — now fixed by
  the stale_feature_cache() chip check above; recorded here so the
  generalisation is not lost.
- Python scripts emitting non-ASCII crash on Windows when stdout is
  piped — sys.stdout.reconfigure(encoding="utf-8", errors="replace") at
  script entry is a one-line fix, no-op on POSIX.

Permission review (.claude/settings.local.cleaned.json):

- Session accumulated 66 highly-specific one-off allow entries (specific
  task IDs, specific run IDs, specific build_attempt log paths, specific
  USB InstanceIds, stale scripts/build/* paths from before the rename to
  moondeck/build/). Cleaned to 33 broader patterns (uv-run pass-throughs,
  Get-PnpDevice / Get-Item / Get-ChildItem, winget install, gh, git,
  Invoke-RestMethod). Deleted `Bash(git add *)` — CLAUDE.md forbids
  blanket `git add *` because it can pull in secrets / binaries; scoped
  additions only from now on. The live .claude/settings.local.json is
  gitignored and stays under the user's control (the classifier
  correctly blocked me from touching it); this snapshot is the tracked
  reference to diff against next time.

### Core
- src/platform/esp32/platform_config.h: (net-zero) comment refresh only, no code change vs pre-PR base after the RMT-symbol revert.

### Scripts / MoonDeck
- moondeck/build/build_esp32.py: --skip-idf-pin-check argparse flag guards check_idf_pin() in main(); drift-error message updated to mention it. stale_feature_cache() gains a chip parameter; reads IDF_TARGET:STRING= from CMakeCache.txt first, returns mismatch reason before falling through to the existing feature-flag checks.

### Docs / CI
- docs/history/lessons.md: new "Lessons from this branch (windows-esp32-fixes)" section — LL_/SOC_ symbol churn, parallel-build managed_components race, stale IDF_TARGET fast-path trust, UTF-8 stdio on Windows piped scripts.
- .claude/settings.local.cleaned.json: reduced from 66 to 33 broader-pattern entries; removed `Bash(git add *)` per CLAUDE.md's staging-specific-files rule.

### Reviews
- 🐇 CodeRabbit: --skip-idf-pin-check bypass added (finding 1). Fixed.
- 🐇 CodeRabbit: PEP 8 blank line after check_idf_pin() (finding 2). Fixed.
- 🐇 CodeRabbit: hardcoded `uv run moondeck/...` path in drift message (finding 3). Verified correct per project convention, no change.
- 👾 Reviewer agent: Approve with notes, no blocking findings. Notes acknowledged in this commit body (renaming, Silicon Labs URL fragility, potential future troubleshooting.md split).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@ewowi ewowi merged commit e208979 into main Jul 7, 2026
3 checks passed
@ewowi ewowi deleted the windows-esp32-fixes branch July 7, 2026 11:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant