Rust Embarcados: Carreira IoT e ARM Cortex-M | Rust Brasil

Carreira em sistemas embarcados com Rust: microcontroladores, IoT, embedded-hal, ARM Cortex-M e RISC-V. Salários e roadmap.

Introdução

Sistemas embarcados representam uma das fronteiras mais empolgantes para Rust. Historicamente dominado por C e Assembly, o mundo embedded está passando por uma transformação silenciosa: Rust oferece as mesmas garantias de acesso direto a hardware e controle de memória que C, mas com segurança de memória em tempo de compilação, um sistema de tipos expressivo e ferramentas modernas de desenvolvimento.

De microcontroladores ARM Cortex-M a processadores RISC-V, de dispositivos IoT a sistemas automotivos, Rust está conquistando seu espaço na programação embedded. O ecossistema embedded-hal criou uma camada de abstração padronizada que permite escrever drivers portáveis entre diferentes famílias de chips, algo que nunca foi alcançado de forma satisfatória com C.

Para desenvolvedores brasileiros, o mercado de embedded com Rust é especialmente interessante: o Brasil possui uma indústria significativa de eletrônica e IoT, e a demanda global por profissionais que dominem Rust embedded está crescendo rapidamente, com salários atrativos tanto em posições nacionais quanto internacionais remotas.

Por Que Rust Para Embedded?

Segurança Sem Overhead

Em sistemas embarcados, bugs podem ter consequências físicas graves — desde dispositivos médicos que falham até sistemas automotivos que se comportam de forma imprevisível. Rust elimina classes inteiras de bugs comuns em C embedded:

  • Buffer overflows: Verificações em tempo de compilação
  • Use-after-free: Sistema de ownership
  • Data races: Garantias de concorrência do tipo system
  • Null pointer dereference: Option<T> em vez de ponteiros nulos
  • Integer overflow: Checked arithmetic disponível sem custo em debug

Zero-Cost Abstractions

Traits, generics e iterators em Rust geram código de máquina tão eficiente quanto C escrito à mão:

// Esta abstração de alto nível...
let soma: u32 = dados.iter()
    .filter(|x| **x > 10)
    .map(|x| x * 2)
    .sum();

// ...gera código de máquina equivalente a um loop C manual

no_std: Rust Sem a Standard Library

Rust pode ser compilado sem a standard library, eliminando qualquer dependência de sistema operacional ou alocação dinâmica de memória:

#![no_std]
#![no_main]

use core::panic::PanicInfo;

#[panic_handler]
fn panic(_info: &PanicInfo) -> ! {
    loop {}
}

// Seu código bare-metal aqui

Ecossistema embedded-hal

O trait embedded-hal padroniza interfaces de hardware, permitindo escrever drivers que funcionam em qualquer microcontrolador:

use embedded_hal::digital::OutputPin;
use embedded_hal::delay::DelayNs;

/// Driver genérico para LED que funciona em qualquer plataforma
struct Led<P: OutputPin> {
    pin: P,
}

impl<P: OutputPin> Led<P> {
    fn new(pin: P) -> Self {
        Led { pin }
    }

    fn ligar(&mut self) {
        self.pin.set_high().ok();
    }

    fn desligar(&mut self) {
        self.pin.set_low().ok();
    }

    fn piscar<D: DelayNs>(&mut self, delay: &mut D, ms: u32) {
        self.ligar();
        delay.delay_ms(ms);
        self.desligar();
        delay.delay_ms(ms);
    }
}

O “Hello World” do mundo embedded – piscar um LED:

//! Pisca LED no STM32F4 (ARM Cortex-M4)
#![no_std]
#![no_main]

use cortex_m_rt::entry;
use panic_halt as _;
use stm32f4xx_hal::{
    pac,
    prelude::*,
};

#[entry]
fn main() -> ! {
    // Obter acesso aos periféricos do microcontrolador
    let dp = pac::Peripherals::take().unwrap();
    let cp = cortex_m::Peripherals::take().unwrap();

    // Configurar o clock do sistema
    let rcc = dp.RCC.constrain();
    let clocks = rcc.cfgr
        .sysclk(84.MHz())
        .freeze();

    // Configurar o GPIO para o LED (pino PA5 no STM32F4 Discovery)
    let gpioa = dp.GPIOA.split();
    let mut led = gpioa.pa5.into_push_pull_output();

    // Configurar o delay usando o SysTick timer
    let mut delay = cp.SYST.delay(&clocks);

    // Loop principal: pisca o LED a cada 500ms
    loop {
        led.set_high();
        delay.delay_ms(500u32);
        led.set_low();
        delay.delay_ms(500u32);
    }
}

Exemplo: Leitura de Sensor de Temperatura (I2C)

Lendo um sensor de temperatura via barramento I2C:

#![no_std]
#![no_main]

use cortex_m_rt::entry;
use panic_halt as _;
use stm32f4xx_hal::{
    pac,
    prelude::*,
    i2c::I2c,
};
use core::fmt::Write;

/// Driver para sensor de temperatura LM75
struct Lm75<I2C> {
    i2c: I2C,
    endereco: u8,
}

impl<I2C, E> Lm75<I2C>
where
    I2C: embedded_hal::i2c::I2c<Error = E>,
{
    const REG_TEMPERATURA: u8 = 0x00;

    fn new(i2c: I2C, endereco: u8) -> Self {
        Lm75 { i2c, endereco }
    }

    fn ler_temperatura(&mut self) -> Result<f32, E> {
        let mut buf = [0u8; 2];
        self.i2c.write_read(
            self.endereco,
            &[Self::REG_TEMPERATURA],
            &mut buf,
        )?;

        // Converter os 2 bytes para temperatura em Celsius
        let raw = ((buf[0] as i16) << 8 | buf[1] as i16) >> 5;
        Ok(raw as f32 * 0.125)
    }
}

#[entry]
fn main() -> ! {
    let dp = pac::Peripherals::take().unwrap();
    let cp = cortex_m::Peripherals::take().unwrap();

    let rcc = dp.RCC.constrain();
    let clocks = rcc.cfgr.sysclk(84.MHz()).freeze();

    // Configurar I2C
    let gpiob = dp.GPIOB.split();
    let scl = gpiob.pb6.into_alternate_open_drain();
    let sda = gpiob.pb7.into_alternate_open_drain();

    let i2c = I2c::new(dp.I2C1, (scl, sda), 100.kHz(), &clocks);

    // Criar driver do sensor
    let mut sensor = Lm75::new(i2c, 0x48);

    // Configurar UART para output
    let gpioa = dp.GPIOA.split();
    let tx = gpioa.pa2.into_alternate();
    let mut serial = dp.USART2.tx(tx, 115_200.bps(), &clocks).unwrap();

    let mut delay = cp.SYST.delay(&clocks);

    loop {
        match sensor.ler_temperatura() {
            Ok(temp) => {
                writeln!(serial, "Temperatura: {:.1}°C", temp).ok();
            }
            Err(_) => {
                writeln!(serial, "Erro ao ler sensor").ok();
            }
        }
        delay.delay_ms(1000u32);
    }
}

Exemplo: Controle de Motor PWM

Controlando um servo motor com PWM:

#![no_std]
#![no_main]

use cortex_m_rt::entry;
use panic_halt as _;
use stm32f4xx_hal::{
    pac,
    prelude::*,
    timer::Channel,
};

/// Controle de servo motor via PWM
struct ServoMotor<PWM> {
    pwm: PWM,
    min_pulso_us: u32,
    max_pulso_us: u32,
}

impl<PWM> ServoMotor<PWM>
where
    PWM: embedded_hal::pwm::SetDutyCycle,
{
    fn new(pwm: PWM, min_us: u32, max_us: u32) -> Self {
        ServoMotor {
            pwm,
            min_pulso_us: min_us,
            max_pulso_us: max_us,
        }
    }

    /// Define o ângulo do servo (0-180 graus)
    fn set_angulo(&mut self, angulo: u8) {
        let angulo = angulo.min(180) as u32;
        let faixa = self.max_pulso_us - self.min_pulso_us;
        let pulso = self.min_pulso_us + (faixa * angulo / 180);

        // Converter microsegundos para duty cycle
        // Período PWM = 20ms (50Hz), duty = pulso / período
        let duty = (pulso * 100) / 20_000;
        let max_duty = self.pwm.max_duty_cycle();
        let duty_valor = (max_duty as u32 * duty / 100) as u16;

        self.pwm.set_duty_cycle(duty_valor).ok();
    }
}

#[entry]
fn main() -> ! {
    let dp = pac::Peripherals::take().unwrap();
    let cp = cortex_m::Peripherals::take().unwrap();

    let rcc = dp.RCC.constrain();
    let clocks = rcc.cfgr.sysclk(84.MHz()).freeze();

    let gpioa = dp.GPIOA.split();
    let canal_pwm = gpioa.pa0.into_alternate();

    // Configurar Timer para PWM a 50Hz (período de 20ms)
    let mut pwm = dp.TIM2.pwm_hz(canal_pwm, 50.Hz(), &clocks);
    pwm.enable(Channel::C1);

    let mut servo = ServoMotor::new(pwm, 500, 2500);
    let mut delay = cp.SYST.delay(&clocks);

    loop {
        // Varrer de 0 a 180 graus
        for angulo in (0..=180).step_by(10) {
            servo.set_angulo(angulo);
            delay.delay_ms(100u32);
        }

        // Varrer de 180 a 0 graus
        for angulo in (0..=180).rev().step_by(10) {
            servo.set_angulo(angulo);
            delay.delay_ms(100u32);
        }

        delay.delay_ms(1000u32);
    }
}

Alvos e Plataformas Suportadas

AlvoDescriçãoChips Comuns
thumbv6m-none-eabiCortex-M0/M0+STM32F0, nRF51, RP2040
thumbv7m-none-eabiCortex-M3STM32F1, LPC1768
thumbv7em-none-eabiCortex-M4/M7 (sem FPU)STM32F4 (soft float)
thumbv7em-none-eabihfCortex-M4/M7 (com FPU)STM32F4, STM32H7
thumbv8m.main-none-eabihfCortex-M33STM32L5, nRF9160

RISC-V

AlvoDescriçãoChips Comuns
riscv32imac-unknown-none-elfRISC-V 32-bitESP32-C3, GD32VF103
riscv32imc-unknown-none-elfRISC-V 32-bit (sem atomic)ESP32-C2
riscv64gc-unknown-none-elfRISC-V 64-bitK210, BL808

Outros Alvos

AlvoDescrição
xtensa-esp32-none-elfESP32 (Xtensa)
xtensa-esp32s2-none-elfESP32-S2
xtensa-esp32s3-none-elfESP32-S3
aarch64-unknown-noneARM 64-bit bare-metal

Crates Essenciais para Embedded

Camada de Abstração de Hardware

CrateDescrição
embedded-halTraits padrão para periféricos
embedded-hal-asyncVersão async do embedded-hal
embedded-ioTraits para I/O
cortex-mAcesso ao processador ARM Cortex-M
cortex-m-rtRuntime para Cortex-M

HALs Específicas

CrateDescrição
stm32f4xx-halHAL para STM32F4
stm32h7xx-halHAL para STM32H7
nrf-halHAL para nRF (Nordic Semiconductor)
rp2040-halHAL para Raspberry Pi Pico (RP2040)
esp-halHAL para ESP32 (Espressif)
atsamd-halHAL para Atmel SAMD

Comunicação e Protocolos

CrateDescrição
smoltcpStack TCP/IP para no_std
embassy-netNetworking async para embedded
defmtLogging eficiente para embedded
postcardSerialização compacta para no_std
heaplessEstruturas de dados sem heap
bbqueueBuffer circular lock-free para no_std

Frameworks Async

CrateDescrição
embassyFramework async completo para embedded
rticReal-Time Interrupt-driven Concurrency
embassy-executorExecutor async para microcontroladores
embassy-timeTimers e delays async

Embassy: O Futuro do Embedded Rust

Embassy é um framework async para embedded que está transformando como escrevemos firmware em Rust:

#![no_std]
#![no_main]

use embassy_executor::Spawner;
use embassy_stm32::gpio::{Level, Output, Speed};
use embassy_time::Timer;
use defmt::info;
use {defmt_rtt as _, panic_probe as _};

#[embassy_executor::main]
async fn main(spawner: Spawner) {
    let p = embassy_stm32::init(Default::default());

    // Spawn de tarefas independentes
    spawner.spawn(tarefa_led(p.PA5)).unwrap();
    spawner.spawn(tarefa_sensor()).unwrap();
    spawner.spawn(tarefa_comunicacao()).unwrap();

    info!("Sistema inicializado!");
}

#[embassy_executor::task]
async fn tarefa_led(pin: embassy_stm32::peripherals::PA5) {
    let mut led = Output::new(pin, Level::Low, Speed::Low);

    loop {
        led.set_high();
        Timer::after_millis(500).await;
        led.set_low();
        Timer::after_millis(500).await;
    }
}

#[embassy_executor::task]
async fn tarefa_sensor() {
    loop {
        // Simular leitura de sensor
        info!("Lendo sensor de temperatura...");
        Timer::after_secs(2).await;
    }
}

#[embassy_executor::task]
async fn tarefa_comunicacao() {
    loop {
        // Simular envio de dados via rede
        info!("Enviando dados via MQTT...");
        Timer::after_secs(5).await;
    }
}

Empresas que Contratam

Semicondutores e Hardware

  • Espressif Systems: Fabricante do ESP32, investindo pesado em Rust
  • Nordic Semiconductor: Suporte Rust para chips nRF
  • STMicroelectronics: Ecossistema crescente em Rust
  • Raspberry Pi Foundation: RP2040 com excelente suporte Rust

Automotivo

  • Volvo: Usando Rust para software de veículos
  • Volkswagen: Explorando Rust para sistemas embarcados
  • Rivian: Veículos elétricos com componentes em Rust
  • Woven by Toyota: Plataforma de mobilidade

IoT e Dispositivos

  • Oxide Computer: Firmware de servidor em Rust
  • Ferrous Systems: Consultoria especializada em Rust embedded
  • Tweede Golf: Consultoria Rust na Holanda
  • Espressif: ESP-IDF com suporte Rust

Aeroespacial e Defesa

  • Lynx Software Technologies: RTOS com componentes Rust
  • Krtkl: Hardware com firmware Rust
  • Empresas de defesa: Sistemas críticos migrando para Rust

No Brasil

  • Indústria automotiva: Fornecedores de autopeças com eletrônica embarcada
  • IoT e agritech: Dispositivos de monitoramento agrícola
  • Telecomunicações: Firmware de equipamentos de rede
  • Startups de hardware: Dispositivos IoT e wearables
  • Consultorias: Projetos embedded para clientes nacionais e internacionais

Roadmap de Habilidades

Nível Júnior (0-12 meses)

  1. Fundamentos Rust: Ownership, lifetimes, traits, generics, no_std
  2. Eletrônica básica: Circuitos digitais, LEDs, botões, resistores, multímetro
  3. Microcontrolador: Começar com RP2040 (Raspberry Pi Pico) ou STM32F4
  4. GPIO e periféricos: Entrada/saída digital, PWM, ADC
  5. Comunicação: UART, SPI, I2C
  6. Ferramentas: probe-rs, defmt, cargo-embed, openocd

Nível Pleno (1-3 anos)

  1. Protocolos avançados: USB, CAN, Ethernet, BLE
  2. RTOS: Embassy ou RTIC para multitasking
  3. DMA: Direct Memory Access para transfers eficientes
  4. Drivers: Escrever drivers para sensores e atuadores
  5. Múltiplos targets: ARM, RISC-V, ESP32
  6. Segurança: Secure boot, criptografia em hardware, TrustZone

Nível Sênior (3+ anos)

  1. Design de sistemas: Arquitetura de firmware complexo
  2. BSP (Board Support Package): Criar suporte para novas placas
  3. Certificações: Safety-critical (IEC 61508, ISO 26262)
  4. HAL contributions: Contribuir para HALs e embedded-hal
  5. Performance: Otimização de código para targets específicos
  6. Liderança: Decisões de arquitetura, escolha de componentes

Expectativas Salariais

Brasil (CLT)

NívelFaixa Salarial (R$/mês)Observações
JúniorR$ 5.000 - R$ 9.000Firmware básico
PlenoR$ 10.000 - R$ 18.000Drivers, protocolos, IoT
SêniorR$ 18.000 - R$ 30.000Arquitetura de firmware
StaffR$ 30.000 - R$ 45.000+Safety-critical, liderança

Remoto Internacional (USD)

NívelFaixa Salarial (USD/ano)Observações
Júnior$55.000 - $85.000Startups IoT
Pleno$85.000 - $130.000Empresas de hardware
Sênior$130.000 - $200.000Automotivo, aeroespacial
Staff$200.000 - $280.000+Safety-critical, liderança

Diferencial Salarial

Desenvolvedores embedded com Rust costumam ganhar 15-30% a mais que equivalentes puramente C, refletindo a raridade da combinação de habilidades e a crescente demanda por firmware seguro.

Hardware Para Começar

Kit Iniciante Recomendado

ItemPreço AproximadoPor Quê
Raspberry Pi Pico (RP2040)R$ 30-50Melhor suporte Rust para iniciantes
STM32F4 DiscoveryR$ 100-150ARM Cortex-M4 com vários periféricos
ESP32-C3 DevKitR$ 40-60RISC-V com Wi-Fi e Bluetooth
Breadboard + jumpersR$ 20-30Para prototipagem
LEDs, resistores, sensoresR$ 30-50Componentes básicos
Debugger ST-Link V2R$ 30-50Para STM32

Investimento Total Inicial

Com aproximadamente R$ 200-400, você pode montar um laboratório de desenvolvimento embedded com Rust capaz de explorar ARM, RISC-V e Wi-Fi.

Projetos Práticos para o Portfólio

  1. LED blink com timer: O clássico hello world embedded
  2. Estação meteorológica: Sensor de temperatura/umidade com display OLED
  3. Controle de servo: PWM para controlar servo motores
  4. Logger de dados: Registrar dados de sensores em cartão SD
  5. Monitor de qualidade do ar: Sensor de CO2/PM2.5 com display
  6. Semáforo inteligente: Máquina de estados com botões e LEDs
  7. Relógio digital: RTC com display de 7 segmentos
  8. Comunicação BLE: Enviar dados de sensor via Bluetooth Low Energy

Recursos de Aprendizado

Livros e Documentação

  • “The Embedded Rust Book”: Documentação oficial do Embedded WG
  • “Discovery Book”: Tutorial hands-on com placa micro:bit
  • “Real-Time Interrupt-driven Concurrency”: Documentação do RTIC
  • “Embassy Book”: Documentação do framework Embassy

Comunidades

  • Rust Embedded Working Group: Grupo oficial de embedded Rust
  • Matrix #rust-embedded: Chat oficial
  • Rust Brasil: Comunidade brasileira
  • Embassy Discord: Comunidade do framework Embassy

Hardware Playgrounds

  • Wokwi: Simulador online de microcontroladores com suporte Rust
  • QEMU: Emulador para ARM e RISC-V
  • Renode: Framework de simulação de sistemas embarcados

Conclusão

Sistemas embarcados com Rust representam uma convergência perfeita de demanda do mercado e capacidade técnica. A segurança de memória de Rust é particularmente valiosa em dispositivos que operam em ambientes críticos – de carros autônomos a dispositivos médicos, de infraestrutura industrial a satélites.

Próximos Passos Concretos

  1. Compre um kit: Raspberry Pi Pico ou ESP32-C3 para começar
  2. Configure o ambiente: Instale Rust com targets thumbv6m-none-eabi e riscv32imc-unknown-none-elf
  3. Faça o LED piscar: Complete o tutorial do Embedded Rust Book
  4. Explore periféricos: I2C, SPI, UART, PWM, ADC
  5. Aprenda Embassy: Framework async moderno para embedded
  6. Construa projetos: Estação meteorológica, logger de dados
  7. Contribua: embedded-hal, HALs específicas, drivers
  8. Publique no GitHub: Documente seus projetos com fotos e diagramas
  9. Participe da comunidade: Rust Embedded WG, Rust Brasil
  10. Busque vagas: Embedded Rust jobs em empresas de IoT, automotivo e hardware

O mundo embedded está em plena transição de C para Rust. Ser um dos primeiros profissionais a dominar essa combinação coloca você em uma posição privilegiada no mercado. Comece hoje – seu próximo projeto pode ser o firmware que roda em milhões de dispositivos.