Source: https://mokxi.com/learn/arduino-pzem-004t-zmpt101b

Learn

# Measure AC voltage and energy with an Arduino: the PZEM-004T and the ZMPT101B

Intermediate

Written by the Mokxi team

Start building free Open the editor

Or see every lesson.

- 455 parts on the bench
- 43 boards running now
- 1.00x real time, on every board

Click to open it in the editor

Press Run. The Uno reads the meter every two seconds: 230.00 V, 4.35 A, 1000.50 W, and the kWh counting up. Drag the voltage, current, power factor and frequency sliders on the meter.

Safety first. Both modules on this page are made to be wired to household mains, and mains can kill you or start a fire. Only a qualified person should connect them, inside a closed enclosure. Here in the browser the mains is never wired at all: it is a set of sliders on each module, so you can learn the code with nothing dangerous anywhere near you.

The PZEM-004T v3.0 is a whole energy meter on a board. It measures the mains itself and hands the Arduino finished numbers over a serial line: voltage, current, power, energy in kWh, frequency and power factor. The ZMPT101B is much simpler. It is a small transformer and an op-amp that turn the mains into a little sine wave on an analog pin, and the Arduino does the math.

The circuit above is an Uno with a PZEM-004T on pins 2 and 3. The meter is set to a 1 kW heater on 230 V. Press Run and it prints a block of readings every two seconds. Drag the sliders on the meter and the next block follows.

## What you need

- An Arduino Uno
- A PZEM-004T v3.0 (the 100 A one comes with a current transformer ring)
- Or a ZMPT101B AC voltage sensor module
- Jumper wires
- For real mains: a qualified person and a closed enclosure

## Wiring

Part and pin | Goes to | Note

PZEM 5V and GND | 5 V and GND | The TTL side only; the mains side is on the screw terminals

PZEM TX | Pin 2 | Into the Uno: SoftwareSerial’s receive pin

PZEM RX | Pin 3 | Out of the Uno: SoftwareSerial’s transmit pin

ZMPT101B VCC and GND | 5 V and GND | On 3.3 V its op-amp has almost no room for the wave

ZMPT101B OUT | A0 | The scaled-down mains sine, centered at 2.5 V

## The PZEM-004T: a meter that talks Modbus

The PZEM-004T has its own metering chip on the mains side and an optocoupled serial port on the other, so the Arduino never touches the mains. The serial port runs at 9600 baud and speaks Modbus-RTU: the Arduino sends a short request frame, and the meter answers with the contents of its registers.

Every reading is a register. Voltage is register 0 in tenths of a volt, so 230.0 V is 2300. Current takes two registers in thousandths of an amp, power two in tenths of a watt, and energy two in whole watt-hours. Then frequency in tenths of a hertz, power factor in hundredths, and an alarm flag.

The library asks for all ten at once. It sends F8 04 00 00 00 0A 64 64: the general address 0xF8 that every meter answers, function 04 (read input registers), start at register 0, read 10, then a two-byte CRC. The meter answers with 25 bytes, and PZEM004Tv30 turns them into floats.

The PZEM-004T v3.0 input registers, from its manual
Register | What it is | Unit | The 1 kW heater

0 | Voltage | 0.1 V | 2300 = 230.0 V

1 and 2 | Current, low and high word | 0.001 A | 4350 = 4.350 A

3 and 4 | Power, low and high word | 0.1 W | 10005 = 1000.5 W

5 and 6 | Energy, low and high word | 1 Wh | counting up

7 | Frequency | 0.1 Hz | 500 = 50.0 Hz

8 | Power factor | 0.01 | 100 = 1.00

9 | Alarm | 0xFFFF or 0 | 0

## The energy monitor sketch

The Uno’s one hardware serial port is busy with the serial monitor, so the meter goes on a SoftwareSerial port on pins 2 and 3. PZEM004Tv30 pzem(pzemSerial) opens it at 9600 baud. Then each of voltage(), current(), power(), energy(), frequency() and pf() returns one reading, or NAN if the meter did not answer.

The library reads the meter at most every 200 ms and hands back the same numbers in between, so the six calls in a row are one Modbus read. It also skips a read in the first 200 ms after reset and returns 0, which is why the sketch waits 250 ms in setup().

The power is volts times amps times the power factor: 230 x 4.35 x 1.00 is 1000.5 W. The energy is that power added up over time, and a 1 kW heater uses 1 Wh every 3.6 seconds, so after 36 seconds the monitor prints "Energy: 0.010 kWh". energy() is in kWh, not Wh.

The PZEM-004T energy monitor

## What the readings do when you drag the sliders

Set the meter to 240 V and 8.5 A, a 2 kW kettle, and the next block reads "Voltage: 240.00 V", "Current: 8.50 A" and "Power: 2040.00 W". Drop the power factor to 0.50 and the power halves, because a motor or a power supply with a poor power factor draws current that does no work.

Drag the voltage to 0 and the sketch prints "Error reading the meter: is it on the mains?" with "Meter address: 0x0" at the start. That is real: the PZEM-004T’s metering side is powered from the mains it measures, so with no mains it does not answer at all, and the library returns NAN. A PZEM on your desk with only USB power reads NAN for the same reason.

Under 0.02 A the 100 A model reads 0 A, 0 W and a power factor of 0. That is its starting current from the manual: a phone charger on standby can be too small to register.

## The ZMPT101B: the mains as a small sine wave

The ZMPT101B module has a tiny voltage transformer, an LM358 op-amp and a blue multi-turn trimmer. OUT rests at half the supply, 2.5 V on an Uno, which analogRead() reads as about 512, and the mains sine rides on top of that, made small. The trimmer sets how small.

One analogRead() is one point on the wave, so it is not the voltage. The voltage is the RMS, the root of the mean of the squares, taken over whole cycles and measured from the middle of the wave. The sketch below finds the middle first, the mean over five 50 Hz cycles, then the RMS about it, then multiplies by the trimmer’s ratio: how many mains volts make one volt at OUT.

On 230 V with the trimmer at a ratio of 600, OUT is a 0.383 V rms sine, and the sketch prints "middle 511.5 OUT 0.384 V rms mains 230.3 V". It takes about 880 readings in each 100 ms window, one every 114 microseconds.

The AC voltmeter, plain code

## The ZMPT101B library, and calibrating it

The ZMPT101B library by Abdurraziq Bachmid does the same in one call. ZMPT101B voltageSensor(A0, 50.0) names the pin and the mains frequency, setSensitivity() takes the trimmer’s ratio, and getRmsVoltage() measures for two mains periods, 40 ms at 50 Hz, and returns the volts. With the sensitivity at 600 it prints "Voltage: 230 V".

Nobody knows the ratio of a real board until they calibrate it. Read the mains with a meter you trust, then multiply the sensitivity by the meter’s reading over the sketch’s. The library starts at 500, and left there this board reads "Voltage: 192 V": 230 x 500 / 600.

Give it the right frequency too. On 60 Hz mains the constructor needs 60.0, because the library measures for one period of the frequency it was given. Told 50.0 on 120 V, 60 Hz mains, it averages over a cycle and a fifth and prints "Voltage: 125 V".

The ZMPT101B library

## The trimmer turned too far, and 3.3 V boards

The LM358 on the ZMPT101B cannot swing its output all the way to its supply. It tops out about 1.5 V short, at 3.5 V on 5 V, which analogRead() reads as 716. Turn the trimmer up to a ratio of 150 on 230 V and the wave wants to reach 4.67 V: its tops are cut off flat, the middle drops to 457, and the plain sketch reads "mains 184.4 V" instead of 230. If the highest reading sits at the same number cycle after cycle, the wave is clipped: turn the trimmer down.

On a 3.3 V board the middle is 1.65 V and the top is 1.8 V, only 0.15 V of room. That is why a ZMPT101B is usually run from 5 V, with its trimmer turned well down for a 3.3 V ADC.

## What the simulation leaves out

The PZEM-004T’s registers read exactly what the sliders say, where a real one is within half a percent. Its answer comes 20 ms after the request here; the manual gives no figure. The ZMPT101B’s wave here is clean, where a real module has some noise and reads a few volts with no mains at all, and its phase shift and the mains’ own distortion are not modeled.

On the classic ESP32 the meter goes on Serial2, a real UART: PZEM004Tv30 pzem(Serial2, 16, 17) with the meter’s TX on GPIO 16 and its TTL side on 3.3 V. The ESP32-S3, C3 and C6 boards’ second UART is not wired to pins yet, so on those use an Uno, a Mega with Serial2, an ESP8266 with SoftwareSerial or a classic ESP32.

## Common mistakes

TX and RX swapped. The meter’s TX goes to the Uno’s receive pin, pin 2 here.

A PZEM-004T on USB alone. The meter only answers when its mains side is powered, so on the bench it reads NAN until it is on the mains.

Reading a ZMPT101B once and calling it the voltage, or forgetting to take off the middle before squaring. Either way the number is mostly the 2.5 V bias.

Leaving the ZMPT101B library’s sensitivity at 500. Calibrate it against a meter.

Wiring a module to mains yourself. If you are not qualified to work on mains, do not: use the simulator, or a plug-in step-down transformer for the ZMPT101B.

## Questions

How do I connect a PZEM-004T to an Arduino Uno?

Its 5V and GND to the Uno’s 5 V and GND, its TX to pin 2 and its RX to pin 3, then SoftwareSerial pzemSerial(2, 3) and PZEM004Tv30 pzem(pzemSerial). The mains side is a job for a qualified person.

Why does my PZEM-004T read NAN?

Most often it is not on the mains: its metering side is powered from the mains it measures, so with only USB power it does not answer. Otherwise check TX and RX, and that the sketch talks at 9600 baud.

Is PZEM-004T energy in kWh or Wh?

The register counts whole watt-hours, and PZEM004Tv30’s energy() divides by 1000, so energy() is kWh. A 1 kW load adds 0.001 every 3.6 seconds.

How do I calibrate a ZMPT101B?

Read the mains with a meter you trust, then set the sensitivity (or the ratio in plain code) to the old value times the meter’s reading over the sketch’s. Check that the wave is not clipped first.

Can I use a ZMPT101B on an ESP32?

The library builds on the ESP32 and reads its 12-bit ADC on 3.3 V. Powered from 3.3 V, the module’s LM358 leaves only 0.15 V of room over the 1.65 V middle, so turn the trimmer well down and check that the wave is not clipped.

Related

## Keep going

Arduino Fingerprint Sensor: Door Lock Code The PZEM-004T part The ZMPT101B part The project page, with the full sketch Intermediate

## Build this for real

Open the editor, change a value and watch the number move with it. Nothing to install, and no account needed.

Start building free Open the editor
