I'm trying to build an AC power/energy meter for some project of mine. I'm using the ACS712 Hall Effect current sensor for this project due to its compact size and very high output linearity. I'm using the 30A version of this sensor. I use the ADS1115 16bit ADC to read the output from ACS712 and process it in the code.
I'm building this on an ESP8266 microprocessor board using Arduino IDE. Below is my code for calculating the power. As a starting point, I've taken the code for now from:
// Arduino Energy Meter V2.0
// This code is for This code is for Wemos(ESP8266) based Energy monitoring Device
// This code is a modified version of sample code from https://github.com/pieman64/ESPecoMon
// Last updated on 30.05.2018
#include <Wire.h>
#include <Adafruit_ADS1015.h>
#include <ESP8266WiFi.h>
#include <ESP8266HTTPClient.h>
Adafruit_ADS1115 ads(0x48); /* Use this for the 16-bit version */
#define SERIAL_BAUD 115200
int ledPower = 12;
// ads.setGain(GAIN_TWOTHIRDS); // 2/3x gain +/- 6.144V 1 bit = 3mV (default)
// ads.setGain(GAIN_ONE); // 1x gain +/- 4.096V 1 bit = 2mV
// ads.setGain(GAIN_TWO); // 2x gain +/- 2.048V 1 bit = 1mV
// ads.setGain(GAIN_FOUR); // 4x gain +/- 1.024V 1 bit = 0.5mV
// ads.setGain(GAIN_EIGHT); // 8x gain +/- 0.512V 1 bit = 0.25mV
// ads.setGain(GAIN_SIXTEEN); // 16x gain +/- 0.256V 1 bit = 0.125mV
unsigned int Sensitivity = 66; // 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 = 247.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 = 12; // V2 slider calibrates this
unsigned int pF = 85; // Power Factor default 95
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
Serial.print("Irms: ");
Serial.print(String(Irms, 3));
Serial.println(" A");
Serial.print("Power: ");
Serial.print(String(power, 3));
Serial.print(" W");
Serial.print("\t Energy Unit(s) (KWh): ");
Serial.println(String((Wh/1000), 4));
}
float getVPP()
{
int16_t adc1;
float result;
int readValue;
int maxValue = 0;
int minValue = 32768;
uint32_t start_time = millis();
while((millis()-start_time) < 950) //read every 0.95 Sec
{
readValue = ads.readADC_SingleEnded(1);
if (readValue > maxValue)
{
maxValue = readValue;
}
if (readValue < minValue)
{
minValue = readValue;
}
}
result = ((maxValue - minValue) * Vcc) / 32768.0;
Serial.print("\nVp-p: ");
Serial.println(String(result, 3));
return result;
}
void setup() {
Serial.begin(115200);
Serial.println("\n Rebooted");
Serial.begin(SERIAL_BAUD);
while(!Serial) {} // Wait
Wire.begin(D3,D4); //Changed defaults to match ESP8266 pin connections: 23 - SDA, 24 - SCL
ads.begin();
WiFi.begin("Tulsani", "homenetwork");
Serial.print("Connecting");
while (WiFi.status() != WL_CONNECTED)
{
delay(500);
Serial.print(".");
}
Serial.println();
Serial.print("Connected, IP address: ");
Serial.println(WiFi.localIP());
pinMode(ledPower,OUTPUT);
}
void loop()
{
getACS712();
delay (2000);
}
For testing, I'm using a 200W filament bulb as load.
Issue that I'm facing is:
When I check the current on my DMM, it shows approx 0.8A of current drawn
Output of the ESP8266, using the above code, shows me a current drawn of approx 1A
AC voltage seen on my DMM is approx 245V (I know its on the higher side. It should be around 230V, but what to do ... its India).
Can someone help me understand/identify where am I going wrong or where the code is going wrong? Would be of immense help!
You should not use 10 Hz sampling rate ADC for reading 50 Hz AC sinewave. In referenced code from instructables, reading is done by arduino, and if it's not as precise 10-bits againts 16-bits ads1115, it's 100x times faster.
That makes sense. Considering ADS1115 sampling rate never crossed my mind. Thanks for that.
I am compelled to use the ESP8266 because of its native WiFi support and the small FF compared to the Arduino. Else 10bit ADC resolution was ok with me.
So if I change the sampling rate of the ADC, this should work as I understand?
Yea, I think you may get better accuracy, close to 3-5 % error if running >1000 samples per seconds. Another drawback in the code, that magnitude calculated based on peak-to-peak value, and it's good for RESISTIVE load only. Do your research
on RMS measurements. There are many arduino projects, energy monitoring etc.
I tried with max of 860 SPS, but that didn't help. Output was a little worse, showing approx 1.25A.
I did search on energy meter projects and I found one using Filters.h library, but I couldn't get my head around what it was doing, and hence decided to try a simple method instead.
I see that the ADS1015 is a 12 bit ADC but can sample at 3300 SPS. Might be that will help.
My load will in all likelihood be an SMPS power supply rated at about 5kW to 6kW. Wonder what kind of load that will be considered. I'll check on that as well.
I still don't understand - why isn't, say 128 SPS on the ADS1115, enough to sample a signal of 50 Hz? Or even 860 SPS?
For testing, I'm using a 200W filament bulb as load.
Issue that I'm facing is:
When I check the current on my DMM, it shows approx 0.8A of current drawn
Output of the ESP8266, using the above code, shows me a current drawn of approx 1A
I'd say 0.8A is "approximately" 1A. If the readings are stable and linear you can calibrate (correct) in software. Virtually all measurement equipment is calibrated with pots, or in software.
Typically straight-line calibration is an offset (added/subtracted) and slope (multiplication). And typically, you calibrate the offset at zero and calibrate the slope at maximum. But, you can choose different calibration points (or more calibration points). You'd want to calibrate close to your expected 5-6KW range, or at least check your calibration in that range.
...I took a quick-look at the ACS712 datasheet and it looks like it would take some "study" to figure-out the accuracy.
AC voltage seen on my DMM is approx 245V (I know its on the higher side. It should be around 230V, but what to do ... its India).
Do you trust your DMM?
Is the light bulb rated at 230V? A little "spreadsheet work" says a 230VAC 200W bulb would be 264.5 Ohms* (we don't know the tolerance of the 200W rating).
At 245V you'd get 0.923A (assuming the resistance doesn't change significantly at higher temperature). That's about mid-way between the DMM and the device you're building, with your device being a little closer...
* Incandescent bulbs measure lower resistance when cold, don't trust the DMM resistance measurement.
deepak4you:
I still don't understand - why isn't, say 128 SPS on the ADS1115, enough to sample a signal of 50 Hz? Or even 860 SPS?
The problem is the way your code does a measurement. Just estimate probability your adc takes a sample at exact peak value of the 50 Hz sinewave, if adc is running at 128 sps? Code drops all data except the maximum and minimum samples. My advise, study RMS math, if it's done correctly, according to standard, than 20-th harmonics of the 50 Hz implies 1000Hz and 2000 Hz Nyquist 's sample rate
DVDdoug:
At 245V you'd get 0.923A (assuming the resistance doesn't change significantly at higher temperature). That's about mid-way between the DMM and the device you're building, with your device being a little closer...
I did some tests of lightbulb current versus voltage using tungsten halogen bulbs and found that once the voltage had exceeded around 20% of rated voltage, the bulbs resistance became quite linear in relation to voltage. In other words bulb resistance does vary in relation to supply voltage.
deepak4you:
I still don't understand - why isn't, say 128 SPS on the ADS1115, enough to sample a signal of 50 Hz? Or even 860 SPS?
Because the maximum frequency present in the mains, in the presence of lots of non-linear loads (switch mode
power supplies and fluorescent lights, inverter microwave ovens) is way higher than 50Hz.
If the mains voltage and current were both truly sinusoidal 128SPS would be (just) enough, but mains current is, in a domestic environment, nothing like a pure sinusoid, and even the voltage can be a bit distorted.
I would recommend 1000 SPS in the first instance, as this isn't astronomical and will capture the major
harmonics well.
I hooked up the ADS1015 12bit ADC and tried it with 2400 SPS and 3300 SPS (max). In both cases, it shows the current of about 0.615 Amps.
I don't know which reading to believe anymore Till date, Arduino's 10bit ADC has provided the most identical reading (I'm not calling it the most correct one yet) compared to the DMM.
Attached is the crude circuit layout. Apologies as always for my poor schematic skills. Please note that the ACS712 and ADS11X5 are Arduino ready breakout boards, so they come with all necessary pull up/down resistors and capacitors.
Worse, the ACS712 is a ratiometric sensor, and you're measuring it with an absolute voltage A/D.
That results in zero-current and max current drift if ACS supply is not stable (USB supply is a no-go).
The ACS712 is AFAIK only rated for 184volt AC.
The board it's mounted on migh even be less than that.
Mains power should be measured with a clamp-on sensor, not with an ACS712.
Leo..
Interesting. I couldn't find a max voltage rating for the ACS712 on a quick glance. Might be I missed it.
Anyway, you think a Current Transformer is a better current sensor in my case? And output of the CT would be proportional to the current I assume? If that is the case, then I could simply replace the ACS712 with a CT and change the voltage references as per the CT output?
A contactless clipon CT is safer for mains AC, not better.
A CT could be used on differential inputs of the ADS1115, so you don't have zero current drift.
Not seen a diagram for that yet, but someone might.
Leo..
deepak4you:
I hooked up the ADS1015 12bit ADC and tried it with 2400 SPS and 3300 SPS (max). In both cases, it shows the current of about 0.615 Amps.
I don't know which reading to believe anymore Till date, Arduino's 10bit ADC has provided the most identical reading (I'm not calling it the most correct one yet) compared to the DMM.
I'm really lost now.
The 30A ACS712 won't give you much better resolution than this, and you will need to calibrate the
zero current point anyway.
If you want accurate current use a CT, not a hall sensor.