Community project

AI Transformer Health Monitoring

ESP32
Photo of AI Transformer Health Monitoring

0jeetsarkar0

Published October 30, 2026

This project builds a comprehensive health monitoring system for a 12 VAC transformer using an ESP32 microcontroller. The system measures AC voltage and current with isolated sensors, monitors winding temperature with a DS18B20 probe, and displays real-time data on an I2C LCD screen. A relay-controlled test load loop with fused protection allows safe demonstration of the transformer's performance under load.

The guide provides a complete wiring diagram, parts list, and step-by-step assembly instructions for connecting the voltage and current measurement circuits, temperature sensor, relay control, and status indicators through an I2C level shifter. Included firmware implements calibration routines, data acquisition with statistical analysis, and automatic protection logic that triggers an alarm and relay when fault conditions are detected, making this an ideal educational project for understanding transformer diagnostics and AC measurement techniques.

Wiring diagram

Loading diagram…

Gather all the parts

QtyComponent
1

Enclosed certified isolated 12 VAC 2 A transformer adapter

Factory-enclosed mains transformer; only its isolated output is accessible. Never open it.

1

1.5 A inline fuse and holder

1.5 A inline fuse and holder; connect as shown in wiring.

1

12 ohm 25 W wirewound test resistor

Dissipates about 12 W at 12 VAC; mount on a heat-resistant surface.

1

ZMPT101B 5 V AC voltage module

Input across isolated 12 VAC only; adjustable gain requires calibration.

1

ACS712 Current Sensor

Hall-effect AC/DC current sensor breakout. It outputs an analog voltage centered around VCC/2 at zero current; sensitivity depends on the 5 A, 20 A, or 30 A variant. Most modules run from 5 V and can output up to 5 V, so connect VIOUT to a 3.3 V MCU ADC through a divider or clamp.

1

DS18B20

Digital temperature sensor using OneWire protocol

1

LCD 16x2 I2C

16x2 character LCD display with I2C backpack

1

BSS138 two-channel I2C level shifter module with pullups

BSS138 two-channel I2C level shifter module with pullups; connect as shown in wiring.

1

3.3 V single relay module selectable HIGH trigger

Set jumper to H. Built-in driver and flyback suppression. NC unused.

1

Piezo Buzzer

Small passive piezo buzzer (TDK PS1240) for beeps, tones and alerts. It has no built-in oscillator, so drive one lead with a 3-30 V square wave from a microcontroller pin using tone() or PWM and connect the other lead to ground. It is loudest around 4 kHz and works from about 2 to 10 kHz; driving both leads in antiphase roughly doubles the volume.

1

10 kohm 1% resistor

10 kohm 1% resistor; connect as shown in wiring.

1

15 kohm 1% resistor

15 kohm 1% resistor; connect as shown in wiring.

1

10 kohm 1% resistor

10 kohm 1% resistor; connect as shown in wiring.

1

15 kohm 1% resistor

15 kohm 1% resistor; connect as shown in wiring.

1

4.7 kohm resistor

4.7 kohm resistor; connect as shown in wiring.

1

10 kohm resistor

10 kohm resistor; connect as shown in wiring.

1

330 ohm resistor

330 ohm resistor; connect as shown in wiring.

1

330 ohm resistor

330 ohm resistor; connect as shown in wiring.

1

330 ohm resistor

330 ohm resistor; connect as shown in wiring.

1

LED

Standard 3mm/5mm through-hole LED. A current-limiting series resistor is added automatically.

1

LED

Standard 3mm/5mm through-hole LED. A current-limiting series resistor is added automatically.

1

LED

Standard 3mm/5mm through-hole LED. A current-limiting series resistor is added automatically.

Assemble it in 8 steps

1. Use only an enclosed isolated transformer

Use a factory-enclosed certified 12 VAC 2 A transformer adapter. Only its two low-voltage output wires are part of this circuit. Power the ESP32 separately by USB. Do not connect either AC output to ESP32 ground.

  • Never connect this circuit to exposed mains or a utility transformer. It is an educational prototype
  • not certified protection.

2. Build the fused test load loop

With all power unplugged, connect AC1 to fuse terminal 1; fuse 2 to ACS712 IP+; IP- to relay COM; relay NO to load terminal 1; load 2 to AC2. Use the ACS712 5 A variant. Set the 3.3 V relay module trigger jumper to H. Leave its NC contact unused. Use screw terminals for this loop, not a solderless breadboard.

  • The 12 ohm 25 W load dissipates approximately 12 W and gets very hot. Mount it away from wires and people on a heat-resistant surface.

3. Connect the voltage and current measurements

Connect ZMPT101B L to fuse 2 and N to AC2. Power both sensor modules from USB 5V and GND. ZMPT OUT goes through rvtop 10 kohm to GPIO32, with rvbottom 15 kohm from GPIO32 to GND. ACS712 VIOUT goes through ritop 10 kohm to GPIO33, with ribottom 15 kohm from GPIO33 to GND. Both dividers reduce a 5 V signal to 3 V maximum. Leave the optional ACS712 FILTER pin unchanged on its breakout.

4. Connect temperature and the relay control

Connect DS18B20 VCC to 3V3, GND to GND, DATA to GPIO4, and the 4.7 kohm pullup between DATA and 3V3. Attach the insulated sensor against the outside of the transformer enclosure, never inside it. This measures case temperature, not winding or oil temperature. Connect relay DC+ to 3V3, DC- to GND and IN to GPIO23. Add the 10 kohm relaydown resistor between IN and GND so the relay stays off during reset. Use a relay module with its own driver and flyback suppression, with coil current within the board regulator's spare capacity.

5. Connect the LCD through the level shifter

Use a standard HD44780 16x2 LCD with PCF8574 I2C backpack at address 0x27. Connect LCD VCC to 5V and GND to GND. Connect level shifter LV to 3V3, HV to 5V, GND to GND, LV1 to GPIO21 and LV2 to GPIO22. Connect HV1 to LCD SDA and HV2 to LCD SCL. Use a BSS138 module with built-in pullups. Adjust the LCD contrast knob if text is invisible.

6. Connect the three lights and alarm

GPIO16 goes through rg 330 ohm to the green LED long leg; GPIO17 through ry 330 ohm to yellow; GPIO18 through rr 330 ohm to red. Each short leg goes to GND. Connect passive piezo SIG to GPIO19 and GND to GND. Green means learned normal, yellow means idle, learning or anomaly, and red means sensor error or a latched trip.

7. Check power and calibrate safely

Check all wiring with power disconnected. Use USB 5 V power capable of 1 A for the ESP32 and modules; do not backfeed a second 5 V supply. Check the board regulator can supply the 3.3 V relay coil. Keep the AC load circuit electrically separate from DC GND. Before enabling monitoring, calibrate against a true-RMS meter following README.md. A software trip only opens the demonstration load, not the transformer primary.

  • The fuse is required because software and relay contacts can fail. Never deliberately short or overheat the transformer to test this project.

8. Calibrate and operate the demonstration

## Calibration and operation 1. With power off, check every connection. Set relay trigger to H. The normally-open load contact must be open at boot. Secure the hot load on a heat-resistant surface. 2. Deploy firmware. Open Schematik's serial console at 115200 baud, using newline-terminated commands. The load remains OFF at boot, including after a power failure. 3. Power the enclosed transformer. Check the probe is attached to its outside case. Give the temperature sensor two seconds to settle. 4. Measure AC output with a true-RMS meter in voltage mode. Adjust the ZMPT gain so serial `rawVrms` is well above 5 mV, typically 100–500 mV, without SENSOR/clipping alarms. Keep gain low enough to avoid waveform flattening; inspect with an oscilloscope if available. A typical mains-range ZMPT module may need substantial gain at only 12 VAC. 5. Send `v 12.00`, replacing 12.00 with the actual meter voltage. Calibration is stored in ESP32 nonvolatile memory. 6. To calibrate current, disconnect power before inserting a correctly fused AC ammeter **in series** with the load. Never put an ammeter across the transformer. Restore power and send `test`. This enables the known-safe 12 ohm load for at most 60 seconds, with temperature/clipping checks but without calibrated voltage/current protection. Send `i 1.00`, replacing 1.00 with measured AC amps (allowed 0.3–1.3 A). Send `off` when done. Disconnect power before removing the meter and restoring wiring. 7. With calibration completed and the test load connected, send `arm`. Voltage must be 10–14 VAC and case temperature below 55 C. The relay closes and a fresh 60-second baseline is learned. Learn only under known healthy, steady conditions. 8. Send `off` to open the relay. A hard trip is latched until reset; first remove the cause. Calibration survives reset, but arming and learning do not. ## What the program does - Once per second: samples both AC sensor signals over 200 ms; removes their DC offsets and computes RMS. Measures case temperature asynchronously. - Cutoff limits: below 10 VAC, above 14 VAC, above 1.3 A, or at/above 60 C. Invalid temperature or ADC clipping also trips. These thresholds apply to this 12 VAC test circuit, not a full-size transformer. - Software response is approximately one second, not fast short-circuit protection. The physical fuse is essential. Sensor disconnection is not comprehensively detectable; do not rely on this prototype for protection. - The AI demonstration is a tiny on-device statistical anomaly detector: learns mean and variance for voltage/current/temperature over 60 healthy readings, then warns beyond four standard deviations, with minimum noise bands. It does not predict remaining life or diagnose winding faults and has no externally trained model. An anomaly warns; fixed safety limits disconnect. - Green: learned normal; yellow: OFF, TEST, LEARN or anomaly; red: sensor fault or latched trip. Alarm sounds for a sensor fault, anomaly or trip. LCD shows values and status, updating only changed rows. Serial logs provide all readings. - Case temperature is not internal winding temperature. ESP32 ADC noise limits small-current accuracy; verify with a meter. This does not measure power factor, real power, harmonics, oil condition or insulation health. Next: wire with both supplies disconnected, inspect it, plug in USB, press Deploy, then calibrate. Do not test by shorting, exposing mains or overheating the transformer.

Review all connections

1. Connections between "transformer" and "ESP32"

FunctiontransformerESP32
powerAC1 → 1.5 A inline fuse and holder 1EXT

2. Connections between "fuse" and "ESP32"

FunctionfuseESP32
data2 → ACS712 Current Sensor IP+EXT

3. Connections between "current" and "ESP32"

FunctioncurrentESP32
dataIP- → 3.3 V single relay module selectable HIGH trigger COMEXT
powerVCC5V
groundGNDGND
analogVIOUT → 10 kohm 1% resistor 1EXT

4. Connections between "relay" and "ESP32"

FunctionrelayESP32
dataNO → 12 ohm 25 W wirewound test resistor 1EXT
powerDC+3V3
groundDC-GND
digitalINGPIO 23

5. Connections between "load" and "ESP32"

FunctionloadESP32
data2 → Enclosed certified isolated 12 VAC 2 A transformer adapter AC2EXT

6. Connections between "voltage" and "ESP32"

FunctionvoltageESP32
dataL → 1.5 A inline fuse and holder 2EXT
dataN → Enclosed certified isolated 12 VAC 2 A transformer adapter AC2EXT
powerVCC5V
groundGNDGND
analogOUT → 10 kohm 1% resistor 1EXT

7. Connections between "lcd" and "ESP32"

FunctionlcdESP32
powerVCC5V
groundGNDGND

8. Connections between "rvtop" and "ESP32"

FunctionrvtopESP32
analog2 → 15 kohm 1% resistor 1EXT

9. Connections between "rvbottom" and "ESP32"

FunctionrvbottomESP32
analog1GPIO 32
ground2GND

10. Connections between "ritop" and "ESP32"

FunctionritopESP32
analog2 → 15 kohm 1% resistor 1EXT

11. Connections between "ribottom" and "ESP32"

FunctionribottomESP32
analog1GPIO 33
ground2GND

12. Connections between "temperature" and "ESP32"

FunctiontemperatureESP32
powerVCC3V3
groundGNDGND
dataDATAGPIO 4

13. Connections between "pullup" and "ESP32"

FunctionpullupESP32
power13V3
data2 → DS18B20 DATAEXT

14. Connections between "shift" and "ESP32"

FunctionshiftESP32
powerLV3V3
powerHV5V
groundGNDGND
i2cLV1GPIO 21
i2cLV2GPIO 22
i2cHV1 → LCD 16x2 I2C SDAEXT
i2cHV2 → LCD 16x2 I2C SCLEXT

15. Connections between "relaydown" and "ESP32"

FunctionrelaydownESP32
data1 → 3.3 V single relay module selectable HIGH trigger INEXT
ground2GND

16. Connections between "rg" and "ESP32"

FunctionrgESP32
data1GPIO 16
data2 → LED ANODEEXT

17. Connections between "green" and "ESP32"

FunctiongreenESP32
groundGNDGND

18. Connections between "ry" and "ESP32"

FunctionryESP32
data1GPIO 17
data2 → LED ANODEEXT

19. Connections between "yellow" and "ESP32"

FunctionyellowESP32
groundGNDGND

20. Connections between "rr" and "ESP32"

FunctionrrESP32
data1GPIO 18
data2 → LED ANODEEXT

21. Connections between "red" and "ESP32"

FunctionredESP32
groundGNDGND

22. Connections between "buzzer" and "ESP32"

FunctionbuzzerESP32
pwmSIGGPIO 19
groundGNDGND

Deploy the firmware

#include <Arduino.h>
#include <Wire.h>
#include <OneWire.h>
#include <DallasTemperature.h>
#include <LiquidCrystal_I2C.h>
#include <Preferences.h>
#include <math.h>
OneWire bus(4);
DallasTemperature temp(&bus);
LiquidCrystal_I2C lcd(0x27,16,2);
Preferences prefs;
const int VP=32, IP=33, RELAY=23, GREEN=16, YELLOW=17, RED=18, BUZZ=19;
float vFactor=0, iFactor=0, rawV=0, rawI=0, volts=0, amps=0, temperature=NAN;
bool armed=false, tripped=false, testing=false, clipped=false;
uint32_t testStart=0, tick=0;
int samples=0;
float mean[3]={}, m2[3]={};
String command;
char previous[2][17]={{0},{0}};
void line(int row,const char *text){
 char padded[17]; memset(padded,' ',16); padded[16]=0;
 memcpy(padded,text,min((size_t)16,strlen(text)));
 if(strcmp(previous[row],padded)){lcd.setCursor(0,row);lcd.print(padded);strcpy(previous[row],padded);}
}
void acquire(){
 double sv=0,si=0,vv=0,ii=0; unsigned n=0; clipped=false;
 uint32_t start=micros();
 do {
  float v=analogReadMilliVolts(VP),i=analogReadMilliVolts(IP);
  if(v<100 || v>3000 || i<100 || i>3000) clipped=true;
  sv+=v;si+=i;vv+=v*v;ii+=i*i;n++; delayMicroseconds(180);
 }while((uint32_t)(micros()-start)<200000); // ten 50 Hz or twelve 60 Hz cycles
 rawV=sqrt(fmax(0.0,vv/n-(sv/n)*(sv/n)));
 rawI=sqrt(fmax(0.0,ii/n-(si/n)*(si/n)));
 volts=rawV*vFactor;amps=rawI*iFactor;
}
void trip(){tripped=true;armed=false;testing=false;digitalWrite(RELAY,LOW);}
void process(String s){
 s.trim();
 if(s=="test" && !tripped && !clipped && isfinite(temperature) && temperature<55){
  testing=true;armed=false;testStart=millis();digitalWrite(RELAY,HIGH);
  Serial.println("TEST load on for 60 seconds maximum. Use known-safe test load only.");
 }else if(s.startsWith("v ")){
  float ref=s.substring(2).toFloat();
  if(ref>5 && ref<18 && rawV>5 && !clipped){vFactor=ref/rawV;prefs.putFloat("vf",vFactor);Serial.println("Voltage calibrated");}
 }else if(s.startsWith("i ")){
  float ref=s.substring(2).toFloat();
  if(ref>0.3 && ref<1.3 && rawI>5 && testing && !clipped){iFactor=ref/rawI;prefs.putFloat("if",iFactor);Serial.println("Current calibrated");}
 }else if(s=="arm" && vFactor>0 && iFactor>0 && !tripped && !clipped && isfinite(temperature) && temperature<55 && volts>=10 && volts<=14){
  testing=false;armed=true;samples=0;memset(mean,0,sizeof(mean));memset(m2,0,sizeof(m2));digitalWrite(RELAY,HIGH);
  Serial.println("Armed: learning 60 normal readings. Reset or off opens load.");
 }else if(s=="off"){armed=false;testing=false;digitalWrite(RELAY,LOW);}
 else Serial.println("Commands: test, v <meter VAC>, i <meter AAC>, arm, off. Trip is latched: remove fault then reset board.");
}
void setup(){
 digitalWrite(RELAY,LOW);pinMode(RELAY,OUTPUT);
 for(int p:{GREEN,YELLOW,RED})pinMode(p,OUTPUT);
 pinMode(VP,INPUT);pinMode(IP,INPUT);analogReadResolution(12);analogSetAttenuation(ADC_11db);
 Serial.begin(115200);Wire.begin(21,22);lcd.init();lcd.backlight();
 temp.begin();temp.setWaitForConversion(false);temp.requestTemperatures();
 prefs.begin("health",false);vFactor=prefs.getFloat("vf",0);iFactor=prefs.getFloat("if",0);
 ledcAttach(BUZZ,4000,8);ledcWrite(BUZZ,0);
 line(0,"Transformer demo");line(1,"Load OFF");
 Serial.println("Isolated 12VAC demo only. Calibrate before arm. Commands: test, v 12.0, i 1.0, arm, off");
 tick=millis();
}
void loop(){
 while(Serial.available()){
  char c=Serial.read();if(c=='\n'){process(command);command="";}else if(c!='\r' && command.length()<60)command+=c;
 }
 if(testing && millis()-testStart>=60000){testing=false;digitalWrite(RELAY,LOW);}
 if(millis()-tick<1000)return;tick=millis();
 temperature=temp.getTempCByIndex(0);temp.requestTemperatures();
 if(temperature<=-126 || temperature>125)temperature=NAN;
 acquire();
 bool sensorBad=!isfinite(temperature)||clipped;
 if((armed||testing) && (sensorBad || temperature>=60))trip();
 if(armed && (volts<10 || volts>14 || amps>1.3))trip();
 bool anomaly=false;
 if(armed){
  float x[3]={volts,amps,temperature};
  if(samples<60){
   samples++;
   for(int j=0;j<3;j++){float d=x[j]-mean[j];mean[j]+=d/samples;m2[j]+=d*(x[j]-mean[j]);}
  }else{
   const float floorSigma[3]={0.20f,0.07f,1.5f};
   for(int j=0;j<3;j++)if(fabs(x[j]-mean[j])>4*fmax(floorSigma[j],sqrt(m2[j]/59)))anomaly=true;
  }
 }
 const char *state=tripped?"TRIP":sensorBad?"SENSOR":testing?"TEST":!armed?"OFF":samples<60?"LEARN":anomaly?"WARN":"OK";
 digitalWrite(GREEN,armed&&samples>=60&&!anomaly);digitalWrite(YELLOW,!tripped&&(!armed||samples<60||anomaly));digitalWrite(RED,tripped||sensorBad);
 ledcWrite(BUZZ,(tripped||anomaly||sensorBad)?128:0);
 char text[48];
 if(vFactor>0 && iFactor>0)snprintf(text,sizeof(text),"%4.1fV %4.2fA",volts,amps);
 else snprintf(text,sizeof(text),"CAL V%3.0f I%3.0f",rawV,rawI);
 line(0,text);snprintf(text,sizeof(text),"%4.1fC %-6s",temperature,state);line(1,text);
 Serial.printf("V=%.2f I=%.3f T=%.2f rawVrms=%.2fmV rawIrms=%.2fmV state=%s learn=%d/60\n",volts,amps,temperature,rawV,rawI,state,samples);
}

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.

Open in Schematik