A linguagem Rust tem se popularizado nesses últimos anos por ser uma linguagem moderna, segura e eficiente, principalmente no quesito gerenciamento e segurança de memória. Sua adoção pelo kernel do Linux, pela Espressif Systems, e as recomendações do governo dos Estados Unidos mostram as tendências da adoção a Rust em geral e também em Sistemas Embarcados.
Neste artigo vamos ver os primeiros passos para utilizar Rust em um ESP32, fazendo o clássico Hello World para piscar um LED.
Introdução
Rust é uma linguagem de programação moderna, projetada para ser produtiva, segura e rápida. Ela foi criada em 2006 por um grupo de pesquisadores da Mozilla, que buscavam uma alternativa ao C++ para desenvolver software de alto desempenho e baixo nível. Rust combina elementos de linguagens funcionais e imperativas, oferecendo recursos como gerenciamento de memória sem garbage collector, inferência de tipos, macros e concorrência sem dados compartilhados. Rust tem uma comunidade ativa e crescente, que contribui com bibliotecas, ferramentas, documentação e projetos de código aberto em diversas áreas.
Em sistemas embarcados, Rust tem crescido sua adoção por possuir algumas vantagens:
- Segurança de memória: previne erros comuns de gerenciamento de memória, como buffer overflow, vazamento de memória, acesso a ponteiros inválidos, utilizando um sistema de Ownership e Borrowing que verifica a validade das referências em tempo de compilação.
- Desempenho: Rust é uma linguagem compilada que não usa um garbage collector, o que reduz a sobrecarga de tempo de execução e o consumo de memória. Além disso possui distinção entre bibliotecas core e standard, pois para execução de bare-metal muito de uma biblioteca standard não é necessário.
- Concorrência: Rust facilita a programação em concorrência, para sistemas embarcados que precisam lidar com múltiplas tarefas simultâneas, tendo suporte a async nativo.
- Portabilidade: Rust suporta vários sistemas operacionais, arquiteturas e plataformas, incluindo sistemas embarcados bare-metal. Rust também permite a integração com código escrito em outras linguagens, como C ou C++, através de uma interface de FFI (Foreign Function Interface).
- Ecossistema: Rust conta com uma comunidade ativa e crescente, que contribui com ferramentas, bibliotecas, frameworks e documentação. Um grande exemplo é o Cargo, o gerenciador de pacotes e de dependências, onde facilmente, como usando pip em python, podemos criar projetos, adicionar e gerenciar dependências.
A Espressif Systems desde 2022, na DevCon22, começou a adoção de Rust pelos motivos mencionados anteriormente, e nessa mesma conferência já dizia que iria desenvolver as novas funcionalidades do esp-idf em rust e reescreveria algumas partes dele para ter essas garantias de segurança também.
Criando ambiente de desenvolvimento Rust
Iniciemos criando um ambiente de desenvolvimento para criar nossos programas em Rust. Nesse artigo usarei VSCode no Linux Ubuntu 22.04.
Basicamente precisamos:
- Instalar compilador e gerenciador de pacotes do Rust
- Instalar bibliotecas VSCode para Rust
- Instalar as bibliotecas para o ESP32 / Xtensa
Instalação do compilador e gerenciador de pacotes
Existem maneiras diferentes de instalar o compilador do Rust, porém a maneira recomendada pelo site oficial é utilizando o gerenciador de ferramentas rustup. Caso precise instalar em um sistema diferente do Linux, utilize esse link para ver outros métodos de instalação.
Execute o comando a seguir:
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
E após a execução, execute essas verificações para garantir que a instalação foi com sucesso tanto do Rust quanto do gerenciador de projetos/pacotes Cargo (durante a escrita deste artigo, a versão atual é 1.77.0):
Instalação das Extensões para trabalhar com Rust no VSCode
O VSCode possui várias extensões para auxiliar na programação de várias linguagens diferentes, não seria diferente com o Rust. Na aba Extensions do VSCode, instale os seguintes pacotes:
- Rust-analyzer: para verificação de sintaxe, auto-complete, documentação etc;
- CodeLLDB : Debbuger
- Even Better TOML: (Opcional) Melhora a edição dos arquivos de configuração de projeto
- Crates : (Opcional) Melhora a verificação de pacotes e suas versões
- Rust Syntax: (Opcional): Melhora a diferenciação da sintaxe com cores adicionais.
Instalação das bibliotecas do ESP32 / Xtensa
E por último, instalemos os pacotes para utilização do esp (espup), e do espflash utilizando o cargo:
cargo install espup
cargo install espflash
PS: Caso obtenha o erro linker 'cc' not found, instale o pacote build-essential no seu linux.
E executemos a instalação do espup para habilitar as ferramentas:
espup install
Criando um projeto Rust para o ESP32
Agora que já estamos com o Rust e o cargo instalados, vamos criar um projeto Hello-World para testar toda nossa instalação do Rust e em seguida criar um Hello-World para o ESP32.
Projeto Hello-World
Para criação de um projeto básico em Rust, independente se é para embarcados ou não, você executa:
cargo new hello-world
Cargo irá criar uma pasta chamada hello-world com os seguintes arquivos:
- Cargo.toml: Arquivo de configuração do projeto, onde conseguimos ver as dependências desse projeto, versão do projeto etc.
- .gitignore: Arquivo que podemos adicionar o que será ignorado durante a utilização de git
- src/main.rs : Na pasta src, ele cria o arquivo principal do Rust que será executado. A extensão rs, é a utilizada para os arquivos do Rust.
O main.rs conterá a declaração da função principal, que é a main() e um comando de println para imprimir a mensagem:
fn main() {
println!("Hello, world!");
}
Executamos então a função build do cargo para compilar o programa. O Cargo então, criará uma pasta chamada target/debug com o executável compilado. Rodamos o executável e vemos a mensagem “Hello World”.
Projeto Hello-World ESP32
Para o projeto focado em ESP32, poderíamos usar os mesmos passos anteriores, porém já existe um template específico para o ESP que podemos usar como modelo para começar o nosso projeto.
Para utilizar modelos, precisamos primeiramente instalar o cargo-generate, e depois apontamos o generate para o template (esp-rs/esp-template) que vamos usar para criar um projeto novo. Esse template pedirá algumas informações para criação do projeto para o modelo específico:
- Project name: Nome do projeto, no nosso caso blink
- MCU: No nosso caso esp32
- Configure advanced template options: Por agora selecionemos false
Do mesmo modo que no outro projeto, ele criará uma pasta com os arquivos do projeto, porém o arquivo main.rs estará como mostra a imagem abaixo. Adicionei alguns comentários para entender melhor o programa:
#![no_std]//desabilita a biblioteca standard no rust, reduzindo bastante o código compilado pois focamos aqui em bare-metal
#![no_main] // desabilita a utilização mandatória de uma função chamada main
use esp_backtrace as _; //importa o backtrace do esp para gerir error (panic na linguagem Rust)
use esp_hal::{clock::ClockControl, peripherals::Peripherals, prelude::*, Delay}; //importa structs do esp_hal
use esp_println::println; // importa um println específico do esp
#[entry] //configuração dizendo que a função a seguir é o ponto de entrada
fn main() -> ! {
let peripherals = Peripherals::take(); //obtem acesso aos periféricos do esp32
let system = peripherals.SYSTEM.split(); //recebe as informações dos periféricos de sistema
let clocks = ClockControl::max(system.clock_control).freeze(); //obtem acesso ao Clock
let mut delay = Delay::new(&clocks); // obtem acesso ao struct Delay para exe
println!("Hello world!"); // imprime Hello World no serial/console do esp32
loop {//looping infinito
println!("Loop...");
delay.delay_ms(1000u32); //configura o delay para 1 segundo do tipo unsigned int 32 bits
}
}
Agora executemos a função build do cargo e já estamos prontos para enviar o programa para o ESP32.
Executando Hello-World no ESP32
Uma vez que já temos o código compilado, usaremos o espflash para enviá-lo para o ESP32 e monitorar sua execução. O cargo já utilizará automaticamente essas ferramentas uma vez que executemos a função run, o que facilita o nosso processo. Podemos ver aqui a execução:
Alterando o Hello-World para Piscar LED
E como o último ponto do nosso artigo, alteremos o projeto blink para piscar o LED, nesse meu caso, da placa doit-esp32-devkit-v1, que fica no GPIO 2.
Criamos uma instância de IO, struct do pacote esp_hal, criamos uma variável chamada led, que recebe um IO do GPIO2 como push&pull e configurado como output.
Após isso usamos o método set_high para acender o LED e set_low para apagar. O código fica então assim:
#![no_std]//desabilita a biblioteca standard no rust, reduzindo bastante o código compilado pois focamos aqui em bare-metal
#![no_main] // desabilita a utilização mandatória de uma função chamada main
use esp_backtrace as _; //importa o backtrace do esp para gerir error (panic na linguagem Rust)
use esp_hal::{clock::ClockControl, peripherals::Peripherals, prelude::*, Delay, IO}; //importa métodos esp_hal
use esp_println::println; // importa um println específico do esp
#[entry] //configuração dizendo que a função a seguir é o ponto de entrada
fn main() -> ! {
let peripherals = Peripherals::take(); //obtem acesso aos periféricos do esp32
let system = peripherals.SYSTEM.split(); //recebe as informações dos periféricos de sistema
let clocks = ClockControl::max(system.clock_control).freeze(); //obtem acesso ao Clock
let mut delay = Delay::new(&clocks); // obtem acesso ao struct Delay para exe
let io = IO::new(peripherals.GPIO, peripherals.IO_MUX); // cria uma instância do GPIO
let mut led = io.pins.gpio2.into_push_pull_output(); // variavel led com GPIO2, push&pull output
println!("Hello world e Blink!"); // imprime Hello World no serial/console do esp32
loop { //looping infinito
led.set_high().unwrap(); //liga LED
println!("ON");
delay.delay_ms(1000u32); //configura o delay para 1 segundo do tipo unsigned int 32 bits
led.set_low().unwrap(); //desliga LED
println!("OFF");
delay.delay_ms(1000u32);
}
}
A execução fica assim:
Conclusões
Como vimos nesse artigo, a linguagem Rust possui muitas ferramentas para ESP tanto quanto para gerir pacotes/dependências e projetos, o que facilita muito o desenvolvimento de software. Já existem templates para alguns microcontroladores, o que agiliza muito o desenvolvimento, sem contar a flexibilidade do Rust para desenvolvimento de código seguro e performático para sistemas embarcados.
A documentação de Rust é bem detalhada e extensa, o que ajuda muito os que estão começando.
Escreva nos comentários quais suas primeiras impressões de Rust, se gostaria de mais artigos sobre a linguagem em si ou sobre ela em outras aplicações no esp32.
Referências
- Rust em Sistemas Embarcados
- The Rust Programming Language Book
- The Rust on ESP Book
- Crédito para a imagem destacada:Zacchaeus Oluwole












Parabéns, muito bom!
Vou fazer meu primeiro projeto com ESP32 e quero usa rust, será meu primeiro contato com rust e tambem com microcontroladores. Esse artigo vai me ajuda muito.