Introdução
Práticas como Integração Contínua (CI) e Entrega Contínua (CD) são pilares do desenvolvimento ágil e DevOps, e têm se tornado cada vez mais presentes no cotidiano dos desenvolvedores. Lançado em 2018, o GitHub Actions é uma ferramenta nativa do GitHub que simplifica a implementação de pipelines de CI/CD.
A ferramenta permite criar fluxos de trabalho que executam ações automáticas, como compilar e testar cada pull request no repositório, garantindo a qualidade do código antes da integração. Além disso, é possível configurar pipelines para implantar alterações mescladas diretamente na produção, promovendo agilidade e confiabilidade no ciclo de desenvolvimento.
Essa prática é amplamente utilizada no desenvolvimento de software tradicional, como plataformas web. No entanto, o mundo dos sistemas embarcados, ainda enfrenta barreiras para adoção mais ampla dessa prática. Entre os principais desafios está a cultura da área, que historicamente prioriza processos manuais, testes locais e iterações mais controladas.
Apesar disso, o CI/CD vem se tornando cada vez mais útil para sistemas embarcados modernos, especialmente em projetos de IoT (Internet das Coisas). Soluções como o ESP-IDF, por exemplo, já oferecem suporte integrado para builds automatizados e testes, facilitando a integração com pipelines de CI/CD e promovendo maior agilidade e eficiência no desenvolvimento de sistemas embarcados.
Neste artigo, vamos desenvolver uma aplicação simples e integrar o GitHub Actions para configurar um pipeline de CI/CD. Esse pipeline será responsável por executar o build automático do firmware com três jobs principais: build, test e delivery, sempre que um pull request for aberto no repositório. O projeto será desenvolvido utilizando a Franzininho WiFi e o ESP-IDF, demonstrando como essas ferramentas podem ser usadas no desenvolvimento de sistemas embarcados.
Pré-requisitos
Antes de começar, certifique-se de ter os seguintes itens configurados:
- Conta no GitHub: Acesse o GitHub e crie uma conta, se ainda não tiver.
- Git instalado no computador: Git
- ESP-IDF instalado no computador: Siga o guia oficial – Get Started – ESP32 – — ESP-IDF Programming Guide v5.4 documentation
Hardware
Para este projeto, utilizarei a Franzininho WiFi Lab01, juntamente com o sensor DHT11 e o LED RGB integrados na placa.
Preparando o Ambiente
Com o ESP-IDF devidamente instalado no sistema e adicionado como extensão no VSCode, siga os passos para criar um novo projeto:
- Abra o VSCode, aperte F1 e digite ESP-IDF: New Project
- Agora, nomeie o projeto e salve-o como quiser. Aqui, o nomeamos como “TEMPLIGHTFLOW”:
- Clique em “Choose Template”, selecione ESP-IDF e depois “hello_world”:
- Depois clique em “Create project using template hello_world” e na caixa de diálogo irá aparecer no canto inferior direito da janela do VSCode. Aperte Yes para abrir o projeto recém-criado em uma nova janela:
- Renomeie o nome do arquivo “hello_world_main.c” para “main.c”. Depois, entre no arquivo “CMakeList.txt” dentro da pasta main e troque “hello_world_main.c” para “main.c”.
- O upload na Franzininho Wifi usa o modo USB-CDC, portanto, abra o menuconfig e habilite esse modo:
Em caso de dúvidas na preparação do Ambiente sugiro assistir o seguinte vídeo:
ESP-IDF: Programe o #ESP32 com a Extensão oficial da Espressif para o VSCODE
Leitura sensor DHT11 e controle LED RGB
Para a leitura do sensor DHT11 utilizaremos o componente dht disponível no ESP Component Registry. Acesse em: zorxx/dht • v1.0.1 • ESP Component Registry.
- Abra o terminal e digite idf.py add-dependency “zorxx/dht^1.0.1” para incluir o componente:
- No arquivo main.c, insira o seguinte código:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"
#include "driver/gpio.h"
#include "esp_system.h"
#include "dht.h" // Biblioteca para DHT11
// Configurações do DHT11
static const dht_sensor_type_t sensor_type = DHT_TYPE_DHT11;
static const gpio_num_t dht_gpio = 15;
// Definições dos LEDs RGB
#define LED_RED 14
#define LED_GREEN 13
#define LED_BLUE 12
// Constantes para temperatura
#define TEMP_HIGH 300 // Limite para temperatura alta (30.0°C)
#define TEMP_LOW 200 // Limite para temperatura baixa (20.0°C)
// Tag para logs
static const char *TAG = "DHT_Example";
// Função para configurar os pinos dos LEDs
void configure_leds(void) {
gpio_reset_pin(LED_RED);
gpio_set_direction(LED_RED, GPIO_MODE_OUTPUT);
gpio_reset_pin(LED_GREEN);
gpio_set_direction(LED_GREEN, GPIO_MODE_OUTPUT);
gpio_reset_pin(LED_BLUE);
gpio_set_direction(LED_BLUE, GPIO_MODE_OUTPUT);
}
// Função para atualizar o estado dos LEDs com base na temperatura
void update_leds(int16_t temperature) {
// Apaga todos os LEDs
gpio_set_level(LED_RED, 0);
gpio_set_level(LED_GREEN, 0);
gpio_set_level(LED_BLUE, 0);
// Acende o LED correspondente
if (temperature > TEMP_HIGH) {
gpio_set_level(LED_RED, 1);
} else if (temperature > TEMP_LOW) {
gpio_set_level(LED_GREEN, 1);
} else {
gpio_set_level(LED_BLUE, 1);
}
}
// Função principal da tarefa
void dht_task(void *pvParameters) {
int16_t temperature = 0;
int16_t humidity = 0;
configure_leds(); // Configura os pinos dos LEDs
while (1) {
// Tenta ler os dados do sensor
if (dht_read_data(sensor_type, dht_gpio, &humidity, &temperature) == ESP_OK) {
// Log dos dados lidos
ESP_LOGI(TAG, "Umidade: %d.%d%%", humidity / 10, abs(humidity % 10));
ESP_LOGI(TAG, "Temperatura: %d.%d°C", temperature / 10, abs(temperature % 10));
// Atualiza os LEDs com base na temperatura
update_leds(temperature);
} else {
ESP_LOGE(TAG, "Erro ao ler os dados do sensor");
}
vTaskDelay(pdMS_TO_TICKS(5000)); // Aguarda 5 segundos
}
}
// Função principal
void app_main(void) {
xTaskCreate(dht_task, "dht_task", configMINIMAL_STACK_SIZE * 3, NULL, 5, NULL);
}
- Compile o código e carregue-o na placa. O comportamento esperado é que o monitor exiba as leituras de temperatura e umidade, enquanto o LED altera sua cor conforme a temperatura registrada.

- Crie um arquivo chamado .gitignore no diretório principal do projeto e insira o seguinte conteúdo:
/build/
/.vscode/
/.devcontainer/
Esta etapa é importante para evitar o envio de arquivos indesejados para o GitHub quando formos realizar o push do projeto em um repositório.
Criando repositório GitHub
Agora, vamos enviar este projeto para um repositório no GitHub. Siga os passos abaixo:
- Faça login na sua conta do GitHub. Crie um novo repositório. Aqui chamarei de TEMPLIGHTFLOW.
- Em seguida, vincule esse repositório ao diretório local do seu projeto. Para isso, entre na pasta do seu projeto, abra o terminal e execute os seguintes comandos:
git init
git remote add origin git@github.com:nomeusuariogithub/nomerepositorio.git
git branch -M main
git checkout -b main
git add .
git commit -m "Initial commit"
git push --set-upstream origin main
Lembrando que é preciso ter o git instalado no seu computador.
Criando o Workflow para GitHub Actions
Agora, vamos configurar o workflow no GitHub Actions para automatizar o processo de CI/CD. Este workflow irá definir os passos necessários para compilar, testar e implantar seu projeto sempre que um pull request for aberto no repositório. Siga os seguintes passos:
Criando um Token Pessoal no GitHub:
Para criar um token pessoal no GitHub, siga os passos abaixo:
- No GitHub, clique no ícone do seu perfil no canto superior direito e vá em Settings.
- Acesse Developer settings → Personal access tokens → Tokens (classic).
- Clique em Generate new token.
- Selecione as permissões repo e workflow para gerar o token.
- Defina um prazo para o token (a fins de teste usei 7 dias para este projeto).
- Após gerar o token, copie-o e guarde-o em um local seguro. Você precisará desse código (token) quando o GitHub solicitar uma senha para autenticação.
Criando um teste usando a biblioteca criterion:
1 – Dentro do seu diretório principal, crie uma pasta chamada test com um arquivo “test_dht.c“.
2 – No arquivo “test_dht.c” insira o seguinte código:
#include <criterion/criterion.h> // Inclui a biblioteca Criterion
// Função para verificar se a temperatura está dentro do intervalo razoável
int reasonable_temperature(int16_t temperature) {
if (temperature > 200 && temperature < 300) { // Entre 20.0°C e 30.0°C
return 1;
} else {
return 0;
}
}
// Função para verificar se a umidade está dentro do intervalo razoável
int reasonable_humidity(int16_t humidity) {
if (humidity >= 300 && humidity <= 800) { // Entre 30.0% e 80.0%
return 1;
} else {
return 0;
}
}
Test(sensor_values, test_temperature) {
// Testa valores de temperatura
cr_assert(reasonable_temperature(250) == 1, "Temperatura 25.0°C deveria ser considerada razoável");
}
Test(sensor_values, test_humidity) {
// Testa valores de umidade
cr_assert(reasonable_humidity(500) == 1, "Umidade 50.0%% deveria ser considerada razoável");
}
Esse código é um exemplo de testes unitários utilizando a biblioteca Criterion, que é uma ferramenta para testes de unidade em C. O objetivo desse código é testar duas funções: reasonable_temperature e reasonable_humidity, que verificam se os valores de temperatura e umidade estão dentro de intervalos razoáveis.
Estamos utilizando essa biblioteca a fim de demonstrar o uso de GitHub Actions na automação de builds e testes básicos. Para testes mais realistas e adequados ao hardware do ESP32, você pode considerar usar frameworks como Unity. Vamos explorar isso em um próximo artigo! 🙂
3 – Finalmente, vamos configurar o workflow:
a – No diretório principal do seu projeto, crie a estrutura de pastas .github/workflows e dentro dela crie o arquivo TempLightFlow.yml.
b – No arquivo yml vamos inserir o seguinte código:
name: TempLightFlow #nome workflow
on:
pull_request:
branches:
- main # executa quando tiver uma pull request no repositório
jobs: # define os Jobs que serão executados no workflow
build: # job compilação projeto
runs-on: ubuntu-latest # usar última versão da distribuição Ubuntu
permissions:
contents: write # permitir escrita em conteúdos
steps:
- name: Repo checkout # nome usado para clonar o repositório
uses: actions/checkout@v4 # action que clona o repositorio
- name: esp-idf build # interação com o ESP-IDF
uses: espressif/esp-idf-ci-action@v1 # action fornecida pela Espressif
with:
esp_idf_version: v5.4 # versão do ESP-IDF a ser utilizada
target: esp32s2 # especifica a plataforma de compilação
- name: Store Artifacts
uses: actions/upload-artifact@v4 #Armazena os artefatos gerados pela compilação
with:
name: TempLightFlow # especifica o nome do artefato criado
path: build/TempLightFlow.bin
test: # job responsável por compilar os testes unitários e gerar os artefatos relacionados
runs-on: ubuntu-latest
permissions:
checks: write
pull-requests: write
needs: [build] # job test só será executado se o Job build for bem-sucedido
steps:
- name: Repo checkout
uses: actions/checkout@v4
- name: Build tests
run: |
sudo apt-get install libcriterion-dev
sudo apt-get install meson
cd test
gcc -o test test_dht.c -lcriterion
./test --xml > test.xml
- name: Show results
uses: EnricoMi/publish-unit-test-result-action@v2
if: always() # sempre executa essa etapa
with:
files: test/test.xml #Arquivo com os resultados dos testes
delivery: # job responsável pela entrega do artefato gerado
runs-on: ubuntu-latest
permissions:
contents: write
needs: [test] # este job depende do job "test" ser bem sucedido
steps:
- name: Repo checkout
uses: actions/checkout@v4 # clona o repositório do GitHub
- name: Download artifacts
uses: actions/download-artifact@v4 # baixa os artefatos gerados no job "build"
with:
name: TempLightFlow
- name: Create release
uses: ncipollo/release-action@v1.13.0 # action para criar release
with:
artifacts: "TempLightFlow.bin" # artefato que será criado na release
tag: 0.1.5 # tag do release
O workflow é chamado TempLightFlow e automatiza o processo de compilação, testes e entrega para um projeto baseado em ESP-IDF. Ele é acionado quando uma pull request é aberta na branch main e possui três jobs (tarefas), que seguem uma ordem lógica:
- build:
- Compila o projeto para a plataforma esp32s2 usando a versão v5.4 do ESP-IDF.
- Gera um artefato binário do firmware (TempLightFlow.bin) e o armazena para etapas futuras.
- A documentação dessa etapa pode ser encontrada aqui
- test:
- Compila e executa testes unitários (usando a biblioteca Criterion).
- Gera um arquivo XML com os resultados dos testes e o publica para análise.
- Depende do job build, ou seja, só executa se a compilação for bem-sucedida.
- delivery:
- Baixa o artefato gerado pelo job build.
- Cria uma release no GitHub com o firmware binário (TempLightFlow.bin) e associa uma tag de versão (0.1.5). Esse binário pode ser disponibilizado para download por dispositivos ESP32 durante o processo de atualização OTA.
- Depende do job test, ou seja, só executa se os testes passarem.
4 – Por fim, envie esse código para o repositório no GitHub usando os comandos:
git add .
git commit -m “workflow github actions”
git push origin main
Testando o Workflow do GitHub Actions com um Bug Intencional
Para simular um cenário de falha, vamos introduzir um bug intencional no arquivo test_dht.c. As modificações serão realizadas nas seguintes linhas:
Test(sensor_values, test_temperature) {
// Testa valores de temperatura
cr_assert(reasonable_temperature(400) == 1, "Temperatura nao deveria ser considerada razoavel");
}
Test(sensor_values, test_humidity) {
// Testa valores de umidade
cr_assert(reasonable_humidity(900) == 1, "Umidade nao deveria ser considerada razoavel");
}
Nos testes acima, estamos verificando se as funções reasonable_temperature e reasonable_humidity consideram os valores 400 (temperatura) e 900 (umidade) como razoáveis. No entanto, esses valores estão fora dos intervalos permitidos pelas funções, que retornam 0 (indicando valores não razoáveis). Como resultado, ambos os testes falharão.
Depois dessa modificação, crie uma nova branch chamada bug para isolar essas modificações. Abra o terminal e use os seguintes comandos:
git checkout -b bug
git add .
git commit -m "upadate Test"
git push origin bug
Após o push da branch, abra uma pull request no repositório para integrar a branch bug à branch principal. Isso acionará o workflow configurado no GitHub Actions, que executará as três tarefas: build, test e delivery.
Para abrir um pull request, acesse o repositório no seu GitHub e vá até a aba “Pull Requests”, localizada no menu superior do repositório. Em seguida, clique no botão “New Pull Request” para iniciar o processo. Na tela de comparação, selecione a branch base (main) no lado esquerdo e a branch que contém as suas alterações (neste caso, bug) no lado direito.
Assim que o pull request for criado, o GitHub Actions será acionado automaticamente. Como introduzimos um bug intencional, você notará que o build será concluído com sucesso, mas os testes falharão. Como o processo de entrega (delivery) está configurado para depender do sucesso dos testes, ele não será realizado. O log gerado pelo workflow fornecerá detalhes sobre os erros detectados, permitindo identificar exatamente onde os problemas ocorreram.
Testando o Workflow do GitHub Actions com Bug Corrigido
Agora, vamos corrigir o bug introduzido no arquivo dht_test.c com as seguintes alterações:
Test(sensor_values, test_temperature) {
// Testa valores de temperatura
cr_assert(reasonable_temperature(280) == 1, "Temperatura eh considerada razoavel");
}
Test(sensor_values, test_humidity) {
// Testa valores de umidade
cr_assert(reasonable_humidity(500) == 1, "Umidade eh considerada razoavel");
}
Após aplicar essas correções, crie uma nova branch chamada bugfix para organizar a alteração. Siga os comandos abaixo para realizar o processo:
git checkout -b bugfix
git add .
git commit -m "test fixes"
git push origin bugfix
Em seguida, abra uma pull request no GitHub para integrar a branch bugfix com a branch base (main). Ao configurar a comparação, selecione a branch main no lado esquerdo e a branch bugfix no lado direito.
Dessa vez, ao abrir o pull request, o workflow será executado novamente. Como o bug foi corrigido, todos os testes deverão passar com sucesso, permitindo que o processo de entrega (delivery) seja concluído com êxito. No final, você verá a criação de uma nova release no repositório do GitHub, contendo o arquivo .bin compilado e disponível como um dos ativos (assets) da release. Este arquivo pode ser utilizado diretamente, por exemplo, para realizar uma atualização OTA (Over-the-Air) em dispositivos, facilitando a distribuição e implantação do código atualizado. Além disso, o repositório incluirá os pacotes de código-fonte nos formatos .zip e .tar.gz o acesso ao código correspondente à release.
Conclusão
Neste artigo, aprendemos como criar um processo completo de automação com GitHub Actions aplicado a um projeto com a Franzininho Wifi usando ESP-IDF.
Essa abordagem permite maior eficiência, rastreabilidade e segurança no desenvolvimento de projetos embarcados, integrando práticas modernas de desenvolvimento e entrega contínua ao ambiente de sistemas embarcados. É um grande passo para aumentar a produtividade e a confiabilidade de projetos, mesmo em um contexto de hardware especializado como o ESP32.
Documentação
Graziele-Rodrigues/TEMPLIGHTFLOW
Documentação do GitHub Actions – GitHub Docs
ESP-IDF Programming Guide – ESP32 – — ESP-IDF Programming Guide v5.4 documentation