À medida que projetos com Arduino e ESP32 evoluem, surgem novas necessidades: modos de debug, firmware de produção, testes automatizados, cenários ou clientes diferentes, comportamentos específicos por versão e até variações de hardware usando o mesmo código-base.
Nesse cenário, usar apenas um projeto simples com um único main.cpp rapidamente se torna inviável. É exatamente aqui que o PlatformIO, integrado ao Visual Studio Code (VSCode), se destaca principalmente por meio do conceito de Environment.
Neste artigo, vamos explorar o que são os environments no PlatformIO, porque eles são fundamentais para projetos profissionais e como usá-los de forma avançada.
Para tornar tudo mais prático, todas as demonstrações serão baseadas em um circuito simples com ESP32, utilizando LEDs e botão, simulando diferentes cenários reais de desenvolvimento.
Ao final, você terá uma visão clara de como usar environments para criar firmwares distintos a partir do mesmo projeto, algo comum e essencial em produtos comerciais de IoT e sistemas embarcados.
De forma prática, um environment é um perfil completo de compilação. Cada projeto de firmware pode ter múltiplas configurações de compilação, definidas usando seções [env] e[env:NAME] no arquivo platformio.ini. Cada uma dessas seções é um environment (ambiente de configuração) que:
Cada environment funciona como um contexto isolado de compilação, onde você pode ativar ou desativar funcionalidades, flags, arquivos ou mesmo modos de build.
Para saber mais sobre o VSCode e o PlatformIO veja o nosso artigo sobre: Como Programar o Arduino com VS Code e PlatformIO
Todos os environments ficam no arquivo: platformio.ini esse é o arquivo de configuração do projeto, nele podemos definir muitas configurações do projeto como, por exemplo:
env) o projeto possui, usar diversas seções [env:NAME] permite que você crie diferentes tarefas de compilação para o mesmo projeto, sem precisar duplicar código ou criar múltiplos repositórios.Esse é um exemplo de configuração de env:
[env] platform = espressif32 board = esp32dev framework = arduino monitor_speed = 115200 [platformio] default_envs = release [env:debug] build_type = debug build_flags = -D DEBUG_MODE build_src_filter = +<main.cpp> [env:release] build_type = release build_flags = -D RELEASE_MODE build_src_filter = +<main.cpp> [env:test] build_flags = -D TEST_MODE build_src_filter = +<main.cpp> [env:other_functionality] build_flags = -D OTHER_FUNCTIONALITY build_src_filter = +<main.cpp> +<other_functionality.cpp> [env:power_saving] build_flags = -D POWER_SAVING build_src_filter = +<main.cpp>
Para a demonstração prática, vamos trabalhar com cinco environments distintos, simulando situações reais de desenvolvimento:
Antes de partir para o código, é fundamental entender o papel de cada environment e como eles impactam diretamente o firmware gerado.
Cada [env:NAME] representa um contexto isolado de build (Compilação), com suas próprias opções de compilação, macros, arquivos e comportamento final do programa.
[env:debug] — Ambiente de desenvolvimento e diagnósticoO environment debug é usado durante o desenvolvimento do firmware. Ele prioriza visibilidade, facilidade de depuração e diagnóstico de problemas.
Serial ficam ativosDEBUG_MODE ficam ativas, no VSCode o código fica em destaqueNa prática, esse firmware não é feito para produção, mas sim para o desenvolvedor entender o que está acontecendo internamente no ESP32.
[env:release] — Ambiente de produção (firmware final)O release representa o firmware final, que será entregue ao usuário ou cliente.
RELEASE_MODE ativaEsse é o firmware que você usaria em um produto real.
[env:test] — Ambiente de testes de hardware e lógicaO environment test é usado para validar o hardware e a lógica do sistema, muito comum em:
TEST_MODE ativaEsse firmware pode ser carregado temporariamente, apenas para validar o circuito verificando se o ESP32, LEDs e botão estão funcionando corretamente.
[env:other_functionality] – Firmware com funcionalidades extras para algum cliente ou aplicação específica;Habilita um arquivo com funcionalidades extras para aplicações especificas ou necessidades específicas de algum cliente.
OTHER_FUNCTIONALITY ativaEsse modelo pode ser usado em alguma necessidade específica para algum cliente.
[env:power_saving] — Firmware customizado para economia de energiaFirmware customizado que acendem os LEDs e após 2 segundos desliga usando fade e faz o ESP32 entrar no modo deep sleep
POWER_SAVING ativaEsse environment pode ser usado para cenários de economia de energia ou com uso de baterias.
A seguir, um exemplo funcional completo, demonstrando claramente como o comportamento muda conforme o environment.
Essa é estrutura dos arquivos:
Código completo (src/main.cpp)
#include <Arduino.h>
#ifdef OTHER_FUNCTIONALITY
void other_functionality_loop(bool buttonEvent);
#endif
#define LED1_PIN 2
#define LED2_PIN 4
#define BUTTON_PIN 15
bool lastButtonState = HIGH;
unsigned long lastDebounceTime = 0;
const unsigned long debounceDelay = 50;
void setup() {
pinMode(LED1_PIN, OUTPUT);
pinMode(LED2_PIN, OUTPUT);
#ifdef POWER_SAVING
// Configura PWM para fade
ledcAttachPin(LED1_PIN, 0); // canal 0
ledcAttachPin(LED2_PIN, 1); // canal 1
ledcSetup(0, 5000, 8); // canal 0, 5kHz, 8 bits
ledcSetup(1, 5000, 8); // canal 1, 5kHz, 8 bits
#endif
pinMode(BUTTON_PIN, INPUT); // Usando resistor de pulldown externo
#if defined(DEBUG_MODE) || defined(TEST_MODE) || defined(OTHER_FUNCTIONALITY)
Serial.begin(115200);
delay(500);
#endif
#ifdef DEBUG_MODE
Serial.println("=== ENV: DEBUG ===");
Serial.println("Modo de desenvolvimento ativo");
Serial.println("Logs detalhados habilitados");
#endif
#ifdef TEST_MODE
Serial.begin(115200);
Serial.println("=== ENV: TEST ===");
Serial.println("Firmware de teste de hardware");
#endif
#ifdef OTHER_FUNCTIONALITY
Serial.println("=== ENV: OTHER_FUNCTIONALITY ===");
#endif
}
// Detecta evento de pressionamento (borda de subida) com debounce para pushbutton
bool buttonPressedEvent() {
static bool lastButtonState = LOW;
static bool debouncedState = LOW;
static unsigned long lastDebounceTime = 0;
bool event = false;
bool reading = digitalRead(BUTTON_PIN);
if (reading != lastButtonState) {
lastDebounceTime = millis();
Serial.println("[BTN] Mudança detectada, debounce iniciado");
}
lastButtonState = reading;
if ((millis() - lastDebounceTime) > debounceDelay) {
if (debouncedState != reading) {
debouncedState = reading;
if (debouncedState == HIGH) {
event = true;
Serial.println("[BTN] Evento de pressionamento detectado!");
}
}
}
return event;
}
void loop() {
bool buttonEvent = buttonPressedEvent();
#ifdef DEBUG_MODE
static unsigned long lastBlink = 0;
if (millis() - lastBlink > 500) {
digitalWrite(LED1_PIN, !digitalRead(LED1_PIN));
lastBlink = millis();
Serial.println("[DEBUG] LED1 piscando (heartbeat)");
}
if (buttonEvent) {
Serial.println("[DEBUG] Botão pressionado");
digitalWrite(LED2_PIN, !digitalRead(LED2_PIN));
Serial.print("[DEBUG] LED2 agora está: ");
Serial.println(digitalRead(LED2_PIN) ? "LIGADO" : "DESLIGADO");
}
delay(10);
#endif
#ifdef RELEASE_MODE
static unsigned long lastBlink = 0;
if (millis() - lastBlink > 500) {
digitalWrite(LED1_PIN, !digitalRead(LED1_PIN));
lastBlink = millis();
}
if (buttonEvent) {
digitalWrite(LED2_PIN, !digitalRead(LED2_PIN));
}
delay(10);
#endif
#ifdef TEST_MODE
if (digitalRead(BUTTON_PIN) == HIGH) {
Serial.println("[TEST] Botão pressionado, piscando LED1");
digitalWrite(LED1_PIN, HIGH);
delay(150);
digitalWrite(LED1_PIN, LOW);
delay(150);
Serial.println("[TEST] Botão pressionado, piscando LED2");
digitalWrite(LED2_PIN, HIGH);
delay(150);
digitalWrite(LED2_PIN, LOW);
delay(150);
}
#endif
#ifdef OTHER_FUNCTIONALITY
// Chama função especial do outro arquivo
other_functionality_loop(buttonEvent);
#endif
#ifdef POWER_SAVING
// Fade nos LEDs e entra em deep sleep após 3 segundos sem interação
static unsigned long lastActivity = 0;
if (buttonEvent) {
for (int i = 0; i <= 255; i += 5) {
ledcWrite(0, i); // LED1
ledcWrite(1, i); // LED2
delay(15);
}
lastActivity = millis();
}
if (millis() - lastActivity > 1500) {
for (int i = 255; i >= 0; i -= 5) {
ledcWrite(0, i);
ledcWrite(1, i);
delay(15);
}
Serial.println("[POWER_SAVING] Entrando em deep sleep para economizar energia");
delay(100);
esp_sleep_enable_ext0_wakeup((gpio_num_t)BUTTON_PIN, 1); // Acorda com nível alto
esp_deep_sleep_start();
}
#endif
}
Código completo (src/other_functionality.cpp)
#include <Arduino.h>
#define LED1_PIN 2
#define LED2_PIN 4
// Funcionalidade adicional: ao detectar o evento de botão, pisca LED1 5x e LED2 3x
void other_functionality_loop(bool buttonEvent) {
static int stage = 0, count = 0;
static unsigned long lastBlink = 0;
static bool ledState = LOW;
if (buttonEvent && stage == 0) {
stage = 1; count = 0; ledState = LOW; lastBlink = millis();
}
if (stage == 1 && millis() - lastBlink > 150) { // Pisca LED1 5x
ledState = !ledState;
digitalWrite(LED1_PIN, ledState);
lastBlink = millis();
if (ledState == HIGH) count++;
if (count >= 5 && ledState == LOW) {
digitalWrite(LED1_PIN, LOW);
stage = 2; count = 0; ledState = LOW; lastBlink = millis();
}
}
if (stage == 2 && millis() - lastBlink > 150) { // Pisca LED2 3x
ledState = !ledState;
digitalWrite(LED2_PIN, ledState);
lastBlink = millis();
if (ledState == HIGH) count++;
if (count >= 3 && ledState == LOW) {
digitalWrite(LED2_PIN, LOW);
stage = 0;
}
}
}
Essa organização permite ativar ou desativar módulos inteiros por environment, sem bagunçar o código.
Todos os materiais podem ser comprados em nossa loja
Abaixo a imagem do circuito simples para a demonstração dos environments

| Componente | GPIO |
|---|---|
| LED 1 | GPIO 2 |
| LED 2 | GPIO 4 |
| Botão | GPIO 15 |
Na imagem a baixo tem um print das mensagens que aparecem quando é usado o environment de Debug:

Vídeo com a demonstração dos environments:
O conceito de environment no PlatformIO vai muito além de uma simples configuração. Ele é a base para:
Neste artigo, vimos como um simples circuito com ESP32, dois LEDs e um botão pode se comportar de cinco formas completamente diferentes, apenas trocando o environment ativo. Se você quer sair do nível hobby e começar a desenvolver firmware de verdade, dominar environments não é opcional é essencial. Para mais materiais como esse, continue acompanhando as postagens semanais do blog e não deixe de visitar nossa loja. Lá, você encontra todos os componentes necessários para desenvolver esse e muitos outros projetos!
Que a força esteja com vocês e até mais!
Algumas referências utilizadas no artigo:
https://docs.platformio.org/en/latest/projectconf/sections/env/index.html
|
A Eletrogate é uma loja virtual de componentes eletrônicos do Brasil e possui diversos produtos relacionados à Arduino, Automação, Robótica e Eletrônica em geral.
Tenha a Metodologia Eletrogate dentro da sua Escola! Conheça nosso Programa de Robótica nas Escolas!