Skip to main content

Crate oxstack_cardputer

Crate oxstack_cardputer 

Source
Expand description

Board support for the M5Stack Cardputer ADV (ESP32-S3FN8, Stamp-S3A).

ⓘ
#[esp_rtos::main]
async fn main(_spawner: Spawner) -> ! {
    let mut board = oxstack_cardputer::init(oxstack_cardputer::default_config());
    let _backlight = board.backlight.init();
    let i2c = board.sys_i2c.into_bus();
    let mut display = board.display.init().await.unwrap();
    let mut keyboard = board.keyboard.init(i2c).await.unwrap();
    let fb = oxstack_cardputer::display::framebuffer();
    loop {
        let ev = keyboard.next_event().await.unwrap();
        // draw into `fb`, then:
        display.flush(fb).await.unwrap();
    }
}

§Model

Board splits the chip’s peripherals into one resource group per board function: display, keyboard, audio, spi_bus and so on. Each group owns exactly the pins and peripherals that function is wired to, so the compiler rules out two drivers fighting over a pin. Groups you never init cost nothing.

Every group has an init that borrows the group (&mut self) and returns a driver tied to that borrow. Drop the driver and the group is free again, so one firmware can start the same driver twice, or the microphone after the speaker, without rebooting:

ⓘ
let mut mic = board.audio.recorder(i2c, rate, gain).await?;
// ... record ...
drop(mic);
let mut speaker = board.audio.player(i2c, rate, volume).await?; // same hardware

Groups are plain structs of esp-hal peripherals with a 'd lifetime. Destructure one to drive the pins yourself, or reborrow() it to lend a shorter-lived copy to a function, exactly as with esp-hal peripherals.

Three things are one-per-boot by nature and keep a consuming API: the shared I²C bus (i2c::SysI2cResources::into_bus), the framebuffer (display::framebuffer) and the Wi-Fi station (net::start_station).

§Project setup

The quickest start is the template in the repository:

cargo install cargo-generate espup espflash --locked
espup install && source ~/export-esp.sh
cargo generate --git https://github.com/theashraf/oxstack templates/cardputer-app

By hand, a firmware crate needs:

  • Dependencies at the versions this crate pins. The ones you use in types are re-exported here ([esp_hal], [esp_rtos], [embassy_executor], [embassy_time], [embassy_sync], [embassy_futures], [esp_bootloader_esp_idf]). Add esp-backtrace (panic handler) and esp-println with defmt-espflash + jtag-serial (log transport) yourself; a library must not pull in a panic handler. #[esp_rtos::main] expands to paths in esp_rtos and embassy_executor, so list those two as direct dependencies too.
  • rust-toolchain.toml selecting the esp channel (espup install).
  • .cargo/config.toml: target xtensa-esp32s3-none-elf, build-std = ["core", "alloc"], rustflags = ["-C", "link-arg=-nostartfiles"] and an espflash flash --monitor --log-format defmt runner.
  • build.rs emitting -Tdefmt.x and, last, -Tlinkall.x.
  • esp_bootloader_esp_idf::esp_app_desc!(); once in main.rs.

API docs are built for the ESP32-S3 and hosted at https://theashraf.github.io/oxstack/oxstack_cardputer/; docs.rs cannot build for Xtensa.

Re-exports§

pub use oxstack_audio as dsp;
pub use oxstack_drivers as drivers;
pub use oxstack_gfx as gfx;
pub use oxstack_input as input;
pub use embassy_executor;
pub use embassy_futures;
pub use embassy_sync;
pub use embassy_time;
pub use esp_bootloader_esp_idf;
pub use esp_hal;
pub use esp_rtos;

Modules§

audio
Audio: ES8311 codec on I²S0 (DMA channel 1) and the internal I²C bus, driving an NS4150B speaker amplifier and fed by an analog MEMS mic.
backlight
LCD backlight on GPIO38, dimmed with LEDC PWM.
battery
Battery voltage on GPIO10 (ADC1) through a 100 kΩ / 100 kΩ divider.
ble
Bluetooth LE: Espressif’s controller (via esp-radio) under the pure-Rust trouble host stack. Requires the ble feature.
display
The 1.14“ 240×135 ST7789V2 LCD, in landscape with the keyboard below.
i2c
The internal I²C bus (GPIO8/9), shared by the TCA8418 keyboard scanner, ES8311 codec and BMI270 IMU.
imu
BMI270 IMU on the internal I²C bus (0x69).
ir
IR transmitter on GPIO44 (LED driven directly through 22 Ω), using an RMT channel with hardware 38 kHz carrier modulation. Get it from crate::rmt::RmtResources::init.
keyboard
The 56-key keyboard, scanned by a TCA8418 on the internal I²C bus.
led
The WS2812-class RGB LED on GPIO21, driven by an RMT channel. Get it from crate::rmt::RmtResources::init.
net
Wi-Fi: scanning, and station networking (connect, DHCP, DNS, UDP/TCP via embassy-net) plus network time. Requires the wifi feature.
pins
Cardputer ADV pin assignment, for reference and for code that drives pins directly. The typed equivalents live in Board.
radio
Wi-Fi and Bluetooth LE through Espressif’s esp-radio.
rmt
The RMT pulse generator and the two LEDs it drives: the WS2812-class RGB LED (GPIO21) and the IR transmitter (GPIO44).
sd
microSD card on the shared SPI bus (SPI2: SCK 40, MOSI 14, MISO 39, CS 12), with FAT through embedded-sdmmc (re-exported as [fs]).

Structs§

Board
Every peripheral of the chip, grouped by what the Cardputer ADV wires it to. Create it with crate::init or Board::new.
ExtResources
EXT header lines other than SPI and I²C.
GroveResources
Grove port signal lines.
RawResources
Peripherals that are mostly register blocks, for low-level code.
RtosResources
What the esp-rtos scheduler needs. crate::init consumes it.
SpareResources
Pins the Cardputer ADV schematic does not route to any connector, plus UART0. Kept rather than dropped so nothing is lost for good.
SpiBusResources
The shared SPI bus and both of its chip selects. sd is in crate::sd.
SystemResources
Chip peripherals with no fixed board role.

Functions§

default_config
The configuration init should normally be given: CPU at 240 MHz.
init
Initialises the chip, starts the esp-rtos scheduler (required for Embassy timers), and returns the board. With the radio feature it also adds the reclaimed-RAM heap that esp-radio needs.