Neste tutorial, apresento um passo a passo completo para configurar o ambiente de desenvolvimento em Rust e criar um primeiro programa para o ESP32. A proposta é partir do zero, instalando e preparando o toolchain específico para a família ESP32, gerar um projeto utilizando os templates oficiais e, por fim, compilar, gravar e executar um exemplo simples que imprime “Hello, World!” na porta serial e faz o LED de teste piscar. O foco é oferecer um guia para quem está dando os primeiros passos na programação embarcada com Rust para o ESP32.
Este experimento foi feito utilizando um módulo ESP-WROOM-32. Embora esse seja um dos modelos mais simples da família ESP32, esse tutorial pode ser aplicado a modelos mais novos também.
Os módulos ESP32 são fabricados pela Espressif. Esse modelo especificamente, tem as seguintes características:
Enfim, ele possui várias características interessantes para aplicações embarcadas e IoT.
O projeto aqui proposto é bem simples, ideal para quem quer dar os primeiros passos na programação Rust para essa plataforma. O programa de exemplo deste tutorial exibe a mensagem “Hello, World!” no terminal, usando a saída serial/USB do módulo, além de fazer piscar o LED azul de teste.
Segue o passo a passo do projeto:
Esse passo é necessário para quem estiver programando no Linux do WSL. Vamos usar a porta USB/serial para que as ferramentas de desenvolvimento façam o upload do programa para dentro do módulo. Além disso, nesse programa de exemplo, o módulo irá se comunicar com o terminal também via USB/serial.
O problema é que o Linux do WSL não “enxerga” as portas do PC por padrão, como no Windows. É necessário configurar o Windows para que ele compartilhe as portas USB com o Linux do WSL. Para fazer isso, instale no Windows o utilitário usbipd, digitando no PowerShell:
winget install --interactive --exact dorssel.usbipd-win
Com o usbipd instalado e o módulo ESP32 conectado a uma porta USB, verifique em qual barramento USB o módulo está conectado através do comando:
usbipd list
Observe em qual barramento USB está conectado o módulo ESP32 (qual dispositivo “novo” apareceu no seu PC). No meu caso, está conectado ao barramento 1-5 (USB-Enhanced-SERIAL CH9102). Agora compartilhe essa porta com o Linux no WSL através dos comandos abaixo, substituindo 1-5 pelo seu barramento onde está conectado o módulo ESP32.
Obs: É provável que você precise abrir o PowerShell como usuário administrador para fazer isso.
usbipd bind --busid 1-5
usbipd attach --wsl --busid 1-5
Este projeto foi desenvolvido em um ambiente WSL (Ubuntu 24.04.1 no Windows) em um PC de 64 bits. O primeiro passo é certificar-se de que o sistema operacional está atualizado. Abra o terminal e digite:
sudo apt update
sudo apt upgrade -y
Agora verifique se o Linux reconhece o dispositivo USB compartilhado pelo Windows. Para fazer isso, instale o pacote usbutils e use o comando lsusb, como mostrado abaixo:
sudo apt install usbutils -y
lsusb
O dispositivo pode ter aparecido com nome diferente, como é o caso mostrado na figura acima. Sem problemas quanto a isso. Normalmente, ao usar lsusb no Linux sem ter feito o compartilhamento antes, a lista de dispositivos é vazia.
As linhas de comando abaixo instalam o compilador Rust e seu conjunto de ferramentas:
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
. "$HOME/.cargo/env" -y
Se quiser verificar se Rust foi instalado corretamente, basta digitar:
cargo --version
Cargo é o gerenciador de pacotes e de compilação do ecossistema Rust. Esse comando deverá mostrar a versão instalada do Cargo. No meu caso, 1.91.1.
Como estamos compilando para outra plataforma, o compilador Rust ainda precisa de ferramentas externas para completar a “linkagem” do binário executável. Ele até consegue fazer o primeiro estágio de compilação, gerando os arquivos “objeto”, mas quem cria o binário final é o “linker” – normalmente o cc, gcc ou clang. Para instalar essas dependências, digite:
sudo apt install build-essential -y
Os comandos abaixo instalam o conjunto de ferramentas e templates de projeto necessários para compilarmos um programa em Rust para os módulos ESP32:
(substituindo SEU_USUARIO pelo seu username no Linux)
cargo install espup
espup install
. /home/SEU_USUARIO/export-esp.sh
cargo install espflash
cargo install esp-config --features=tui
curl --proto '=https' --tlsv1.2 -LsSf https://github.com/probe-rs/probe-rs/releases/latest/download/probe-rs-tools-installer.sh | sh
cargo install esp-generate
Com as ferramentas e templates instalados, digite o comando abaixo para criar o projeto:
esp-generate
Primeiramente, o esp-generate questionará para qual módulo deverá ser o projeto. No meu caso, por se tratar de um modelo ESP-WROOM-32, trata-se da opção “esp32”. Verifique qual é o seu modelo de ESP32 e escolha a opção adequada. Usando as setas para cima e para baixo do teclado, selecione o módulo correspondente e pressione Enter.
Depois disso, o esp-generate perguntará qual é o nome do projeto. Nesse caso, “hello_blink“.
Finalmente, o esp-generate permitirá escolher quais recursos (“features”) devem estar automaticamente disponíveis no projeto. Como esse tutorial é apenas de primeiros passos, vamos deixar o padrão. Para fazer isso, basta salvar as opções pressionando “s“.
Isso conclui a criação do projeto usando o template.
O que vamos fazer agora é acessar o projeto recém-criado e adicionar o crate esp-println ao projeto. Isso vai fazer com que possamos usar a macro println!, redirecionando sua saída para a porta serial do ESP32.
cd hello_blink
cargo add esp-println --features esp32
O arquivo principal do binário é src/bin/main.rs. Altere-o para que ele tenha o conteúdo mostrado abaixo:
#![no_std]
#![no_main]
#![deny(
clippy::mem_forget,
reason = "mem::forget is generally not safe to do with esp_hal types, especially those \
holding buffers for the duration of a data transfer."
)]
use esp_hal::clock::CpuClock;
use esp_hal::gpio::{Level, Output, OutputConfig};
use esp_hal::main;
use esp_hal::time::{Duration, Instant};
use esp_println::println;
#[panic_handler]
fn panic(_: &core::panic::PanicInfo) -> ! {
loop {}
}
esp_bootloader_esp_idf::esp_app_desc!();
#[main]
fn main() -> ! {
let config = esp_hal::Config::default().with_cpu_clock(CpuClock::max());
let peripherals = esp_hal::init(config);
// Configura o LED no GPIO2 (LED embutido do ESP32)
let mut led = Output::new(peripherals.GPIO2, Level::Low, OutputConfig::default());
// Imprime mensagem na serial
println!("Hello, World!");
loop {
// Troca o estado do led
led.toggle();
// Aguarda 250ms
let delay_start = Instant::now();
while delay_start.elapsed() < Duration::from_millis(250) {}
}
}
Para compilar e executar o projeto, digite (com o ESP32 conectado à porta USB):
cargo run
Conforme mostrado no início do tutorial, o resultado deve ser “Hello, World!” impresso no terminal e o LED azul do módulo ESP32 piscando:
Se der a mensagem de erro “error: linker `xtensa-esp32-elf-gcc` not found“, basta digitar novamente:
(substituindo SEU_USUARIO pelo seu username no Linux)
. /home/SEU_USUARIO/export-esp.sh
Se for exibida a mensagem de erro ao se conectar com a porta serial, normalmente é falta de permissão. Basta observar na mensagem de erro qual é o dispositivo (/dev/…) e adicionar a permissão. No caso da mensagem acima, houve um erro ao acessar o dispositivo /dev/ttyACM0. O comando para conceder permissão então é:
sudo chmod 666 /dev/ttyACM0