Skip to content

v0.4.0

Choose a tag to compare

@hsteinhaus hsteinhaus released this 14 Jun 18:17
5939149

[0.4.0] - 2026-06-14

BSP code absorbed from the alternator-regulator application (its altreg-*
board crates), toward the goal of applications containing only task wiring.
Relicensed from GPL-3.0-only to MIT OR Apache-2.0 on import (same copyright
holder).

Added

  • BSP↔binary boundary — the binary collapses to an entry shell. New surfaces
    so per-board boilerplate lives here, not in the application:
    • mem::init_heap(HeapProfile, Option<PSRAM>) (new heap feature, implied
      by psram): the BSP owns the global heap — declares the esp-alloc DRAM
      regions for HeapProfile::{Default,Lvgl,Coex} (HIL-proven per-board sizes),
      so a binary never calls esp_alloc::heap_allocator!. (heap pulls
      esp-bootloader-esp-idf for the reclaimed-ROM region.)
    • io::console::install(spawner, Config) (#31): one-call logging — register
      the log backend + (when Config::serial is Some) bring up the chip
      transport (UART0 / USB-Serial-JTAG CDC) and spawn the drain; returns
      Console { rx } for serial_cmd (log TX + command RX on one port, no probe).
      Plus console::markers (PANIC/CONSOLE_DROP/PREV_PANIC, a stable HIL
      contract) and the new console-serial feature (off = R9 production
      backstop: no serial symbols; panic still breadcrumbs + halts).
    • RTC panic breadcrumb (#31 R8): console::on_panic records {location, reason-digest} to RTC-fast persistent RAM before halt; take_panic_breadcrumb()
      reads it once at boot — a crash survives the RWDT reset and is reported as the
      PREV_PANIC line, identical on both targets.
    • panic-handler feature + app_desc! macro: the BSP exports
      #[panic_handler] (→ on_panic) and wraps the esp-idf app descriptor, so a
      binary opts in instead of hand-rolling them.
    • board::run_app_core (new multicore feature, pulls esp-rtos): parks
      • starts the APP core on an InterruptExecutor (encapsulating the park_core
        JTAG-reset workaround), running a caller closure with the SendSpawner.
    • io::InputCaps + io::input_caps(): the board's input model (Keypad
      vs Pointer), so a UI installs the matching indev without hardcoding the
      board. ButtonEvent affirmed as a positional, app-vocabulary-free contract.
  • board::display (new display feature, pulls lcd-async): ILI9342C
    panel bring-up shared by both boards — init_ili9342c (CoreS3, no reset
    pin / AW9523B + SPI SoftReset) and init_ili9342c_with_reset (Fire27),
    SCREEN_W/SCREEN_H, the Ili9342c type alias. De-dupes the builder
    config previously copied in every example and in the application.
  • board::spi2: the shared SPI2 display + SD-card bus. Per-board
    Spi2Resources (pins + DMA channel) → into_parts(dma_rx, dma_tx)
    Spi2Parts::finish(card_cs) which shares the bus, initialises the display
    (unconditionally — a dead/absent SD card must never cost the UI) and returns
    a generic-CS SD SpiDevice. The SD driver stays with the app (sdspi is
    not on crates.io); the module docs spell out the bounded-retry pre-init
    pattern and the chip-specific display/SD join-order asymmetry.
  • board::cores3::Gpio35Dc + gpio35_disable_output: register-level
    GPIO35 MISO/DC muxing (the CoreS3 shares GPIO35 between SPI2 MISO and
    display DC). Spi2Parts::finish re-muxes to MISO after display init — the
    ordering that otherwise costs an sd_card.init() that never completes.
  • board::cores3::Board / board::fire27::Board (Board::split): the
    boards' pin wiring as data — SPI2/display/SD pins, the internal I2C0 bus
    (hardened config: 400 kHz, BusTimeout::BusCycles(20), and on the S3 a
    25 ms transaction SoftwareTimeout — the HIL-proven fix for the
    stuck-transaction yield_now spin that wedges an InterruptExecutor),
    buttons, radio, UART0/USB-Serial-JTAG, the SK6812 LED pin, PSRAM, free M5-Bus
    pins, and board::SystemResources (timers, SW interrupts, CPU_CTRL,
    LPWR). Plus board::init() (esp-hal at max CPU clock; heap stays with the
    app).
  • board::spi2::Spi2Resources::into_display_only (feature display): the
    display on the descriptor-backed SpiDmaBus with no SD path, for DMA
    display-only apps (the lvgl example). DC is a plain Output on both boards
    (on CoreS3 a configured output routes GPIO35's pad, unlike Gpio35Dc which
    needs with_miso). Returns a DisplayBus { display, backlight (Fire27) }.
  • io::buttons: unified ButtonEvent/ButtonId/ButtonAction input
    events for the whole Core family, plus (new buttons feature, pulls
    async-button) the Fire27 front-panel A/B/C driver
    (ButtonResources::into_buttonsButtons::next_event).
  • io::touch_buttons: CoreS3 touch→button emulation over ft6336u
    three zones in the bottom strip, short/multi-tap/long-press state machine,
    emitting the same ButtonEvent as the physical buttons.
  • io::watchdog::watchdog_feed_loop: RWDT hardware-reset backstop, armed
    and fed from the executor whose wedging it guards.
  • must_spawn!: replacement for embassy-executor's Spawner::must_spawn
    (dropped in 0.10), panicking with call-site context.

Changed — breaking

  • board::cores3 is now gated behind the cores3 feature (it was
    unconditionally public; using it from a fire27 build was meaningless).

Examples

  • Unified the per-board example crates into one examples/demos crate.
    The board is selected by a fire27 (default) / cores3 cargo feature; each
    bin (display, i2c_scan, m5go, wifi_sta, onewire, lvgl, coex)
    builds for both boards from a single source, leaning on Board::split +
    board::display + the io loops, with the per-board glue concentrated in
    examples/demos/src/board.rs. The LVGL bin's main is now ~50 lines (the
    flush glue / view / keypad indev moved to examples/demos/src/ui/).
    examples/common (pure chip-agnostic drawing helpers) is unchanged. Build
    per board: cargo build -p demos --bin <name> (Fire27, default) or add
    --no-default-features --features cores3 --target xtensa-esp32s3-none-elf;
    lvgl/coex are gated by required-features, onewire is Fire27-only.
    Each bin keeps its panic-handler / esp_app_desc!() top-matter inline (no
    macro), so it reads as a self-contained, copy-pasteable starting point. The
    shared esp-hal-fork example deps are hoisted into [workspace.dependencies].
  • The coex bin is gated by required-features = ["coex"] and built on its
    own (--bin coex --features coex): esp-radio's coexist blob is a crate-global
    link dependency that only the BLE-initialising bin can satisfy, so the coex
    feature must stay off while the non-BLE bins are built (it is, for every
    normal cargo build / --workspace / -p demos invocation).
  • Input is now unified across boards via the new io::buttons::ButtonEvent
    (the display bin reads Fire27 buttons / CoreS3 touch through one loop), and
    logging is unified on the log facade (CoreS3 over RTT at Info).
  • The sensor/peripheral demos (i2c_scan, m5go, wifi_sta, coex,
    onewire) now render through one shared common::draw_panel (a cyan
    board/title header + body lines) so they look alike, and each shows
    everything it has on screen — not the console alone: onewire gained a
    display (sensor count + per-sensor ROM + °C), and wifi_sta/coex now show
    the nearby-AP scan (via the shared demos::net, which also de-dups the
    net_demo task). The display/lvgl demos keep their own rendering.
  • The display bin is now a clear input-capabilities demo: a per-position
    (Left/Center/Right) readout of the last ButtonEventtap / tap x2… /
    HELD (long) — so multi-tap count and long-press are legible, identical on
    Fire27 buttons and CoreS3 touch.
  • The lvgl bin is now interactive: three focusable LVGL buttons navigated
    from the front panel (PREV/NEXT/ENTER), identical on both boards — the
    unified ButtonEvent is mapped to LVGL keys feeding an oxivgl KeypadState
    (run_app_nav_keypad_events), routed to the view's focus group. Per-board
    bring-up is board::lvgl_bringup (display + the right Input); on CoreS3 the
    one I2C bus resets the panel and drives touch. The previous raw-FFI
    Fire27-only keypad glue is replaced by this unified, both-boards path.
    CoreS3 additionally runs a direct-touch POINTER indev (oxivgl 0.5
    PointerIndev, fed by an async FT6336U poll task bridging the I2C read into
    the indev's sync read callback), so on-screen widgets can be tapped by
    coordinate — the bottom-strip keys stay on the BSP button API
    (TouchButtons, multi-tap / long-press, multi_tap_ms tuned to 150 ms for
    snappy focus-nav) and taps land anywhere on screen (#32 I3).
  • Adopted oxivgl 0.5 (from crates.io, no git pin) for the lvgl bin:
    KeypadIndev/KeypadState + the new PointerIndev/PointerState.
  • New sd bin (gated by --features sd): the one demo exercising
    board::spi2::finish() — the display + SD shared bus including the CoreS3
    GPIO35 MISO/DC mux. Mounts the FAT filesystem read-only and lists the root
    dir; handles MBR-partitioned cards (mounts the first FAT partition via a
    StreamSlice) and superfloppies (FAT at sector 0). Pulls the sdspi +
    embedded-fatfs fork (not on crates.io → example-only).
  • Bumped the esp-hal fork pin (example/local builds only — the published
    library uses stock esp-hal =1.1.1) to b7a4c74a, which scopes the I2C
    NACK-recovery skip to ESP32 so the ESP32-S3 shared I2C bus recovers after a
    NACK again. Without it a single NACK (e.g. probing an absent address) could
    poison every later transaction on the S3 bus — manifesting as dead CoreS3
    touch or a cold-boot black screen. Surfaced downstream in alternator-regulator.