Skip to content

Repository files navigation

esp32-rust-workspace

A bare-metal Rust workspace for ESP32 microcontroller projects using esp-hal with optional Embassy async support.

Overview

This project provides templates and examples for running Rust on an ESP32-WROOM-32 module without an operating system (no_std). It supports both blocking (synchronous) and async (Embassy) programming models.

Hardware

  • Target: ESP32 (Xtensa LX6 dual-core)
  • Module: ESP32-WROOM-32
  • Bootloader: esp-idf compatible

Prerequisites

  • Rust installed via rustup
  • USB cable for connecting to your ESP32 board

Development Environment Setup

1. Install the ESP Rust toolchain

cargo install espup --locked
espup install

2. Set up environment variables

For the current shell session:

.$HOME/export-esp.sh

To make it permanent, add to your shell config:

# For bashecho'. $HOME/export-esp.sh'>>~/.bashrc
# For zshecho'. $HOME/export-esp.sh'>>~/.zshrc

3. Install development tools

# Flashing tool
cargo install espflash --locked
# Project generator (optional, for creating new projects)
cargo install esp-generate --locked
# Debugging tools (optional)
cargo install probe-rs-tools

Building and Flashing

Build the project

cargo build --release

Flash and run a specific binary

cargo run --release --bin test

This will compile, flash to the connected ESP32, and open a serial monitor.

Flash only (without monitor)

espflash flash target/xtensa-esp32-none-elf/release/<binary-name>

Monitor serial output

espflash monitor

Project Structure

esp32-rust-workspace/
├── Cargo.toml # Project dependencies and binary definitions
├── build.rs # Build script for linker configuration
├── rust-toolchain.toml # Specifies the esp Rust toolchain
├── docs/ # Documentation
│ ├── std-vs-no_std.md
│ ├── converting-to-embassy.md
│ ├── rust-macros.md
│ ├── esp32-pinout-reference.md
│ ├── esp32-cheatsheet.md
│ ├── rust-cheatsheet.md
│ └── rust-project-organization.md
└── src/
├── lib.rs # Shared library code
├── bin/ # Binary targets (defined in Cargo.toml)
├── examples/ # Working example projects
│ ├── tp223_touch_sensor.rs # TP223 touch sensor with Embassy
│ └── lm75_temperature.rs # LM75 I2C temperature sensor
└── scraps/ # Templates and work-in-progress
├── template_embassy.rs # Template using esp_hal_embassy (async)
├── template_blocking.rs # Template using blocking esp_hal
├── test.rs # LED blink test
└── touch.rs # Touch input test

Templates

template_embassy.rs (Async with Embassy)

Use this template for projects that benefit from async/await:

  • Non-blocking delays with Timer::after_millis().await
  • Task spawning with #[embassy_executor::task]
  • Power-efficient (CPU sleeps during delays)
  • Best for: multi-tasking, power-sensitive applications

template_blocking.rs (Synchronous)

Use this template for simple, straightforward projects:

  • Blocking delays with busy-wait loops
  • No async runtime overhead
  • Simpler mental model
  • Best for: simple applications, learning esp-hal basics

Examples

tp223_touch_sensor.rs

Demonstrates using a TP223 capacitive touch sensor (TK43 module) with Embassy async:

  • GPIO input with edge detection
  • Async polling with non-blocking delays
  • Touch/release event detection

lm75_temperature.rs

Demonstrates I2C communication with an LM75 temperature sensor:

  • I2C master setup on GPIO21 (SDA) and GPIO22 (SCL)
  • Device detection and error handling
  • Temperature reading with 0.5°C resolution
  • Output in Celsius and Fahrenheit
cargo run --release --bin lm75_temperature

Wiring:

LM75 PinESP32 Pin
VCC3.3V
GNDGND
SDAGPIO21
SCLGPIO22

Dependencies

CrateVersionPurpose
esp-hal0.23Hardware abstraction layer for ESP32
esp-hal-embassy0.6Embassy async runtime integration
esp-backtrace0.15Panic handler with backtrace support
esp-println0.13Serial output (println! macro)
embassy-executor0.7Async task executor
embassy-time0.4Async timers and delays
embedded-hal1.0Hardware abstraction traits for drivers
critical-section1.2Interrupt-safe critical sections

Important: no_std Environment

This is a bare-metal (no_std) project. You cannot use standard library crates designed for desktop applications. See docs/std-vs-no_std.md for details.

What you CAN'T use

  • env_logger, log with std backends - use esp-println instead
  • std::time - use esp_hal::time or embassy_time instead
  • std::io - use embedded-io traits instead
  • Most crates that don't explicitly support no_std

Serial Output

Use esp-println for debugging:

use esp_println::println;println!("Hello from ESP32!");println!("Counter: {}", counter);

GPIO Notes

The following pins have special functions on the ESP32-WROOM-32:

Bootstrap pins (usable, but check datasheet):

  • GPIO0, GPIO2, GPIO5, GPIO12, GPIO15

Reserved pins (used by the module's flash - do not use):

  • GPIO6-11, GPIO16, GPIO20

See docs/esp32-pinout-reference.md for detailed pinout information.

Adding New Binaries

  1. Create your .rs file in src/bin/ or reference a file elsewhere
  2. Add a [[bin]] entry to Cargo.toml:
[[bin]]
name = "my_project"path = "./src/bin/my_project.rs"
  1. Build and run:
cargo run --release --bin my_project

Troubleshooting

"linker xtensa-esp32-elf-gcc not found"

The ESP toolchain environment isn't loaded. Run:

.$HOME/export-esp.sh

Errors about Option, Iterator, Copy not found

You've added a dependency that requires the standard library. Remove the incompatible dependency and find a no_std alternative.

No output after boot messages

Your code compiles but produces no visible output. Add esp-println and use println!() for debug output.

Embassy timer not working

Make sure to initialize Embassy with a timer:

let timg0 = TimerGroup::new(peripherals.TIMG0);
esp_hal_embassy::init(timg0.timer0);

Resources

License

MIT

About

No description, website, or topics provided.

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages