Accéder au contenu principal

Easy measure of AC Voltage using Arduino and ZMPT101B



        Hello guys, and welcome, before we start anything, I want to warn you about messing with domestic voltage, power line, wall power…. pay attention and be very careful it’s dangerous.

Watch the tutorial for further explanations.

             So the project today is how to measure AC voltage up to 250V, in both 50Hz and 60Hz, using the ZMPT101B, that’s the name of the transformer only, but you’ll find it around with this name or « AC voltage sensor ».


          This module used with the right code, is far better for measures, than the other methodes that uses transformer + rectifier + voltage divider…., these kind of things can be dangerous if not done right, and also don’t give the right values all the time because it doesn’t make any difference if the signal si square or triangular, even though it does affect seriously the RMS, and the RMS is what we want to measure, and for this we should keep the same shape of the signal when adapting it for the Arduino, and here is when the module is handy.



The module takes the signal we want to measure, here a 220V domestic power, it has around 311V as its peak.


The module transformer brings it back to 2.5V peak.



Then it adds a 2.5V offset to adapt it to the Arduino.


          And here pay attention, some codes around are used with this module, they only do a sampling of the signal and measure the peaks then a little multiplication,and it can show you the « RMS » of this signal, but those codes work only for perfect sinewave signal, if you’re measuring another shape of the signal it would be false.

Light dimmer.jpg

And for example here I used a light dimmer, which is based on a Triac and you know the « weird » shape of the signal that a Triac makes.


Here you can see, to measure the RMS you shouldn’t only measure the peaks and do the same calculations for the Sinewave signal.

But you should do it the right way and it’s very difficult to code, that’s why I found a simple library as always to do the work for us 😀

But you should know how it’s done and it’s easy to find around.


For the measures I’m using an OLED display, you can check My previous tutorial, on how to use it with different examples.





This is the whole wiring, and as mentionned I’m using an 128×32 OLED screen you can use it or no, the module is powred by 5v and delivers an analog signal.



The first thing you need to do, is calibrating the module by its potentiometer, you wire the module, plug the Arduino and upload this simple code, and don’t forget to place you measuring probes to the power socket (BE CAREFUL !!!) and you already know the voltage over.

void setup() {

void loop() 

Open the serial plotter of the Arduino IDE:


You’ll see something like this in your serial plotter, it means you don’t acquire the whole signal, adjust the potentiometer until you have something that looks like a sinewavish thing. I know that’s not the AC sinewave signal.



When you have something like this you can add a delay to see a nice sinewave, here it’s because I’m measuring right after my light dimmer you’ll see a little peak before the signal peak if you measure the socket directly you won’t have this.

void setup() {

void loop() 


Now your module is calibrated, the code calibrations will be based on it so try to not change it.


The library I’m using is Filters.h, it reduces the amount of work for you, Download here, or from Github. I don’t know if they are the same because I had it for 2 years.

Adafruit OLED display libraries


Here’s the code I’ve used: Download here



The values are sure not perfect and 100% Accurate but as you see you need very precise calibrations to make it perfect, and don’t forget you’re using a 10$ or less module, and there it’s compared to a multimeter that uses TRMS, True RMS.



     A non TRMS Multimeter will give you a false reading for a non Sinewave signal too, as you can see the cheap one (blue) compared to our project, it’s 40V difference which is not good. (NB: the other multimeter is not measuring at that moment, check the video for more).

That’s all folks, I hope you like it, be careful and if you have any probel feel free to contact me, don’t forget to support the channel by a subscribe.


Yassine Tout afficher

Electronics, Automation engineering student... I try to share my little experience with other electronics amateur.

41 commentaires sur “Easy measure of AC Voltage using Arduino and ZMPT101B Laisser un commentaire

  1. Hi,

    I really like this project

    Can you tell me, from your code , where you got the values below ?
    float intercept = -0.04; // to be adjusted based on calibration testing
    float slope = 0.0405; // to be adjusted based on calibration testing
    current_Volts= current_Volts*(40.3231); //Further calibrations for the amplitude

    I have adjusted the Trim Pot as you suggested ..and the readings look good – . what is my next step to calibrating ?
    How do I get the the readings translated to the correct voltage.
    NOTE: My AC voltage is 240 Volts so I assume the values you have above are not correct for me ?
    How do I get the values I need ?



    • Hi, did you ask me on hackster? because I had the same question toda, and I responded there.
      I’ll just copy it here:
      « Hi, to understand better you can check the « RunningStatistics.cpp » file from the library, you’ll find the mathematical functions, it takes the input which is an analog one, do some sampling then calculate the average square value and after the standard deviation…
      For the intercept and slope you can make the input as 0 and you should have 0 then take a known value and calibrate the slope
      For the « current_Volts= current_Volts*(40.3231) » actually I had this lying around for 2 years and first it was mean’t to measure « Current » so multiplied by (40.3231).
      About the multimeter, cheap multimeters measure all AC signals as sinewaves, if you want to use it to calibrate, measure AC sinewave signal from a socket or after a transformer, but since I used a Triac based light dimmer I had to use a TRMS multimeter to get the right value. »

      You can start by the values given above, as I said they are very sensitive, or you can put the Analog input to 0 and make slop=0 then adjust the intercept to get 0 as the result, then put the voltage to a known value, and the intercept to its new value and adjust the slope.
      For the last line to calibrate you can remove it, if you do this. Mein was based on an code I made before to measure current… so I had to readjust


  2. I am trying to get voltage reading on ESP32. I have used level shifting to protect damages from 5 voltage. I have connected ESP32 Pin 2 or Pin 15 with ZMPT101B via shifter. but there is always 4095 or 2189 reading even I connect or disconnect cable from module ZMPT101B OR 4 Channel Bi-Directional Logic Level Shifter.
    please advise what is wrong with this


    • Hi, sorry I didn’t tried it with an ESP32, but first the module should be powred by 5v, you should use an external source as the operating voltage of the ESP is around 3.3v I think, use the VCC and GND for power, then for the ESP32 use an Analog input and GND with the Signal and GND from the module, and make sure to use a proper level shifter, I think a voltage divider can do the work.

      When the module is powred (with 5v) it will send 2.5V (512) to the Arduino (when there’s no AC power to its input) and when you add an AC power source the voltage will be around 0-5V (0-1023) these values depend on the board ADC,for Arduino UNO it’s from 0-1023 10 bits). For the ESP32 I think it has a 12bits one so the values will be (0-4096) for (0-3.3V)

      The module could (I’m not sure) work with 3.3V, You can try to power it using 3.3V and GND from the ESP32, Measure with a multimeter the value should be around 1.6V, if this works it means you can wire it directly as the max voltage can never be higher than 3.3V


  3. Thank you for reply, Please advise further for module working as I am fear that module is faulty. How I check it as I do not have Arduino UNO with me. I have powered ZMPT101B module with 5v. and passed out via proper level shifter and then input it into pin15(analogread) of ESP32. but there is always 4096 value on it. voltage is almost 2.6v to 3.3 v on pin15. but there is no change in this value even I connect or disconnect AC power. when I pull out cable from shifter-To-ESP32(pin15), there is zero value on it.
    i was stuck in it.


    • First, please make sure everything is working fine:
      1) Power the ZMPT101B with (5V/GND), do not add AC voltage, measure the (Signal/Gnd) pins with a multimeter (DC Voltage), the voltage should be around 2.5V, because 2.5V is the offset.
      ==> if this works it means your module is working well

      2) Try this but this time use (3.3V/GND), no AC voltage -> measure -> it should be half 3.3 which is 1.65V.
      and can work with 3.3V and you can plug it directly to
      ==> if this works you can power the module directly from the ESP32 and use it without a level shifter

      3) If the (2) doesn’t work and (1) works well, try to use the level shifter, and make sure it’s working well, wire everything and check some test codes.

      The 4096 is the higher value of the Analog to Digital Converter and for it it means 3.3V, make sure it’s not higher (because it will keep the same value).

      The best way to test is only analogRead(signalpin) and use the Serial Plotter of the Arduino IDE, as I did


  4. 1. at 5v, measured the (Signal/Gnd) pins with a multimeter (DC Voltage) reading 2.53v
    2. at 3.3v, this time use (3.3V/GND), no AC voltage -> measured reading 1.65v
    1.2 both does not work, conenction and ground are ok. voltage level remain same(i.e 2.53 for 5v, 1.65v for 3.3v when AC voltage is connected. there is no change in behaviour of ZMPT101B voltage levels. I am not sure but it does not work as signal-out value remain same.


  5. So for (3.3V/Gnd) no AC -> 1.65 it’s good it means there’s the offset and the module is working.

    Do not use the level shifter, just the module directly with ESP32, POWERED with 3.3V/GND from the ESP and use an analog input.

    Test first with only this line « Serial.println(analogRead(A0)); » add the necessary things like void setup, serial begin…., upload the code to the board and open the serial plotter in Arduino IDE, you’ll see values around 1.65V (2046) maybe.

    Add now an AC voltage and check the shape of the signal, turn the potentiometer unitl you have a shape like I found, you can add a delay after the serial print if you want.


  6. thank you for your conitnous support, I have applied 220v AC , but there is no change in value of Serial.println(analogRead(15)). it always display 4095. even I remove or apply AC voltage. shape signal is second thing, but first there should be change in analog value 1.65v and 3.3v . but it is same


    • No problem, do you use « Serial plotter » to see the shape of the signal?
      Also don’t forget that the module delivers a sinewave signal not a continuous one, if you just use the serial monitor to see the values it will show strange different values, and it would be difficult to measure them when AC is on.
      The better way as I said is to use the serial plotter !!!


  7. you are right , I used Serial Monitor and view plotter, code run perfectly with Ardnino Un R3 , but this code does not work with ESP32. but serial monitor help lot to tune ware form but is entirely different in upper corner and it seem to be remain straight , I have try my best to make it pure but upper corners remain same.
    i have used other site code which is same for arduino and ESP32. but with esp32 it does not reflect AC values in workable reading. it show abrupt changes in reading which is not good.
    i do not see people working on ESP32 much on internet


    • You can add a delay like i did of 100ms or swipe through some values… Also try to use a different baude rate maybe, and you should use the potentiometer of the module to set its amplification otherwise the signle will not be readable… But I can’t give you concrete things as I didn’t use it with an ESP32


    • Ashraf, if it’s still actual project for you, I’ll reply.
      I’m doing exact the same thing and trying to make ZMPT101B work with ESP32. Haven’t calibrated it yet, but I can surely answer, why you’re getting 4095 ADC reading all the time. It’s because reference voltage (Vref) for ADC in ESP32 is 1.1 V. So your 1.65 V translate into 4095, because it’s out of range. You should use ADC channel attenuation. which can be 2.5, 6 or 11 dB. 11 dB reduces input signal to about 1/3.6, so 3.3/3.6 < 1.1 V and it should show you AC sine on graphs. Unfortunately I don't use Arduino IDE, I use ESP-IDF, so as for Arduino code, I can't tell you anything


    • You need 2 other variables for all:
      Sensor=2; for 3 inputs

      3 different currents volts,
      3 of these part:
      RunningStatistics inputStats;
      inputStats.setWindowSecs( windowLength );
      Sensor = analogRead(A0);

      each one has its name, and 3 of this part as well:

      current_Volts = intercept + slope * inputStats.sigma(); //Calibartions for offset and amplitude
      current_Volts= current_Volts*(40.3231);

      if you understand one you’ll be able to do the others


  8. Thank you Sir,
    it works

    /* This code works with ZMPT101B AC voltage sensor module
    * It permits you to measure any AC voltage up to 250V, BE CAREFUL !!!
    * The functions from Filters library permits you to calculate the True RMS of a signal
    * Refer to or SurtrTech YouTube channel for more details

    #include //Easy library to do the calculations

    float testFrequency = 50; // test signal frequency (Hz)
    float windowLength = 40.0/testFrequency; // how long to average the signal, for statistist

    int SensorR = 0; //Sensor analog input, here it's A0
    int SensorS = 1; //Sensor analog input, here it's A0
    int SensorT = 2; //Sensor analog input, here it's A0

    float intercept = -0.04; // to be adjusted based on calibration testing
    float slope = 0.0405; // to be adjusted based on calibration testing
    //float slope = 0.043; // to be adjusted based on calibration testing

    //float current_Volts; // Voltage
    float current_VoltsR; // Voltage
    float current_VoltsS; // Voltage
    float current_VoltsT; // Voltage

    unsigned long printPeriod = 1000; //Refresh rate
    unsigned long previousMillis = 0;

    void setup() {
    Serial.begin( 9600 ); // start the serial port

    void loop() {

    RunningStatistics inputStatsR; //Easy life lines, actual calculation of the RMS requires a load of coding
    inputStatsR.setWindowSecs( windowLength );

    RunningStatistics inputStatsS; //Easy life lines, actual calculation of the RMS requires a load of coding
    inputStatsS.setWindowSecs( windowLength );

    RunningStatistics inputStatsT; //Easy life lines, actual calculation of the RMS requires a load of coding
    inputStatsT.setWindowSecs( windowLength );

    // inputStats.input(Sensor);

    while( true ) {
    SensorR = analogRead(A0); // read the analog in value:
    SensorS = analogRead(A1); // read the analog in value:
    SensorT = analogRead(A2); // read the analog in value:
    inputStatsR.input(SensorR); // log to Stats function
    inputStatsS.input(SensorS); // log to Stats function
    inputStatsT.input(SensorT); // log to Stats function

    if((unsigned long)(millis() - previousMillis) >= printPeriod) {
    previousMillis = millis(); // update time every second

    Serial.print( "\n" );

    current_VoltsR = intercept + slope * inputStatsR.sigma(); //Calibartions for offset and amplitude
    current_VoltsR = current_VoltsR*(40.3231); //Further calibrations for the amplitude
    current_VoltsS = intercept + slope * inputStatsS.sigma(); //Calibartions for offset and amplitude
    current_VoltsS = current_VoltsS*(40.3231); //Further calibrations for the amplitude
    current_VoltsT = intercept + slope * inputStatsT.sigma(); //Calibartions for offset and amplitude
    current_VoltsT = current_VoltsT*(40.3231); //Further calibrations for the amplitude

    //current_VoltsR= current_VoltsR*(42); //Further calibrations for the amplitude

    Serial.print( "\tVoltage (R): " );
    Serial.print( current_VoltsR ); //Calculation and Value display is done the rest is if you're using an OLED display

    Serial.print( "\tVoltage (S): " );
    Serial.print( current_VoltsS ); //Calculation and Value display is done the rest is if you're using an OLED display

    Serial.print( "\tVoltage (T): " );
    Serial.print( current_VoltsT ); //Calculation and Value display is done the rest is if you're using an OLED display





  9. Hello Yassine,
    I’m very happy with your project, but I still experience some difficulties calibrating the readings.
    If I understood you correctly, First I set the Slope=0, put no voltage to the module, and adjust intercept, so the value given in the serial monitor to be 0. After that i supply with known voltage and adjust Slope while I reach the known voltage.
    The problem is that when finished calibrating and supply a different voltage it gives back wrong readings.
    Am I doing something wrong?


    • The calibration of the slope and intercept can be pretty tricky, it’s best to start from the given values then modify them a little bit, otherwise put the AC voltage to 0, remove the slope and intercept completly,

      current_Volts = intercept + slope * inputStats.sigma(); —> «  »current_Volts = inputStats.sigma(); » »

      First add the « intercept » until you get « current_volts=0 » which means:

      «  »current_Volts = inputStats.sigma() + intercept; » »

      then put the AC voltage on a know value, then set the slope until you get the right value

      «  »current_Volts = slope*inputStats.sigma() + intercept; » »


  10. Great video tutorial with written explanation. Questions: 1- I’m using an ESP32 microcontroller with Home Assistant (ESPHome). Are you aware of anyone who has used the ZMPT101B module with Home Assistant? 2-Any Suggestions about incorporating the 2 libraries into Home assistant (Adafruit SSD1306 library, Adafruit GFX library)?

    Thanks again for your tutorial.


  11. Hello Yassine,
    I am looking to use ZMPT101B only to monitor if our street power is ON, and therefore do not need to show graphics of sine wave. The application is simply to monitor 1-phase 120v or 2-phase 240 volts coming into house.
    Nassau, Bahamas is having terrible power issues this summer, where BPL shuts off parts of the city for 3-4 hours, rotating limited power to homes.
    When BPL switches power back on, many homes get immediately hit with power “surges” that burn out electrics, fridges, radios, TV, etc.
    I noticed that homes with private generators avoid these surges, as their switching system on generator waits 30-60 seconds after the power comes back on, before switching back over to BPL’s grid.
    As we don’t have a generator, I’m developing a “latching relay” (Panasonic 50A and 90A relays) system that can wait 30-60 seconds before allowing power into our home.
    Therefore, I need an Arduino ( program that can monitor BPL’s grid, switching OFF my home’s power internally, when power is lost (will use UPS to power Arduino), then watching (with ZMPT) until BLP’s power comes back on — to then switch the entire house back ON again to BPL’s grid.
    Any thoughts of how to do this with ZMPT ?


    • Hello yes you can add the ZMPT101B Right after your main breaker or before the device you want to protect, just don’t forget that it’s max voltage is 250V, and you can use a simple code for examlpe to measure peak to peak, but if your goal is just to know whether there’s voltage or not, you can use an optocoupler but always check the Max rating…
      For the latching relay you can just buy one too, for the 60s delay use an Arduino board if you want

      Aimé par 1 personne

      • Thanks for your advice. I’m testing on a Argon board, as this will send out messages via the API, so I can get alerts when power is cut. Particle can also send short « heartbeat » messages, with data on voltage, stats, etc. What I like most is you can send a command back to the Particle board from an app on your phone, web site, etc. This could be used to shut off house circuits, or do a manual switch-on via your phone…
        We’ll have L1+N for lights, appliances and small devices, plus L1+L2+N for a cistern pump and A/C compressor. The 240V is quite important, even though I don’t expect a pump to suffer from a power surge, as compared to small electronics, TV, etc.
        I found this GitHub:
        If I get this working, I’ll post a .ino file back to you.


  12. Nevermind, I hit the problem.
    ESP-12 (and ESP8266) has the watchdog(WDT) turned on by default. If the watchdog timer isn’t periodically reset then it will automatically reset your ESP8266. The watchdog is reset every time loop() runs or you call delay() or yield() but if you have blocking code like the above ( when you check milliseconds) then the watchdog may time out. I have just replaced the miliseconds check with a delay(1000).



  13. hi, I’m about to do the project that using ZMPT101b,, but, first of all I need to draw the sketch in fritzing apps.
    I was looking for the ZMPT101b parts library in (.fzpz) format on internet, but haven’t find any result.
    so, could you share that ZMPT101b parts library in .fzpz format please?


  14. Hi, i tested it exactly as your code and it works pefectly, but now i want to run it inside a bigger project and i get a lot of noise (same hardware, more complex arduino code). i suspect is because i do other work in the main arduino loop besides calling this functions.

    i added a « delay(3) » inside the loop to simulate other work and got the same problem.

    Do you have an idea of what could it be?


    • Did you remove that « while(true) »? Because you should remove it if you’re using the code in another project.
      And of course it would be affected if you’re running a big code beside it, and those are the limits of the Arduino unfortunately, all I can do is to recommend you to measure the voltage for few seconds (Try and find what’s suits you)… then execute the other code



Entrez vos coordonnées ci-dessous ou cliquez sur une icône pour vous connecter:


Vous commentez à l'aide de votre compte Déconnexion /  Changer )

Photo Google

Vous commentez à l'aide de votre compte Google. Déconnexion /  Changer )

Image Twitter

Vous commentez à l'aide de votre compte Twitter. Déconnexion /  Changer )

Photo Facebook

Vous commentez à l'aide de votre compte Facebook. Déconnexion /  Changer )

Connexion à %s

%d blogueurs aiment cette page :