Windows ESP32 build: RMT symbol, install all toolchains, IDF drift check + serial-driver docs#41
Conversation
…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>
|
Important Review skippedAuto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: ASSERTIVE Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
… 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>
|
@coderabbitai, review all 3 commits in this pr and report your ifindings in copy pastable text so agents can process it |
🧩 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-onlyLength 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.diffLength of output: 8397 🏁 Script executed: #!/bin/bash
cat /tmp/pr41.diffLength 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.pyLength 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.pyLength 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.pyLength of output: 730 🏁 Script executed: #!/bin/bash
pwd
ls
fd -t d moondeck || true
fd build_esp32.py || trueLength of output: 369 🏁 Script executed: #!/bin/bash
ls moondeck
find moondeck -maxdepth 2
git log --oneline -5
git statusLength 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.pyLength 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.pyLength 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 -20Length of output: 3373 🏁 Script executed: #!/bin/bash
sed -n '20,75p' moondeck/build/build_esp32.pyLength of output: 2926 🏁 Script executed: #!/bin/bash
sed -n '520,575p' moondeck/build/build_esp32.pyLength of output: 2994 🤖 web_search executed:
Length of output: 7911 🤖 web_search executed:
Length of output: 4753 Confirmed valid. Now compiling findings.
✅ Action performedReview finished.
|
…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>
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-beta1IDF pin. All 4 items below are independent of the beta1 pin bump (c80b704) this branch is rebased onto.install.bat esp32only installs the Xtensa toolchain, so anesp32p4-ethconfigure dies with "riscv32-esp-elf-gcc … not found in PATH".setup_esp_idf.pynow invokesinstall.bat all— same ~50 MB RISC-V-toolchain increment on Windows asinstall.bat esp32,esp32p4and covers any future chip target the codebase gains.build_esp32.pyrunscheck_idf_pin()right afterfind_idf()— before we sink minutes into a compile. Compares the local IDF HEAD SHA againstPINNED_IDF_COMMITand, 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-checkbypass, 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_commitmoved fromsetup_esp_idf.pyintobuild_esp32.py(the primary consumer);setup_esp_idf.pyimports them.setup_esp_idf.pyso the drift warning (⚠ IDF commit drift …) doesn't crash on Windows cp1252 when stdout is piped (Tee-Object, CI log capture). Standardsys.stdout.reconfigure(encoding="utf-8", errors="replace")at script entry; no-op on POSIX.src/platform/esp32/platform_config.hafter the three commits below is comment-only, no code change vs pre-PR base. Explanation of the round-trip: commit 1 switched fromRMT_LL_TX_CANDIDATES_PER_INST(hal/rmt_ll.h, marked "internal") toSOC_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 indocs/history/lessons.md— the LL/SOC dance during IDF's HAL component split.Unknownwith no COM allocated, both MoonDeck's port dropdown and Chrome's Web Serial picker come up empty. New### Windows: USB-serial driverssection indocs/building.md: symptom diagnosis (MoonDeck / Chrome / Device Manager / PowerShell), chip → driver table keyed on InstanceId VID (1A86CH340 /10C4CP210x /0403FTDI), copy-pasteable PowerShell for the headless CP210x install viapnputil /add-driver, plus related Windows gotchas (ghost COM ports, wedged CH340 drivers, cable/port).gettingstarted.mdStep 2 links to the section rather than duplicating.Follow-up in commit
88a5362Pre-merge gate outputs bundled into one commit before merge:
--skip-idf-pin-checkargparse flag threaded to guardcheck_idf_pin()(symmetric withsetup_esp_idf.py --no-checkout); PEP 8 blank-line fix; hardcodeduv run moondeck/…path verified correct per project convention.stale_feature_cache()now takes achipparameter and checksIDF_TARGET:STRING=inCMakeCache.txtbefore the existing feature-flag checks. An interruptedset-target esp32p4(e.g. RISC-V toolchain not yet in PATH) can leave the build dir with a fallbackesp32sdkconfig 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.docs/history/lessons.md: RMT LL_/SOC_ symbol churn, parallel-buildmanaged_components/race, staleIDF_TARGETfast-path trust, Windows UTF-8 stdio for piped scripts..claude/settings.local.cleaned.json): reduced session-accumulated 66 highly-specific entries to 33 broader patterns; removedBash(git add *)per CLAUDE.md's staging-specific-files rule.Merge context
Rebased on top of
3724c4f(the beta1 pin bump). One conflict insetup_esp_idf.py(their bump touched the same lines my de-dup removed) — kept the de-dup structure, moved beta1's constant values intobuild_esp32.py(the new source of truth), left the_checkout_pinnedhint text alone (their update passed through untouched).docs/building.mdauto-merged.Test plan
--skip-idf-pin-checkbypasses the check.setup_esp_idf.pycompletesinstall.bat all→ Xtensa + RISC-V toolchains both present under%USERPROFILE%\.espressif\tools\.setup_esp_idf.pyruns cleanly underTee-Object(noUnicodeEncodeErroron the drift warning).silabs.comviapnputil /add-driververified — two CP2102N boards go fromProblemCode 28toStatus: OKand appear asCOM3+COM4; MoonDeck's port list picks them up without restart.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.esp32p4firmware flags mismatch, matching case returnsNone, argparse unaffected.Not in this PR
pnputil /add-driverbut don't script it (needs a signed.zipdownload + elevated shell). Possible MoonDeck helper later.managed_components/race is documented as a lesson but not fixed in code.build_esp32.pycurrently has no queue; caller-side serialisation.🤖 Generated with Claude Code