making a watt meter with ACS712

Hi All,

I have tried to sketch a program for calculating Ac power and current and voltage ,So I put Pisces of codes from here and there. The result is not correct and stable
I am using an Arduino uno and ACS712 with lcd 2x16.
My complete code is here for you to help me where I am wrong.


#include <LiquidCrystal.h>
LiquidCrystal lcd(8, 7, 6, 5, 4, 3, 2);
int backLight = 9;
const int Sensor_Pin = A0;
unsigned int Sensitivity = 185;   // 185mV/A for 5A, 100 mV/A for 20A and 66mV/A for 30A Module
float Vpp = 0; // peak-peak voltage 
float Vrms = 0; // rms voltage
float Irms = 0; // rms current
float Supply_Voltage = 220.0;           // reading from DMM
float Vcc = 5.0;         // ADC reference voltage // voltage at 5V pin 
float power = 0;         // power in watt              
//float Wh =0 ;             // Energy in kWh
unsigned long last_time =0;
unsigned long current_time =0;
unsigned long interval = 100;
unsigned int calibration = 100;  // V2 slider calibrates this
unsigned int pF = 85;           // Power Factor default 95
//float bill_amount = 0;   // 30 day cost as present energy usage incl approx PF 
//unsigned int energyTariff = 8.0; // Energy cost in INR per unit (kWh)
void getACS712();
float getVPP();
void getACS712() {  // for AC
 Vpp = getVPP();
  Vrms = (Vpp/2.0) *0.707; 
  Vrms = Vrms - (calibration / 10000.0);     // calibtrate to zero with slider
  Irms = (Vrms * 1000)/Sensitivity ;
  if((Irms > -0.015) && (Irms < 0.008)){  // remove low end chatter
    Irms = 0.0;
  power= (Supply_Voltage * Irms) * (pF / 100.0); 
 // last_time = current_time;
 // current_time = millis();    
 // Wh = Wh+  power *(( current_time -last_time) /3600000.0) ; // calculating energy in Watt-Hour
 // bill_amount = Wh * (energyTariff/1000);
//const int analogInPin = A0;

// Number of samples to average the reading over
// Change this to make the reading smoother... but beware of buffer overflows!
const int avgSamples = 10;

int sensorValue = 0;

float Vref = 2500; // Output voltage with no current: ~ 2500mV or 2.5V
void setup()
  pinMode(backLight, OUTPUT); //set pin 9 as output
analogWrite(backLight, 150); //controls the backlight intensity 0-254
lcd.begin(16,2); // columns, rows. size of display
lcd.clear(); // clear the screen

  lcd.print("   YA ALLAH    ");
void loop()
   for (int i = 0; i < avgSamples; i++)
    sensorValue += analogRead(Sensor_Pin);

    // wait 2 milliseconds before the next loop
    // for the analog-to-digital converter to settle
    // after the last reading:
    delay(2);//this was 2 milisecond for inspection is set to 100

sensorValue = sensorValue / avgSamples;

  // The on-board ADC is 10-bits -> 2^10 = 1024 -> 5V / 1024 ~= 4.88mV
  // The voltage is in millivolts
  float voltage = 4.88 * sensorValue;

  // This will calculate the actual current (in mA)
  // Using the Vref and sensitivity settings you configure
  float current = -(voltage - Vref) * Sensitivity;
  power= (Supply_Voltage * current/10000) * (pF / 100.0); 
  // This is the raw sensor value, not very useful without some calculations

  Serial.println(" mV");

  Serial.println(" mA");
  Serial.print("POWER :");
  Serial.println(" Watt");
Serial.println(""); // print the next sets of parameter after a blank line
  // Reset the sensor value for the next reading
  sensorValue = 0;

// getACS712(); 
//  Serial.print("VOLTAGE : ");
//Serial.print("CURRENT :");

//Serial.print("ENERGY CONSUMED :");

lcd.setCursor(16,1); // set the cursor outside the display count
lcd.print(" "); // print empty character
//////////////////////////////////////////print power and energy to a LCD//////////////////////////////////////////////// 
lcd.setCursor(0,0); // set the cursor at 1st col and 1st row
lcd.print(" W ");
lcd.setCursor(1,1); // set the cursor at 1st col and 2nd row
//lcd.print("WH ");
lcd.print(" mA");
float getVPP()
  float result; 
  int readValue;                
  int maxValue = 0;             
  int minValue = 1024;          
  uint32_t start_time = millis();
  while((millis()-start_time) < 950) //read every 0.95 Sec
     readValue = analogRead(Sensor_Pin);    
     if (readValue > maxValue) 
         maxValue = readValue; 
     if (readValue < minValue) 
         minValue = readValue;
   result = ((maxValue - minValue) * Vcc) / 1024.0;  
   return result;

So what does it do, and how is it different from what you want it to do?

When there is no load connected this is what it shows:

509 sensor value
2483.92 mV
2974.77 mA
POWER :55.63 Watt

sensor value
2479.04 mV
3877.59 mA
POWER :72.51 Watt

and when a load is connected this what you see:

2479.04 mV
3877.59 mA
POWER :72.51 Watt

2479.04 mV
3877.59 mA
POWER :72.51 Watt

2483.92 mV
2974.77 mA
POWER :55.63 Watt

2479.04 mV
3877.59 mA
POWER :72.51 Watt

What do you expect to see, then?

Your timing for calls to analogRead is all over the place to my mind.

With an AC waveform the simplest approach is to directly sum the rms value over a whole mains cycle.
This means summing the squares of the readings, then taking the square root of the mean.

In loop() you take a straight mean of the current summed over a 20ms period (which depending on where you are might be 1 or 1.2 cycles. Are you attempting to calibrate out the zero? Because that is sort of what's happening. Summing over 20ms for 50Hz mains should be roughly zero.

getVPP reads for 0.95 seconds taking the max and min, to get peak-to-peak reading. If the mains is sinusoidal this is a good estimate of rms. If not, it isn't.

The standard approach for sampling a mains waveform is to sum the squares and the values over a whole number of mains cycles. From this you can calculate the mean and subtract it out, and calculate the mean square (again subtracting out the square of the mean). This gives a direct measure of true rms, whether the
mains is sinusoidal or not (although ideally you sample current and voltage together and sub their product to
get true power directly, so that the power factor doesn't have to be fudged).

Your code never calls getVPP or getACS712, BTW.

Have you checked the specifications of the ACS712? Its noisy and fairly low accuracy, being a hall-effect current sensor, its not designed for accurate measurements. It will pick up nearby magnetic fields for one thing - even
turning it in the earth's magnetic field may influence the values returned a little.

Thanks for comments.
Because of all that I changed the sensor to CT and it is working ok for resistive load but for other loads like led bulb it shows the power 10 times more than it should .Don,t know whats wrong with this now.

This is my code for using ZMCT103C

//Michael Klements
//The DIY Life
//26 February 2017

#include <LiquidCrystal.h>

int currentPin = 1;              //Assign CT input to pin 1
double kilos = 0;
int peakPower = 0;
unsigned long startMillis;
unsigned long endMillis;
LiquidCrystal lcd(8, 7, 6, 5, 4, 3,2);  //Assign LCD screen pins, as per LCD shield requirements
int backLight = 9;
void setup() 
  // Serial.begin(9600);
  pinMode(backLight, OUTPUT); //set pin 9 as output
analogWrite(backLight, 150); //controls the backlight intensity 0-254
  lcd.begin(16,2);              // columns, rows.  use 16,2 for a 16x2 LCD, etc.
  startMillis = millis();
void loop() 
  int current = 0;
  int maxCurrent = 0;
  int minCurrent = 1000;
  for (int i=0 ; i<=200 ; i++)  //Monitors and logs the current input for 200 cycles to determine max and min current
    current = analogRead(currentPin);    //Reads current input and records maximum and minimum current
    if(current >= maxCurrent)
      maxCurrent = current;
    else if(current <= minCurrent)
      minCurrent = current;
  if (maxCurrent <= 517)
    maxCurrent = 516;
  double RMSCurrent = ((maxCurrent - 516)*0.707)/181.10444;    //Calculates RMS current based on maximum value
  int RMSPower = 220*RMSCurrent;    //Calculates RMS Power Assuming Voltage 220VAC, change to 110VAC accordingly
  if (RMSPower > peakPower)
    peakPower = RMSPower;

  lcd.setCursor(0,0);           // Displays all current data

it is working ok for resistive load but for other loads like led bulb it shows the power 10 times more than it should .Don,t know whats wrong with this now.

You're using a fixed Power Factor of .85. Could that be the problem?

Part of the problem may be in how you measure the current.

200 measurements, each taking about 110 us, is 22 ms of sampling. That's at least one complete cycle. But the problem sill is that you just take maximum and minimum current, which may not work very well as the LED is using a switching power supply for input so its current draw is anything but constant.

Instead of using min/max it may be better to calculate the average current based on all 200 values. In that case I'd also change this number to be one or two complete cycles (so 2 ms of measuring on 50 Hz mains, a little less on 60 Hz mains).

More issues: you apparently use the 5A model, so 185 mA/A sensitivity. Absolute outputs call for an absolute input - thus use the internal reference.

That also deals with the second issue: resolution. At the standard 5V Vcc reference (which is never exactly 5V of course - meaning any change in Vcc will mess up your readings!) you get about 5 mV per ADC unit. Using the internal reference that's 1.1 mV. A complication is of course that that sensor has a zero-current voltage of 2500 mV here... Still you measuring an absolute output with a ratiometric reference is an issue.

Supply voltage 220V - I though most nominal 220V systems are at 230-240V nowadays. Taking your last measurement:

2479.04 mV
3877.59 mA
POWER :72.51 Watt

3.88A * 220V = 853.6W So that's your 0.85 power factor and another factor of 10 off in that calculation.

For your LED lights: a typical LED light is about 10W, that's 0.0455 mA. At 185 mV/A that would be a reading of 8.4 mV - that's less than two ADC points, thus drowns in the noise.

More issues: you apparently use the 5A model, so 185 mA/A sensitivity. Absolute outputs call for an absolute input - thus use the internal reference.

ACS712 sensors are ratiometric, so that 185mV/A is only correct if the supply is 5.0volt (which is rarely is).
The code SHOULD convert A/D value to current directly, without the misleading mV/A conversion.
End result is the same though.
Using the internal Aref will surely lead to instability of zero Amps and span.

Note that ACS712 sensors, and the boards they are mounted on, are not designed for 230volt AC.
A current transformer should be a lot safer.

ACS712 sensors are ratiometric,

Ah, thanks for the correction there. The problem of measuring very small currents remains, though...

Zero current should be calculated with two variables, a minValue and a maxValue (both int).
Before measurements the maxValue should be set to zero, and the ACS712 should update that (to >=512).
The min value should be set before measurement to 1023, and the ACS712 should update that (to <=512).
Than you can be sure min and max are the same (about 511 or 512 or 513).
That way zero current always calculates to zero Amps.
Plenty of those ACS712 examples floating on the net.
You never are going to get more than ~800 A/D values spread out over the 5Amp of the sensor.

A Lot topics for me to think about.....completely lost at the moment.