When you transition from basic DC circuit theory to real-world AC power systems, measuring voltage with a multimeter is no longer enough. Electrical engineering students and professionals need to understand apparent power, reactive power, power factor, and True RMS measurements under non-linear loads. Building a True RMS AC power analyzer is one of the most practical arduino projects for electrical engineering students because it bridges embedded firmware with high-voltage AC theory, Modbus communication, and signal isolation.

This guide walks you through building a benchtop True RMS power meter using an Arduino Nano and the PZEM-004T v3.0 energy module. We will cover the exact hardware, provide production-ready firmware with error handling, and debug the most common Modbus timeout failures you will encounter on the bench.

Project Overview & Difficulty Rating

Unlike average-responding sensors that assume a perfect sine wave, True RMS (Root Mean Square) sensors calculate the effective heating value of the AC waveform. This is critical when measuring modern loads like LED drivers, variable frequency drives (VFDs), and switching power supplies, which introduce heavy harmonic distortion. The PZEM-004T v3.0 handles the heavy lifting of analog-to-digital conversion and DSP (Digital Signal Processing) internally, communicating the results via an isolated Modbus-RTU UART link.

Difficulty Rating: Intermediate (3.5/5)
Estimated Build Time: 2 hours (hardware) + 1 hour (firmware & calibration)
Core EE Concepts: True RMS, Power Factor (PF), Modbus-RTU, Opto-isolation, I2C bus capacitance.

PZEM-004T v3.0 Specification Sheet

ParameterSpecificationNotes for EE Applications
Voltage Range80V - 260V ACNominal 110V/220V systems; 50/60Hz
Current Range0A - 100ARequires external split-core CT (included)
Resolution0.1V / 0.001A / 0.1WSufficient for branch circuit monitoring
Measurement Accuracy1.0% + 2 digitsClass 1.0 per IEC 62053-21 standards
CommunicationModbus-RTU over UART (TTL)Opto-isolated TX/RX; default 9600 baud

Hardware BOM & Pin Mapping

To ensure the code compiles and runs exactly as written below, use the specific board variants listed. Substituting an ESP32 will require changing the SoftwareSerial implementation to HardwareSerial or ESP32-specific SoftwareSerial libraries.

  • Microcontroller: Arduino Nano v3 (ATmega328P, 16MHz, 5V logic). Note: Ensure you select the "ATmega328P (Old Bootloader)" option in the Arduino IDE if using a clone board.
  • Power Module: PZEM-004T v3.0 (Modbus TTL version, not the v1.0 opto-coupler serial version).
  • Current Transformer: 100A/50mA Split-Core SCT (usually included with the PZEM-004T v3).
  • Display: 0.96" I2C OLED (SSD1306 driver, 128x64 resolution, 4-pin I2C interface).
  • Power Supply: 5V 1A USB power bank or isolated AC-DC buck converter (Hi-Link HLK-PM01) for standalone operation.

Pin Mapping Table

ComponentModule PinArduino Nano PinWire Color (Suggested)
PZEM-004TVCC (5V)5VRed
PZEM-004TGNDGNDBlack
PZEM-004TTXD2 (Software RX)Yellow
PZEM-004TRXD3 (Software TX)Orange
SSD1306 OLEDVCC5VRed
SSD1306 OLEDGNDGNDBlack
SSD1306 OLEDSCLA5Blue
SSD1306 OLEDSDAA4Green

Mains Wiring & Safety Protocols

WARNING: LETHAL VOLTAGE. This project interfaces directly with 120V/240V AC mains. A fault can cause fatal electrocution or arc flash. De-energize the circuit at the breaker panel before making any connections. Use a tested CAT III or CAT IV multimeter to verify the circuit is dead. If you are not trained in mains wiring, have a licensed electrician terminate the AC side. NEC-style guidance requires all mains connections to be housed in a fire-rated, grounded metal or PVC junction box.

The PZEM-004T terminal block handles the high-voltage connections. Follow this sequence strictly:

  1. Prepare the Junction Box: Mount the PZEM module inside a standard 4x4 PVC or metal junction box. Use nylon standoffs to prevent the module's solder joints from contacting the enclosure.
  2. Wire the Voltage Terminals (L & N): Connect the AC Line (Hot) to the L terminal and the AC Neutral to the N terminal on the PZEM block. Use a minimum of 18 AWG stranded wire for the voltage sense lines. Do not reverse Line and Neutral; while the meter will still read voltage, it violates standard polarization and can create a shock hazard on the load side.
  3. Route the Current Transformer (CT): Clamp the split-core CT around the Line (Hot) wire only.
    • If you clamp both Line and Neutral, the magnetic fields cancel out, and the meter will read 0A.
    • Ensure the arrow printed on the CT points toward the load (away from the breaker panel).
  4. Secure the CT Plug: Push the 3.5mm audio-style jack firmly into the PZEM module. A loose connection here will leave the CT secondary open-circuited. An open-circuited CT under load can generate thousands of volts, destroying the module and posing a severe shock hazard.
  5. Close and Verify: Close the junction box lid. Apply power at the breaker. The PZEM module's onboard LED should blink, indicating it is sampling the AC waveform.

Complete Firmware & Modbus Implementation

The following C++ code targets the Arduino Nano v3 (ATmega328P). It uses the SoftwareSerial library to communicate with the PZEM module on pins D2/D3, leaving the hardware UART (D0/D1) free for USB debugging via the Serial Monitor.

Required Libraries (Install via Arduino Library Manager):

  • PZEM004Tv30 by Oleksandr Zalatov
  • Adafruit SSD1306 and Adafruit GFX
#include <SoftwareSerial.h>
#include <PZEM004Tv30.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

// --- Pin Definitions ---
#define PZEM_RX_PIN 2
#define PZEM_TX_PIN 3

#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_RESET -1
#define OLED_I2C_ADDR 0x3C

// --- Object Initialization ---
SoftwareSerial pzemSW(PZEM_RX_PIN, PZEM_TX_PIN);
PZEM004Tv30 pzem(pzemSW);
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

void setup() {
  Serial.begin(115200); // Hardware serial for USB debugging
  pzemSW.begin(9600);   // PZEM Modbus default baud rate

  // Initialize OLED
  if(!display.begin(SSD1306_SWITCHCAPVCC, OLED_I2C_ADDR)) {
    Serial.println(F("SSD1306 allocation failed"));
    for(;;); // Halt if display fails
  }
  display.clearDisplay();
  display.setTextSize(1);
  display.setTextColor(SSD1306_WHITE);
  display.println("PZEM Booting...");
  display.display();
  delay(2000); // Allow PZEM DSP to stabilize
}

void loop() {
  // Fetch measurements from Modbus registers
  float voltage = pzem.voltage();
  float current = pzem.current();
  float power = pzem.power();
  float energy = pzem.energy();
  float pf = pzem.pf();

  // --- Error Handling & Debugging ---
  // The PZEM library returns NaN (Not a Number) on Modbus CRC/Timeout errors
  if (isnan(voltage) || isnan(current)) {
    Serial.println("ERROR: PZEM Read Timeout or NaN received.");
    display.clearDisplay();
    display.setCursor(0,0);
    display.println("MODBUS ERROR");
    display.println("Check RX/TX Pins");
    display.display();
    delay(2000);
    return; // Skip OLED rendering of bad data
  }

  // --- Serial Output ---
  Serial.print("V: "); Serial.print(voltage);
  Serial.print(" | I: "); Serial.print(current, 3);
  Serial.print(" | P: "); Serial.print(power);
  Serial.print(" | PF: "); Serial.println(pf);

  // --- OLED Rendering ---
  display.clearDisplay();
  
  display.setCursor(0, 0);
  display.setTextSize(2);
  display.print(voltage, 1);
  display.print("V");
  
  display.setTextSize(1);
  display.setCursor(0, 20);
  display.print("I: "); display.print(current, 3); display.println(" A");
  
  display.setCursor(0, 30);
  display.print("P: "); display.print(power, 1); display.println(" W");
  
  display.setCursor(0, 40);
  display.print("E: "); display.print(energy, 1); display.println(" kWh");
  
  display.setCursor(0, 50);
  display.print("PF: "); display.println(pf, 2);

  display.display();
  delay(1000); // Modbus polling interval
}

Debugging: "NaN" Outputs and Modbus Timeouts

When working with Modbus-RTU over SoftwareSerial, you will inevitably encounter communication failures. The most common symptom is the Serial Monitor printing Voltage: NaN V and Current: NaN A, or the OLED freezing on the "MODBUS ERROR" screen.

The PZEM004Tv30 library returns NaN (Not a Number) when the underlying Modbus CRC (Cyclic Redundancy Check) fails, or when the UART buffer times out waiting for the 8-byte response frame from the sensor.

The First 3 Things to Check

  1. RX/TX Cross-Wiring: The PZEM TX must connect to the Arduino RX (D2), and PZEM RX to Arduino TX (D3). If you wire TX-to-TX, the bus will deadlock immediately.
  2. Baud Rate Mismatch: The PZEM v3.0 operates strictly at 9600 baud. If you accidentally initialize pzemSW.begin(115200), the microcontroller will sample the bits at the wrong intervals, resulting in garbage data that fails the CRC check.
  3. Power Supply Brownouts: The PZEM module draws up to 100mA when sampling and transmitting. If you are powering the Nano via a weak USB hub, the 5V rail may sag below 4.5V during a Modbus transmission, resetting the PZEM's internal DSP and causing a timeout.

Ranked Causes for Persistent NaN Errors

RankCauseFix / Measurement Threshold
1SoftwareSerial interrupt collisionsDisable I2C OLED updates during the pzem.voltage() call. SoftwareSerial disables interrupts while listening, which can corrupt I2C timing.
2Logic Level Mismatch (5V vs 3.3V)The PZEM TX outputs 3.3V. The Nano (5V) reads this as HIGH fine. However, ensure the Nano TX (5V) isn't back-feeding. The PZEM v3 has an optocoupler on RX, so it is 5V tolerant, but verify with a multimeter that the RX pin sees ~5V when idle.
3Missing Common GroundMeasure resistance between PZEM GND and Nano GND. It must read < 1 ohm. Without a shared ground reference, the UART signal floats.
4Corrupted Modbus AddressIf the PZEM address was changed from the default 0xF8, the library won't find it. Use the library's setAddress() example to reset it.

Extending and Simplifying the Build

Depending on your lab requirements or project scope, you may need to scale this build up or down.

How to Simplify (The "Quick-Check" Version)

If you just need to verify a load's power factor and don't care about a standalone display, strip out the SSD1306 OLED code entirely. Rely on the Arduino IDE's Serial Plotter (Ctrl+Shift+L). Change the Serial output to CSV format: Serial.print(voltage); Serial.print(","); Serial.println(current);. This gives you a real-time graphing tool for AC inrush currents without spending $6 on an I2C display.

How to Extend (The "Capstone" Version)

To turn this into a full capstone-level data logger:

  • Add MQTT Telemetry: Swap the Arduino Nano for an ESP32 DevKit v1. Use the ESP32's hardware UART2 (pins 16/17) for the PZEM, and connect to WiFi. Push the True RMS and Power Factor data to an MQTT broker (like Mosquitto) to feed a Grafana dashboard.
  • Harmonic Analysis: The PZEM-004T only outputs calculated RMS values. To measure Total Harmonic Distortion (THD), you must bypass the PZEM and use a dedicated ADC (like the ADS1115) paired with a ZMPT101B voltage transformer, sampling at ≥10kHz to capture up to the 50th harmonic. Note that this requires writing your own Fast Fourier Transform (FFT) firmware.

FAQ: Arduino Projects for Electrical Engineering

What are the most practical arduino projects for electrical engineering students?

Beyond this True RMS power meter, the most valuable projects for EE students involve closed-loop control and signal processing. Building a PID-controlled DC-DC buck converter using an Arduino to generate high-frequency PWM (via Timer1 registers) teaches feedback loop stability. Another excellent project is a digital phase-angle dimmer for inductive loads, which requires zero-crossing detection circuits (using an H11AA1 optocoupler) and precise interrupt timing to fire a TRIAC.

How accurately can arduino projects for electrical engineering measure AC power factor?

When using dedicated DSP modules like the PZEM-004T or ADE9000, you can achieve Class 0.5 accuracy (within 0.5% error) for power factor measurements between 0.1 and 1.0 lagging/leading. However, if you attempt to calculate PF manually using an Arduino's internal 10-bit ADC and basic voltage/current transformers, your accuracy will degrade significantly (often >5% error) due to ADC phase shift, quantization noise, and the inability to sample both channels simultaneously without a dual-core microcontroller or external simultaneous-sampling ADC.

Should I use an ESP32 instead of a Nano for arduino projects for electrical engineering?

Use the Arduino Nano if your project is strictly a benchtop tool requiring simple I2C displays and basic UART sensors. The Nano's 5V logic is highly tolerant of noisy lab environments. Upgrade to an ESP32 (specifically the ESP32-WROOM-32) if your project requires WiFi/Bluetooth telemetry, high-speed ADC sampling (the ESP32 has dual 12-bit SAR ADCs), or if you need to run an RTOS (FreeRTOS) to handle concurrent tasks like Modbus polling and MQTT publishing without blocking the main loop. For high-voltage isolation projects, the ESP32's 3.3V logic requires careful level-shifting or opto-isolation when interfacing with 5V industrial sensors.