Print this. Tape it to your monitor. Refer to it whenever you're working with a new peripheral.
1. CREATE → Find the struct, call new() or use the builder
2. CONFIGURE → Find the Config struct, explore the enums
3. CONTROL → Find the trait impls and inherent methods
Top section — struct definition, what it is
impl StructName — constructors (new()) and inherent methods
impl Trait for StructName — portable methods (embedded-hal)
Associated types — click through to Config structs, enums
#![allow(unused)]
fn main() {
// Output
let mut led = Output::new(pin, Level::Low, OutputConfig::default());
led.set_high();
led.set_low();
led.toggle();
// Input
let button = Input::new(pin, InputConfig::default().with_pull(Pull::Up));
let pressed: bool = button.is_low();
let level: Level = button.get_level();
}
Type Key Variants
LevelHigh, Low
PullUp, Down, None
DriveStrength_5mA, _10mA, _20mA, _40mA
OutputConfig.with_drive_strength(), .with_open_drain()
#![allow(unused)]
fn main() {
// Setup
let i2c = I2c::new(peripherals.I2C0, Config::default())
.with_sda(sda_pin)
.with_scl(scl_pin);
// Bus scan
for addr in 0x01..=0x7F {
if i2c.write(addr, &[]).is_ok() {
println!("Found device at 0x{:02X}", addr);
}
}
// Using a driver crate
let mut sensor = DriverCrate::new(i2c, address);
let reading = sensor.read_value().unwrap();
}
#![allow(unused)]
fn main() {
let config = Config::default()
.with_frequency(Rate::from_khz(400)); // Standard: 100, Fast: 400
}
#![allow(unused)]
fn main() {
use core::cell::RefCell;
use critical_section::Mutex;
use core::sync::atomic::{AtomicBool, Ordering};
// Shared state
static BUTTON: Mutex<RefCell<Option<Input>>> = Mutex::new(RefCell::new(None));
static FLAG: AtomicBool = AtomicBool::new(false);
// Setup: configure interrupt and move into shared state
button.listen(Event::FallingEdge);
critical_section::with(|cs| {
BUTTON.borrow_ref_mut(cs).replace(button);
});
// Handler
#[handler]
fn gpio_handler() {
critical_section::with(|cs| {
if let Some(button) = BUTTON.borrow_ref_mut(cs).as_mut() {
button.clear_interrupt(); // MUST clear!
}
});
FLAG.store(true, Ordering::Relaxed);
}
// Main loop
loop {
if FLAG.swap(false, Ordering::Relaxed) {
led.toggle();
}
}
}
Trigger Fires When
Event::FallingEdgeHIGH → LOW
Event::RisingEdgeLOW → HIGH
Event::AnyEdgeAny transition
- Add `unstable` feature to esp-hal in `Cargo.toml`
- Always call `clear_interrupt()` in the handler
- Keep handlers short — set a flag, handle in main loop
BSP ──────────── board.led(), board.i2c()
Driver Crate ─── SensorDriver::new(impl I2c, addr)
embedded-hal ─── trait I2c, trait OutputPin
HAL (esp-hal) ── I2c::new(), Output::new()
PAC ──────────── Raw registers (auto-generated)
# Create a new project
esp-generate --chip esp32c3 project-name
# Build
cargo build --release
# Flash and monitor
espflash flash target/riscv32imc-unknown-none-elf/release/PROJECT --monitor
# Monitor only (already flashed)
espflash monitor
# Add a dependency
cargo add crate-name
Resource URL
esp-hal docs (ESP32-C3) docs.espressif.com/projects/rust/esp-hal/latest/esp32c3/esp_hal/
crates.io crates.io
docs.rs docs.rs
esp-hal GitHub github.com/esp-rs/esp-hal
The Embedded Rustacean theembeddedrustacean.com
Embassy (async) embassy.dev