core-s3 0.2.0

Board support crate for the M5Stack CoreS3 ESP32-S3 kit.
Documentation

core-s3

Rust board support package for the M5Stack CoreS3 K128 (ESP32-S3) with optional support for an M5Stack Gateway H2 Thread/Zigbee co-processor.

The crate is intentionally #![no_std] and keeps the reusable BSP layer small:

  • board metadata and pin/device maps for CoreS3 peripherals
  • display constants and a dirty-region sprite framebuffer for efficient partial repainting
  • power/battery status types ready for AXP2101 integration
  • feature-gated Gateway H2 metadata and UART bring-up behind gateway-h2
  • example firmware crates and CI/release automation

Hardware note: pin maps are scaffolded from the supplied CoreS3 materials and should be validated on the exact CoreS3/base stack revision before relying on every peripheral in production.

Repository layout

crates/core-s3/          no_std BSP crate
examples/hello_world/   minimal ESP32-S3 firmware skeleton
examples/dirty_regions/ display sprite example
examples/dual_core/     PRO CPU + APP CPU example
examples/gateway_h2/    Gateway H2 UART + Matter/Thread scaffold example
.github/workflows/      PR validation and firmware release

Features

Feature Description
defmt Enables defmt formatting for supported dependencies.
gateway-h2 Exposes core_s3::gateway_h2, Gateway H2 metadata, BSP UART bring-up, and Matter-over-Thread configuration types.

Display dirty-region sprite

core_s3::display::DirtySprite stores an off-screen framebuffer and tracks only changed rectangles. Drawing through embedded-graphics marks dirty regions automatically; flush_dirty / flush_dirty_at then blit only final pixels for the changed regions into the real display target, avoiding visible clear-then-redraw flashes.

use core_s3::display::DirtySprite;
use embedded_graphics::{pixelcolor::Rgb565, prelude::*, primitives::{PrimitiveStyle, Rectangle}};

type FullscreenSprite = DirtySprite<Rgb565, 320, 240, { 320 * 240 }, 32>;

let mut sprite = FullscreenSprite::new(Rgb565::BLACK)?;
Rectangle::new(Point::new(16, 16), Size::new(64, 32))
    .into_styled(PrimitiveStyle::with_fill(Rgb565::WHITE))
    .draw(&mut sprite)
    .unwrap();

// Later, once a concrete LCD DrawTarget is configured:
// sprite.flush_dirty(&mut display)?;
# Ok::<(), core_s3::display::DirtySpriteError>(())

For RAM-sensitive UI, prefer smaller per-widget sprites and compose them into the panel.

Building

Install the ESP Rust toolchain with espup, then:

cargo +esp check --workspace --all-features --target xtensa-esp32s3-none-elf
cargo +esp build -p hello_world --release --target xtensa-esp32s3-none-elf
cargo +esp build -p dirty_regions --release --target xtensa-esp32s3-none-elf
cargo +esp build -p dual_core --release --target xtensa-esp32s3-none-elf
cargo +esp build -p gateway_h2 --release --features gateway-h2 --target xtensa-esp32s3-none-elf

Flash an example with cargo-embed:

cargo +esp embed --package hello_world --release --target xtensa-esp32s3-none-elf
# or, through the workspace alias:
cargo +esp flash --package hello_world --release

Embed.toml is configured for the ESP32-S3 target and probe-rs flashing. If you have more than one compatible probe connected, set the probe VID/PID locally in Embed.toml.

Matter over Thread with Gateway H2

Enable gateway-h2 to use Gateway H2 metadata, the crate-owned CoreS3-to-H2 UART bring-up helper, and Matter-over-Thread setup configuration types. The BSP intentionally does not depend on or re-export a Matter stack: firmware crates should depend on rs-matter or another Matter implementation directly and own their concrete Matter server.

Gateway H2 firmware is commonly OpenThread RCP/Spinel, OpenThread CLI, or a standalone Thread/Zigbee application depending on what is flashed to the ESP32-H2. It is not assumed to be an AT-command modem. The BSP initializes the host UART transport; consumer firmware is responsible for the concrete H2 protocol driver, Thread joining/commissioning flow, Matter endpoints, persistence, and Home Assistant behavior.

For Home Assistant validation, a Raspberry Pi 5 running Home Assistant OS also needs a Thread Border Router/radio, such as Home Assistant Connect ZBT-1/SkyConnect or another supported OpenThread Border Router. This crate can provide the CoreS3 device side, but Home Assistant will only discover it once consumer firmware runs a real Matter server over a Thread network visible to Home Assistant.

[dependencies]
core-s3 = { version = "0.1", features = ["gateway-h2"] }
use core_s3::gateway_h2::{
    matter::{MatterOverThreadConfig, MatterServerConfig, ThreadDatasetConfig},
    GatewayH2,
};

let gateway = GatewayH2::GROVE_UART;
let matter = MatterServerConfig::new(0xFFF1, 0x8001, 3840, 20_202_021, "CoreS3 Gateway H2");
let thread = ThreadDatasetConfig::new("core-s3-thread", 0x1234, [0; 8], 15, [0xAA; 16]);
let setup = MatterOverThreadConfig::new(gateway, matter, thread);
# let _ = setup;

Dual-core support

ESP32-S3 has two Xtensa LX7 cores: the PRO CPU starts main, and the APP CPU can be started by firmware. This BSP supports both cores through esp-hal's CpuControl API; see examples/dual_core for a minimal example that starts the APP CPU with its own stack and shares state through a critical-section mutex.

Release workflow

Pushing a tag like v0.1.0 runs .github/workflows/release.yml, builds release firmware, converts each example to an ESP32-S3 .bin image with espflash save-image, and publishes only .bin firmware artifacts.