Índice:
Neste projeto vamos transformar um ESP32 em um medidor inteligente de bateria, capaz de medir a tensão de uma pilha AAA de 1,5 V e mostrar o resultado diretamente no navegador do celular ou computador.
Além da tensão, o ESP32 calcula uma estimativa do nível de carga e apresenta tudo em uma página web com uma bateria animada, que vai enchendo e mudando de cor conforme o nível medido.
O projeto é relativamente simples, mas reúne vários conceitos interessantes:
- conversão analógico-digital;
- leitura de tensão;
- ESP32;
- Wi-Fi;
- servidor web;
- HTML;
- CSS;
- JavaScript;
- IoT.
Ao final, teremos um pequeno instrumento conectado à rede sem necessidade de instalar aplicativo.
Objetivo do projeto
Nosso ESP32 fará três tarefas principais:
- medir a tensão da pilha AAA;
- estimar aproximadamente o nível de carga;
- disponibilizar essas informações em uma página web.
O funcionamento geral será:
Pilha AAA
│
▼
GPIO34 / ADC
│
▼
ESP32
│
├── mede tensão
│
├── estima porcentagem
│
└── cria servidor Web
│
▼
Wi-Fi da rede
│
▼
Celular / computador
Componentes necessários
Para montar o projeto você precisará de:
- 1 ESP32 DevKit / ESP-WROOM-32
- 1 pilha AAA alcalina de 1,5 V
- suporte para pilha AAA ou fios
- protoboard
- jumpers
- cabo USB
- opcional: resistor de aproximadamente 10 kΩ para proteção da entrada
- opcional: capacitor de 100 nF para estabilizar a leitura
Não é necessário utilizar display físico, porque nossa interface será exibida diretamente pelo navegador.
Qual pino vamos utilizar?
Utilizaremos:
GPIO34
para a leitura da bateria.
Esse pino pertence ao ADC1 do ESP32, o que é importante porque o projeto utiliza Wi-Fi.
Em muitos ESP32 clássicos, o ADC2 apresenta limitações enquanto o Wi-Fi está ativo. Por isso GPIO34 é uma escolha interessante para este tipo de aplicação.
Ligação da bateria
Como estamos utilizando somente uma pilha AAA, cuja tensão fica aproximadamente entre 1 V e 1,6 V, podemos medir diretamente com o ESP32.
A ligação básica é:
PILHA AAA
(+) ───────────── GPIO34
(-) ───────────── GND
Se quiser acrescentar uma proteção simples, podemos colocar um resistor em série:
AAA (+)
│
[10 kΩ]
│
└──────── GPIO34
AAA (-)
│
GND
Esse resistor não forma um divisor de tensão nesse caso. Ele apenas limita corrente em situações acidentais.
Atenção à tensão máxima
O GPIO do ESP32 trabalha em níveis de aproximadamente:
0 a 3,3 V
Neste projeto estamos medindo uma única célula AAA de aproximadamente 1,5 V.
Portanto, ela está dentro dessa faixa.
Não utilize este mesmo circuito para medir diretamente:
bateria de 9 V
12 V
24 V
fonte automotiva
bateria Li-Ion acima de 3,3 V
rede elétrica
Para tensões superiores seria necessário utilizar divisor resistivo e proteção apropriada.
Como o ESP32 mede a tensão?
O ESP32 possui internamente um conversor A/D.
A ideia básica é transformar uma tensão analógica em um número digital.
Entretanto, durante nossos testes percebemos que simplesmente utilizar:
analogRead()
e converter manualmente de 0–4095 para 0–3,3 V não fornecia a melhor precisão.
Por isso utilizamos:
analogReadMilliVolts()
Essa função retorna diretamente uma estimativa da tensão em milivolts.
Por exemplo:
1420 mV
equivale a:
1,420 V
Também fazemos diversas leituras e calculamos uma média para deixar o valor apresentado mais estável.
Porcentagem de uma pilha AAA
Existe um detalhe importante.
Não podemos afirmar que:
1,50 V = 100%
0,75 V = 50%
0 V = 0%
porque a descarga de uma pilha alcalina não é perfeitamente linear.
Além disso, a tensão depende de:
- corrente consumida;
- temperatura;
- fabricante;
- química da pilha;
- idade;
- resistência interna;
- tempo desde a última utilização.
Por isso o percentual apresentado neste projeto é uma estimativa.
Usaremos aproximadamente:
| Tensão | Carga estimada |
| 1,55 V ou mais | 100% |
| 1,50 V | 95% |
| 1,45 V | 85% |
| 1,40 V | 75% |
| 1,35 V | 65% |
| 1,30 V | 50% |
| 1,25 V | 40% |
| 1,20 V | 30% |
| 1,15 V | 20% |
| 1,10 V | 10% |
| 1,05 V | 5% |
| abaixo disso | 0% |
Para um testador mais avançado, poderíamos medir a tensão sob carga, o que fornece uma indicação mais útil sobre o estado real da pilha.
A página web
O ESP32 vai hospedar uma página diretamente em sua memória.
Quando acessarmos seu endereço IP no navegador, veremos:
MEDIDOR DE BATERIA

┌──────────────────┐
│██████████████ │▐
└──────────────────┘
75%
1,40 V
Atualização em tempo real
A bateria muda de cor:
Verde → carga alta
Amarelo → carga intermediária
Vermelho → carga baixa
Além disso, o preenchimento possui transição suave e um efeito de brilho.
Configurando a Arduino IDE
É necessário ter o suporte ao ESP32 instalado na Arduino IDE.
Depois selecione sua placa em:
Ferramentas → Placa → ESP32 Arduino
Por exemplo:
ESP32 Dev Module
Depois selecione a porta COM correta.
Configurando o Wi-Fi
No código você encontrará:
const char* ssid = “SEU_WIFI”;
const char* password = “SUA_SENHA”;
Substitua pelas informações da sua rede.
Por exemplo:
const char* ssid = “MinhaRede”;
const char* password = “12345678”;
O celular ou computador utilizado para visualizar o dashboard deverá estar na mesma rede local.
Código completo
#include <WiFi.h>
#include <WebServer.h>
// =====================================================
// WIFI
// =====================================================
const char* ssid = "SEU_WIFI";
const char* password = "SUA_SENHA";
// =====================================================
// ADC
// =====================================================
#define PINO_BATERIA 34
// =====================================================
// SERVIDOR
// =====================================================
WebServer server(80);
// =====================================================
// LEITURA DA TENSÃO
// =====================================================
float lerTensaoBateria() {
const int AMOSTRAS = 40;
uint32_t soma_mV = 0;
for (int i = 0; i < AMOSTRAS; i++) {
soma_mV += analogReadMilliVolts(PINO_BATERIA);
delay(2);
}
float media_mV =
soma_mV / (float)AMOSTRAS;
return media_mV / 1000.0;
}
// =====================================================
// ESTIMATIVA DA PORCENTAGEM
// =====================================================
int calcularPorcentagem(float v) {
if (v >= 1.55) return 100;
if (v >= 1.50) return 95;
if (v >= 1.45) return 85;
if (v >= 1.40) return 75;
if (v >= 1.35) return 65;
if (v >= 1.30) return 50;
if (v >= 1.25) return 40;
if (v >= 1.20) return 30;
if (v >= 1.15) return 20;
if (v >= 1.10) return 10;
if (v >= 1.05) return 5;
return 0;
}
// =====================================================
// PÁGINA WEB
// =====================================================
const char pagina[] PROGMEM = R"rawliteral(
<!DOCTYPE html>
<html lang="pt-BR">
<head>
<meta charset="UTF-8">
<meta
name="viewport"
content="width=device-width, initial-scale=1.0">
<title>Medidor de Bateria ESP32</title>
<style>
* {
box-sizing: border-box;
}
body {
margin: 0;
height: 100vh;
display: flex;
justify-content: center;
align-items: center;
font-family:
Arial,
Helvetica,
sans-serif;
background:
radial-gradient(
circle at top,
#183153,
#07111f
);
color: white;
}
.card {
width: 360px;
padding: 35px;
border-radius: 25px;
background:
rgba(255,255,255,0.08);
backdrop-filter:
blur(10px);
box-shadow:
0 20px 60px
rgba(0,0,0,0.45);
text-align: center;
}
h1 {
margin-top: 0;
font-size: 25px;
letter-spacing: 2px;
}
.subtitulo {
opacity: 0.7;
margin-bottom: 35px;
}
/* =======================================
BATERIA
======================================= */
.bateria-container {
display: flex;
justify-content: center;
align-items: center;
margin: 30px 0;
}
.bateria {
position: relative;
width: 220px;
height: 105px;
border:
6px solid white;
border-radius: 15px;
padding: 7px;
}
.bateria::after {
content: "";
position: absolute;
right: -21px;
top: 28px;
width: 15px;
height: 40px;
background: white;
border-radius:
0 6px 6px 0;
}
.carga {
width: 0%;
height: 100%;
border-radius: 7px;
background: #00e676;
transition:
width 0.8s ease,
background 0.8s ease;
position: relative;
overflow: hidden;
}
/* Efeito de brilho */
.carga::after {
content: "";
position: absolute;
top: 0;
left: -100%;
width: 70%;
height: 100%;
background:
linear-gradient(
90deg,
transparent,
rgba(255,255,255,0.35),
transparent
);
animation:
brilho 2s infinite;
}
@keyframes brilho {
0% {
left: -100%;
}
100% {
left: 150%;
}
}
/* =======================================
DADOS
======================================= */
.porcentagem {
font-size: 55px;
font-weight: bold;
margin-top: 25px;
}
.tensao {
font-size: 25px;
margin-top: 10px;
opacity: 0.85;
}
.status {
margin-top: 20px;
font-size: 14px;
opacity: 0.6;
}
</style>
</head>
<body>
<div class="card">
<h1>
MEDIDOR DE BATERIA
</h1>
<div class="subtitulo">
ESP32 • ADC • Wi-Fi
</div>
<div class="bateria-container">
<div class="bateria">
<div
id="carga"
class="carga">
</div>
</div>
</div>
<div
id="porcentagem"
class="porcentagem">
--%
</div>
<div
id="tensao"
class="tensao">
-.-- V
</div>
<div
id="status"
class="status">
Atualização em tempo real
</div>
</div>
<script>
async function atualizar() {
try {
const resposta =
await fetch("/dados");
const dados =
await resposta.json();
const porcentagem =
dados.porcentagem;
document
.getElementById(
"porcentagem"
)
.innerHTML =
porcentagem + "%";
document
.getElementById(
"tensao"
)
.innerHTML =
dados.tensao
.toFixed(2) +
" V";
const carga =
document
.getElementById(
"carga"
);
carga.style.width =
porcentagem + "%";
if (
porcentagem > 60
) {
carga.style.background =
"#00e676";
}
else if (
porcentagem > 25
) {
carga.style.background =
"#ffca28";
}
else {
carga.style.background =
"#ff5252";
}
}
catch (erro) {
console.log(
erro
);
}
}
setInterval(
atualizar,
1000
);
atualizar();
</script>
</body>
</html>
)rawliteral";
// =====================================================
// ENDPOINT DE DADOS
// =====================================================
void enviarDados() {
float tensao =
lerTensaoBateria();
int porcentagem =
calcularPorcentagem(
tensao
);
String json =
"{";
json +=
"\"tensao\":" +
String(
tensao,
3
);
json +=
",";
json +=
"\"porcentagem\":" +
String(
porcentagem
);
json +=
"}";
server.send(
200,
"application/json",
json
);
}
// =====================================================
// SETUP
// =====================================================
void setup() {
Serial.begin(
115200
);
delay(
1000
);
pinMode(
PINO_BATERIA,
INPUT
);
analogReadResolution(
12
);
analogSetPinAttenuation(
PINO_BATERIA,
ADC_11db
);
// ================================================
// WIFI
// ================================================
WiFi.mode(
WIFI_STA
);
WiFi.begin(
ssid,
password
);
Serial.println();
Serial.print(
"Conectando ao WiFi"
);
while (
WiFi.status() !=
WL_CONNECTED
) {
delay(
500
);
Serial.print(
"."
);
}
Serial.println();
Serial.println(
"WiFi conectado!"
);
Serial.print(
"Acesse: http://"
);
Serial.println(
WiFi.localIP()
);
// ================================================
// SERVIDOR
// ================================================
server.on(
"/",
[]() {
server.send_P(
200,
"text/html",
pagina
);
}
);
server.on(
"/dados",
enviarDados
);
server.begin();
Serial.println(
"Servidor iniciado!"
);
}
// =====================================================
// LOOP
// =====================================================
void loop() {
server.handleClient();
}
Gravando o código
Depois de colocar o nome e a senha da sua rede Wi-Fi:
- conecte o ESP32 ao computador;
- selecione a placa correta;
- selecione a porta COM;
- clique em Carregar.
Após terminar, abra o Monitor Serial em: 115200 baud
O ESP32 deverá mostrar algo semelhante a: Conectando ao WiFi…
WiFi conectado!
Acesse: http://192.168.0.105
Servidor iniciado!
O IP será diferente em cada rede.
Acessando pelo celular
Pegue o endereço mostrado no Monitor Serial.
Por exemplo: http://192.168.0.105
Abra esse endereço no navegador.
Você verá a página do medidor.
Não existe servidor externo e nenhum aplicativo precisa ser instalado.
A página está sendo enviada diretamente pelo próprio ESP32.
Testando a bateria
Conecte:
AAA (+) → GPIO34
AAA (-) → GND
O dashboard deverá começar a apresentar a tensão.
Por exemplo:
1,47 V
85%
A animação da bateria acompanha o percentual.
Atualização em tempo real
O navegador executa:
setInterval(
atualizar,
1000
);
Isso significa que, aproximadamente a cada segundo, o navegador pergunta ao ESP32:
/dados
O ESP32 responde algo parecido com:
{
“tensao”: 1.472,
“porcentagem”: 85
}
O JavaScript pega esses dados e atualiza a interface sem recarregar a página.
Esse é um conceito muito utilizado em sistemas IoT e dashboards embarcados.
Por que fazer várias leituras?
Em vez de utilizar apenas uma conversão: analogReadMilliVolts(PINO_BATERIA);
fazemos: 40 leituras
e calculamos a média.
Isso ajuda a reduzir:
- ruído;
- pequenas oscilações;
- alterações rápidas no display;
- instabilidade natural do ADC.
Melhorando ainda mais a estabilidade
Se a leitura oscilar muito, podemos colocar um capacitor de:
100 nF
entre:
GPIO34
e
GND
A montagem fica:
AAA (+)
│
├──────── GPIO34
│
[100 nF]
│
GND
AAA (-) ─── GND
Esse capacitor ajuda a filtrar ruído de alta frequência.
Limitações da porcentagem
É importante entender que o ESP32 está medindo tensão, não energia armazenada diretamente.
Portanto: 1,30 V
não significa necessariamente que exista exatamente: 50% da energia original.
A porcentagem apresentada serve como referência visual. Para construir um testador mais preciso, poderíamos adicionar uma carga conhecida.
Por exemplo:
Pilha
│
resistor de carga
│
transistor / MOSFET
│
ESP32
O ESP32 poderia:
- medir a tensão em vazio;
- ativar a carga;
- esperar alguns milissegundos;
- medir novamente;
- analisar a queda de tensão.
Isso permitiria avaliar melhor a resistência interna e o estado da pilha.
Possíveis evoluções
Depois dessa primeira versão podemos acrescentar:
- teste automático sob carga;
- medição de resistência interna;
- histórico da bateria;
- gráfico de tensão ao longo do tempo;
- várias baterias simultaneamente;
- armazenamento das medições;
- MQTT;
- integração com Home Assistant;
- envio para banco de dados;
- display OLED local;
- alarme de bateria fraca;
- medição de baterias Li-Ion com circuito adequado.
Aplicações
O conceito utilizado aqui pode ser aproveitado em diversos produtos:
- equipamentos alimentados por bateria;
- sistemas IoT;
- sensores remotos;
- nobreaks;
- sistemas solares;
- robôs;
- dispositivos portáteis;
- manutenção eletrônica;
- monitoramento industrial.
A grande diferença é que nossa medição não fica limitada ao equipamento físico.
Ela passa a estar disponível pela rede.
Conclusão
Neste projeto construímos um medidor de bateria Wi-Fi utilizando ESP32, capaz de medir uma pilha AAA e apresentar a tensão e uma estimativa visual da carga diretamente em uma página web. Apesar de simples, o projeto reúne conceitos extremamente importantes de eletrônica e sistemas embarcados.
Utilizamos o ADC do ESP32 para transformar uma tensão analógica em informação digital, processamos essa informação no microcontrolador e disponibilizamos tudo através de um servidor web.
O resultado é um pequeno instrumento IoT que pode ser acessado pelo celular sem necessidade de instalar qualquer aplicativo. A partir dessa estrutura podemos evoluir facilmente para sistemas muito mais sofisticados de monitoramento de energia, baterias e equipamentos eletrônicos.
Saiba mais sobre a parceria Saravati e Sara Educação
Este “Guia de Montagem” é uma colaboração especial entre a Saravati e a Sara Educação, criado pelo Professor Felipe Rosa. Nosso objetivo é enriquecer a comunidade de entusiastas da eletrônica, IoT e automação com recursos educacionais de alta qualidade. Através dessa parceria, buscamos inspirar e capacitar criadores em seus projetos, disponibilizando esses guias em nosso blog e nas redes sociais.
