Configurar o WiFi de um ESP32 diretamente no código funciona bem durante o desenvolvimento, mas não é prático quando o dispositivo precisa ser instalado em diferentes ambientes ou entregue a usuários finais. Para resolver isso, o WiFiManager cria automaticamente um ponto de acesso temporário e exibe um portal de configuração acessível pelo navegador, permitindo que o usuário selecione a rede WiFi e informe a senha sem necessidade de cabo USB ou recompilação do firmware.
Neste artigo, vamos explorar como transformar esse portal padrão em uma interface mais completa e amigável, incorporando:
O objetivo é criar um portal de configuração mais profissional, adequado para aplicações reais com ESP32.
O portal padrão do WiFiManager cumpre bem o papel de configurar SSID e senha, mas foi projetado com foco técnico. Em aplicações reais, especialmente quando o ESP32 faz parte de um produto, é desejável oferecer uma experiência mais clara e intuitiva para o usuário final.
Queríamos ir além da configuração básica de WiFi e criar um portal que:
Em outras palavras, buscamos transformar o portal técnico padrão em uma interface mais amigável, organizada e visualmente consistente — algo que qualquer usuário pudesse compreender e utilizar sem instruções adicionais.
Essa necessidade de clareza, simplicidade e personalização motivou a criação de um portal customizado, com temas visuais e um fluxo mais direto, adequado tanto para projetos pessoais quanto para produtos comerciais baseados em ESP32.
A rigor, qualquer modelo de ESP32 atende ao projeto. A única atenção seria com relação ao pino do botão LED_BUILTIN que pode mudar por modelo.
O WiFiManager simplifica a configuração de redes WiFi no ESP32 criando automaticamente um ponto de acesso temporário e exibindo um portal de configuração acessível pelo navegador. O fluxo padrão é simples e funciona sem qualquer customização adicional.
Abaixo está um exemplo mínimo de uso:
#include <WiFiManager.h>
void setup()
{
Serial.begin(115200);
WiFiManager wm;
// Inicia o portal caso não consiga conectar ao último WiFi conhecido
bool ok = wm.autoConnect("Portal-ESP32", "12345678");
if (!ok)
{
Serial.println("Falha ao conectar. Reiniciando...");
delay(1000);
ESP.restart();
}
Serial.println("Conectado ao WiFi!");
}
void loop()
{
// Código principal da aplicação
}
Como esse código funciona?
Esse é o fluxo padrão do WiFiManager — simples, direto e suficiente para muitos projetos.
A partir daqui, começamos a evoluir para um portal mais completo, com parâmetros adicionais, temas visuais e uma experiência mais amigável para o usuário final.

Figura 1 – Reconhecendo o SSID do WifiManager na Rede Wifi

Figura 2 – Confirmação antes da Conexão

Figura 3 – Conectado no SSID do WifiManager
O WiFiManager já oferece um portal funcional para configurar SSID e senha, mas seu layout padrão foi pensado para uso técnico. Em muitos projetos com ESP32 — especialmente quando o dispositivo faz parte de um produto — é importante oferecer uma experiência mais simples e intuitiva para o usuário final.
Ao analisar o portal padrão, percebemos algumas oportunidades de melhoria:
Com isso em mente, buscamos criar um portal que:
O objetivo não era substituir o WiFiManager, mas aproveitar sua base sólida e construir uma interface mais amigável, organizada e visualmente consistente — algo adequado tanto para projetos pessoais quanto para aplicações comerciais.
Antes de mergulharmos nas customizações, é importante entender como as peças principais do projeto se encaixam. A solução final combina dois componentes fundamentais:
O WiFiManager entra em ação apenas quando o dispositivo ainda não conhece a rede WiFi ou quando o usuário decide reconfigurá-la. Depois que as credenciais são salvas, o ESP32 passa a operar normalmente, e quem assume o controle é o AsyncWebServer.
A arquitetura geral pode ser resumida assim:
Essa separação clara entre configuração e operação traz várias vantagens:
Ao longo das próximas seções, vamos detalhar cada parte dessa arquitetura, mostrando como o portal foi simplificado, como os parâmetros foram adicionados, como os temas foram aplicados e como o reboot automático garante um funcionamento confiável.
O WiFiManager permite personalizar o menu exibido no portal de configuração através do método setMenu(). Por padrão, o portal inclui várias páginas, mas você pode escolher exatamente quais delas deseja exibir, deixando o portal mais simples e adequado ao usuário final.
A biblioteca define internamente diversos tokens que representam cada item de menu. Aqui estão os principais:
| Token | Função no Portal |
|---|---|
"wifi" | Página principal de configuração WiFi (lista de redes, SSID, senha). |
"wifinoscan" | Igual ao "wifi", mas sem escanear redes (útil para ambientes com interferência). |
"info" | Exibe informações do dispositivo (IP, MAC, RSSI, etc.). |
"param" | Página separada para parâmetros personalizados. |
"close" | Fecha o portal sem salvar nada. |
"restart" | Reinicia o dispositivo diretamente pelo portal. |
"exit" | Sai do portal e tenta conectar ao WiFi configurado. |
"erase" | Apaga todas as credenciais WiFi salvas. |
"update" | Página de atualização OTA (quando habilitada). |
"sep" | Apenas um separador visual no menu. |
"custom" | Permite adicionar páginas personalizadas. |
Esses tokens permitem montar o menu exatamente como você deseja. Por exemplo, o menu padrão costuma incluir várias dessas opções, o que pode deixar o portal mais técnico do que o necessário.
Como nosso objetivo é simplificar a experiência do usuário, reduzimos o menu para apenas o essencial:
wm.setMenu({"wifi", "info", "sep", "exit"});
Por que essa escolha?
Essa simplificação:
Nas próximas seções, vamos ver como adicionar parâmetros personalizados, aplicar temas e consolidar tudo em uma única página.

Figura 4 – Tela Principal do WifiManager sem Customização

Figura 5 – Tela Principal do WifiManager com o Tema Dark

Figura 6 – Tela Principal do WifiManager com o Tema Light
Um dos recursos mais poderosos do WiFiManager é a possibilidade de adicionar parâmetros personalizados ao portal de configuração. Isso permite que o usuário configure não apenas o WiFi, mas também outras informações importantes para o funcionamento do dispositivo — tudo na mesma página.
Esses parâmetros podem ser usados para:
O WiFiManager fornece a classe WiFiManagerParameter para criar esses campos.
Exemplo básico de parâmetro personalizado
O exemplo abaixo adiciona um campo para o nome do dispositivo:
#include <WiFiManager.h>
WiFiManager wm;
// Buffer para armazenar o valor do parâmetro
char deviceName[40] = "MeuESP32";
void setup() {
Serial.begin(115200);
// Cria o parâmetro (id, label, valor inicial, tamanho máximo)
WiFiManagerParameter p_deviceName("devname", "Nome do Dispositivo", deviceName, 40);
// Adiciona o parâmetro ao WiFiManager
wm.addParameter(&p_deviceName);
// Inicia o portal de configuração
bool ok = wm.autoConnect("Portal-ESP32", "12345678");
if (!ok) {
Serial.println("Falha ao conectar. Reiniciando...");
delay(1000);
ESP.restart();
}
// Atualiza o valor salvo
strcpy(deviceName, p_deviceName.getValue());
Serial.print("Nome configurado: ");
Serial.println(deviceName);
}
void loop() {
// Código principal da aplicação
}
Como esse código funciona?
deviceName) para armazenar o valor.WiFiManagerParameter.addParameter().getValue().Simples, direto e extremamente útil.
Por que colocar tudo na mesma página?
O WiFiManager permite criar uma página separada para parâmetros ("param"), mas isso adiciona navegação extra e mais um botão de SAVE. Como nosso objetivo é simplificar o fluxo, optamos por:
Isso reduz erros e deixa o portal mais intuitivo.
Dicas importantes ao criar parâmetros
"devname").Combinação de campos de <select>, checkbox e campos invisíveis
Além de campos de texto, o WiFiManager permite criar parâmetros que serão renderizados como <select> e checkbox usando HTML direto no construtor do WiFiManagerParameter. Uma estratégia muito útil é combinar:
<select> ou checkbox visível (que o usuário manipula)Por exemplo, para escolher o tema:
// Campo invisível que guarda o valor efetivo do tema
WiFiManagerParameter p_themeValue("theme", "", "default", 10, "style='display:none;'");
// Campo visível (select) que o usuário enxerga
const char* themeHtml =
"<label for='theme_sel'>Tema</label>"
"<select id='theme_sel' onchange=\"document.getElementById('theme').value=this.value;\">"
"<option value='default'>Default</option>"
"<option value='dark'>Dark</option>"
"<option value='light'>Light</option>"
"</select>";
WiFiManagerParameter p_themeSelect(themeHtml);
// Adiciona ambos
wm.addParameter(&p_themeValue);
wm.addParameter(&p_themeSelect);
Mesma ideia vale para checkbox:
"0" ou "1" via JavaScriptEssa abordagem tem duas vantagens:
getValue()Depois do SAVE, você só lê:
String theme = p_themeValue.getValue(); // "default", "dark" ou "light"

Figura 7 – Três tipos de campos: Seleção, Checkbox e Texto
Além da página principal de configuração, o WiFiManager permite adicionar páginas personalizadas ao portal. Uma das mais úteis é a página “Info”, que exibe dados importantes sobre o dispositivo, como:
Esses dados ajudam o usuário a entender o estado atual do ESP32 e facilitam diagnósticos, especialmente em ambientes onde o dispositivo será instalado por terceiros.
Habilitando a Página “Info”
O WiFiManager já possui uma página de informações interna, e basta incluí-la no menu:
wm.setMenu({"wifi", "info", "sep", "exit"});
Com isso, o item Info aparece no menu e exibe automaticamente:
Essa página é gerada pelo próprio WiFiManager e já atende a maioria dos casos.
Criando uma página “Info” personalizada
Se você quiser exibir informações adicionais — como nome do dispositivo, versão do firmware ou dados específicos da aplicação — pode criar sua própria rota usando setCustomMenuHTML() ou setCustomHeadElement() combinados com setCustomMenuHTML().
Aqui vai um exemplo simples usando uma rota customizada:
wm.setCustomMenuHTML("<a href=\"/info\">Info</a>");
wm.setWebServerCallback([&](WiFiManager *wm) {
wm->server->on("/info", HTTP_GET, [&]() {
String page = "<html><body>";
page += "<h2>Informações do Dispositivo</h2>";
page += "IP: " + WiFi.localIP().toString() + "<br>";
page += "MAC: " + WiFi.macAddress() + "<br>";
page += "RSSI: " + String(WiFi.RSSI()) + " dBm<br>";
page += "Firmware: 1.0.0<br>";
page += "</body></html>";
wm->server->send(200, "text/html", page);
});
});
O que esse código faz?
/info dentro do próprio servidor do WiFiManagerEssa abordagem permite personalizar totalmente o conteúdo exibido, mantendo o portal organizado e profissional.
Por que incluir uma página “Info”?
A página “Info” é um complemento natural ao portal customizado, oferecendo transparência e utilidade sem complicar o fluxo de configuração.

Figura 8 – Tela Info do WifiManager com o Tema Dark
O WiFiManager permite inserir elementos HTML e CSS personalizados dentro do portal de configuração usando o método setCustomHeadElement(). Isso abre espaço para criar uma identidade visual própria, deixando o portal mais agradável e coerente com o produto final.
No nosso projeto, implementamos três temas:
A seleção do tema é feita por meio de um parâmetro personalizado (como vimos na seção anterior), e o CSS correspondente é injetado dinamicamente no portal.
Inserindo CSS customizado
O WiFiManager permite adicionar qualquer conteúdo dentro da tag <head> da página. Isso inclui:
Para aplicar um tema, basta inserir o CSS desejado:
if (theme == "dark") {
wm.setCustomHeadElement(
"<style>"
"body{background:#222;color:#eee;font-family:Arial;}"
"input,select{background:#333;color:#eee;border:1px solid #555;}"
"</style>"
);
}
else if (theme == "light") {
wm.setCustomHeadElement(
"<style>"
"body{background:#f5f5f5;color:#333;font-family:Arial;}"
"input,select{background:#fff;color:#333;border:1px solid #ccc;}"
"</style>"
);
}
Esse CSS é aplicado somente ao portal do WiFiManager, sem interferir no servidor web principal do dispositivo.
Corrigindo campos invisíveis no tema Default
O tema Default do WiFiManager exibe todos os campos adicionados, inclusive aqueles que deveriam permanecer invisíveis (como os campos ocultos usados para <select> e checkbox). Para resolver isso, os temas Dark e Light incluem regras CSS que garantem que esses campos permaneçam ocultos:
#theme {
display: none;
}
Isso mantém a interface limpa e evita confusão para o usuário.
Por que usar temas?
O resultado é um portal mais moderno, agradável e intuitivo — algo que realmente parece parte de um produto final.
Personalizando o Título do Portal
O WiFiManager permite alterar o título exibido no topo da página de configuração usando o método setTitle(). Esse título aparece dentro do <header> do portal e pode conter HTML completo, permitindo:
<span style="">)Isso abre espaço para deixar o portal com uma identidade visual própria, reforçando que ele faz parte de um produto final — não apenas de um firmware técnico.
Exemplo de título customizado
wm.setTitle("<center>⭐ WiFiManager <u>Customizado</u> ⭐</center>");
Esse título será renderizado exatamente como está, incluindo:
Você pode ir além:
wm.setTitle( "<center>" "<h2 style='margin:0;color:#4CAF50;'>🌐 Configuração do Dispositivo</h2>" "<small>Portal Customizado</small>" "</center>" );
Por que isso é útil?
O WiFiManager, por padrão, organiza suas funcionalidades em várias páginas: WiFi, Parâmetros, Informações, Reset, entre outras. Embora isso faça sentido para uso técnico, para o usuário final essa estrutura pode gerar dúvidas e aumentar a chance de erro.
Ao desenvolver o portal customizado, adotamos uma abordagem diferente: concentrar todas as informações relevantes em uma única página, com um único botão de SAVE. Essa decisão simplifica o fluxo e torna o processo de configuração muito mais intuitivo.
Por que consolidar tudo em uma página?
1. Reduz navegação desnecessária
O usuário não precisa alternar entre telas para configurar WiFi, parâmetros e opções adicionais. Tudo está ali, visível e acessível.
2. Evita múltiplos botões de SAVE
No portal padrão, cada página tem seu próprio botão de salvar. Isso pode levar o usuário a:
Com uma única página, isso simplesmente não acontece.
3. Facilita o entendimento
O usuário vê:
Tudo no mesmo contexto, sem surpresas.
4. Melhora a experiência em dispositivos móveis
A maioria dos usuários acessa o portal pelo celular. Uma única página reduz:
5. Deixa o portal com “cara de produto”
Uma interface simples, direta e sem distrações transmite profissionalismo e confiança.
Como isso é implementado no WiFiManager?
O segredo está em duas decisões:
1. Remover páginas desnecessárias do menu
Como vimos na seção 4:
wm.setMenu({"wifi", "info", "sep", "exit"});
Isso garante que o usuário só veja o essencial.
2. Adicionar todos os parâmetros na página principal
Em vez de usar a página "param", adicionamos tudo diretamente:
wm.addParameter(&p_deviceName); wm.addParameter(&p_themeValue); wm.addParameter(&p_themeSelect); wm.addParameter(&p_checkboxValue);
O WiFiManager automaticamente renderiza esses campos na página WiFi.
Resultado
O portal final fica:
Essa abordagem é especialmente útil quando o ESP32 será configurado por pessoas sem conhecimento técnico — instaladores, clientes finais ou usuários domésticos.

Figura 9 – Tela com Todas as Definições do Programa
Quando usamos o WiFiManager em seu fluxo padrão, ele próprio reinicia o ESP32 após salvar as credenciais. Porém, ao criar um portal customizado — especialmente quando adicionamos parâmetros personalizados, temas e páginas extras — esse comportamento pode mudar. Em alguns casos, o WiFiManager retorna o controle ao código sem reiniciar automaticamente, o que pode gerar inconsistências.
Por isso, adotamos a prática de forçar um reboot após o SAVE, garantindo que o dispositivo inicie em um estado limpo e previsível.
Por que o reboot é necessário?
1. Libera recursos internos
Durante o portal de configuração, o WiFiManager cria:
Após o SAVE, esses recursos não são mais necessários. O reboot garante que tudo seja desmontado corretamente.
2. Evita conflitos com o AsyncWebServer
O servidor principal da aplicação (AsyncWebServer) precisa iniciar com:
Sem o reboot, o servidor do WiFiManager pode deixar portas ocupadas ou estados intermediários que causam falhas intermitentes.
3. Garante que os parâmetros personalizados sejam aplicados
Após o SAVE, os valores dos parâmetros:
O reboot garante que o dispositivo já inicie com todas as configurações ativas.
4. Comportamento mais previsível para o usuário
O usuário aperta SAVE, o dispositivo reinicia e entra em operação normal. Simples, direto e sem ambiguidades.
Como implementar o reboot automático?
O WiFiManager retorna true quando o usuário salva as configurações. Assim, basta verificar esse retorno:
bool ok = wm.autoConnect("Portal-ESP32", "12345678");
if (!ok) {
Serial.println("Falha ao conectar. Reiniciando...");
delay(1000);
ESP.restart();
}
// Se chegou aqui, o SAVE foi bem-sucedido
Serial.println("Configurações salvas. Reiniciando...");
delay(1000);
ESP.restart();
Esse padrão garante que:
Resultado
Com o reboot automático:
Essa etapa, embora simples, é fundamental para garantir a estabilidade do portal customizado.
Alternativas para Encerrar o Portal do WiFiManager Após o SAVE
Quando o usuário salva as configurações no portal do WiFiManager, o dispositivo precisa liberar a porta 80 para que o AsyncWebServer possa assumir o controle. Existem três maneiras de fazer isso, cada uma com vantagens e desvantagens.
1) Reboot automático após o SAVE (solução adotada)
Essa é a abordagem usada no projeto. Após o SAVE, o ESP32 reinicia, o portal é encerrado automaticamente e a porta 80 fica livre para o AsyncWebServer.
Por que funciona tão bem?
Essa é a solução mais profissional para produtos reais.
2) Rodar o portal do WiFiManager em outra porta (ex.: 8080)
O WiFiManager permite mudar a porta do portal:
wm.setWebPortalPort(8080);
O portal ficaria acessível em:
http://192.168.4.1:8080
Por que não usamos essa alternativa?
Funciona, mas não é ideal para um fluxo simples e amigável.
3) Encerrar o portal manualmente com stopWebPortal()
O WiFiManager oferece:
wm.stopWebPortal();
Mas isso só funciona quando o portal está rodando em modo não bloqueante:
wm.setConfigPortalBlocking(false);
wm.startConfigPortal("Portal-ESP32", "12345678");
E no loop:
wm.process(); // mantém o portal vivo
if (configuracaoSalva) {
wm.stopWebPortal(); // libera a porta 80
server.begin(); // inicia o AsyncWebServer
}
Desvantagens:
É uma alternativa válida, mas não tão estável quanto o reboot.
Resumo das Alternativas
Alternativa | Vantagens | Desvantagens | Adequada para |
1) Reboot após SAVE | simples, robusta, intuitiva | reinicia o dispositivo | produtos reais, usuários finais |
2) Portal na porta 8080 | evita conflito sem reboot | menos natural, exige digitar porta | ambientes técnicos, protótipos |
3) stopWebPortal() | evita reboot | fluxo complexo, pode quebrar captive portal | usuários avançados, projetos customizados |
As três alternativas funcionam, mas cada uma tem um impacto diferente no fluxo e na experiência do usuário. Para um produto real — simples, confiável e intuitivo — a melhor escolha continua sendo:
Reboot automático após o SAVE
E é exatamente essa a abordagem adotada no projeto.
Após o SAVE, o firmware salva os parâmetros na NVS e atualiza as variáveis globais que alimentam o HTML. Em seguida, o reboot garante que o portal do WiFiManager seja encerrado e a porta 80 fique livre para o AsyncWebServer.
Depois que o WiFiManager cumpre seu papel — coletar SSID, senha e parâmetros personalizados — o ESP32 reinicia e entra no modo de operação normal. Nesse momento, quem assume o controle é o AsyncWebServer, que será responsável por:
A integração entre WiFiManager e AsyncWebServer é simples, mas precisa seguir uma ordem correta para evitar conflitos.
Ordem correta de inicialização
Após o reboot:
Essa ordem garante que:
Exemplo básico de integração
#include <WiFi.h>
#include <AsyncTCP.h>
#include <ESPAsyncWebServer.h>
AsyncWebServer server(80);
void setup() {
Serial.begin(115200);
// Conecta ao WiFi com as credenciais salvas pelo WiFiManager
WiFi.begin();
Serial.print("Conectando");
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("\nWiFi conectado!");
Serial.print("IP: ");
Serial.println(WiFi.localIP());
// Rota principal
server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
String page = "<h2>Status do ESP32</h2>";
page += "IP: " + WiFi.localIP().toString() + "<br>";
page += "RSSI: " + String(WiFi.RSSI()) + " dBm<br>";
request->send(200, "text/html", page);
});
// Inicia o servidor
server.begin();
}
void loop() {
// Lógica principal da aplicação
}
Por que o AsyncWebServer só deve iniciar após o reboot?
1. Evita conflito de portas
O WiFiManager usa internamente um servidor web próprio. Se o AsyncWebServer for iniciado antes do reboot, ambos podem tentar usar a porta 80.
2. Garante estabilidade da pilha WiFi
O portal de configuração cria um AP temporário e manipula rotas internas. O reboot garante que tudo seja desmontado corretamente.
3. Evita comportamentos intermitentes
Sem reboot, podem ocorrer:
4. Fluxo mais previsível
O usuário aperta SAVE, o dispositivo reinicia e entra em operação normal. Simples e profissional.
Resultado final
Com essa integração:
Essa separação clara entre configuração e operação é o que torna o projeto robusto e adequado para aplicações reais.
Configurações Importantes de Timeout no WiFiManager
O WiFiManager oferece dois parâmetros essenciais para controlar o comportamento do portal de configuração e da tentativa de conexão ao roteador. Ajustá‑los corretamente evita que o dispositivo entre no modo AP sem necessidade ou fique preso indefinidamente aguardando interação do usuário.
1) Timeout do Portal de Configuração
wm.setConfigPortalTimeout(300);
Define o tempo máximo, em segundos, que o portal de configuração ficará ativo sem interação do usuário.
→ o WiFiManager encerra o modo AP e retorna ao fluxo normal do programa.
Por que isso é importante?
Sem esse timeout, o ESP32 pode ficar preso eternamente no modo AP caso o usuário:
Com o timeout, o dispositivo sempre retorna ao modo operacional, evitando travamentos.
2) Timeout de Conexão ao Roteador
wm.setConnectTimeout(60);
Define o tempo máximo (em segundos) que o WiFiManager tentará conectar ao roteador depois que as credenciais já estão salvas.
Esse parâmetro é crítico porque, sem ele, o comportamento padrão é:
→ o WiFiManager assume que a conexão falhou e entra no modo AP, mesmo com SSID e senha corretos.
Por que isso acontece?
Porque o WiFiManager tenta conectar rapidamente. Se o roteador demorar mais que o esperado para autenticar, o ESP32 entende como falha.
O que o timeout resolve?
Com setConnectTimeout(60), você garante:
Observações sobre a Implementação
1) Alias e mDNS
O alias informado no portal do WiFiManager é utilizado para registrar o serviço mDNS, permitindo que o usuário acesse a aplicação através de um endereço amigável, como:
http://mywm.local
Isso evita que o usuário precise descobrir o IP do dispositivo (por exemplo: http://192.168.18.15) para acessar a interface web.
2) Indicação visual com o LED_BUILTIN
O LED_BUILTIN é usado como indicador de estado do dispositivo:
Essa sinalização visual facilita o diagnóstico rápido do estado atual do ESP32.
3) Persistência dos parâmetros
Os três parâmetros configurados no portal (Modo, Logs e Alias) são armazenados usando Preferences e recuperados no início da aplicação.
Já o SSID e a senha são persistidos automaticamente pelo WiFiManager na NVS (Non‑Volatile Storage) — a memória flash interna do ESP32 usada para armazenar pares chave/valor de forma permanente.
4) Atualização dinâmica via WebSocket
A aplicação atualiza dinamicamente, a cada 1000 ms, os campos:
Esses valores variam constantemente e, por isso, são enviados via WebSocket. Os demais campos são estáticos e carregados apenas na renderização inicial da página.
5) Botão físico (GPIO 0) para reentrar no modo de configuração
Durante a operação normal, o dispositivo pode ser colocado novamente no modo de configuração pressionando o botão interno do ESP32 (GPIO 0). Esse recurso é essencial para:
A lógica típica é:
pinMode(0, INPUT_PULLUP);
if (digitalRead(0) == LOW) { // Botão pressionado
Serial.println("Botão pressionado. Entrando no modo de configuração...");
wm.resetSettings(); // Opcional: apaga SSID/senha
wm.startConfigPortal("Portal-ESP32", "12345678");
}
Esse mecanismo garante que o usuário sempre tenha uma forma física, simples e confiável de recuperar o dispositivo, mesmo quando:

Figura 10 – Tela da Aplicação AsyncWebServer
//------------------------------------------------------------------------------------------------------------------
// Objetivos : . Usar o WifiManager com campos customizados:
// 1) Campo de <Select>
// 2) [x] Checkbox
// . Alterar o tema (dark/light/default) e título do html do WifiManager
// . Salvar/Recuperar os parâmetros do Prefs
// Observação: LED Acesso -> Modo de Configuração
// LED Piscando -> WebServer Ativo
// Autor : Dailton Menezes - Mar/2026
//------------------------------------------------------------------------------------------------------------------
//-------------------------------------
// Definição das Bibliotecas Utilizadas
//-------------------------------------
#include <Arduino.h>
#include <WiFi.h>
#include <WiFiManager.h>
#include <Preferences.h>
#include <AsyncTCP.h>
#include <ESPAsyncWebServer.h>
#include <ArduinoJson.h>
#include <ESPmDNS.h>
//--------------------------------
// Definição de Constantes Gerais
//--------------------------------
#define INTERVALO 1000 // Intervalo em ms para enviar via WebSocket, Cleanup, etc.
#define SSID_AP "ESP32_Config_AP" // SSID do Modo AP
#define SENHA_AP "12345678" // Senha do Modo AP
#define TIMEOUT_PORTAL 300 // Tempo máximo que o Portal ficará ativo em seg
#define TIMEOUT_CONEXAO 60 // Tempo máximo de conexão com o Roteador em seg
//--------------------------------
// Definição dos Pinos Utilizados
//--------------------------------
#define RESET_BUTTON_PIN 0 // Pino do Botão para forçar entrar na configuração
#define LED_PIN 2 // Pino do LED_BUILTIN do ESP32
//---------------------------------------------------------------
// Seleção de Tema -> Descomente um e deixe os demais comentados
//---------------------------------------------------------------
// #define TEMA_DEFAULT
#define TEMA_DARK
// #define TEMA_LIGHT
//--------------------------------
// Definição de Variáveis Globais
//--------------------------------
volatile bool factoryResetRequested = false;
Preferences prefs;
char modo_operacao[10] = "";
char habilitar_log[5] = "";
char aliasname[30] = "";
String saved_modo;
String saved_log;
String saved_alias;
unsigned long lastDisplay = 0;
unsigned long lastCleanup = 0;
//--------------------------------
// Definição do Serviço Web
//--------------------------------
AsyncWebServer server(80);
AsyncWebSocket ws("/ws");
//-------------------------------------
// Definição dos Campos do WifiManager
//-------------------------------------
WiFiManager wm;
WiFiManagerParameter *html_select;
WiFiManagerParameter *html_checkbox;
WiFiManagerParameter *modo_param;
WiFiManagerParameter *log_param;
WiFiManagerParameter *alias_name;
bool wifiSalvo = false;
bool paramsSalvos = false;
bool portalFoiUsado = false;
//----------------------------------
// Prototipação de Rotinas e Funções
//----------------------------------
void IRAM_ATTR onFactoryReset();
void configModeCallback(WiFiManager *wm);
void preparaWifiManager();
void processaWifiManager();
void setupServer();
bool setDNSNAME(String nome);
String formatUptime();
void onWsEvent(AsyncWebSocket *server, AsyncWebSocketClient *client,
AwsEventType type, void *arg, uint8_t *data, size_t len);
void notifyClients();
void aplicaTemaWiFiManager();
void adicionaPaginaExtra();
void webServerReadyCallback();
//-------------------------
// Definição do Tema Dark
//-------------------------
const char wm_css_dark[] PROGMEM = R"rawliteral(
<style>
/* ======== TEMA DARK PARA WIFIMANAGER ======== */
/* Fundo geral */
body {
background-color: #121212 !important;
color: #e0e0e0 !important;
font-family: Arial, sans-serif;
}
/* Títulos */
h1, h2, h3 {
color: #ffffff !important;
}
/* Inputs */
input[type="text"],
input[type="password"],
input[type="number"],
select {
background-color: #1e1e1e !important;
color: #ffffff !important;
border: 1px solid #444 !important;
padding: 8px !important;
border-radius: 4px !important;
}
/* Botões */
button, input[type="submit"] {
background-color: #333 !important;
color: #fff !important;
border: 1px solid #555 !important;
padding: 10px 16px !important;
border-radius: 4px !important;
cursor: pointer;
}
button:hover, input[type="submit"]:hover {
background-color: #444 !important;
}
/* Links */
a {
color: #80bfff !important;
}
/* Separadores */
hr {
border: 1px solid #333 !important;
}
/* ======== CORREÇÃO DA LISTA DE SSIDs ======== */
#networks,
.wifi,
.wifi li,
.ssid {
display: block !important;
visibility: visible !important;
background-color: #1b1b1b !important;
color: #ffffff !important;
border: 1px solid #333 !important;
padding: 10px !important;
margin-bottom: 8px !important;
border-radius: 4px !important;
}
/* Hover nos SSIDs */
.wifi li:hover {
background-color: #2a2a2a !important;
}
/* Nome da rede */
.ssid span {
color: #fff !important;
font-weight: bold !important;
}
/* Força exibição da tabela de redes */
table {
color: #fff !important;
background-color: #1b1b1b !important;
border: 1px solid #333 !important;
}
table td, table th {
border: 1px solid #333 !important;
padding: 6px !important;
}
/* Corrige o botão de scan */
#scan {
background-color: #333 !important;
color: #fff !important;
border: 1px solid #555 !important;
}
/* Corrige o spinner de scan */
#scanning {
color: #fff !important;
}
/* Painéis */
div {
background-color: transparent !important;
}
/* ======== OCULTA CAMPOS INVISÍVEIS DO WIFIMANAGER ======== */
input[type="hidden"],
.wm_hidden,
#hidden,
input[name="modo"],
input[name="log"] {
display: none !important;
visibility: hidden !important;
height: 0 !important;
margin: 0 !important;
padding: 0 !important;
border: none !important;
}
</style>
)rawliteral";
//-------------------------
// Definição do Tema Light
//-------------------------
const char wm_css_light[] PROGMEM = R"rawliteral(
<style>
/* ======== TEMA LIGHT PARA WIFIMANAGER ======== */
/* Fundo geral */
body {
background-color: #f2f2f2 !important;
color: #000 !important;
font-family: Arial, sans-serif;
}
/* Títulos */
h1, h2, h3 {
color: #005bbb !important;
}
/* Inputs */
input[type="text"],
input[type="password"],
input[type="number"],
select {
background-color: #ffffff !important;
color: #000 !important;
border: 1px solid #005bbb !important;
padding: 8px !important;
border-radius: 4px !important;
}
/* Botões */
button, input[type="submit"] {
background-color: #005bbb !important;
color: #fff !important;
border: 1px solid #004999 !important;
padding: 10px 16px !important;
border-radius: 4px !important;
cursor: pointer;
}
button:hover, input[type="submit"]:hover {
background-color: #0070dd !important;
}
/* Links */
a {
color: #005bbb !important;
}
/* Separadores */
hr {
border: 1px solid #ccc !important;
}
/* ======== LISTA DE SSIDs (CORRIGIDA) ======== */
#networks,
.wifi,
.wifi li,
.ssid {
display: block !important;
visibility: visible !important;
background-color: #ffffff !important;
color: #000 !important;
border: 1px solid #cccccc !important;
padding: 10px !important;
margin-bottom: 8px !important;
border-radius: 4px !important;
}
/* Hover nos SSIDs */
.wifi li:hover {
background-color: #e6f0ff !important;
}
/* Nome da rede */
.ssid span {
color: #000 !important;
font-weight: bold !important;
}
/* Tabela de redes */
table {
color: #000 !important;
background-color: #ffffff !important;
border: 1px solid #ccc !important;
}
table td, table th {
border: 1px solid #ccc !important;
padding: 6px !important;
}
/* Botão de scan */
#scan {
background-color: #005bbb !important;
color: #fff !important;
border: 1px solid #004999 !important;
}
/* Spinner de scan */
#scanning {
color: #005bbb !important;
}
/* Painéis */
div {
background-color: transparent !important;
}
/* ======== OCULTA CAMPOS INVISÍVEIS ======== */
input[type="hidden"],
.wm_hidden,
#hidden,
input[name="modo"],
input[name="log"] {
display: none !important;
visibility: hidden !important;
height: 0 !important;
margin: 0 !important;
padding: 0 !important;
border: none !important;
}
</style>
)rawliteral";
//----------------------------------
// HTML Principal
//----------------------------------
const char status_html[] PROGMEM = R"rawliteral(
<!DOCTYPE html>
<html lang='pt-br'>
<head>
<meta charset='UTF-8'>
<meta name='viewport' content='width=device-width, initial-scale=1'>
<title>Status do ESP32</title>
<style>
body { background:#121212; color:#fff; font-family:Arial; padding:20px; }
h1 { color:#4da3ff; }
.card { background:#1e1e1e; padding:20px; border-radius:10px; margin-bottom:20px; }
.label { font-weight:bold; color:#4da3ff; }
</style>
</head>
<body>
<h1>Status do ESP32</h1>
<div class='card'><span class='label'>Modo de Operação:</span> %MODO%</div>
<div class='card'><span class='label'>Logs Habilitados:</span> %LOGS%</div>
<div class='card'><span class='label'>MAC Address:</span> %MAC%</div>
<div class='card'><span class='label'>IP Local:</span> %IP%</div>
<div class='card'><span class='label'>RSSI (Sinal WiFi):</span> <span id='rssi'>--</span> dBm</div>
<div class='card'><span class='label'>Free RAM:</span> <span id='ram'>--</span> bytes</div>
<div class='card'><span class='label'>Uptime:</span> <span id='uptime'>--</span></div>
<div class='card'><span class='label'>Versão Firmware:</span> 1.0</div>
<script>
var gateway = `ws://${window.location.hostname}/ws`;
var websocket;
function initWebSocket() {
websocket = new WebSocket(gateway);
websocket.onopen = () => console.log("WS conectado");
websocket.onclose = () => { console.log("WS desconectado"); setTimeout(initWebSocket, 2000); };
websocket.onmessage = (event) => {
let data = JSON.parse(event.data);
document.getElementById("uptime").innerHTML = data.uptime;
document.getElementById("ram").innerHTML = data.ram;
document.getElementById("rssi").innerHTML = data.rssi;
};
}
window.addEventListener('load', initWebSocket);
</script>
</body>
</html>
)rawliteral";
//--------------------------------
// Rotina de Inicialização Geral
//--------------------------------
void setup()
{
// Inicialza a Serial
Serial.begin(115200);
delay(1000);
// Hello no Terminal
Serial.println("WifiManager Customizado V1.0 Mar/2026");
// Define o Pino do LED Builtin e apaga
pinMode(LED_PIN, OUTPUT);
digitalWrite(LED_PIN, LOW);
// Define o Pino do Botão de BOOT interno do ESP32
pinMode(RESET_BUTTON_PIN, INPUT);
attachInterrupt(digitalPinToInterrupt(RESET_BUTTON_PIN), onFactoryReset, RISING);
// Carregar valores salvos
prefs.begin("my-app", false);
saved_modo = prefs.getString("modo", "0");
saved_log = prefs.getString("log", "0");
saved_alias = prefs.getString("alias", "mywm");
prefs.end();
// Mostra os valores salvos
Serial.printf("Saved Modo: %s\n", saved_modo.c_str());
Serial.printf("Saved Logs: %s\n", saved_log.c_str());
Serial.printf("Saved Alias: %s\n", saved_alias.c_str());
// Prepara os valores salvos para uso
strncpy(modo_operacao, saved_modo.c_str(), sizeof(modo_operacao));
strncpy(habilitar_log, saved_log.c_str(), sizeof(habilitar_log));
strncpy(aliasname, saved_alias.c_str(), sizeof(aliasname));
// Processa o WifiManager (Menu, campos, timeout, callback) e chama Portal se necessário
processaWifiManager();
// Apaga o LED informativo de WM Ativo
digitalWrite(LED_PIN, LOW);
// Recupera os parâmetros do WM
int modo = atoi(modo_param->getValue());
int logs = atoi(log_param->getValue()); // 0 ou 1
saved_alias = alias_name->getValue();
// Seta o DNSNAME
setDNSNAME(saved_alias);
// Mostra os parâmetros no terminal
Serial.printf("Modo: %d\n", modo);
Serial.printf("Logs: %d\n", logs);
bool logsEnabled = strcmp(log_param->getValue(), "1") == 0;
Serial.printf("Logs habilitados: %s\n", logsEnabled ? "Sim" : "Não");
// Salvar valores
prefs.begin("my-app", false);
prefs.putString("modo", modo_param->getValue());
prefs.putString("log", log_param->getValue());
prefs.putString("alias", alias_name->getValue());
prefs.end();
// Incializa o Server
setupServer();
}
//--------------------------------
// Loop Principal da Aplicação
//--------------------------------
void loop()
{
// Pega o atual ciclo
unsigned long now = millis();
// Verifica se pressionou o botão de entar no modo de configuração
if (factoryResetRequested)
{
wm.resetSettings();
delay(100);
ESP.restart();
}
// Verifica se conectado
if (WiFi.status() == WL_CONNECTED)
{
if (now-lastDisplay > INTERVALO)
{
digitalWrite(LED_PIN,!digitalRead(LED_PIN));
lastDisplay = now;
}
}
else
{
Serial.println("Reconectando...");
WiFi.reconnect();
}
// Cleanup de conexões Web inativas
if (now-lastCleanup > INTERVALO)
{
notifyClients();
ws.cleanupClients();
lastCleanup = now;
}
}
//--------------------------------------------
// Definição da Rotina de Interrupção do Botão
//--------------------------------------------
void IRAM_ATTR onFactoryReset()
{
factoryResetRequested = true;
}
//--------------------------------------
// Definição do Callback do WifiManager
//--------------------------------------
void configModeCallback(WiFiManager *wm)
{
Serial.println("Entrou no modo de configuração!");
digitalWrite(LED_PIN, HIGH); // acende o LED informativo fo modo de configuração
portalFoiUsado = true;
}
//--------------------------------------
// Prepara o ambiente do WifiManager
//--------------------------------------
void preparaWifiManager()
{
// Callbacks e timeouts
wm.setAPCallback(configModeCallback);
wm.setConfigPortalTimeout(TIMEOUT_PORTAL);
wm.setConnectTimeout(TIMEOUT_CONEXAO);
// Menu customizado
//std::vector<const char*> menu = { "wifi", "param", "info", "sep", "restart" };
//std::vector<const char*> menu = { "wifi", "wifinoscan", "param", "info", "sep", "restart" };
std::vector<const char *> menu = {"wifi", "info", "sep", "exit"};
#ifdef TEMA_DEFAULT
#else
wm.setMenu(menu);
#endif
// Callback para adicionar página extra quando o servidor estiver pronto
wm.setWebServerCallback(webServerReadyCallback);
// Título da página
#ifdef TEMA_DEFAULT
#else
wm.setTitle("<center>⭐ WifiManager ⭐ <u>Customizado</u></center>");
#endif
// Tema
#ifdef TEMA_DEFAULT
#else
aplicaTemaWiFiManager();
#endif
// -----------------------------
// CAMPOS PERSONALIZADOS
// -----------------------------
// SELECT
String html_select_str =
"<label for='modo_sel'>Modo de Operação:</label>"
"<select id='modo_sel' onchange=\"document.getElementsByName('modo')[0].value=this.value;\">"
"<option value='0' " + String(saved_modo == "0" ? "selected" : "") + ">Normal</option>"
"<option value='1' " + String(saved_modo == "1" ? "selected" : "") + ">Econômico</option>"
"<option value='2' " + String(saved_modo == "2" ? "selected" : "") + ">Turbo</option>"
"</select><br>";
char* select_buffer = new char[html_select_str.length() + 1];
strcpy(select_buffer, html_select_str.c_str());
html_select = new WiFiManagerParameter(select_buffer);
wm.addParameter(html_select);
// CHECKBOX
String html_checkbox_str =
"<label>"
"<input type='checkbox' id='log_chk' " +
String(saved_log == "1" ? "checked" : "") +
" onchange=\"document.getElementsByName('log')[0].value=this.checked?'1':'0';\">"
" Habilitar Logs"
"</label><br>";
char* checkbox_buffer = new char[html_checkbox_str.length() + 1];
strcpy(checkbox_buffer, html_checkbox_str.c_str());
html_checkbox = new WiFiManagerParameter(checkbox_buffer);
wm.addParameter(html_checkbox);
// Campos invisíveis
modo_param = new WiFiManagerParameter("modo", "", saved_modo.c_str(), 10);
log_param = new WiFiManagerParameter("log", "", saved_log.c_str(), 5);
wm.addParameter(modo_param);
wm.addParameter(log_param);
// Alias
alias_name = new WiFiManagerParameter("alias", "Informe o AliasName para o mDNS", saved_alias.c_str(), 30);
wm.addParameter(alias_name);
// Sincronismo de parâmetros
wm.setSaveParamsCallback([]() {
Serial.println("Parâmetros salvos.");
paramsSalvos = true;
});
// Sincronismo de WiFi
wm.setSaveConfigCallback([]() {
Serial.println("Config WiFi salva.");
wifiSalvo = true;
});
}
//--------------------------------------
// Processa o WifiManager
//--------------------------------------
void processaWifiManager()
{
preparaWifiManager();
Serial.println("Iniciando WiFiManager...");
bool conectado = wm.autoConnect(SSID_AP, SENHA_AP);
if (!conectado)
{
Serial.println("Falha ao conectar. Reiniciando...");
delay(2000);
ESP.restart();
}
// Se o portal foi usado, precisamos salvar os parâmetros e reiniciar
if (portalFoiUsado)
{
Serial.println("Portal foi usado. Salvando parâmetros...");
// Salva no Preferences
Preferences prefs;
prefs.begin("my-app", false);
prefs.putInt("modo", atoi(modo_param->getValue()));
prefs.putInt("logs", atoi(log_param->getValue()));
prefs.putString("alias", String(alias_name->getValue()));
prefs.end();
// Atualiza as variáveis globais usadas pelo HTML
strncpy(modo_operacao, modo_param->getValue(), sizeof(modo_operacao));
strncpy(habilitar_log, log_param->getValue(), sizeof(habilitar_log));
strncpy(aliasname, alias_name->getValue(), sizeof(aliasname));
Serial.println("Parâmetros atualizados e salvos com sucesso!");
Serial.println("Reiniciando para liberar porta 80...");
delay(1000);
ESP.restart();
}
// Se chegou aqui, o portal NÃO foi usado → porta 80 está livre
Serial.println("WiFiManager finalizado. Iniciando AsyncWebServer...");
}
//--------------------------------------
// Inicializa o WebServer
//--------------------------------------
void setupServer()
{
ws.onEvent(onWsEvent);
server.addHandler(&ws);
server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
String page = status_html;
page.replace("%MODO%", saved_modo);
page.replace("%LOGS%", saved_log == "1" ? "Sim" : "Não");
page.replace("%MAC%", WiFi.macAddress());
page.replace("%IP%", WiFi.localIP().toString());
request->send(200, "text/html; charset=UTF-8", page);
});
server.begin();
Serial.printf("Servidor Web iniciado. Acesse http://%s ou http://%s.local\n",WiFi.localIP().toString(),saved_alias.c_str());
}
//---------------------------------
// Define o HostName como DNS NAME
//---------------------------------
bool setDNSNAME(String nome)
{
WiFi.setHostname(nome.c_str());
bool ok = MDNS.begin(nome.c_str());
if (ok)
{
MDNS.addService("http", "tcp", 80);
MDNS.setInstanceName(nome.c_str()); // Adicionar o nome da instância
}
return ok;
}
//--------------------------------------
// Converte millis() para dias.hh:mm:ss
//--------------------------------------
String formatUptime()
{
//unsigned long ms = millis();
uint64_t us = esp_timer_get_time();
unsigned long ms = us / 1000;
unsigned long seconds = ms / 1000;
unsigned long minutes = seconds / 60;
unsigned long hours = minutes / 60;
unsigned long days = hours / 24;
seconds %= 60;
minutes %= 60;
hours %= 24;
char buffer[20];
if (days > 0) sprintf(buffer, "%lu.%02lu:%02lu:%02lu", days, hours, minutes, seconds);
else sprintf(buffer, "%02lu:%02lu:%02lu", hours, minutes, seconds);
return String(buffer);
}
//--------------------------------------
// Trata Eventos do WebSocket
//--------------------------------------
void onWsEvent(AsyncWebSocket *server, AsyncWebSocketClient *client,
AwsEventType type, void *arg, uint8_t *data, size_t len)
{
if (type == WS_EVT_CONNECT) {
Serial.printf("WebSocket conectado: %u\n", client->id());
}
else if (type == WS_EVT_DISCONNECT) {
Serial.printf("WebSocket desconectado: %u\n", client->id());
}
}
//--------------------------------------
// Notifica campos variáveis para o Html
//--------------------------------------
void notifyClients()
{
StaticJsonDocument<200> doc;
doc["uptime"] = formatUptime();
doc["ram"] = ESP.getFreeHeap();
doc["rssi"] = WiFi.RSSI();
String json;
serializeJson(doc, json);
ws.textAll(json);
}
//--------------------------------------
// Aplica o Tema para o WifiManager
//--------------------------------------
void aplicaTemaWiFiManager()
{
// Tema
#ifdef TEMA_DARK
wm.setCustomHeadElement(wm_css_dark);
#endif
#ifdef TEMA_LIGHT
wm.setCustomHeadElement(wm_css_light);
#endif
#ifdef TEMA_DEFAULT
// Não seta nada → usa o tema nativo do WiFiManager
#endif
}
//------------------------------------------------
// Callback WifiManager Ativo e define Handle /info
//------------------------------------------------
void webServerReadyCallback()
{
Serial.println("Servidor interno do WiFiManager pronto!");
wm.server->on("/info", HTTP_GET, []() {
String page = "<!DOCTYPE html><html lang='pt-br'><head>";
page += "<meta charset='UTF-8'>";
page += "<meta name='viewport' content='width=device-width, initial-scale=1'>";
// injeta o mesmo CSS do tema atual
#if WM_THEME == WM_THEME_DARK
page += wm_css_dark;
#elif WM_THEME == WM_THEME_LIGHT
page += wm_css_light;
#endif
page += "<title>Informações do Sistema</title>";
page += "</head><body>";
page += "<div class='wrap'>";
page += "<h1>Informações Avançadas</h1>";
page += "<div class='card'><b>MAC Address:</b> " + WiFi.macAddress() + "</div>";
page += "<div class='card'><b>IP Local:</b> " + WiFi.localIP().toString() + "</div>";
page += "<div class='card'><b>RSSI:</b> " + String(WiFi.RSSI()) + " dBm</div>";
page += "<div class='card'><b>Free RAM:</b> " + String(ESP.getFreeHeap()) + " bytes</div>";
page += "<div class='card'><b>Uptime:</b> " + formatUptime() + "</div>";
page += "<div class='card'><b>Versão Firmware:</b> 1.0</div>";
page += "<br><a class='btn' href='/'>Voltar</a>";
page += "</div>"; // wrap
page += "</body></html>";
wm.server->send(200, "text/html", page);
});
}
Embora este projeto seja focado principalmente em software — WiFiManager, parâmetros personalizados, temas e AsyncWebServer — o hardware necessário é extremamente simples: apenas o módulo ESP32. Ainda assim, dois elementos físicos desempenham papéis fundamentais no fluxo de operação: os botões BOOT e RESET.
A figura abaixo destaca esses dois botões no ESP32 de 30 pinos, indicando claramente suas funções no contexto do projeto.

Figura 11 – Diagrama do Circuito
Botão BOOT (GPIO 0) — Entrada no Modo de Configuração
O botão BOOT, conectado internamente ao GPIO 0, é o elemento-chave para permitir que o usuário reentre no modo de configuração mesmo após o dispositivo já estar operando normalmente com o AsyncWebServer.
Ele funciona como um “atalho físico” para recuperar o dispositivo em campo, sem depender de:
Ao pressionar o BOOT durante o boot (ou por um tempo específico durante a operação), o firmware detecta o nível lógico baixo no GPIO 0 e inicia o WiFiManager em modo AP, permitindo reconfigurar SSID, senha e parâmetros personalizados.
Esse recurso é essencial para:
Em produtos reais, esse botão funciona como uma espécie de “modo de segurança” do ESP32.
Botão RESET — Reinicialização Completa do Sistema
O botão RESET reinicia o ESP32 por completo, interrompendo qualquer operação em andamento e reiniciando o firmware desde o início.
No contexto deste projeto, ele é útil para:
Embora o firmware já execute um reboot automático após o SAVE (como explicado na Seção 9), o botão RESET continua sendo uma ferramenta prática para testes e manutenção.
Por que destacar esses botões no diagrama?
Mesmo em um projeto sem componentes externos, esses dois botões:
Ao mostrar o ESP32 com os botões destacados, o leitor entende que o hardware é simples, mas o fluxo de uso é bem pensado e robusto — algo fundamental para quem pretende transformar o projeto em um produto real.
Criar um portal de configuração realmente amigável para o ESP32 vai além de simplesmente conectar o dispositivo ao WiFi. Envolve pensar na experiência do usuário final, reduzir passos desnecessários e apresentar as informações de forma clara e organizada.
Neste artigo, mostramos como transformar o WiFiManager em um portal mais completo e profissional, reunindo:
Essa abordagem simplifica o uso, evita confusão e deixa o fluxo mais previsível. Também adotamos um reboot automático após o SAVE, garantindo que o servidor web principal do dispositivo inicie de forma confiável — uma prática importante quando combinamos WiFiManager com AsyncWebServer.
O resultado é um portal claro, prático e adequado tanto para projetos pessoais quanto para produtos comerciais baseados em ESP32. Mais do que uma customização estética, trata-se de uma melhoria real na usabilidade e na confiabilidade do processo de configuração.
Tenha a Metodologia Eletrogate dentro da sua Escola! Conheça nosso Programa de Robótica nas Escolas!