Skip to content

Latest commit

 

History

History
117 lines (93 loc) · 6.27 KB

File metadata and controls

117 lines (93 loc) · 6.27 KB

rust-enc — LilyGo T-Encoder-Pro Firmware (pure Rust)

Pure-Rust, no_std, Embassy-async firmware for the LilyGo T-Encoder-Pro (ESP32-S3-R8). v1 = clean HAL + driver foundation bringing up every peripheral with a minimal embedded-graphics demo. Mirrors the conventions of the proven tdecknav project (/home/ray/projects/mapper/tdecknav).

Hardware (ESP32-S3-R8)

  • 16 MB flash, 8 MB OCTAL/OPI PSRAMESP_HAL_CONFIG_PSRAM_MODE=octal (never quad).
  • Display: CO5300 AMOLED, 390×390, RGB565, QSPI. SDIO0=11 SDIO1=13 SDIO2=7 SDIO3=14 SCLK=12 CS=10 RST=4 EN(power)=3. Framebuffer ≈ 304 KB → PSRAM.
  • Touch: CST816 I2C 0x15. Shared I2C SDA=5 SCL=6. INT=9 RST=8.
  • Encoder: A=1, B=2, button=0 (GPIO0 is a boot strap pin).
  • Buzzer: GPIO17, LEDC PWM (2000 Hz, 8-bit in C reference).
  • Pin source of truth: /home/ray/projects/encoder/T-Encoder-Pro/libraries/Mylibrary/pin_config.h.

Critical hardware gotchas

  • Display flush is a CHUNKED QSPI burst, not a single PSRAM DMA write. First chunk cmd=0x32 / addr=0x003C00; continuation chunks use Command::None/Address::None; stage each chunk through a small DRAM DMA buffer (PSRAM is the source via copy).
  • CS is MANUAL. Build the display SPI without Spi::with_cs; drive GPIO10 as a plain Output, low across the whole burst, high only at the end. Hardware CS pulses between transfers and breaks the RAMWR continuation.
  • Reg writes: cmd=0x02, addr=(reg<<8). Init table ported from Arduino_CO5300.h.
  • Encoder: PCNT needs explicit high/low limits + interrupt accumulation (limited-width counter). Document GPIO0 strap behavior (download mode if held at reset).

Architecture

  • Workspace monorepo. Library crates dual-target (no_std device + std host tests): enc-config, enc-co5300, enc-touch, enc-input, enc-ui, enc-bus. Device binary enc-app (xtensa-only). Plus xtask and optional enc-host simulator.
  • Single async init_hardware() -> BootHandles; main spawns per-module Embassy tasks (render, encoder, button, touch, buzzer, ui). Comms via embassy-sync Channel/Signal over StaticCell. spawn_or_halt! (park, never panic).
  • Display driver generic over a Co5300Bus trait (begin_pixels/push_chunk/end_pixels) so the DMA bus can swap to a blocking fallback without touching driver/UI. Co5300 implements embedded-graphics DrawTarget.
  • Large statics (framebuffer) in PSRAM via a custom linker section (.psram_uninit_ext, copy tdecknav/nav-app/dram2_ext.x).

Build / flash / tooling

  • All cargo commands go through ./cargo-esp (loads ESP env + RUSTUP_TOOLCHAIN=esp).
  • Device is on /dev/ttyACM0 (USB 303a:1001, native USB-Serial/JTAG).
  • Flash/run: ./cargo-esp run -p enc-app (runner probe-rs run --chip esp32s3; espflash flash --monitor is the fallback). espflash, probe-rs, espup installed.
  • Custom partition table (enc-app/partitions.csv, adds a dedicated settings partition for NVS): the enc-app espflash runner passes --partition-table partitions.csv. Manual espflash flash needs that flag too, else settings persistence is silently disabled (graceful — falls back to defaults).
  • esp-hal = "=1.1.1"; derive the whole esp/embassy version matrix from the upstream esp-hal 1.1.1 esp32s3 embassy template and freeze Cargo.lock. Do not copy tdecknav's older pins verbatim.

Mandatory Rust rules (rust-dev)

  1. Files < 500 lines; modules atomic; tests in separate files/modules.
  2. clippy pedantic, clean — zero warnings. No #[allow(...)] anywhere; fix or delete.
  3. No unwrap()/expect()/panic!() in production paths (tests OK). Propagate via ? and typed error enums.
  4. No #[ignore]d tests. No excluded tests.
  5. No Box/dyn and no needless clones/allocations without explicit user sign-off.
  6. Minimize dependencies (major-version-only where possible; esp ecosystem needs exact pins). Run cargo deny check on dependency changes.
  7. Idiomatic, statement-oriented Rust: pattern matching, typed enums, errors over codes.
  8. Comments and rustdoc short and to the point.

Post-change pipeline (run in order; restart from top on any failure)

./cargo-esp clippy --fix --all-features --allow-dirty --all-targets --workspace
./cargo-esp fix --all-features --allow-dirty --all-targets --workspace
./cargo-esp fmt --all
# only after the above are clean:
./cargo-esp test --verbose --all-features --workspace   # host-target crates

Route noisy command output through context-mode (ctx_execute); print only summaries.

anodized specs (anodized = "0.5")

Apply #[spec(...)] to non-trivial / pub / boundary-crossing functions with non-trivial input domains or output guarantees (e.g. framebuffer index/window bounds, encoder detent sign/remainder, widget-within-area). Specs compile away by default; validate with RUSTFLAGS="--cfg anodized_panic" ./cargo-esp test. Never relax a spec to make a test pass.

Coverage

./cargo-esp tarpaulin --all-features --workspace --out lcov then cargo crap — no coverage drop, newly added functions never excluded. (Pure host-target crates; drivers are verified on-device per the plan's phase acceptance gates.)

Workflow rules

  • Run all shell commands via context-mode (ctx_execute/ctx_batch_execute), not raw Bash, except whitelist mutations (git writes, mkdir/mv/rm, package installs).
  • Verify code with Codex (ask-codex skill) before EVERY commit. Adversarial review; fix findings before committing. Codex is read-only — we implement.
  • Update config.example.toml when configuration changes.
  • Scope discipline: fix warnings in modules you touch; no unrequested side-quests.

Build order (each phase compiles, flashes, verifies on device)

0 scaffold + octal-PSRAM smoke test → 1a QSPI transport spike (manual CS, chunked burst) → 1b solid color → 2 embedded-graphics demo → 3 encoder → 4 touch → 5 integrated UI. Full plan: ~/.claude/plans/we-re-now-planning-the-pure-simon.md.

Key reference files

  • T-Encoder-Pro/libraries/Arduino_GFX-1.5.0/src/databus/Arduino_ESP32QSPI.cpp — QSPI protocol.
  • T-Encoder-Pro/libraries/Arduino_GFX-1.5.0/src/display/Arduino_CO5300.h — init table.
  • tdecknav/nav-app/src/boot.rs, .cargo/config.toml, dram2_ext.x, Cargo.toml — patterns to mirror.