Introdução
Programação de sistemas embarcados tradicionalmente é dominada por C e, em menor grau, C++. Mas Rust está mudando esse cenário rapidamente. A combinação de segurança de memória sem garbage collector, abstrações zero-cost, suporte a no_std (programação sem a biblioteca padrão) e um ecossistema crescente de HALs (Hardware Abstraction Layers) torna Rust uma alternativa real e cada vez mais adotada para microcontroladores e dispositivos IoT.
Ao contrário de C, onde buffer overflows, dangling pointers e data races são bugs comuns e frequentemente explorados em dispositivos conectados, Rust elimina essas classes inteiras de vulnerabilidades em tempo de compilação. Para dispositivos IoT que operam em ambientes hostis e raramente recebem atualizações de segurança, essa garantia é especialmente valiosa.
O Ecossistema Embedded Rust
Conceitos Fundamentais
no_std: Permite compilar Rust sem a biblioteca padrão (std), necessário para microcontroladores sem sistema operacional. Você ainda tem acesso aocore(tipos primitivos, iteradores, Option/Result) e opcionalmente aoalloc(alocação dinâmica)embedded-hal: Traits que definem interfaces genéricas para GPIO, SPI, I2C, UART, PWM, etc. Permite escrever drivers portáteis entre diferentes microcontroladores- PAC (Peripheral Access Crate): Gerado automaticamente a partir de arquivos SVD, fornece acesso de baixo nível aos registradores do chip
- HAL (Hardware Abstraction Layer): Implementa os traits de
embedded-halpara um chip específico, oferecendo uma API segura e idiomática
Plataformas Suportadas
| Plataforma | Crate Principal | Exemplos de Chips |
|---|---|---|
| ARM Cortex-M | cortex-m, cortex-m-rt | STM32, nRF52, RP2040 |
| ESP32 | esp-hal, esp-idf-hal | ESP32, ESP32-S3, ESP32-C3 |
| RISC-V | riscv, riscv-rt | ESP32-C3, GD32V |
| AVR | avr-device | ATmega328P (Arduino) |
| Raspberry Pi Pico | rp-hal | RP2040 |
Ferramentas de Desenvolvimento
| Ferramenta | Função |
|---|---|
| probe-rs | Flash, debug e RTT logging para ARM e RISC-V |
| cargo-embed | Integração probe-rs com Cargo (flash + monitor) |
| espflash | Flash para chips ESP32 |
| defmt | Framework de logging ultra-eficiente para embarcados |
Cargo.toml para um Projeto Embarcado (STM32)
[package]
name = "sensor-iot"
version = "0.1.0"
edition = "2021"
[dependencies]
cortex-m = "0.7"
cortex-m-rt = "0.7"
panic-halt = "1.0"
stm32f4xx-hal = { version = "0.22", features = ["stm32f411"] }
embedded-hal = "1.0"
defmt = "0.3"
defmt-rtt = "0.4"
[profile.release]
opt-level = "z" # Otimizar para tamanho
lto = true
codegen-units = 1
debug = true # Manter debug info para probe-rs
Exemplo Prático: Sensor de Temperatura IoT com STM32
Vamos construir um dispositivo que lê temperatura de um sensor DHT22 via GPIO e envia os dados por UART.
#![no_std]
#![no_main]
use cortex_m_rt::entry;
use panic_halt as _;
use stm32f4xx_hal::{
gpio::{Output, PushPull, Pin},
pac,
prelude::*,
serial::{config::Config, Serial},
timer::SysTimerExt,
};
use core::fmt::Write;
/// Estrutura para armazenar leitura do sensor
struct Leitura {
temperatura: f32,
umidade: f32,
}
/// Lê dados de um sensor DHT22 conectado ao pino especificado.
/// Simplificado para demonstração — em produção use a crate `dht-sensor`.
fn ler_sensor_simulado(contador: u32) -> Leitura {
// Em um projeto real, você usaria a crate `dht-sensor`
// com o embedded-hal para comunicação com o sensor
Leitura {
temperatura: 22.5 + (contador % 10) as f32 * 0.3,
umidade: 65.0 + (contador % 5) as f32 * 1.2,
}
}
#[entry]
fn main() -> ! {
// Obter periféricos do chip
let dp = pac::Peripherals::take().unwrap();
let cp = cortex_m::Peripherals::take().unwrap();
// Configurar clocks
let rcc = dp.RCC.constrain();
let clocks = rcc.cfgr
.use_hse(8.MHz())
.sysclk(84.MHz())
.freeze();
// Configurar delay
let mut delay = cp.SYST.delay(&clocks);
// Configurar GPIO
let gpioa = dp.GPIOA.split();
let gpioc = dp.GPIOC.split();
// LED de status (PC13 na maioria das placas STM32)
let mut led: Pin<'C', 13, Output<PushPull>> = gpioc.pc13.into_push_pull_output();
// Configurar UART (PA2 = TX, PA3 = RX)
let tx_pin = gpioa.pa2;
let rx_pin = gpioa.pa3;
let mut serial = Serial::new(
dp.USART2,
(tx_pin, rx_pin),
Config::default().baudrate(115200.bps()),
&clocks,
)
.unwrap();
writeln!(serial, "Sensor IoT iniciado - Rust Embedded").unwrap();
writeln!(serial, "Lendo temperatura a cada 2 segundos...").unwrap();
let mut contador: u32 = 0;
// Loop principal
loop {
// Piscar LED para indicar leitura
led.set_low();
delay.delay_ms(100u32);
led.set_high();
// Ler sensor
let leitura = ler_sensor_simulado(contador);
// Enviar dados via UART
writeln!(
serial,
"[{}] Temp: {:.1}°C | Umidade: {:.1}%",
contador,
leitura.temperatura,
leitura.umidade
)
.unwrap();
// Alerta se temperatura ultrapassar limiar
if leitura.temperatura > 30.0 {
writeln!(serial, "⚠ ALERTA: Temperatura elevada!").unwrap();
}
contador = contador.wrapping_add(1);
delay.delay_ms(2000u32);
}
}
Exemplo com ESP32 (usando esp-rs)
O ecossistema esp-rs permite programar chips ESP32 em Rust, tanto em modo no_std quanto com a framework ESP-IDF (que inclui Wi-Fi, Bluetooth, etc.).
[package]
name = "esp32-sensor"
version = "0.1.0"
edition = "2021"
[dependencies]
esp-idf-hal = "0.44"
esp-idf-svc = "0.50"
esp-idf-sys = { version = "0.36", features = ["binstart"] }
embedded-svc = "0.28"
anyhow = "1"
log = "0.4"
use esp_idf_hal::delay::FreeRtos;
use esp_idf_hal::gpio::PinDriver;
use esp_idf_hal::peripherals::Peripherals;
use esp_idf_svc::wifi::{EspWifi, BlockingWifi, ClientConfiguration, Configuration};
use esp_idf_svc::eventloop::EspSystemEventLoop;
use esp_idf_svc::nvs::EspDefaultNvsPartition;
use esp_idf_svc::http::client::EspHttpConnection;
use embedded_svc::http::client::Client;
use anyhow::Result;
use log::info;
fn conectar_wifi(
wifi: &mut BlockingWifi<EspWifi<'static>>,
ssid: &str,
senha: &str,
) -> Result<()> {
wifi.set_configuration(&Configuration::Client(ClientConfiguration {
ssid: ssid.try_into().unwrap(),
password: senha.try_into().unwrap(),
..Default::default()
}))?;
wifi.start()?;
wifi.connect()?;
wifi.wait_netif_up()?;
info!("Conectado ao Wi-Fi! IP: {:?}", wifi.wifi().sta_netif().get_ip_info()?);
Ok(())
}
fn enviar_dados(temperatura: f32, umidade: f32) -> Result<()> {
let mut cliente = Client::wrap(EspHttpConnection::new(&Default::default())?);
let url = format!(
"http://meu-servidor.com/api/sensor?temp={:.1}&umid={:.1}",
temperatura, umidade
);
let resposta = cliente.get(&url)?.submit()?;
info!("Dados enviados. Status: {}", resposta.status());
Ok(())
}
fn main() -> Result<()> {
esp_idf_svc::sys::link_patches();
esp_idf_svc::log::EspLogger::initialize_default();
let peripherals = Peripherals::take()?;
let sysloop = EspSystemEventLoop::take()?;
let nvs = EspDefaultNvsPartition::take()?;
// Configurar LED
let mut led = PinDriver::output(peripherals.pins.gpio2)?;
// Configurar Wi-Fi
let mut wifi = BlockingWifi::wrap(
EspWifi::new(peripherals.modem, sysloop.clone(), Some(nvs))?,
sysloop,
)?;
conectar_wifi(&mut wifi, "MinhaRede", "MinhaSenha123")?;
// Loop principal
loop {
led.set_high()?;
FreeRtos::delay_ms(100);
led.set_low()?;
// Simular leitura de sensor
let temperatura = 25.3;
let umidade = 68.0;
info!("Temperatura: {:.1}°C, Umidade: {:.1}%", temperatura, umidade);
if let Err(e) = enviar_dados(temperatura, umidade) {
log::error!("Erro ao enviar dados: {:?}", e);
}
FreeRtos::delay_ms(5000);
}
}
RTIC: Framework de Concorrência em Tempo Real
RTIC (Real-Time Interrupt-driven Concurrency) é um framework que utiliza o sistema de prioridades de interrupção do hardware para garantir concorrência segura sem overhead de runtime:
#![no_std]
#![no_main]
use panic_halt as _;
#[rtic::app(device = stm32f4xx_hal::pac, dispatchers = [EXTI0])]
mod app {
use stm32f4xx_hal::{
gpio::{Output, PushPull, Pin},
prelude::*,
timer::MonoTimerUs,
};
#[shared]
struct Shared {
temperatura: f32,
}
#[local]
struct Local {
led: Pin<'C', 13, Output<PushPull>>,
}
#[init]
fn init(ctx: init::Context) -> (Shared, Local) {
let rcc = ctx.device.RCC.constrain();
let clocks = rcc.cfgr.sysclk(84.MHz()).freeze();
let gpioc = ctx.device.GPIOC.split();
let led = gpioc.pc13.into_push_pull_output();
// Agendar primeira leitura
ler_sensor::spawn().unwrap();
(
Shared { temperatura: 0.0 },
Local { led },
)
}
#[task(shared = [temperatura], priority = 2)]
async fn ler_sensor(mut ctx: ler_sensor::Context) {
loop {
// Ler sensor (simplificado)
let temp = 23.5;
ctx.shared.temperatura.lock(|t| *t = temp);
// Reagendar para daqui a 1 segundo
// Em produção, use monotonic timer
}
}
#[task(local = [led], shared = [temperatura], priority = 1)]
async fn piscar_led(ctx: piscar_led::Context) {
loop {
ctx.local.led.toggle();
}
}
}
Empresas Usando Rust em Embarcados
- Espressif: Fabricante dos chips ESP32, mantém oficialmente o projeto
esp-rscom suporte completo a Rust - Oxide Computer: Computadores para data center com firmware inteiro escrito em Rust (projeto Hubris OS)
- Arm: Investindo em suporte a Rust para a plataforma Cortex-M e colaborando com o Embedded Working Group
- Infineon: Suporte a Rust para microcontroladores PSoC e XMC
- Volvo: Experimentando Rust para software automotivo embarcado
- Framework (laptop): Componentes de firmware com Rust
- System76: Firmware de laptops e teclados (projeto ec, firmware de embedded controller)
Como Começar
- Aprenda Rust primeiro: Domine ownership e borrowing com nosso tutorial de primeiros passos
- Configure cross-compilation: Siga nosso guia de cross-compilation para instalar toolchains para ARM e RISC-V
- Comece com Raspberry Pi Pico: O RP2040 é o chip mais acessível para aprender Rust embarcado, com documentação excelente
- Explore ESP32: Se precisa de Wi-Fi/Bluetooth, o ESP32 com
esp-rsé a porta de entrada mais prática - Pratique
no_std: Comece escrevendo código sem a biblioteca padrão, entendacoreealloc - Estude o Embedded Rust Book: Recurso oficial da comunidade Rust para desenvolvimento embarcado
Hardware Recomendado para Iniciantes
| Placa | Preço Aprox. | Destaques |
|---|---|---|
| Raspberry Pi Pico | R$ 30 | RP2040, excelente documentação Rust |
| ESP32-C3 DevKit | R$ 40 | RISC-V, Wi-Fi, Bluetooth, esp-rs |
| STM32 Nucleo-F411RE | R$ 80 | ARM Cortex-M4, ST-Link integrado |
| BBC micro:bit v2 | R$ 120 | Sensores integrados, LED matrix, BLE |
| nRF52840 DK | R$ 200 | BLE 5.0, USB, excelente para IoT |
Conclusão
Rust está se estabelecendo como uma alternativa séria ao C para desenvolvimento embarcado. A segurança de memória, o sistema de tipos e as abstrações zero-cost fazem de Rust uma escolha natural para dispositivos IoT que precisam ser seguros, confiáveis e eficientes. Com o suporte oficial da Espressif para ESP32 e o crescimento do ecossistema embedded-hal, nunca houve um momento melhor para começar com Rust embarcado.
Veja Também
- Guia de Cross-Compilation — Configure seu ambiente para compilar para diferentes arquiteturas
- Rust para DevOps — Infraestrutura e automação com Rust
- Rust para Desenvolvimento Web — Conecte seus dispositivos IoT a APIs Rust
- Tutorial: Primeiros Passos com Rust — Fundamentos da linguagem
- Tutorial: Tratamento de Erros — Essencial para código embarcado robusto
- Empresas que Usam Rust — Veja quem usa Rust em embarcados e IoT