Community project
Testador de Bonine - Compact Power Monitor Switch
Hugo Oliveira
Published July 16, 2026 · Updated July 16, 2026

The Testador de Bonine is a compact power monitor built around an ESP32 and Adafruit INA219 current sensor that diagnoses irrigation solenoid coils in the field. By measuring voltage and current through a test circuit, it identifies whether a coil is functioning normally, open-circuited, or short-circuited within seconds.
This guide provides a complete wiring diagram, parts list, and step-by-step assembly instructions for connecting the INA219 sensor to the ESP32 via Grove connector, mounting the series resistor, and attaching the test probes. The included firmware implements real-time diagnostics with visual feedback on the display, allowing users to quickly validate solenoid health during installation or troubleshooting.
Wiring diagram
Interactive · read-only
Pan and zoom to explore the wiring. Remix the project to edit it in your own workspace.
Parts list
Bill of materials| Component | Qty | Notes |
|---|---|---|
| Adafruit INA219 High-Side DC Current Sensor | 1 | INA219 high-side current and bus-voltage monitor breakout. It is powered from 3.3V or 5V and communicates over I2C. Route the measured load current through VIN+ and VIN-; those shunt terminals are part of the power path, not MCU GPIO. |
| Resistor100 Ω | 1 | Through-hole resistor (current-limiting in series with an LED) |
| Ponta de prova positiva (fio vermelho) | 1 | Fio/ponta de prova para ligar ao terminal positivo da bobine da eletroválvula |
| Ponta de prova negativa (fio preto) | 1 | Fio/ponta de prova para ligar ao terminal negativo da bobine da eletroválvula |
Assembly
6 stepsMateriais necessários
Separa os seguintes componentes: M5StickS3, breakout Adafruit INA219, resistor 100Ω (castanho-preto-castanho), cabo Grove 4 pinos (HY2.0-4P), 2 fios com pinças jacaré ou pontas de prova (vermelho e preto).
- Tip: O cabo Grove já tem os 4 pinos corretos — usa-o para ligar o INA219 ao M5StickS3
- Tip: Qualquer breakout INA219 compatível com I2C e 3.3V serve
Ligar INA219 ao M5StickS3 via Grove
Liga o cabo Grove 4 pinos ao conector HY2.0-4P do M5StickS3 (na parte de baixo do dispositivo). O cabo tem 4 fios: preto=GND, vermelho=3V3, branco=G9(SDA), amarelo=G10(SCL). Liga o outro extremo ao INA219: GND→GND, VCC→VCC, SDA→SDA, SCL→SCL.
- Tip: O conector Grove só entra num sentido — não forces
- Tip: Verifica que os fios coincidem: preto-GND, vermelho-VCC, branco-SDA, amarelo-SCL
- ⚠ O INA219 é catalogado como módulo 5V mas funciona perfeitamente com 3.3V nesta aplicação — não ligues a 5V para evitar problemas de nível lógico
Montar o resistor 100Ω em série
O resistor 100Ω serve de proteção/limitador de corrente. Liga um extremo do resistor (P1) ao rail 3.3V do INA219 ou ao pino 3V3 do conector Grove. Liga o outro extremo (P2) ao pino VIN+ do INA219.
- Tip: Podes usar uma mini breadboard para facilitar as ligações
- Tip: O resistor 100Ω tem código de cores: castanho-preto-castanho-dourado
- ⚠ Não saltes este resistor — protege o M5StickS3 de um eventual curto-circuito na bobine
Ligar as pontas de prova
Liga um fio vermelho (ponta de prova positiva) ao pino VIN- do INA219. Liga um fio preto (ponta de prova negativa) ao GND. Estas serão as 2 pontas que tocam nos terminais da bobine da eletroválvula.
- Tip: Usa pinças jacaré nas pontas para facilitar a ligação à bobine no campo
- Tip: O comprimento do fio não é crítico — 30 a 50 cm é suficiente
- ⚠ Confirma a polaridade: vermelho=VIN-, preto=GND
Verificar ligações antes de ligar
Antes de ligar o M5StickS3 via USB-C, verifica: 1) INA219 ligado ao Grove corretamente, 2) Resistor 100Ω entre 3V3 e VIN+, 3) Fio vermelho em VIN-, 4) Fio preto no GND. O circuito de teste fica: 3.3V → resistor 100Ω → VIN+ → (shunt INA219) → VIN- → fio vermelho → bobine → fio preto → GND.
- Tip: Fotografa as ligações para referência futura
Usar o testador no campo
Liga o M5StickS3 via USB-C (ou usa a bateria interna de 250mAh). O ecrã mostra 'TESTADOR BOBINE - Prima o botão A'. Encosta o fio vermelho e o fio preto aos 2 terminais da bobine da eletroválvula (sem polaridade definida nas bobines AC/resistivas). Carrega no Botão A (lateral do M5StickS3) e aguarda 2 segundos.
- Tip: Nas bobines resistivas não há polaridade — podes inverter os fios
- Tip: Para um resultado mais fiável, testa com a bobine desligada do sistema de rega
- Tip: Resultado VERDE = bobine boa. Resultado VERMELHO = bobine partida/aberta. Resultado LARANJA = curto-circuito.
- ⚠ Não toques nos terminais metálicos das pontas de prova durante o teste
- ⚠ Não uses este testador em bobines que ainda estejam ligadas a 12V/24V — desliga sempre a alimentação do sistema de rega primeiro
Pin assignments
Board wiring reference| Pin | Connection | Type |
|---|---|---|
| 3V3 | ina219 VCC | power |
| GND | ina219 GND | ground |
| GPIO 9 | ina219 SDA | i2c |
| GPIO 10 | ina219 SCL | i2c |
| EXT | ina219 VIN+ → Resistor P2 | data |
| 3V3 | resistor1 P1 | power |
| EXT | ina219 VIN- → Ponta de prova positiva (fio vermelho) TIP | data |
| GND | probe_neg TIP | ground |
Firmware
ESP32#include <Arduino.h>
#include <M5Unified.h>
#include <Wire.h>
#include <Adafruit_INA219.h>
// I2C externo no Grove (G9=SDA, G10=SCL)
#define SDA_PIN 9
#define SCL_PIN 10
// Limiares de diagnóstico para bobines de rega típicas (via 3.3V + 100Ω)
// Bobine boa: resistência 20–80Ω → corrente ~14–110mA com 3.3V + 100Ω série
#define CURRENT_MIN_MA 5.0f // Abaixo: circuito aberto / bobine queimada
#define CURRENT_MAX_MA 120.0f // Acima: curto-circuito
#define VOLTAGE_MIN_V 0.5f // Tensão mínima esperada no shunt
// Hoisted type definitions
enum State { IDLE, TESTING, RESULT_OK, RESULT_OPEN, RESULT_SHORT };
// Forward declarations
void drawIdle();
void drawTesting(float elapsed);
void drawResult(State s);
Adafruit_INA219 ina219;
State state = IDLE;
float measuredCurrentMA = 0;
float measuredVoltageV = 0;
float measuredPowerMW = 0;
float measuredResistance = 0;
unsigned long testStart = 0;
const unsigned long TEST_DURATION = 2000; // 2 segundos de leitura média
// Acumuladores para média
float sumCurrent = 0;
float sumVoltage = 0;
int sampleCount = 0;
void drawIdle() {
M5.Display.fillScreen(0x1A3A); // Azul escuro
M5.Display.setTextColor(TFT_WHITE);
M5.Display.setTextSize(1);
M5.Display.setCursor(10, 10);
M5.Display.setTextSize(2);
M5.Display.println("TESTADOR");
M5.Display.println("BOBINE");
M5.Display.setTextSize(1);
M5.Display.println("");
M5.Display.setTextColor(TFT_CYAN);
M5.Display.println("Ligue os 2 fios");
M5.Display.println("a bobine e prima");
M5.Display.println("o botao A");
M5.Display.println("");
M5.Display.setTextColor(TFT_YELLOW);
M5.Display.println(" [A] Iniciar");
}
void drawTesting(float elapsed) {
M5.Display.fillScreen(0x2820); // Verde escuro escuro
M5.Display.setTextColor(TFT_YELLOW);
M5.Display.setTextSize(2);
M5.Display.setCursor(5, 5);
M5.Display.println("A MEDIR...");
M5.Display.setTextSize(1);
M5.Display.setTextColor(TFT_WHITE);
M5.Display.println("");
M5.Display.print("Corrente: ");
M5.Display.setTextColor(TFT_CYAN);
M5.Display.print(measuredCurrentMA, 1);
M5.Display.println(" mA");
M5.Display.setTextColor(TFT_WHITE);
M5.Display.print("Tensao: ");
M5.Display.setTextColor(TFT_CYAN);
M5.Display.print(measuredVoltageV, 2);
M5.Display.println(" V");
M5.Display.setTextColor(TFT_WHITE);
M5.Display.print("Resist: ");
M5.Display.setTextColor(TFT_CYAN);
if (measuredCurrentMA > 0.5f) {
M5.Display.print(measuredResistance, 0);
M5.Display.println(" Ohm");
} else {
M5.Display.println("--- Ohm");
}
// Barra de progresso
M5.Display.setTextColor(TFT_WHITE);
M5.Display.println("");
int barWidth = (int)((elapsed / TEST_DURATION) * 110);
if (barWidth > 110) barWidth = 110;
M5.Display.drawRect(10, 175, 110, 12, TFT_WHITE);
M5.Display.fillRect(11, 176, barWidth, 10, TFT_GREEN);
M5.Display.setTextColor(TFT_DARKGREY);
M5.Display.setCursor(10, 192);
M5.Display.println("[A] Cancelar");
}
void drawResult(State s) {
if (s == RESULT_OK) {
M5.Display.fillScreen(0x0440); // Verde escuro
M5.Display.setTextColor(TFT_GREEN);
M5.Display.setTextSize(2);
M5.Display.setCursor(15, 8);
M5.Display.println("BOBINE");
M5.Display.println(" OK!");
} else if (s == RESULT_OPEN) {
M5.Display.fillScreen(0x4000); // Vermelho escuro
M5.Display.setTextColor(TFT_RED);
M5.Display.setTextSize(2);
M5.Display.setCursor(5, 8);
M5.Display.println("QUEIMADA");
M5.Display.println("/ABERTA");
} else if (s == RESULT_SHORT) {
M5.Display.fillScreen(0x4200); // Laranja escuro
M5.Display.setTextColor(TFT_ORANGE);
M5.Display.setTextSize(2);
M5.Display.setCursor(10, 8);
M5.Display.println("CURTO-");
M5.Display.println("CIRCUITO");
}
M5.Display.setTextSize(1);
M5.Display.setTextColor(TFT_WHITE);
M5.Display.setCursor(5, 80);
M5.Display.print("Corrente: ");
M5.Display.print(measuredCurrentMA, 1);
M5.Display.println(" mA");
M5.Display.print("Tensao: ");
M5.Display.print(measuredVoltageV, 2);
M5.Display.println(" V");
M5.Display.print("Resist: ");
if (measuredCurrentMA > 0.5f) {
M5.Display.print(measuredResistance, 0);
M5.Display.println(" Ohm");
} else {
M5.Display.println("--- Ohm");
}
if (s == RESULT_OPEN) {
M5.Display.setTextColor(TFT_YELLOW);
M5.Display.println("");
M5.Display.println("Substituir bobine");
} else if (s == RESULT_SHORT) {
M5.Display.setTextColor(TFT_YELLOW);
M5.Display.println("");
M5.Display.println("Substituir bobine");
}
M5.Display.setTextColor(TFT_DARKGREY);
M5.Display.setCursor(5, 200);
M5.Display.println("[A] Novo teste");
}
void setup() {
auto cfg = M5.config();
M5.begin(cfg);
M5.Display.setRotation(0);
M5.Display.setBrightness(200);
Wire.begin(SDA_PIN, SCL_PIN);
if (!ina219.begin(&Wire)) {
M5.Display.fillScreen(TFT_RED);
M5.Display.setTextColor(TFT_WHITE);
M5.Display.setTextSize(1);
M5.Display.setCursor(5, 80);
M5.Display.println("ERRO: INA219");
M5.Display.println("nao encontrado!");
M5.Display.println("Verifica ligacoes");
while (1) delay(100);
}
// Calibração para leitura até 400mA (sobra margem para curtos)
ina219.setCalibration_16V_400mA();
drawIdle();
}
void loop() {
M5.update();
if (state == IDLE) {
if (M5.BtnA.wasPressed()) {
state = TESTING;
testStart = millis();
sumCurrent = 0;
sumVoltage = 0;
sampleCount = 0;
measuredCurrentMA = 0;
measuredVoltageV = 0;
}
}
else if (state == TESTING) {
if (M5.BtnA.wasPressed()) {
// Cancelar
state = IDLE;
drawIdle();
return;
}
unsigned long elapsed = millis() - testStart;
// Leitura contínua
float current_mA = ina219.getCurrent_mA();
float bus_V = ina219.getBusVoltage_V();
float shunt_V = ina219.getShuntVoltage_mV() / 1000.0f;
// Filtra leituras negativas (ruído)
if (current_mA < 0) current_mA = 0;
measuredCurrentMA = current_mA;
measuredVoltageV = bus_V;
// Resistência = V shunt / I (estimativa da bobine, excluindo o 100Ω série)
// V total = 3.3V, V no 100Ω = current_mA/1000 * 100, V na bobine = resto
float v_serie = (current_mA / 1000.0f) * 100.0f;
float v_bobine = 3.3f - v_serie - shunt_V;
if (current_mA > 0.5f) {
measuredResistance = (v_bobine / (current_mA / 1000.0f));
if (measuredResistance < 0) measuredResistance = 0;
} else {
measuredResistance = 9999;
}
sumCurrent += current_mA;
sumVoltage += bus_V;
sampleCount++;
drawTesting((float)elapsed);
if (elapsed >= TEST_DURATION) {
// Calcula médias
measuredCurrentMA = sumCurrent / sampleCount;
measuredVoltageV = sumVoltage / sampleCount;
float v_s = (measuredCurrentMA / 1000.0f) * 100.0f;
float v_b = 3.3f - v_s;
if (measuredCurrentMA > 0.5f) {
measuredResistance = v_b / (measuredCurrentMA / 1000.0f);
if (measuredResistance < 0) measuredResistance = 0;
} else {
measuredResistance = 9999;
}
// Diagnóstico
if (measuredCurrentMA < CURRENT_MIN_MA) {
state = RESULT_OPEN;
} else if (measuredCurrentMA > CURRENT_MAX_MA) {
state = RESULT_SHORT;
} else {
state = RESULT_OK;
}
drawResult(state);
}
delay(100);
}
else if (state == RESULT_OK || state == RESULT_OPEN || state == RESULT_SHORT) {
if (M5.BtnA.wasPressed()) {
state = IDLE;
drawIdle();
}
}
}“Deploy to device” opens this project in Schematik, where you can flash it to your board over USB.
Remix this project
Make it yours in one click
Open a full copy of this project in your own Schematik workspace — diagram, code, parts, and assembly steps included. Swap the sensor, add features, or redesign the whole thing with AI. The author's original stays untouched.