Rust en el ESP32: esp-idf-hal, esp-hal y embassy sin dramas

Cómo programar el ESP32 en Rust: las dos rutas (esp-idf-hal con std y esp-hal no_std), la toolchain con espup, ejemplos y cuándo merece la pena.

También disponible en: EN, ES

Serie Introducción al ESP32 Parte 14 Ver la serie →
Rust sobre el ESP32

Rust en el ESP32 dejó de ser un experimento hace tiempo. Hoy el ecosistema esp-rs es estable y sorprendentemente agradable, con una ventaja enorme: el compilador te evita errores de memoria y de concurrencia que en C te cuestan una tarde de depuración.

La contrapartida: hay que aprender Rust y montar una toolchain aparte.

Dos rutas, no una

RutaQué esIdeal para
esp-idf-halRust sobre ESP-IDF, con stdReutilizar ESP-IDF, Wi-Fi, proyectos grandes
esp-halno_std, sin ESP-IDFControl total, binarios pequeños, async
embassyFramework async sobre esp-halTareas concurrentes elegantes

La regla: si quieres Wi-Fi fácil y las APIs de Espressif, esp-idf-hal. Si quieres exprimir y no te importa lo no_std, esp-hal + embassy.

Toolchain

El instalador oficial es espup:

cargo install espup
espup install          # instala targets Xtensa y RISC-V
. $HOME/export-esp.sh  # o el script que genera

Y para flashear, cargo-espflash:

cargo install cargo-espflash
cargo espflash flash --monitor

Versión actual del HAL: esp-hal 1.2.0.

use esp_idf_svc::hal::delay::FreeRtos;
use esp_idf_svc::hal::gpio::PinDriver;
use esp_idf_svc::hal::peripherals::Peripherals;

fn main() -> anyhow::Result<()> {
    esp_idf_svc::sys::link_patches();
    let peripherals = Peripherals::take()?;
    let mut led = PinDriver::output(peripherals.pins.gpio2)?;

    loop {
        led.set_high()?;
        FreeRtos::delay_ms(1000);
        led.set_low()?;
        FreeRtos::delay_ms(1000);
    }
}

Fíjate en el detalle: el compilador te obliga a declarar el pin como salida antes de escribir. En C, un pinMode olvidado es un bug silencioso; aquí no compila.

Async con embassy

Lo mejor del camino no_std es embassy: tareas asíncronas de verdad, sin loop() ni máquinas de estado a mano.

#[embassy_executor::task]
async fn parpadeo(mut led: Output<'static, Gpio2>) {
    loop {
        led.toggle();
        Timer::after_millis(500).await;
    }
}

Dos tareas que «duermen» a la vez sin bloquearse: es la forma moderna de escribir firmware concurrente.

Ventajas reales

  • Seguridad: sin use-after-free, sin carreras de datos, sin null.
  • Concurrencia honesta: si compila, normalmente no se pisa la memoria.
  • Binarios pequeños en no_std.
  • cargo: dependencias, tests y documentación en un solo sitio.

Inconvenientes

  • Curva de aprendizaje de Rust (préstamos, async, no_std).
  • Ecosistema más joven que Arduino: menos librerías listas.
  • Toolchain aparte: espup, targets, versión de Rust concreta.
  • Menos ejemplos y tutoriales que en C/C++.

¿Cuándo elegirlo?

Sí:

  • Te importa la fiabilidad y quieres menos bugs raros.
  • Ya sabes Rust (o quieres aprenderlo en serio).
  • Necesitas async real o binarios muy pequeños.
  • Es un proyecto a largo plazo donde el mantenimiento importa.

No:

  • Es tu primer contacto con microcontroladores: empieza en Arduino o MicroPython.
  • Necesitas una librería concreta que solo existe en C++.
  • Vas con prisa.

Resumen

  • Dos rutas: esp-idf-hal (con std, sobre ESP-IDF) y esp-hal (no_std), más embassy para async.
  • Toolchain con espup y flasheo con cargo-espflash.
  • El compilador evita errores que en C son silenciosos.
  • esp-hal va por la 1.2.0 y el ecosistema ya es maduro.
  • Merece la pena si te importa el mantenimiento y la seguridad.

Siguiente paso: cambiamos de tercio con los IDE: cuál usar y por qué. O vuelve al hub de la serie.

Entradas relacionadas

↑↓ navegar ↵ abrir Esc cerrar