Current Sensing Problem with ACS712 ?

Hellow Friends !

I connected ACS712-20A sensor to my home made arduino ( arduino uno ).
( for ACS712 sensor, i bought only the sensor and made the circuitry my self as mentioned in the datasheet )

  • for Cf --- > i used 0.1uF instead of 1nF cap

Source connected is 230V AC / 50Hz
Load connected is a 230V AC / 1500W water heater

..........................................................

My problem is the voltage readings are not stable as I was expecting.

My code is below ...

// the setup routine runs once when you press reset:
void setup() {
// initialize serial communication at 9600 bits per second:
Serial.begin(9600);
}

// the loop routine runs over and over again forever:
void loop() {
// read the input on analog pin 0:
int sensorValue = analogRead(A3);
delay(1);
// Convert the analog reading (which goes from 0 - 1023) to a voltage (0 - 5V):
float voltage = sensorValue * (5.0 / 1023.0);
// print out the value you read:
Serial.println(voltage);
delay(500);
}

Serial monitor outputs are as below

before connecting the load
2.48
2.48
2.48
2.48
2.51
2.50
2.50
2.50
2.51
2.52
2.53
2.48
after connecting the load
2.47
2.50
2.93
3.07
3.00
2.72
2.36
2.05
1.93
2.05
2.37
2.77
2.99
3.06
2.87
2.52
2.06
1.94
1.94
2.22
2.64

I also tried using the EmonLib from openenergymonitor.org . But it didn't work as well.

Your comments are highly appreciated.

Thanks,
Dileesha.

Hi friends,

Then i tried EmonLib again . I didnt make any change, i just uploaded the EmonLib example ( emonLib > examples > voltage_and_current.ino ) into the arduino ic.

I get some output but it's fluctuating a lot . Please see the results i got in Serial Monitor .

WITHOUT LOAD
rmsVoltage : 267.22 rmsCurrent : 0.04
rmsVoltage : 268.10 rmsCurrent : 0.04
rmsVoltage : 266.13 rmsCurrent : 0.04
rmsVoltage : 277.60 rmsCurrent : 0.04
rmsVoltage : 271.81 rmsCurrent : 0.04
rmsVoltage : 219.10 rmsCurrent : 0.03 ( voltage has dropped )
rmsVoltage : 271.05 rmsCurrent : 0.04
rmsVoltage : 259.77 rmsCurrent : 0.04
rmsVoltage : 267.94 rmsCurrent : 0.04

WITH LOAD

rmsVoltage : 254.26 rmsCurrent : 0.44
rmsVoltage : 253.22 rmsCurrent : 0.44
**rmsVoltage : 202.97 rmsCurrent : 0.35 ** ( voltage has dropped )
rmsVoltage : 256.09 rmsCurrent : 0.43
rmsVoltage : 255.41 rmsCurrent : 0.45
rmsVoltage : 251.49 rmsCurrent : 0.43
rmsVoltage : 254.72 rmsCurrent : 0.43

Used arduino CODE is below

// EmonLibrary examples openenergymonitor.org, Licence GNU GPL V3

#include "EmonLib.h" // Include Emon Library
EnergyMonitor emon1; // Create an instance

void setup()
{
Serial.begin(9600);

emon1.voltage(2, 6340.26, 1.7); // Voltage: input pin, calibration, phase_shift
emon1.current(3, 1.1); // Current: input pin, calibration.
}

void loop()
{
emon1.calcVI(20,2000); // Calculate all. No.of half wavelengths (crossings), time-out
emon1.serialprint(); // Print out all variables
}

Your help will be highly appreciated .

Thanks,
Dileesha.

You could try doing a average reading to smooth out glitches. Try either a rolling average (2 samples) or sample and add the value x times then divide by x to get average.

@Riva

Thanks for your comment.

Could you please show me how to make that change to the code ?

Im a newbie, so i don't know much coding work.

Thanks.

Dileesha:
Could you please show me how to make that change to the code ?

Untested rolling average code.

int sensorValue;
// the setup routine runs once when you press reset:
void setup() {
  // initialize serial communication at 9600 bits per second:
  Serial.begin(9600);
  sensorValue = analogRead(A3); // Prime rolling average
}

// the loop routine runs over and over again forever:
void loop() {
  // read the input on analog pin 3:
  sensorValue += (analogRead(A3) / 2); // get rolling average
  // Convert the analog reading (which goes from 0 - 1023) to a voltage (0 - 5V):
  float voltage = sensorValue * (5.0 / 1023.0);
  // print out the value you read:
  Serial.println(voltage);
  delay(500);
}

Untested average of 20 readings code

// the setup routine runs once when you press reset:
void setup() {
  // initialize serial communication at 9600 bits per second:
  Serial.begin(9600);
}

// the loop routine runs over and over again forever:
void loop() {
  // read the input on analog pin 0:
  int sensorValue = 0;
  for (byte x = 0; x < 20; x++) {
    sensorValue += analogRead(A3);
  }
  sensorValue = sensorValue / 20;
  // Convert the analog reading (which goes from 0 - 1023) to a voltage (0 - 5V):
  float voltage = sensorValue * (5.0 / 1023.0);
  // print out the value you read:
  Serial.println(voltage);
  delay(500);
}

@Riva

I tried that . Below is what i got in serial printer .

without a load

3.77
5.05
6.32
7.51
8.75
10.00
11.24
12.48
13.71
14.97
16.17
17.42
18.67
19.92
21.18
22.45
23.69
24.93
26.17
27.41
28.67
29.92
31.17
32.42
33.68
34.93
36.21
37.47
38.69
39.93
41.17
42.41
43.65
44.89
46.13
47.38
48.62
49.86
51.10
52.35
53.59
54.83
56.07
57.31
58.55
59.79
61.04
62.28
63.52
64.76
65.99
67.23
68.47
69.73
70.98
72.24
73.43
74.68
75.93
77.18
78.43
79.68
80.93
82.19
83.47
84.68
85.92
87.17
88.41
89.65
90.88
92.14
93.31
94.56
95.82
97.07
98.32
99.60
100.87
102.11
103.35
104.59
105.83
107.07
108.31
109.56
110.82
112.02
113.27
114.52
115.77
117.02
118.27
119.53
120.78
122.04
123.32
124.60
125.87
127.05
128.29
129.53
130.77
132.01
133.26
134.50
135.74
136.98
138.22
139.46
140.70
141.95
143.19
144.43
145.67
146.91
148.15
149.39
150.63
151.86
153.10
154.34
155.57
156.81
158.04
159.28
-159.79
-158.56
-157.32
-156.09
-154.85
-153.61
-152.38
-151.14
-149.90
-148.67
-147.42
-146.16
-144.91
-143.65
-142.39
-141.14
-139.94
-138.73
-137.48
-136.23
-134.98
-133.73
-132.48
-131.23
-129.98
-128.72
-127.47
-126.22
-124.97
-123.72
-122.47
-121.21
-119.96
-118.70
-117.45
-116.20
-114.95
-113.70
-112.44
-111.19
-109.94

with a load

5.00
6.26
7.51
8.76
10.01
11.27
12.61
13.92
15.19
16.42
17.64
18.84
20.04
21.23
22.43
23.64
24.85
26.09
27.35
28.64
29.97
31.31
32.68
34.08
35.50
36.95
38.41
39.90
41.42
42.95
44.47
45.99
47.51
49.02
50.50
51.96
53.39
54.73
56.10
57.43
58.72
59.94
61.08
62.17
63.20
64.18
65.15
66.12
67.09
68.06
69.05
70.05
71.08
72.11
73.12
74.17
75.32
76.51
77.75
79.06
80.40
81.78
83.20
84.64
86.11
87.60
89.10
90.62
92.15
93.67
95.20
96.72
98.25
99.77
101.30
102.82
104.31
105.79
107.24
108.66
110.05
111.41
112.73
114.00
115.22
116.38
117.51
118.55
119.56
120.60
121.63
122.65
123.67
124.67
125.65
126.63
127.60
128.56
129.53
130.50
131.50
132.54
133.63
134.78
136.03
137.34
138.71
140.05
141.51
143.01
144.53
146.05
147.58
149.05
150.46
151.84
153.18
154.47
155.71
156.91
158.08
159.23
-159.96
-158.90
-157.88
-156.90
-155.87
-154.84
-153.81
-152.78
-151.76
-150.75
-149.75
-148.76

Please help ?

So you are measuring a AC current and expecting a steady measurement voltage? I would think the values would track the rms AC current as it goes positive and negative at a 60Hz rate?

Lefty

@Riva

Then i just now tried your modified code ( second code you have put )

It gave me below result . But still its fluctuating ..

with load

2.48
2.02
1.95
2.32
2.76
3.03
2.92
2.49
2.03
1.94
2.22
2.68
2.99
3.01
2.67
2.17
1.96
2.01
2.45
2.80
3.03
2.94

with load no load

2.48
2.48
2.50
2.46
2.50
2.50
2.55
2.44
2.48
2.48
2.46
2.50
2.50
2.54
2.44
2.48

Any suggestion ?

@Lefty

Yes I want to measure 50Hz AC voltage . And yes I got the rms values and did the math ( i uploaded the EmonLib library example which has all math in it ) .

For your reference ill post the complete library ..

EmonLib.cpp / EmonLib.h are below

------------- H File ---------------------

/*
Emon.h - Library for openenergymonitor
Created by Trystan Lea, April 27 2010
GNU GPL
*/

#ifndef EmonLib_h
#define EmonLib_h

#if defined(ARDUINO) && ARDUINO >= 100

#include "Arduino.h"

#else

#include "WProgram.h"

#endif

class EnergyMonitor
{
public:

void voltage(int _inPinV, double _VCAL, double _PHASECAL);
void current(int _inPinI, double _ICAL);

void voltageTX(double _VCAL, double _PHASECAL);
void currentTX(int _channel, double _ICAL);

void calcVI(int crossings, int timeout);
double calcIrms(int NUMBER_OF_SAMPLES);
void serialprint();

long readVcc();
//Useful value variables
double realPower,
apparentPower,
powerFactor,
Vrms,
Irms;

private:

//Set Voltage and current input pins
int inPinV;
int inPinI;
//Calibration coeficients
//These need to be set in order to obtain accurate results
double VCAL;
double ICAL;
double PHASECAL;

//--------------------------------------------------------------------------------------
// Variable declaration for emon_calc procedure
//--------------------------------------------------------------------------------------
int lastSampleV,sampleV; //sample_ holds the raw analog read value, lastSample_ holds the last sample
int lastSampleI,sampleI;

double lastFilteredV,filteredV; //Filtered_ is the raw analog value minus the DC offset
double lastFilteredI, filteredI;

double phaseShiftedV; //Holds the calibrated phase shifted voltage.

double sqV,sumV,sqI,sumI,instP,sumP; //sq = squared, sum = Sum, inst = instantaneous

int startV; //Instantaneous voltage at start of sample window.

boolean lastVCross, checkVCross; //Used to measure number of times threshold is crossed.
int crossCount; // ''

};

#endif

---------- .CPP file ( i will put as two parts ) --------------------------

/*
Emon.cpp - Library for openenergymonitor
Created by Trystan Lea, April 27 2010
GNU GPL
*/

//#include “WProgram.h” un-comment for use on older versions of Arduino IDE
#include “EmonLib.h”

#if defined(ARDUINO) && ARDUINO >= 100

#include “Arduino.h”

#else

#include “WProgram.h”

#endif

//--------------------------------------------------------------------------------------
// Sets the pins to be used for voltage and current sensors
//--------------------------------------------------------------------------------------
void EnergyMonitor::voltage(int _inPinV, double _VCAL, double _PHASECAL)
{
inPinV = _inPinV;
VCAL = _VCAL;
PHASECAL = _PHASECAL;
}

void EnergyMonitor::current(int _inPinI, double _ICAL)
{
inPinI = _inPinI;
ICAL = _ICAL;
}

//--------------------------------------------------------------------------------------
// Sets the pins to be used for voltage and current sensors based on emontx pin map
//--------------------------------------------------------------------------------------
void EnergyMonitor::voltageTX(double _VCAL, double _PHASECAL)
{
inPinV = 2;
VCAL = _VCAL;
PHASECAL = _PHASECAL;
}

void EnergyMonitor::currentTX(int _channel, double _ICAL)
{
if (_channel == 1) inPinI = 3;
if (_channel == 2) inPinI = 0;
if (_channel == 3) inPinI = 1;
ICAL = _ICAL;
}

//--------------------------------------------------------------------------------------
// emon_calc procedure
// Calculates realPower,apparentPower,powerFactor,Vrms,Irms,kwh increment
// From a sample window of the mains AC voltage and current.
// The Sample window length is defined by the number of half wavelengths or crossings we choose to measure.
//--------------------------------------------------------------------------------------
void EnergyMonitor::calcVI(int crossings, int timeout)
{
int SUPPLYVOLTAGE = readVcc();
int crossCount = 0; //Used to measure number of times threshold is crossed.
int numberOfSamples = 0; //This is now incremented

//-------------------------------------------------------------------------------------------------------------------------
// 1) Waits for the waveform to be close to ‘zero’ (500 adc) part in sin curve.
//-------------------------------------------------------------------------------------------------------------------------
boolean st=false; //an indicator to exit the while loop

unsigned long start = millis(); //millis()-start makes sure it doesnt get stuck in the loop if there is an error.

while(st==false) //the while loop…
{
startV = analogRead(inPinV); //using the voltage waveform
if ((startV < 550) && (startV > 440)) st=true; //check its within range
if ((millis()-start)>timeout) st = true;
}

//-------------------------------------------------------------------------------------------------------------------------
// 2) Main measurment loop
//-------------------------------------------------------------------------------------------------------------------------
start = millis();

while ((crossCount < crossings) && ((millis()-start)<timeout))
{
numberOfSamples++; //Count number of times looped.

lastSampleV=sampleV; //Used for digital high pass filter
lastSampleI=sampleI; //Used for digital high pass filter

lastFilteredV = filteredV; //Used for offset removal
lastFilteredI = filteredI; //Used for offset removal

//-----------------------------------------------------------------------------
// A) Read in raw voltage and current samples
//-----------------------------------------------------------------------------
sampleV = analogRead(inPinV); //Read in raw voltage signal
sampleI = analogRead(inPinI); //Read in raw current signal

//-----------------------------------------------------------------------------
// B) Apply digital high pass filters to remove 2.5V DC offset (centered on 0V).
//-----------------------------------------------------------------------------
filteredV = 0.996*(lastFilteredV+sampleV-lastSampleV);
filteredI = 0.996*(lastFilteredI+sampleI-lastSampleI);

//-----------------------------------------------------------------------------
// C) Root-mean-square method voltage
//-----------------------------------------------------------------------------
sqV= filteredV * filteredV; //1) square voltage values
sumV += sqV; //2) sum

//-----------------------------------------------------------------------------
// D) Root-mean-square method current
//-----------------------------------------------------------------------------
sqI = filteredI * filteredI; //1) square current values
sumI += sqI; //2) sum

see part 2

----------------- .CPP file part 2 --------------------------------

//-----------------------------------------------------------------------------
// E) Phase calibration
//-----------------------------------------------------------------------------
phaseShiftedV = lastFilteredV + PHASECAL * (filteredV - lastFilteredV);

//-----------------------------------------------------------------------------
// F) Instantaneous power calc
//-----------------------------------------------------------------------------
instP = phaseShiftedV * filteredI; //Instantaneous Power
sumP +=instP; //Sum

//-----------------------------------------------------------------------------
// G) Find the number of times the voltage has crossed the initial voltage
// - every 2 crosses we will have sampled 1 wavelength
// - so this method allows us to sample an integer number of half wavelengths which increases accuracy
//-----------------------------------------------------------------------------
lastVCross = checkVCross;
if (sampleV > startV) checkVCross = true;
else checkVCross = false;
if (numberOfSamples==1) lastVCross = checkVCross;

if (lastVCross != checkVCross) crossCount++;
}

//-------------------------------------------------------------------------------------------------------------------------
// 3) Post loop calculations
//-------------------------------------------------------------------------------------------------------------------------
//Calculation of the root of the mean of the voltage and current squared (rms)
//Calibration coeficients applied.

double V_RATIO = VCAL *((SUPPLYVOLTAGE/1000.0) / 1023.0);
Vrms = V_RATIO * sqrt(sumV / numberOfSamples);

double I_RATIO = ICAL *((SUPPLYVOLTAGE/1000.0) / 1023.0);
Irms = I_RATIO * sqrt(sumI / numberOfSamples);

//Calculation power values
realPower = V_RATIO * I_RATIO * sumP / numberOfSamples;
apparentPower = Vrms * Irms;
powerFactor=realPower / apparentPower;

//Reset accumulators
sumV = 0;
sumI = 0;
sumP = 0;
//--------------------------------------------------------------------------------------
}

//--------------------------------------------------------------------------------------
double EnergyMonitor::calcIrms(int NUMBER_OF_SAMPLES)
{

int SUPPLYVOLTAGE = readVcc();

for (int n = 0; n < NUMBER_OF_SAMPLES; n++)
{
lastSampleI = sampleI;
sampleI = analogRead(inPinI);
lastFilteredI = filteredI;
filteredI = 0.996*(lastFilteredI+sampleI-lastSampleI);

// Root-mean-square method current
// 1) square current values
sqI = filteredI * filteredI;
// 2) sum
sumI += sqI;
}

double I_RATIO = ICAL *((SUPPLYVOLTAGE/1000.0) / 1023.0);
Irms = I_RATIO * sqrt(sumI / NUMBER_OF_SAMPLES);

//Reset accumulators
sumI = 0;
//--------------------------------------------------------------------------------------

return Irms;
}

void EnergyMonitor::serialprint()
{
// Serial.print(realPower);
// Serial.print(’ ‘);
//Serial.print(apparentPower);
//Serial.print(’ ‘);
Serial.print(Vrms);
Serial.print(’ ‘);
Serial.print(Irms);
Serial.print(’ ‘);
//Serial.print(powerFactor);
//Serial.println(’ ');
delay(2);
}

//thanks to http://hacking.majenko.co.uk/making-accurate-adc-readings-on-arduino
//and Jérôme who alerted us to http://provideyourown.com/2012/secret-arduino-voltmeter-measure-battery-voltage/

long EnergyMonitor::readVcc() {
long result;

#if defined(AVR_ATmega168) || defined(AVR_ATmega328) || defined (AVR_ATmega328P)
ADMUX = _BV(REFS0) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);
/* #elif defined(AVR_ATmega32U4) || defined(AVR_ATmega1280) || defined(AVR_ATmega2560)
ADMUX = _BV(REFS0) | _BV(MUX4) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);
#elif defined (AVR_ATtiny24) || defined(AVR_ATtiny44) || defined(AVR_ATtiny84)
ADMUX = _BV(MUX5) | _BV(MUX0);
#elif defined (AVR_ATtiny25) || defined(AVR_ATtiny45) || defined(AVR_ATtiny85)
ADMUX = _BV(MUX3) | _BV(MUX2);*/
#endif

delay(2); // Wait for Vref to settle
ADCSRA |= _BV(ADSC); // Convert
while (bit_is_set(ADCSRA,ADSC));
result = ADCL;
result |= ADCH<<8;
result = 1126400L / result; //1100mV*1024 ADC steps http://openenergymonitor.org/emon/node/1186
return result;
}

One problem I can see with the open energy monitor library is that the values computers by calcVI will be disturbed if an interrupt (e.g. serial or timer) occurs when calcVI is not waiting for an ADC conversion to complete. To get accurate RMS readings, it should take samples at regular intervals over a whole number of half cycles. It does the whole-number-of-half-cycles bit, but it relies on there being no interruptions to get the regular intervals. When I built a project that included a mains voltage monitor, I used an interrupt to take regular samples (every 500us AFAIR) to try to get regular sample intervals. Even better would be to trigger the ADC conversions from a free-running counter/timer.

You may get more consistent readings if you pass a larger number of crossings (and a correspondingly larger timeout) to calcVI.

@DC42

Thanks much to you !

I followed you and increased the number of crossings . But i reduced the timeout . Then it seems to work better than earlier time .

Still there is a fluctuation that happens once for every 8 or 10 seconds . I think that’s because of the capacitor I used ( i used a 0.1uF capacitor instead of 1nF capacitor for Cf value / in data sheet they have asked to use a 1nF cap ).

Below is the code after modification

// EmonLibrary examples openenergymonitor.org, Licence GNU GPL V3

#include “EmonLib.h” // Include Emon Library
EnergyMonitor emon1; // Create an instance

void setup()
{
Serial.begin(9600);

emon1.voltage(2, 6340.26, 1.7); // Voltage: input pin, calibration, phase_shift
emon1.current(3, 1.1); // Current: input pin, calibration.
}

void loop()
{
emon1.calcVI(200,1000); // Calculate all. No.of half wavelengths (crossings), time-out
emon1.serialprint(); // Print out all variables
}

And also I have another problem . I think that must be posted in the programming category . So I posted there . Please use this link to that post .
http://arduino.cc/forum/index.php/topic,163843.0.html/url][/b]

200 half cycles takes 2000ms for a 50Hz mains, plus the sampling code needs time to wait for a zero crossing at the start. So you should increase the timeout to something higher, say 2500ms.

Using a 0.1uF filter capacitor instead of 1nF will slightly reduce the sensitivity at 50Hz and also introduce a small phase shift, but it doesn't account for your occasional low readings. You are getting both low voltage and low current readings at the same time, so either the mains really is dropping for short periods quite often, or something is wrong with the sampling code.

I thought the ACS712 wasn't suitable for such high voltage. Max it can handle is 30V.