O que é o Zephyr RTOS e por que utilizá-lo
O Zephyr RTOS é um sistema operacional de tempo real de código aberto, mantido pela Linux Foundation, projetado especificamente para sistemas embarcados modernos. Diferentemente de RTOS tradicionais focados apenas em microcontroladores simples, o Zephyr foi concebido desde o início para escalar desde MCUs pequenos até sistemas embarcados complexos, com forte ênfase em portabilidade, segurança, modularidade e padronização.
Um dos grandes diferenciais do Zephyr é sua arquitetura fortemente orientada a Kconfig, Device Tree e CMake, permitindo que o mesmo código-fonte seja recompilado para diferentes placas e arquiteturas apenas alterando parâmetros de build. Isso muda profundamente o fluxo de desenvolvimento quando comparado a abordagens tradicionais baseadas em IDEs proprietárias e projetos fortemente acoplados ao hardware.
Do ponto de vista prático, o Zephyr resolve problemas clássicos enfrentados em projetos embarcados de médio e grande porte, como:
- crescimento descontrolado do código,
- dificuldade de reutilização entre placas diferentes,
- dependência excessiva de ferramentas específicas de fabricantes,
- e ausência de um modelo claro de configuração de hardware e software.
Além disso, o Zephyr já nasce integrado a um ecossistema moderno, com suporte oficial a centenas de placas, arquiteturas como ARM Cortex-M, RISC-V, Xtensa, além de um conjunto robusto de drivers, stacks de comunicação, sistema de arquivos, rede, USB, Bluetooth, sensores e sincronização de threads.
Zephyr no contexto STM32
No caso específico da família STM32, o Zephyr se destaca por:
- suportar diretamente placas NUCLEO e DISCOVERY,
- abstrair completamente a dependência do STM32Cube HAL quando desejado,
- permitir o uso de exemplos oficiais prontos (samples),
- e oferecer um fluxo de build reproduzível e automatizado via linha de comando.
Neste tutorial, trabalharemos com duas placas bastante comuns em ambientes educacionais e profissionais:
- NUCLEO-F411RE, usando um exemplo simples (
blinky); - NUCLEO-F429ZI, usando um exemplo mais avançado (
threads), explorando concorrência real.
A escolha desses dois exemplos não é arbitrária: ela permite observar claramente como o Zephyr escala de um firmware elementar para um sistema multitarefa sem alterar o fluxo básico de desenvolvimento.
O fluxo mental do Zephyr (antes de escrever código)
Antes de entrarmos em instalação e comandos, é importante compreender o modelo mental do Zephyr:
- O código da aplicação é apenas uma parte do sistema.
- A placa é descrita por Device Tree, não por arquivos
.hespecíficos. - As funcionalidades do sistema são habilitadas via Kconfig.
- O build é sempre feito fora da árvore da aplicação (out-of-tree build).
- O comando
westorquestra todo o processo: fetch, build, flash e debug.
Esse modelo pode parecer estranho no início, especialmente para quem vem de ambientes como Arduino, STM32CubeIDE ou ESP-IDF, mas ele se mostra extremamente poderoso à medida que o projeto cresce.
Preparando o ambiente de desenvolvimento do Zephyr
Antes de compilar qualquer exemplo, é fundamental preparar corretamente o ambiente. O Zephyr adota um fluxo totalmente baseado em linha de comando, multiplataforma e reproduzível, o que é uma grande vantagem em ambientes profissionais, CI/CD e times distribuídos.
Nesta seção vamos entender o que precisa estar instalado, por que cada ferramenta existe e como elas se encaixam no fluxo de desenvolvimento.
2.1 Conceito central: o papel do west
O west é a ferramenta central do Zephyr. Ele não é apenas um “build tool”, mas sim um meta-ferramenta que gerencia:
- repositórios Git (manifesto do Zephyr),
- build (invocando CMake + Ninja),
- flashing,
- debugging,
- e extensão de comandos específicos do ecossistema Zephyr.
Em termos práticos, pense no west como:
uma combinação de repo + cmake wrapper + flash/debug frontend.
Você não compila Zephyr sem o west.
2.2 Ferramentas necessárias
Para trabalhar com o Zephyr, você precisa de quatro camadas básicas de ferramentas:
1. Python (obrigatório)
O Zephyr utiliza Python para:
- o
west, - scripts de build,
- geração de código,
- e validações internas.
Versão recomendada:
- Python 3.8 ou superior
Verifique:
python3 --version
2. Toolchain (compilador)
Para placas STM32 (ARM Cortex-M), o Zephyr utiliza:
- GNU Arm Embedded Toolchain
Ela fornece:
arm-none-eabi-gccld,objcopy,objdump- suporte completo a Cortex-M3/M4/M7
Verificação:
arm-none-eabi-gcc --version
Importante: o Zephyr não usa o compilador do STM32CubeIDE por padrão. Ele trabalha com toolchains independentes.
3. CMake e Ninja
O sistema de build do Zephyr é baseado em:
- CMake → geração de build
- Ninja → execução rápida do build
Verificação:
cmake --version
ninja --version
4. Ferramentas de flash/debug
Para placas NUCLEO, normalmente usamos:
- OpenOCD (via ST-Link)
Verificação:
openocd --version
2.3 Criando o workspace do Zephyr
O Zephyr trabalha em um workspace, que é um diretório contendo:
- o kernel Zephyr,
- módulos externos,
- aplicações,
- e builds fora da árvore.
Fluxo típico:
zephyr-workspace/
├── zephyr/
├── modules/
├── bootloader/
├── tools/
└── applications/
Caso ainda não tenha o west instalado, execute o seguinte comando com o python.
python -m venv zephyr-workspace/.venv
source zephyr-workspace/.venv/bin/activate
pip install westEntão continue com a criação inicial:
west init zephyr-workspace
cd zephyr-workspace
west update
west zephyr-export
Esses comandos:
- Inicializam o manifesto,
- Baixam o kernel e módulos,
- Exportam variáveis de ambiente necessárias ao build.
Uma vez feito isso, você não precisa repetir para cada projeto.
2.4 Entendendo o conceito de build fora da árvore
No Zephyr, o diretório de build nunca fica misturado com o código-fonte.
Isso permite:
- múltiplos builds para placas diferentes,
- builds paralelos,
- comparação entre configurações,
- limpeza sem apagar código.
Por exemplo:
applications/
├── nucleof411re/
│ └── build/
├── nucleof429zi/
│ └── build/
Cada pasta representa um alvo de hardware específico.
2.5 Por que isso é importante na prática
Esse modelo resolve vários problemas clássicos:
- você pode compilar o mesmo exemplo para várias placas sem conflitos;
- o código original dos samples do Zephyr nunca é modificado;
- o build fica totalmente reprodutível;
- facilita automação e versionamento.
Isso será explorado explicitamente nas próximas seções, quando montarmos:
- o build do blinky para a NUCLEO-F411RE;
- e o build do threads para a NUCLEO-F429ZI.
Primeiro build prático: NUCLEO-F411RE com o exemplo blinky
Nesta seção faremos o primeiro contato prático com o Zephyr, compilando e gravando um firmware extremamente simples, porém fundamental: o blinky.
Apesar da simplicidade funcional (piscar um LED), esse exemplo permite entender todo o fluxo real de trabalho no Zephyr: organização de diretórios, comando de build, seleção de placa e gravação do firmware.
3.1 Por que começar com o blinky
O blinky é o “Hello World” dos sistemas embarcados. No contexto do Zephyr, ele é especialmente útil porque:
- não depende de periféricos complexos,
- usa apenas GPIO,
- valida rapidamente se toolchain, placa e debug estão funcionando,
- já utiliza Device Tree para mapear o LED da placa.
Ou seja: se o blinky funciona, todo o ambiente está corretamente configurado.
3.2 Organização do diretório para a NUCLEO-F411RE
Partimos do princípio que você já está no diretório raiz do workspace do Zephyr.
Agora criaremos um diretório dedicado exclusivamente a esta placa:
mkdir nucleof411re
cd nucleof411re
Esse diretório não contém código-fonte, apenas o resultado do build.
Essa prática é altamente recomendada em projetos reais, pois permite manter builds separados por hardware.
3.3 Comando de build do blinky
Com o diretório criado, execute exatamente o comando abaixo:
west build -p always -b nucleof411re ../zephyr/samples/blinky
Vamos destrinchar esse comando em detalhes.
3.4 Explicando o comando west build em profundidade
west build
É o comando responsável por:
- configurar o CMake,
- gerar os arquivos de build,
- e compilar o firmware.
-p always
Força o pristine build, ou seja:
- apaga qualquer build anterior,
- garante que nenhuma configuração antiga interfira no resultado.
Em ambientes profissionais, isso evita bugs difíceis de rastrear.
-b nucleof411re
Seleciona explicitamente a placa alvo.
Esse parâmetro faz com que o Zephyr:
- carregue o Device Tree da NUCLEO-F411RE,
- selecione o SoC STM32F411,
- configure clock, GPIO, memória e periféricos corretamente.
../zephyr/samples/blinky
Indica o código-fonte da aplicação, que neste caso é:
- um exemplo oficial do Zephyr,
- localizado dentro da árvore do kernel.
Note que não copiamos o código, apenas o referenciamos.
3.5 O que acontece internamente durante o build
Ao executar o comando, o Zephyr:
- Lê o Device Tree da placa
- Processa as opções de Kconfig
- Gera código de inicialização
- Compila o kernel RTOS
- Compila a aplicação (blinky)
- Linka tudo em um único firmware
Ao final, você terá algo como:
nucleof411re/
└── build/
├── zephyr/
│ ├── zephyr.elf
│ ├── zephyr.hex
│ └── zephyr.bin
Esses arquivos são os artefatos finais de firmware.
3.6 Gravando o firmware na NUCLEO-F411RE
Com a placa conectada via USB (ST-Link), execute:
west flash
O west irá:
- detectar automaticamente o runner (OpenOCD),
- identificar o ST-Link,
- gravar o firmware,
- e reiniciar a placa.
Se tudo estiver correto, o LED da NUCLEO-F411RE começará a piscar.
3.7 Cenários comuns e soluções
Placa não detectada
- Verifique o cabo USB
- Confirme permissões de acesso (Linux)
- Teste:
lsusb
Erro de OpenOCD
- Confirme se o OpenOCD suporta o ST-Link da placa
- Atualize a versão se necessário
Build falha
- Verifique se o nome da placa está correto (
nucleof411re) - Confirme se o workspace foi inicializado corretamente
3.8 O que você aprendeu até aqui
Neste ponto, você já domina:
- o fluxo básico do Zephyr,
- build fora da árvore,
- seleção explícita de placa,
- e gravação de firmware via
west.
Na próxima seção, subiremos o nível e trabalharemos com:
- NUCLEO-F429ZI,
- exemplo
threads, - e conceitos reais de concorrência no Zephyr.
Exemplo avançado: NUCLEO-F429ZI com o sample threads
Após validar o ambiente com o blinky, o próximo passo natural é entender como o Zephyr se comporta em um cenário multitarefa real. Para isso, utilizaremos o sample threads, que demonstra criação, escalonamento e execução concorrente de múltiplas threads.
Esse exemplo é extremamente importante porque ele já expõe o Zephyr como RTOS de fato, e não apenas como um firmware bare-metal com delays.
4.1 Por que usar o sample threads
O sample threads foi escolhido porque ele:
- cria múltiplas threads com prioridades diferentes,
- demonstra preempção,
- utiliza delays baseados no kernel,
- mostra claramente o papel do escalonador.
Na prática, ele permite observar:
- como o Zephyr alterna tarefas,
- como prioridades influenciam execução,
- e como o kernel se comporta em tempo real.
Isso é especialmente relevante na NUCLEO-F429ZI, que possui:
- Cortex-M4F mais potente,
- maior quantidade de RAM e Flash,
- e é típica de aplicações industriais e IoT mais complexas.
4.2 Organização do diretório para a NUCLEO-F429ZI
Agora vamos voltar para o diretório principal do projeto (workspace ou pasta onde você está organizando os builds) e criar um novo diretório específico para esta placa:
cd ..
mkdir nucleof429zi
cd nucleof429zi
Assim como no caso anterior, este diretório conterá apenas o build, mantendo os projetos totalmente isolados por hardware.
4.3 Comando de build do sample threads
Execute o comando exatamente como definido:
west build -p always -b nucleof429re ../zephyr/samples/threads
Observação importante:
Apesar do diretório se chamarnucleof429zi, o identificador da placa no Zephyr énucleof429re, e é esse nome que deve ser usado no parâmetro-b.
4.4 Análise detalhada do comando
A estrutura do comando é a mesma usada no blinky, o que reforça a consistência do fluxo de trabalho no Zephyr.
-p always
Garante um build limpo, evitando resíduos de builds anteriores.-b nucleof429re
Seleciona o Device Tree e as configurações corretas da NUCLEO-F429ZI.../zephyr/samples/threads
Aponta para um exemplo que já faz uso intensivo do kernel RTOS.
Essa repetição do padrão de build é proposital: uma vez entendido, ele não muda, independentemente da complexidade da aplicação.
4.5 O que o sample threads faz internamente
Sem entrar ainda no código linha a linha (faremos isso adiante), o comportamento geral é:
- Criação de múltiplas threads via
k_thread_create - Cada thread executa um loop infinito
- Cada uma imprime mensagens periódicas
- Delays são feitos com
k_msleep() - O escalonador decide qual thread executa a cada instante
Em placas com saída serial ativa, você verá mensagens intercaladas no terminal, evidenciando o funcionamento do RTOS.
4.6 Gravando o firmware na NUCLEO-F429ZI
Com a placa conectada:
west flash
O processo é idêntico ao anterior:
- detecção automática do ST-Link,
- uso do OpenOCD,
- gravação e reset da placa.
Se houver saída serial configurada, você poderá acompanhar a execução das threads via terminal.
4.7 Cenários práticos e problemas comuns
Nada aparece no terminal
- Confirme a porta serial correta (
/dev/ttyACM*) - Verifique baudrate configurado pelo sample
- Use:
minicom -D /dev/ttyACM0
Build demora mais
- Normal: o kernel RTOS completo está sendo compilado
- O sample
threadsativa mais subsistemas do Zephyr
Diferença de comportamento entre placas
- Esperado: clock, memória e Device Tree mudam
- O código da aplicação permanece o mesmo
4.8 O que este exemplo consolida
Com este segundo projeto, você agora compreende:
- como o Zephyr escala de simples para complexo,
- como reutilizar o mesmo fluxo de build,
- como separar builds por placa,
- e como o Zephyr se comporta como RTOS real.
Na próxima seção, vamos comparar os dois cenários, discutir boas práticas de organização de projetos Zephyr e apresentar variações comuns de uso em projetos reais.
Excelente, vamos consolidar o aprendizado e elevar o nível de entendimento arquitetural.
Seção 5 – Comparando os dois cenários e boas práticas com Zephyr
Agora que você já executou dois builds reais, em duas placas diferentes, usando dois níveis distintos de complexidade, é o momento ideal para analisar o que muda, o que permanece igual e como isso se traduz em boas práticas para projetos reais com Zephyr.
Esta seção é essencial para transformar o “tutorial executado” em conhecimento estruturado.
5.1 O que NÃO muda entre os projetos
Independentemente da placa ou do exemplo utilizado, alguns pontos permanecem invariáveis:
- Fluxo de build
- Sempre via
west build - Sempre fora da árvore do código-fonte
- Sempre com seleção explícita da placa (
-b)
- Sempre via
- Organização
- Um diretório de build por placa
- Código-fonte reutilizado sem modificação
- Builds isolados e reproduzíveis
- Ferramentas
- Mesma toolchain
- Mesmo CMake
- Mesmo Ninja
- Mesmo OpenOCD / ST-Link
Isso é um dos maiores diferenciais do Zephyr:
👉 a complexidade da aplicação não altera o fluxo de trabalho.
5.2 O que MUDA entre o blinky e o threads
| Aspecto | Blinky (F411RE) | Threads (F429ZI) |
|---|---|---|
| Tipo de aplicação | Firmware simples | RTOS completo |
| Uso do kernel | Mínimo | Intensivo |
| Concorrência | Não | Sim |
| Memória | Baixa | Moderada |
| Tempo de build | Curto | Maior |
| Escalonador | Praticamente ocioso | Ativo constantemente |
Essa comparação deixa claro que o Zephyr:
- não “força” RTOS quando não é necessário,
- mas escala naturalmente quando o sistema cresce.
5.3 Organização recomendada para projetos reais
Uma estrutura típica e bem organizada para projetos com Zephyr seria:
zephyr-workspace/
├── applications/
│ ├── app_sensor/
│ ├── app_gateway/
│ └── app_control/
├── builds/
│ ├── nucleof411re/
│ ├── nucleof429zi/
│ └── custom_board/
├── zephyr/
└── modules/
Nessa abordagem:
- applications/ contém apenas código
- builds/ contém apenas artefatos de compilação
- cada placa tem seu próprio diretório
- o mesmo aplicativo pode ser compilado para múltiplos hardwares
Esse modelo é altamente recomendado para:
- produtos comerciais,
- firmware industrial,
- projetos acadêmicos reprodutíveis,
- e pipelines de CI/CD.
5.4 Cenários comuns no dia a dia com Zephyr
Cenário 1 – Uma aplicação, várias placas
Você pode compilar a mesma aplicação para:
- NUCLEO-F411RE
- NUCLEO-F429ZI
- uma placa customizada
Sem alterar uma linha do código da aplicação.
Cenário 2 – Evolução progressiva
Um projeto pode começar como:
blinky→ validação de hardware- depois virar um
threads→ multitarefa - depois incluir drivers, rede, sensores, etc.
Tudo isso sem “reiniciar” o projeto.
Cenário 3 – Debug e validação
Como os builds são separados:
- você pode manter builds antigos,
- comparar binários,
- validar regressões,
- e depurar problemas específicos de hardware.
5.5 Erros comuns de iniciantes (e como evitar)
❌ Criar um único diretório de build para todas as placas
✅ Um diretório por placa
❌ Copiar samples para dentro do projeto
✅ Referenciar samples diretamente
❌ Alterar código do kernel
✅ Configurar via Kconfig e Device Tree
❌ Depender de IDE proprietária
✅ Usar linha de comando como base
5.6 O que vem depois dos “primeiros passos”
Após dominar o que foi apresentado até aqui, os próximos passos naturais no Zephyr são:
- criação de aplicações próprias (fora de
samples) - entendimento profundo de Kconfig
- uso de Device Tree overlays
- configuração de drivers
- integração com FreeRTOS-like concepts (mutex, semáforos, filas)
- debug avançado com
west debug
Esses temas já entram no nível intermediário a avançado e podem ser explorados em tutoriais específicos.
Conclusão, resumo prático e material SEO
Chegamos ao final deste tutorial de primeiros passos com o Zephyr RTOS, percorrendo desde a preparação do ambiente até a execução de exemplos reais em duas placas STM32 amplamente utilizadas. O objetivo não foi apenas “fazer funcionar”, mas construir entendimento técnico sólido sobre o fluxo de trabalho do Zephyr.
6.1 Resumo prático do que foi aprendido
Ao longo do tutorial, você aprendeu de forma progressiva:
- O que é o Zephyr RTOS e por que ele se diferencia de abordagens tradicionais
- Como preparar corretamente o ambiente de desenvolvimento
- O papel central da ferramenta
west - O conceito de build fora da árvore
- Como organizar builds por placa
- Como compilar e gravar firmware para:
- NUCLEO-F411RE usando o sample
blinky - NUCLEO-F429ZI usando o sample
threads
- NUCLEO-F411RE usando o sample
- Como o Zephyr escala naturalmente de aplicações simples para sistemas multitarefa
- Boas práticas de organização aplicáveis a projetos reais, acadêmicos e industriais
Mais importante: você percebeu que o fluxo de desenvolvimento não muda à medida que a aplicação cresce. Essa estabilidade é um dos maiores ganhos do Zephyr em projetos de médio e grande porte.
6.2 Comparação implícita com outros ecossistemas
Sem entrar em disputas diretas, fica claro que o Zephyr:
- evita o acoplamento excessivo a fabricantes,
- promove reutilização real de código,
- facilita automação e CI/CD,
- e força o desenvolvedor a pensar em arquitetura desde cedo.
Para quem já trabalha com FreeRTOS, o Zephyr não substitui conceitos — ele os organiza de forma mais sistemática, integrando kernel, drivers e configuração de hardware em um único modelo coerente.
6.3 Quando o Zephyr é a escolha certa
O Zephyr é especialmente indicado quando:
- o projeto precisa escalar para múltiplas placas,
- há preocupação com manutenção a longo prazo,
- o sistema exige multitarefa real,
- existe interesse em portabilidade e padronização,
- ou o projeto evoluirá para IoT, conectividade ou segurança.
Para projetos muito simples, ele pode parecer “grande demais”. Para projetos médios e grandes, ele rapidamente se paga.