Note, there are several updates to this device described after the initial description. Scroll down to see them.
I'm using current transformers (200A) that clip around the main power lines coming into the house. These feed one of the analog inputs to an Arduino Duemilanove. I also sample the wall power level to give me a voltage reference and use a basic wall wart for power. Originally I had a Wishield for wifi since there is no network connection near the power panel, but Wifi proved to unreliable over time. I changed my link to an XBee and eventually installed them all over the house.
I'm using current transformers (200A) that clip around the main power lines coming into the house. These feed one of the analog inputs to an Arduino Duemilanove. I also sample the wall power level to give me a voltage reference and use a basic wall wart for power. Originally I had a Wishield for wifi since there is no network connection near the power panel, but Wifi proved to unreliable over time. I changed my link to an XBee and eventually installed them all over the house.
The distance from my power monitor to the House Controller was through a number of walls and proved to be too much for the little XBee to do. I installed an intermediate XBee roughly half way between the two devices and the store-forward capabilities of the XBees took care of the problem nicely.
Every couple of seconds the Arduino samples the power levels, calculates power and holds the values in variables. Every 10 seconds I transmit the data over the XBee network to the House Controller which in turn forwards it with other data from the house on a minute basis to several web based data services. The reason I transmit it every 10 seconds is to update the Power Display I have on a wall that tells me the current power usage at a glance.
So, it's power panel to Arduino to XBee to House Controller to the web. Confused yet?
I'm only serving comma separated values so they're easy to parse. In order they are, real power, apparent power, power factor, rms current, rms voltage and frequency. This makes it really easy to grab the data and forward it using simple code.
Details on the Implementation
Somehow, I needed to measure the current flowing through the wires into the house. There are a number of devices out there that can do this, but they're expensive, won't work on my house, inadequate or the company wouldn't respond to questions. And yes, I did try to call them and even left messages. But prowling the web looking at these devices I discovered a little component called the current transformer.
These things are a coil of wire that wraps around the wire and inductively measures the power flowing through a wire. There are a ton of web descriptions of these devices so learning about them was relatively easy; finding one at a reasonable price was a different matter. Finally, I found a nice device that would work at my house. I have a 400 Amp service that splits into two 200 Amp distributions. I'm currently only using half of it for 200 Amps but the wires leading to it are a little over a half inch in diameter. I actually broke the first device I tried by forcing it over the wire.
Here are the two devices, one on each leg of the 240 split phase mains we have here in the US. They were not hard to install; just clip them around the mains and run the wire out of the box. There's very little risk of shock since the wires are insulated and you don't have to touch them to clip the CT (current transformer) around the wire. They tended to slip down the wire so a couple of plastic ties hold them in place.
You can actually tie these in series, attach a multimeter and get a reasonable indication of the power usage by watching the voltage rise and fall as you turn on appliances. The voltage output has to be compared against some standard before you actually know how much power you're using, but you can certainly tell they are working.
The CTs are available from ITeadStudio and usually have about a two week lead time. Besides the 200 Amp version I used, there are other styles and ratings available. Just be sure to get one big enough to go around the wire.
Most of my implementation was blatantly stolen from Trystan Lea. Please visit his site at http://openenergymonitor.org/emon/ for an in-depth discussion of his ongoing implementation. I took his Arduino code for power monitoring, modified it to work with two current transformers, and added network code to serve data based on a simple html request. Trystan has single phase 240 power and didn’t need to consider using double CTs, but don’t worry, you simply tie the two of them in series if you have split phase 220. My device hasn’t made it past the breadboard stage. Why bother right now? It works fine and I can get back to it anytime.
Most of my implementation was blatantly stolen from Trystan Lea. Please visit his site at http://openenergymonitor.org/emon/ for an in-depth discussion of his ongoing implementation. I took his Arduino code for power monitoring, modified it to work with two current transformers, and added network code to serve data based on a simple html request. Trystan has single phase 240 power and didn’t need to consider using double CTs, but don’t worry, you simply tie the two of them in series if you have split phase 220. My device hasn’t made it past the breadboard stage. Why bother right now? It works fine and I can get back to it anytime.
Original Device
The transformer on the left is a 10V center taped step down to provide a reference for the AC mains. The wires coming in the center are the outputs from the CTs and are tied in series. I use the center connection of the CTs to calibrate a single CT; one has to do this only once in the beginning. Yes, I know that the transformer could be used to power the Arduino, that will come in time, but for now…it works, don’t fix it. The wire leaving the Arduino and looping up to the right is the Wifi antenna; I got the Wishield with the separate antenna to extend the range and prevent worrying about where I put the device.
My future plans for this device are to add screw terminals for the incoming wires, get power from the step down transformer removing the wall wart and solder the various parts down on a proto board to pretty it up. Heck, someday I might actually mount it on a pretty plastic base instead of the piece of plywood I found. I’ll never cover it up though since it’s too much fun to show off the circuitry.
Trystan’s site (http://openenergymonitor.org/emon/) has tons of details on how the measurements are made and links to the source he used. Each implementation will have some nuance that will need adaptation though so use his work as a starting example; consider my work as a recreation and proof of concept. But basically rapid measurements of the instantaneous voltage and current are squared, summed and averaged to get the Vrms and Irms. These are used along with the power factor to get a fairly accurate measure of real power usage. I simply combined this with a very simple web server to pass the data along.
Update March19, 2011: I had a failure of a WiShield in the power display that I will detail on that page later, but this pointed out that I need to have a wireless communication path that has massive availability. I chose XBee as the tool to use. Since this entire set of projects is for saving power, I started with the power monitor. After doing a bunch of work to enable the device to send over XBee radio I realized I had forgotten how I had built the darn thing. So I reverse engineered my own device to get the schematic and am posting it here to help other folks and provide a reference for myself next time.
Simple isn't it? Nothing but a couple of voltage sources and dividers to scale the values for the Arduino to read.
Update March19, 2011: I had a failure of a WiShield in the power display that I will detail on that page later, but this pointed out that I need to have a wireless communication path that has massive availability. I chose XBee as the tool to use. Since this entire set of projects is for saving power, I started with the power monitor. After doing a bunch of work to enable the device to send over XBee radio I realized I had forgotten how I had built the darn thing. So I reverse engineered my own device to get the schematic and am posting it here to help other folks and provide a reference for myself next time.
Simple isn't it? Nothing but a couple of voltage sources and dividers to scale the values for the Arduino to read.
The Arduino Sketch #1, Using Ethernet
//Basic energy monitoring sketch plus kwh and frequency calc - by Trystan Lea
//Licenced under GNU General Public Licence more details here
// openenergymonitor.org
/*
* Credits:
* Most of the power measurement and calculations were invented by Trystan Lea and documented at
* http://openenergymonitor.org/. I modified it for the split phase 240 in residential use in the U.S.
* I'm using a ladyada boot rom (purchased directly from her site) to overcome weak network problems
* and multiple vulnerable arduinos. A lot of the remaining code was taken from example sketches
* supplied by the Arduino site www.arduino.cc
*
* Open source rules!
* Dave Thompson
*/
//Sketch measures voltage and current.
//and then calculates useful values like real power,
//apparent power, powerfactor, Vrms, Irms, frequency and kwh.
#include <WiServer.h>
#include <avr/wdt.h>
#define WIRELESS_MODE_INFRA 1
#define WIRELESS_MODE_ADHOC 2
// Wireless configuration parameters ----------------------------------------
unsigned char local_ip[] = {192,168,0,200}; // IP address of WiShield
unsigned char gateway_ip[] = {192,168,0,1}; // router or gateway IP address
unsigned char subnet_mask[] = {255,255,255,0}; // subnet mask for the local network
PROGMEM const prog_char ssid[] = {"whatever"}; // max 32 bytes
unsigned char security_type = 1; // 0 - open; 1 - WEP; 2 - WPA; 3 - WPA2
// WPA/WPA2 passphrase
const prog_char security_passphrase[] PROGMEM = {"whatever"}; // max 64 characters
// WEP 128-bit keys
// sample HEX keys
prog_uchar wep_keys[] PROGMEM = { 0x11, 0x11, 0x11, 0x11, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Key 0
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Key 1
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Key 2
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // Key 3
};
// setup the wireless mode
// infrastructure - connect to AP
// adhoc - connect to another WiFi device
unsigned char wireless_mode = WIRELESS_MODE_INFRA;
unsigned char ssid_len;
unsigned char security_passphrase_len;
// End of wireless configuration parameters ----------------------------------------
//Setup variables
int numberOfSamples = 3000;
//Set Voltage and current input pins
int inPinV = 4;
int inPinI = 5;
//Calibration coeficients
double VCAL = 0.592;
double ICAL = 03.2;
double PHASECAL = 0.1;
//Sample variables
int lastSampleV,lastSampleI,sampleV,sampleI;
//Filter variables
double lastFilteredV, lastFilteredI, filteredV, filteredI = 0;
double filterTemp;
//Stores the phase calibrated instantaneous voltage.
double calibratedV;
//Power calculation variables
double sqI,sqV,instP,sumI,sumV,sumP;
//Useful value variables
double realPower,
apparentPower,
powerFactor,
Vrms,
Irms;
#define NETTIMECHECK 120
long netTimeout = 0; // counter to check for net activity
//--ENERGY MEASURMENT VARIABLES------------------------------
//Calculation of kwh
//time taken since last measurment timems = tmillis - ltmillis;
unsigned long ltmillis, tmillis, timems;
//time when arduino is switched on... is it 0?
unsigned long startmillis;
//kwhTotal is cumulative kwh today, the other 3 are historical over the last 3 days for comparison.
double kwhTotal =0.0;
//-----------------------------------------------------------
//--FREQUENCY MEASURMENT VARIABLES---------------------------
//time in microseconds when the voltage waveform
//last crossed zero.
unsigned long vLastZeroMsec;
//Micro seconds since last zero-crossing
unsigned long vPeriod;
//Sum of vPeriod's to obtain an average.
unsigned long vPeriodSum;
//Number of periods summed
unsigned long vPeriodCount;
//Frequency
float freq;
//Used to filter out fringe vPeriod readings.
//Configured for 50Hz
//- If your 60Hz set expPeriod = 16666
unsigned long expPeriod = 16666;
unsigned long filterWidth = 2000;
//-----------------------------------------------------------
// This is my page serving function that generates web pages
boolean sendMyPage(char* URL) {
/* The assumption is that if the code got here, the network is alive. However
if too much time passes without someone requesting data, the network may
have died. Therefore, measure the time and after it passes set a watchdog
to expire. In this routine the timer will be reset and the main loop will
set the watchdog.
*/
netTimeout = millis(); // made it, reset the timer
wdt_reset(); // turn off the watchdog
wdt_disable(); // and disable it just in case
Serial.println("net request");
// Check if the requested URL matches "/"
if (strcmp(URL, "/") == 0) {
WiServer.print(realPower);
WiServer.print(", ");
WiServer.print(apparentPower);
WiServer.print(", ");
WiServer.print(powerFactor);
WiServer.print(", ");
WiServer.print(Irms);
WiServer.print(", ");
WiServer.print(Vrms);
WiServer.print(", ");
WiServer.print(freq);
// URL was recognized
return true;
}
// URL not found
return false;
}
/* Measurement calculations */
int PwrCalcs()
{
for (int n=0; n<numberOfSamples; n++) // gather samples
{
//Used for offset removal
lastSampleV=sampleV;
lastSampleI=sampleI;
// wireless server has to have some time to work
WiServer.server_task();
//Read in voltage and current samples.
sampleV = analogRead(inPinV);
sampleI = analogRead(inPinI);
//Used for offset removal
lastFilteredV = filteredV;
lastFilteredI = filteredI;
//Digital high pass filters to remove 2.5V DC offset.
filteredV = 0.996 * (lastFilteredV+sampleV-lastSampleV);
filteredI = 0.996 * (lastFilteredI+sampleI-lastSampleI);
//Phase calibration goes here.
calibratedV = lastFilteredV + PHASECAL * (filteredV - lastFilteredV);
//Root-mean-square method voltage
//1) square voltage values
sqV= calibratedV * calibratedV;
//2) sum
sumV += sqV;
//Root-mean-square method current
//1) square current values
sqI = filteredI * filteredI;
//2) sum
sumI += sqI;
//Instantaneous Power
instP = abs(calibratedV * filteredI);
//Sum
sumP += instP;
//--FREQUENCY MEASURMENT---------------------------
if (n==0) vLastZeroMsec = micros();
//Check for zero crossing from less than zero to more than zero
if (lastFilteredV < 0 && filteredV >= 0 && n>1)
{
//period of voltage waveform
vPeriod = micros() - vLastZeroMsec;
//Filteres out any erronous period measurments
//Increases accuracy considerably
if (vPeriod > (expPeriod-filterWidth) && vPeriod<(expPeriod+filterWidth))
{
vPeriodSum += vPeriod;
vPeriodCount++;
}
vLastZeroMsec = micros();
}
} //end of sample gathering
//Calculation of the root of the mean of the voltage and current squared (rms)
//Calibration coeficients applied.
Vrms = VCAL*sqrt(sumV / numberOfSamples);
Irms = ICAL*sqrt(sumI / numberOfSamples);
//Calculation power values
realPower = VCAL*ICAL*sumP / numberOfSamples;
apparentPower = Vrms * Irms;
powerFactor = realPower / apparentPower;
//FREQUENCY CALCULATION--------------------------
freq = (1000000.0 * vPeriodCount) / vPeriodSum;
vPeriodSum=0;
vPeriodCount=0;
//------------------------------------------------
//--ENERGY MEASURMENT CALCULATION----------------
//Calculate amount of time since last realpower measurment.
ltmillis = tmillis;
tmillis = millis();
timems = tmillis - ltmillis;
//Calculate number of kwh consumed.
//kwhTotal = kwhTotal + ((realPower/1000.0) * 1.0/3600.0 * (timems/1000.0));
//Output to serial
Serial.print("RP=");
Serial.print(realPower);
Serial.print(" AP=");
Serial.print(apparentPower);
Serial.print(" PF=");
Serial.print(powerFactor);
Serial.print(" V=");
Serial.print(Vrms);
Serial.print(" I=");
Serial.print(Irms);
Serial.print(" KW=");
Serial.print(kwhTotal);
Serial.print(" F=");
Serial.println(freq);
//Reset accumulators
sumV = 0;
sumI = 0;
sumP = 0;
}
void setup()
{
Serial.begin(57600);
Serial.println("Initialize web server");
wdt_enable(WDTO_8S); // I'm giving the network 8 seconds to set itself up
// Initialize WiServer and have it use the sendMyPage function to serve pages
WiServer.init(sendMyPage);
// Enable Serial output and ask WiServer to generate log messages (optional)
wdt_reset(); // made it, turn off the watchdog until things start working
wdt_disable();
Serial.println("Enable webserver");
WiServer.setIndicatorPins(5, 6); //This could make it fun to monitor.
//However, the Wishield uses 2,8,9,10,11,12,13
//debug mode for webserver
//WiServer.enableVerboseMode(true);
netTimeout = millis(); // timer to make sure the net is active
//--ENERGY MEASURMENT SETUP--------------------------------
//Calculation of kwh
tmillis = millis();
startmillis=tmillis;
//---------------------------------------------------------
}
void loop()
{
// get power calcs into variables
PwrCalcs();
// Run WiServer
WiServer.server_task();
delay(100);
if ((millis() - netTimeout) >= (NETTIMECHECK * 1000)) //has too much time passed?
{
wdt_enable(WDTO_8S); // eight seconds from now the device will reboot
Serial.println("net timeout, rebooting");
while(1) {}
}
}
Update April 16,2012: After literally months of perfect operation, the device failed. I really don't have a clue what happened, but it quit transmitting power information over my XBee network. A simple reset got it running again which indicates the problem is, most likely, software, not hardware. I didn't get enough to fully understand the failure so I really don't know what to fix. But, since the device has been running really well for months, I just decided to put in an automatic reboot on a periodic basis to clean out any problems that may be building up in the code.
Initially, I wanted to get the correct time from the XBee network and set the Arduino clock for real, but when the Arduino is spending most of its time calculating, it doesn't leave much time for decoding a semi-constant stream of XBee traffic. See, each power reading takes about a second and the incoming data can easily get lost during that second. So, I just gave up on having the correct time on the little device. I added the timeAlarm library for the Arduino playground to the device to handle timing the reset. This allowed me to set up a timer that causes the device to send the power data every 5 seconds. This stuff worked perfectly. I don't know when the device will reset exactly, but it will at roughly 24 hour intervals starting from when it last rebooted.
When I came here to update this blog, I noticed that I never posted the code that uses my XBee network instead of the WiShield and ethernet. I also noticed that the drawing of the home network and photo of the device are way out of date. Guess I'll take care of updating those. I want to keep the WiShield code online to help people that want a web based device, so I'll have both examples here to confuse the reader a little more.
Arduino Sketch #2, Using XBee network
/*
Version 6 of the monitor; I had the device hang up for around 12 hours not
reporting the proper power over the XBee network. The house controller was seeing a
power usage of zero and assumed (correctly) that this was invalid and didn't
update pachube. I didn't think to get enough data from the various devices around
the house before resetting the monitor, so I don't know exactly what happened.
However, the monitor doesn't reset itself to clean out any problems, and since the
reset fixed it; it must have been a software problem. The easiest way to fix this
without information is to just have the board reset itself periodically. So, I'm
just setting the time to something and having it reboot every 24 hours. That will
restart all the stuff and allow it come up cleanly. To do this I added the timeAlarm
library. There are two timers, one to report the power and the other to cause the
reset. I also moved the reporting function into it's own routine partly because that
made it easier to hook it to a timer and partly to clean up the display logic. Since
I have lots of memory on this device, I used sprintf() to format the string for transmission
This can also provide an example for folks trying to do something similar.
Version 5 of the monitor. The first version was ethernet enabled and
used wifi to communicate. Later, I added an XBee and transmitted the data over
it also. Now, I removed the WiShield that allowed connection over the ethernet.
I was getting more and more hangups and resets over time. I suspect there is a
problem in the code with large packets that occasionally travel around and the
device isn't supported anymore, why spend time fixing the code?
So the device transmits only over the XBee now. Which makes it a remote power
monitor that forwards data to another device that can listen to it.
Actually, this isn't a bad idea at all. I can pick up the data from any other
device around the house and do whatever I want with it.
/*
* Credits:
* Most of the power measurement and calculations were invented by Trystan Lea and documented at
* http://openenergymonitor.org/. I modified it for the split phase 240 in residential use in the U.S.
* I'm using a ladyada boot rom (purchased directly from her site) to overcome unexpected problems
* and multiple vulnerable arduinos. A lot of the remaining code was taken from example sketches
* supplied by the Arduino site www.arduino.cc
* Basic energy monitoring sketch plus kwh and frequency calc - by Trystan Lea
* Licenced under GNU General Public Licence more details here
* openenergymonitor.org
*
* Open source rules!
* Pin usage:
* 3,4 for newsoftserial XBee comm
* Analog 0 current sensor
* Analog 1 voltage sensor
*/
//Sketch measures voltage and current.
//and then calculates useful values like real power,
//apparent power, powerfactor, Vrms, Irms, frequency and kwh.
#include <avr/wdt.h>
#include <MemoryFree.h>
#include <avr/pgmspace.h>
#include <SoftwareSerial.h>
#include <Time.h>
#include <TimeAlarms.h>
SoftwareSerial xbeeSerial = SoftwareSerial(3,4);
char Dbuf[100]; // general purpose buffer
//Setup variables
int numberOfSamples = 3000;
//Set Voltage and current input pins
int inPinV = 4;
int inPinI = 5;
//Calibration coeficients
double VCAL = 0.592;
double ICAL = 03.2;
double PHASECAL = 0.1;
//Sample variables
int lastSampleV,lastSampleI,sampleV,sampleI;
//Filter variables
double lastFilteredV, lastFilteredI, filteredV, filteredI = 0;
double filterTemp;
//Stores the phase calibrated instantaneous voltage.
double calibratedV;
//Power calculation variables
double sqI,sqV,instP,sumI,sumV,sumP;
//Useful value variables
double realPower,
apparentPower,
powerFactor,
Vrms,
Irms;
//--ENERGY MEASURMENT VARIABLES------------------------------
//Calculation of kwh
//time taken since last measurment timems = tmillis - ltmillis;
unsigned long ltmillis, tmillis, timems;
//time when arduino is switched on... is it 0?
unsigned long startmillis;
//--FREQUENCY MEASURMENT VARIABLES---------------------------
//time in microseconds when the voltage waveform
//last crossed zero.
unsigned long vLastZeroMsec;
//Micro seconds since last zero-crossing
unsigned long vPeriod;
//Sum of vPeriod's to obtain an average.
unsigned long vPeriodSum;
//Number of periods summed
unsigned long vPeriodCount;
//Frequency
float freq;
//Used to filter out fringe vPeriod readings.
//Configured for 50Hz
//- If your 60Hz set expPeriod = 16666
unsigned long expPeriod = 16666;
unsigned long filterWidth = 2000;
//-----------------------------------------------------------
/* Measurement calculations */
void PwrCalcs()
{
for (int n=0; n<numberOfSamples; n++) // gather samples
{
//Used for offset removal
lastSampleV=sampleV;
lastSampleI=sampleI;
//Read in voltage and current samples.
sampleV = analogRead(inPinV);
sampleI = analogRead(inPinI);
//Used for offset removal
lastFilteredV = filteredV;
lastFilteredI = filteredI;
//Digital high pass filters to remove 2.5V DC offset.
filteredV = 0.996 * (lastFilteredV+sampleV-lastSampleV);
filteredI = 0.996 * (lastFilteredI+sampleI-lastSampleI);
//Phase calibration goes here.
calibratedV = lastFilteredV + PHASECAL * (filteredV - lastFilteredV);
//Root-mean-square method voltage
//1) square voltage values
sqV= calibratedV * calibratedV;
//2) sum
sumV += sqV;
//Root-mean-square method current
//1) square current values
sqI = filteredI * filteredI;
//2) sum
sumI += sqI;
//Instantaneous Power
instP = abs(calibratedV * filteredI);
//Sum
sumP += instP;
//--FREQUENCY MEASURMENT---------------------------
if (n==0) vLastZeroMsec = micros();
//Check for zero crossing from less than zero to more than zero
if (lastFilteredV < 0 && filteredV >= 0 && n>1)
{
//period of voltage waveform
vPeriod = micros() - vLastZeroMsec;
//Filteres out any erronous period measurments
//Increases accuracy considerably
if (vPeriod > (expPeriod-filterWidth) && vPeriod<(expPeriod+filterWidth))
{
vPeriodSum += vPeriod;
vPeriodCount++;
}
vLastZeroMsec = micros();
}
} //end of sample gathering
//Calculation of the root of the mean of the voltage and current squared (rms)
//Calibration coeficients applied.
Vrms = VCAL*sqrt(sumV / numberOfSamples);
Irms = ICAL*sqrt(sumI / numberOfSamples);
//Calculation power values
realPower = VCAL*ICAL*sumP / numberOfSamples;
apparentPower = Vrms * Irms;
powerFactor = realPower / apparentPower;
//FREQUENCY CALCULATION--------------------------
freq = (1000000.0 * vPeriodCount) / vPeriodSum;
vPeriodSum=0;
vPeriodCount=0;
//------------------------------------------------
//--ENERGY MEASURMENT CALCULATION----------------
//Calculate amount of time since last realpower measurment.
ltmillis = tmillis;
tmillis = millis();
timems = tmillis - ltmillis;
//Reset accumulators
sumV = 0;
sumI = 0;
sumP = 0;
}
// this function outputs the current free memory to the serial port
// really nice to use in debugging and making sure the board doesn't
// fail running out of memory
void showMem(){
strcpy_P(Dbuf,PSTR("Mem = "));
Serial.print(Dbuf);
Serial.println(freeMemory());
}
// this set of functions are for a software reset of the board
// the reset function allows a call to location zero which will emulate a reset
// the resetMe funtion allows a normal call from the timer routines
void(* resetFunc) (void) = 0; //declare reset function @ address 0
void resetMe(){ // for periodic resets to be sure nothing clogs it up
Serial.println("Periodic Reset - Normal Operation");
resetFunc();
}
// this little function will return the first two digits after the decimal
// point of a float as an int to help with sprintf() (won't work for negative values)
int frac(float num){
return( (num - (int)num) * 100);
}
// report the power usage over XBee network and out the Serial Port
void reportPower(){
memset(Dbuf,0,sizeof(Dbuf));
Serial.print("Broadcast--");
// first construct the payload line
sprintf(Dbuf,"Power,%d.%02d,%d.%02d,%d.%02d,%d.%02d,%d.%02d,%d.%02d\r",
(int)realPower, frac(realPower),
(int)apparentPower, frac(apparentPower),
(int)powerFactor, frac(powerFactor),
(int)Vrms, frac(Vrms),
(int)Irms, frac(Irms),
(int)freq, frac(freq));
// Display it on the serial monitor for debugging
Serial.print(Dbuf);
Serial.print("\n");
xbeeSerial.print(Dbuf); // out to XBee
}
void setup()
{
Serial.begin(9600);
xbeeSerial.begin(9600);
//--ENERGY MEASURMENT SETUP--------------------------------
tmillis = millis();
startmillis=tmillis;
//---------------------------------------------------------
Serial.println("I'm alive ");
Serial.println("Setting timer for reporting");
/* I really don't care what time it is on this device
it just measure time and reports. But, I want the timer capability
to allow a reset every 24 hours and to handle the reporting function
so I just set the time to something reasonable and get on with the
rest of the work.
*/
setTime(0,0,0,1,1,12);
Alarm.timerRepeat(5, reportPower); // report the power usage every 5 seconds
Alarm.alarmRepeat(23,59,0,resetMe); // periodic reset to keep things cleaned up
// I use a lot of libraries and sometimes they have bugs
// as well as hang ups from various hardware devices
showMem(); // to make sure I don't make it too big to fit in ram reliably
Serial.println("Init done");
xbeeSerial.println("Is the XBee there?");
wdt_enable(WDTO_8S); // No more than 8 seconds of inactivity
}
/*
The loop() just calculates power over and over again. There is a timer
set in setup() that causes the device to report every few seconds.
The loop() also resets the watchdog timer so it doesn't time out.
*/
void loop()
{
// get power calcs into variables
PwrCalcs();
wdt_reset(); // watchdog timer set back to zero
Alarm.delay(0); // This causes the alarm timer to update
}
Notice that the comments at the top are almost as long as the code? Yes, I have trouble keeping track of the various problems, changes, and solutions to my devices over months of time. It's funny how often I come here to see what I did about a particular problem. Nice way to keep a diary of this kind of thing. Notice also that the loop() routine only calculates the power, updates the watchdog, and updates the alarm timer. If it hangs in a loop somewhere the watchdog will reset it and start over. Every 5 seconds the alarm code causes it to send the data; that's about as simple an implementation as I can come up with. The timers are set up in the setup() routine and the reporting is done using the callback routine reportPower(). The XBee for this device is set up in transparent mode; this is a specific mode for the XBees and you'll understand this when you start working with the little devices, but it means that I don't have to have special encoding or decoding software to use it.
Device as of April 16, 2012
Three people now have asked me how to put the CTs in series. It's actually pretty simple to do: first put them on the incoming power wire, see the picture of my power panel with the CTs installed. Then hook one color wire from one CT to the other color wire from the other CT. The remaining two wires are your input. Measure the input and then hook two wire of the same color together and measure again. You want the highest reading. Notice above that I have a black and white wire running to the center of the three screw input block; that's where I short them together. I just wrapped them together and screwed them down. The other two wires on the other terminals are my input.
Now, I'll let the device run until the next problem crops up.
Update, February 14, 2013: I decided to move the software forward from Arduino IDE 21 to the latest, 1.0.3. I expected this to be a pain, so I decided to make it worse and convert the XBee code to use the library created by Andrew Rapp. I noticed a while back that the library had been updated to work with Software Serial and supported the multiple serial ports on the Mega2560 board. This means I can still use the software serial pins and have the normal serial pins for debugging and other things.
Well, it turned out to be pretty painless. Yes, there were a few items that I had to work out, but it took less than a morning to make all the changes and return the device to service. The code was greatly simplified by using the library, although I now have the XBee running in API mode 2 to fit with the library. This is OK though since I discovered that I can monitor it with a little XBee sniffer I put together, or tell the XBee to transmit in broadcast for a while if I think there are problems. Here's the updated code:
The Arduino Sketch
/*
Version 9, The changes to this device to stop using XBee broadcast fixed all the
problems with it failing over time. A month ago I discovered the XBee library
had been updated to support the SoftwareSerial library and this means I can now
use the library to simplify the code I have to write, and rejoin the mainstream
of XBee work. This version has the library associated and it made the XBee send
much, much simpler. I don't check the transmit response frame to see what happened
with the transmission. I will if there seems to be a problem at any point. However,
that always brings up the problem of what to do with an error report. I have
nowhere to send it.
Version 8, Well, as I add devices to the network, I have to stop using broadcast.
Each device is echoing the broadcast and totally clogging up the network with
retransmissions. So, this version is the same as 7 except it sends the packets to
the house controller. The house controller is responsible for updating status now,
so that isn't too bad.
Version 7 of the monitor; still trying to get around the problem of short lines.
I got a clue when it stopped a couple of days ago. Monitoring the XBee traffic, it
was stopping in exactly the same place each time. Then after a second or two it would
continue. The lines were always complete, but characters inside were separated by a
couple of seconds. This meant that the clock or something else was able to get in
the middle of the transmission. This was happening on every single line, so the
controller couldn't decode the transmission. I switched the XBee to API mode and put
code in to support it so that each packet was complete. Now to wait a week or so to
see if this gets rid of the problem. Months of working just fine and then this shows
up. Funny how something like this hides forever.
Version 6 of the monitor; I had the device hang up for around 12 hours not
reporting the proper power over the XBee network. The house controller was seeing a
power usage of zero and assumed (correctly) that this was invalid and didn't
update pachube. I didn't think to get enough data from the various devices around
the house before resetting the monitor, so I don't know exactly what happened.
However, the monitor doesn't reset itself to clean out any problems, and since the
reset fixed it; it must have been a software problem. The easiest way to fix this
without information is to just have the board reset itself periodically. So, I'm
just setting the time to something and having it reboot every 24 hours. That will
restart all the stuff and allow it come up cleanly. To do this I added the timeAlarm
library. There are two timers, one to report the power and the other to cause the
reset. I also moved the reporting function into it's own routine partly because that
made it easier to hook it to a timer and partly to clean up the display logic. Since
I have lots of memory on this device, I used sprintf() to format the string for transmission
This can also provide an example for folks trying to do something similar.
Version 5 of the monitor. The first version was ethernet enabled and
used wifi to communicate. Later, I added an XBee and transmitted the data over
it also. Now, I removed the WiShield that allowed connection over the ethernet.
I was getting more and more hangups and resets over time. I suspect there is a
problem in the code with large packets that occasionally travel around and the
device isn't supported anymore, why spend time fixing the code?
So the device transmits only over the XBee now. Which makes it a remote power
monitor that forwards data to another device that can listen to it.
Actually, this isn't a bad idea at all. I can pick up the data from any other
device around the house and do whatever I want with it.
/*
* Credits:
* Most of the power measurement and calculations were invented by Trystan Lea and documented at
* http://openenergymonitor.org/. I modified it for the split phase 240 in residential use in the U.S.
* I'm using a ladyada boot rom (purchased directly from her site) to overcome unexpected problems
* and multiple vulnerable arduinos. A lot of the remaining code was taken from example sketches
* supplied by the Arduino site www.arduino.cc
* Basic energy monitoring sketch plus kwh and frequency calc - by Trystan Lea
* Licenced under GNU General Public Licence more details here
* openenergymonitor.org
*
* Open source rules!
* Pin usage:
* 3,4 for newsoftserial XBee comm
* Analog 0 current sensor
* Analog 1 voltage sensor
*/
//Sketch measures voltage and current.
//and then calculates useful values like real power,
//apparent power, powerfactor, Vrms, Irms, frequency and kwh.
#include <avr/wdt.h>
#include <MemoryFree.h>
#include <avr/pgmspace.h>
#include <SoftwareSerial.h>
#include <Time.h>
#include <TimeAlarms.h>
#include <XBee.h>
char verNum[] = "Version 9";
SoftwareSerial xbeeSerial = SoftwareSerial(3,4);
XBee xbee = XBee();
char Dbuf[100]; // general purpose buffer
//Setup variables
int numberOfSamples = 3000;
//Set Voltage and current input pins
int inPinV = 4;
int inPinI = 5;
//Calibration coeficients
double VCAL = 0.592;
double ICAL = 03.2;
double PHASECAL = 0.1;
//Sample variables
int lastSampleV,lastSampleI,sampleV,sampleI;
//Filter variables
double lastFilteredV, lastFilteredI, filteredV, filteredI = 0;
double filterTemp;
//Stores the phase calibrated instantaneous voltage.
double calibratedV;
//Power calculation variables
double sqI,sqV,instP,sumI,sumV,sumP;
//Useful value variables
double realPower,
apparentPower,
powerFactor,
Vrms,
Irms;
//--ENERGY MEASURMENT VARIABLES------------------------------
//Calculation of kwh
//time taken since last measurment timems = tmillis - ltmillis;
unsigned long ltmillis, tmillis, timems;
//time when arduino is switched on... is it 0?
unsigned long startmillis;
//--FREQUENCY MEASURMENT VARIABLES---------------------------
//time in microseconds when the voltage waveform
//last crossed zero.
unsigned long vLastZeroMsec;
//Micro seconds since last zero-crossing
unsigned long vPeriod;
//Sum of vPeriod's to obtain an average.
unsigned long vPeriodSum;
//Number of periods summed
unsigned long vPeriodCount;
//Frequency
float freq;
//Used to filter out fringe vPeriod readings.
//Configured for 50Hz
//- If your 60Hz set expPeriod = 16666
unsigned long expPeriod = 16666;
unsigned long filterWidth = 2000;
//-----------------------------------------------------------
/* Measurement calculations */
void PwrCalcs()
{
for (int n=0; n<numberOfSamples; n++) // gather samples
{
//Used for offset removal
lastSampleV=sampleV;
lastSampleI=sampleI;
//Read in voltage and current samples.
sampleV = analogRead(inPinV);
sampleI = analogRead(inPinI);
//Used for offset removal
lastFilteredV = filteredV;
lastFilteredI = filteredI;
//Digital high pass filters to remove 2.5V DC offset.
filteredV = 0.996 * (lastFilteredV+sampleV-lastSampleV);
filteredI = 0.996 * (lastFilteredI+sampleI-lastSampleI);
//Phase calibration goes here.
calibratedV = lastFilteredV + PHASECAL * (filteredV - lastFilteredV);
//Root-mean-square method voltage
//1) square voltage values
sqV= calibratedV * calibratedV;
//2) sum
sumV += sqV;
//Root-mean-square method current
//1) square current values
sqI = filteredI * filteredI;
//2) sum
sumI += sqI;
//Instantaneous Power
instP = abs(calibratedV * filteredI);
//Sum
sumP += instP;
//--FREQUENCY MEASURMENT---------------------------
if (n==0) vLastZeroMsec = micros();
//Check for zero crossing from less than zero to more than zero
if (lastFilteredV < 0 && filteredV >= 0 && n>1)
{
//period of voltage waveform
vPeriod = micros() - vLastZeroMsec;
//Filteres out any erronous period measurments
//Increases accuracy considerably
if (vPeriod > (expPeriod-filterWidth) && vPeriod<(expPeriod+filterWidth))
{
vPeriodSum += vPeriod;
vPeriodCount++;
}
vLastZeroMsec = micros();
}
} //end of sample gathering
//Calculation of the root of the mean of the voltage and current squared (rms)
//Calibration coeficients applied.
Vrms = VCAL*sqrt(sumV / numberOfSamples);
Irms = ICAL*sqrt(sumI / numberOfSamples);
//Calculation power values
realPower = VCAL*ICAL*sumP / numberOfSamples;
apparentPower = Vrms * Irms;
powerFactor = realPower / apparentPower;
//FREQUENCY CALCULATION--------------------------
freq = (1000000.0 * vPeriodCount) / vPeriodSum;
vPeriodSum=0;
vPeriodCount=0;
//------------------------------------------------
//--ENERGY MEASURMENT CALCULATION----------------
//Calculate amount of time since last realpower measurment.
ltmillis = tmillis;
tmillis = millis();
timems = tmillis - ltmillis;
//Reset accumulators
sumV = 0;
sumI = 0;
sumP = 0;
}
// this function outputs the current free memory to the serial port
// really nice to use in debugging and making sure the board doesn't
// fail running out of memory
void showMem(){
strcpy_P(Dbuf,PSTR("Mem = "));
Serial.print(Dbuf);
Serial.println(freeMemory());
}
// this set of functions are for a software reset of the board
// the reset function allows a call to location zero which will emulate a reset
// the resetMe funtion allows a normal call from the timer routines
void(* resetFunc) (void) = 0; //declare reset function @ address 0
void resetMe(){ // for periodic resets to be sure nothing clogs it up
Serial.println("Periodic Reset - Normal Operation");
resetFunc();
}
// this little function will return the first two digits after the decimal
// point of a float as an int to help with sprintf() (won't work for negative values)
int frac(float num){
return( (num - (int)num) * 100);
}
// report the power usage over XBee network and out the Serial Port
void reportPower(){
memset(Dbuf,0,sizeof(Dbuf));
Serial.print("Broadcast--");
// first construct the payload line
sprintf(Dbuf,"Power,%d.%02d,%d.%02d,%d.%02d,%d.%02d,%d.%02d,%d.%02d\r",
(int)realPower, frac(realPower),
(int)apparentPower, frac(apparentPower),
(int)powerFactor, frac(powerFactor),
(int)Vrms, frac(Vrms),
(int)Irms, frac(Irms),
(int)freq, frac(freq));
// Display it on the serial monitor for debugging
Serial.print(Dbuf);
Serial.print("\n");
sendStatusXbee(Dbuf);
}
void setup()
{
Serial.begin(9600);
Serial.println(verNum);
xbeeSerial.begin(9600);
// This sets the XBee library to use the software serial port
xbee.setSerial(xbeeSerial);
//--ENERGY MEASURMENT SETUP--------------------------------
tmillis = millis();
startmillis=tmillis;
//---------------------------------------------------------
Serial.println("I'm alive ");
Serial.println("Setting timer for reporting");
/* I really don't care what time it is on this device
it just measure time and reports. But, I want the timer capability
to allow a reset every 24 hours and to handle the reporting function
so I just set the time to something reasonable and get on with the
rest of the work.
*/
setTime(0,0,0,1,1,12);
Alarm.timerRepeat(5, reportPower); // report the power usage every 5 seconds
Alarm.alarmRepeat(23,59,0,resetMe); // periodic reset to keep things cleaned up
// I use a lot of libraries and sometimes they have bugs
// as well as hang ups from various hardware devices
showMem(); // to make sure I don't make it too big to fit in ram reliably
pinMode(6,OUTPUT);
digitalWrite(6,LOW);
Serial.println("Init done");
wdt_enable(WDTO_8S); // No more than 8 seconds of inactivity
}
/*
The loop() just calculates power over and over again. There is a timer
set in setup() that causes the device to report every few seconds.
The loop() also resets the watchdog timer so it doesn't time out.
*/
void loop()
{
// get power calcs into variables
PwrCalcs();
wdt_reset(); // watchdog timer set back to zero
Alarm.delay(0); // This causes the alarm timer to update
}
Of course, I have the XBee handling in a separate source file (these are known as 'tabs' in the Arduino IDE) so this is the code that resulted from moving to the XBee library:
The XBee 'tab'
XBeeAddress64 HouseController = XBeeAddress64(0x0013A200, 0x4055B0A6);
XBeeAddress64 Broadcast = XBeeAddress64(0x00000000, 0x0000ffff);
void sendStatusXbee(char *msg){
byte checksum = 0;
char xbeeOut[100];
int i, length;
digitalWrite(6,HIGH); // indicator light on
Alarm.timerOnce(1, indicatorOff); // Turn the indicator led off in a second
ZBTxRequest zbtx = ZBTxRequest(HouseController, (uint8_t *)msg, strlen(msg));
xbee.send(zbtx);
}
void indicatorOff(){
digitalWrite(6,LOW);
}
Notice that I have both the addresses for the House Controller and Broadcast in the file. All I have to do to revert to broadcast for debugging is to use the broadcast address. Nice little capability; I may automate that in the future.


Cool how you glued together various components to build a smart solution that really seems to pay off
ReplyDeleteI saw your post on jeelabs and thought I'd just check your project out.
Thanks for visiting. This is, and has been, an interesting project that actually pays off.
ReplyDeleteHello Dave, Nice work! do you mind if I link through to your write-up I'm sure it would be of great use to people. I have put a link at the bottom of the page here:
ReplyDeletehttp://openenergymonitor.org/emon/node/58
Thanks, Trystan
Of course I don't mind. Especially since I stole so many ideas from you and your site.
ReplyDeleteAs I understand it, you have put the two CT's on the 220V mains input wires in series so you are only using a single ADC channel on the Arduino. Is this accurate?
ReplyDeleteOtherwise, nice work. I am building a similar system based on Trystan's info as well. Thanks for sharing.
-Jon
Yes, the two CTs are in series on one input to the Arduino. I calibrated them one at a time using a current meter around the wire then hooked them up. If you get too low a level when you hook them in series reverse the phase on one of them. I did the final test with a 2KW load (large light) plugged into one side then the other.
ReplyDeleteNice work and great website.
ReplyDeleteThanks for sharing.
Dawn.
Hi, I noticed that you and Trystan Lea seem to disagree in the RMS calculation section 1).
ReplyDeleteYou square the CalibratedV (what Lea calls shiftedV), but Lea squares filteredV...
It seems to me that you are probably correct, can you confirm that? Thanks!
Y'know, I'm not sure there's a significant difference. I chose to square the calibrated value because it matched the metering equipment I was using that way. I'm not sure why Trystan did it his way. However, I was able to read a commercial meter and compare it to mine and that gave me a bunch more confidence in the voltage reading.
ReplyDeleteBasically, I wanted my values to be as close to a calibrated commercial source as I could get to keep the debugging and tweaking down.
I'm from Brazil and we have 2 x 110v phases and 1 neutral.
ReplyDeleteHow did you measure voltage on 2 phases? You wire your transformer only on one phase or each leg of transformer input on each phase and you don't use the neutral? Is it better to use two transformers to get two different reading from each phase?
Take a look at the pictures above, I used two CTs, one on the hot leg of each phase. You can then wire them in series to get the total power used or read each one in the code and add them together. To calibrate them, only hook one up and put a controlled load on that leg of the power.
ReplyDeleteDave,
ReplyDeleteI am trying to get my graphs to work in Pachube. Right now, I only have one graph and cannot figure out how to get multiple datastreams. How did you get yours to works.
Thanks,
Paul.
Notice in the sketch I sent a comma separated string of values, something like:
ReplyDelete100,200,300,400,500
Pachube itself separated these out into separate data streams. At first I had forgotten a couple of things and sent about 10 items separated by strings and it took some time to figure out why I had too many datastreams showing up. I simply deleted the extras on pachube and corrected my code.
So, each value in the csv (comma separated values) string is assigned to a data stream.
I was too lazy to learn JSON or EML or the other methods when something this simple was available.
Dave,
ReplyDeleteThanks for your reply.
I finally figured it out. If the content_length is larger than the length of the data being received it won't work.
I just put in a number for my content_length. Do you have a formula to find the data length or did you just do it by putting in a numerical value too.
Thanks,
Paul.
I have two different devices that update to pachube right now. I'll be using only one in a couple of weeks. I'm moving my update operation to a more dedicated device. However, with the WiShield, it takes care of the length for me and I don't have to do anything. With an 'official' arduino ethernet board, I get the data string all formated then then do a strlen(dataString), send the standard header, with the length in it and lastly, the data string.
ReplyDeleteHere's the code fragment that I use:
strcpy_P(Dbuf2, PSTR("%d,%d,0.%d,%d.%02d,%d.%02d,%d.%02d"));
sprintf(dataBuf,Dbuf2,
(int)round(realPower),
(int)round(apparentPower),
(int)(powerFactor*100),
(int)rmsCurrent,
(int)(((rmsCurrent+0.005) - (int)rmsCurrent) * 100),
(int)rmsVoltage,
(int)(((rmsVoltage+0.005) - (int)rmsVoltage) * 100),
(int)(frequency),
(int)(((frequency+0.005) - (int)frequency) * 100));
strcpy_P(Dbuf,PSTR("Pachube Connecting..."));
Serial.print(Dbuf);
if(pachube.connect()){
strcpy_P(Dbuf,PSTR("OK"));
lastConnectionTime = millis();
Serial.println(Dbuf);
tNow = now();
strcpy_P(Dbuf,PSTR("PUT /api/feedNumber.csv HTTP/1.1\n"));
pachube.print(Dbuf);
strcpy_P(Dbuf,PSTR("Host: www.pachube.com\n"));
pachube.print(Dbuf);
strcpy_P(Dbuf,PSTR("X-PachubeApiKey:bunch of numbers\n"));
pachube.print(Dbuf);
strcpy_P(Dbuf,PSTR("Content-Length: "));
pachube.print(Dbuf);
pachube.println(strlen(dataBuf), DEC); // this has to be a println
strcpy_P(Dbuf,PSTR("Content-Type: text/csv\n"));
pachube.print(Dbuf);
strcpy_P(Dbuf,PSTR("Connection: close\n"));
pachube.println(Dbuf);
pachube.println(dataBuf);
pachube.stop();
}
Notice that I had some trouble with the darn floating point numbers getting them into a sprintf that doesn't support floats. But that was easier than writing special code for it.
Hope this helps
Dave,
ReplyDeleteCool. Thanks for the code. I had problem with floats too. So I just stuck with client.print instead of sprintf but it is kind of a hack job and I would like to pretty it up.
cheers,
Paul.
The information you have on this page is great, and it's prompted me to start getting into Arduino projects. I've been able to pretty much piece together the general way I could go about duplicating your work, but I cannot seem to source the ac/ac step-down transformer.
ReplyDeleteAny suggestions would be greatly appreciated.
Something like this will work fine. http://search.digikey.com/scripts/DkSearch/dksus.dll?Cat=590573&k=T1004-ND It's a wall wart for supplying power for a clock or something. I picked mine up at Fry's Electronics when I was there drooling over other stuff. Just look for something that will take line voltage to 6-12VAC. The only hard requirement is that it be AC to AC, which leave out cell phone chargers and such. You can tailor the voltage divider to provide reference voltage to the Arduino which only needs a volt or two.
ReplyDeleteHi Dave!
ReplyDeleteGreat work! Can u explain where this come from (mathematically)?
"kwhTotal = kwhTotal + ((realPower/1000.0) * 1.0/3600.0 * (timems/1000.0));"
Thanks!
Gabriel
add real power / 1000 (power in watts) / number of seconds in an hour * how long it has really been in seconds
ReplyDeleteNotice that I don't actually use this number. There is a small amount of drift from what I read and what they read. It's a tiny amount, but it adds up over time to totally confuse me. I could have tailored this more and added corrections, but what I was really interested in was what I'm using right now. Controlling it on an ongoing basis is better than looking at it at the end of the month.
Hey, this is a really cool setup, I'm building a project where the arduino acts as a webserver and hosts a simple html page to control relays. I cannot perform exactly what I like though because of the below errors. Seems the sample libraries we used are the same so I thought maybe you ran into this problem too. The sample code Webserver works and I can host a html page in one big string, but I want to print different things to the server to change/update the website on current values. This may be a bit different but you seem knowledgeable so I thought I would ask. Also, having trouble with the get request to receive data when a button on the webpage is clicked. Thank you very much.
ReplyDeleteC:\Users\COLINV~1\AppData\Local\Temp\build8672606563252562455.tmp/sketch_may11a.cpp:283: undefined reference to `WiServer'
C:\Users\COLINV~1\AppData\Local\Temp\build8672606563252562455.tmp/sketch_may11a.cpp:173: undefined reference to `Server::server_task()'
This looks like the compiler can't find the library. The source needs to be in the library directory of the Arduing IDE and you have to restart the IDE before it will know it is there. Also, it can happen if you forget the #include for the WiShield software up at the top of the module.
ReplyDeleteDear, The port analog arduino number of A4 and A5 I turn on the circuit above? I can connect the output of 5 V arduino too?
ReplyDeleteYes, I used the Arduino 5V and ground.
ReplyDeleteWhy are you using 320Ohm burden resistor with this CT?
ReplyDeleteI made some calculations and on 3.3v I would use 36Ohm resistors or 56Ohm on 5v.
If I remember right, it was what I had on hand at the time. It's easy to correct for values (within reason) in the code. That's the beauty of using a computer.
ReplyDeleteI truly like to reading your post. Thank you so much for taking the time to share such a nice information. I'll definitely add this great post in my article section.
ReplyDeleteFreight Forwarding Software
I was looking for a way to send you a private email, but it doesn't look like there's a way. I hate to offer any feedback that isn't completely constructive, so I hope this helps you at some point in the project:
ReplyDeleteThis is a great project and I love what you're doing. Just one issue that might really confuse you or someone else at some point trying to duplicate this(unless I'm really missing something).
The way you have this configured is not really measuring what you think it's measuring. I'm sure you've calibrated out some error but various reactive appliances will not read correctly (unless, like I said, I'm really missing something). The two 110 lines coming in to your home are two separate feeds. They are 180 degrees out of phase and power various device in your home either completely separately, or in the case of a 220 dryer, as a combined (but out of phase) load. From your photo, you've got the CT's placed out of phase from each other, which will (sort of) provide an additive effect. However, since reactive loads will shift the phase of each one a different amount, what you end up with is two semi-out of phase signals combined. To add insult to injury, you are only using one of the two lines for your voltage reading. As such, any reactive voltage components on the second line are completely ignored. As such, a reactive component on the unmeasured line will read as if it is purely resistive.
You might think the error is small, but I would estimate that if you calibrate to a load on one line, a load of the same power consumption but a different reactive component on the other line could easily read up to 20-40% off.
Please let me know if I'm missing something. Again, thanks for all the great work!
You're right. The situation is that I have to make some trade offs or spend a heck of a lot of money setting up highly accurate devices. The goal here is not to get the numbers exact, but close enough.
ReplyDeleteThe trade off is not between the numbers I get and a very accurate reading, it's between a coarse reading of instantaneous power or nothing at all. See, the only method that's available without instrumentation, is to read the meter and write it down, come back later and read the meter and write it down...etc. Each of these is after the fact and I already owe the money for the period that the measurement took. If the power company provided meters that had a pulse output, the readings would be as accurate as the power meter provides, but that's not the case in my area.
Regarding power factor. I did a long test of power factor and it never varied more than around 3%, which corresponded to the numbers the power company said their reading were. So, things like dryer motors, A/C motors and such don't really cause much problem. I measured the voltage on the two legs of the split phase 220 and graphed them over a week and they never varied more than a volt from each other. Actually, this made sense because all my heavy loads are 220 and suck from both sides.
Things changed a bit when I got a permanent magnet motor for the swimming pool. It's highly reactive and causes a drop in the power factor that definitely will cause a misreading. I'll work on that problem someday, but for now, the period is so small I'm just ignoring it.
Notice that I don't try to accumulate the power usage over time (at least not anymore) I use the device to tell me trends, tracing high usage periods and monitoring the usage of appliances. When I first put it in, I did accumulate values and they corresponded within 2% to the power company values. Then I went through a period of flaky power and recurring outages. That convinced me that I didn't want to try and argue total usage with the power company.
I'd just lose.
There are several factors that help me in this. My major loads are all 220. I only have one seriously reactive load. I balanced my 110V legs by moving appliances (actually repositioning the breakers) such that the two legs are close to each other. And probably a couple of things I've forgotten. Over the past year and some months my peak demand bills from the power company have matched pretty darn closely the numbers I get going back through the readings.
ReplyDeleteRegarding calibration: It was tough at first. I started out using a resistive load (toaster) and comparing it to a Kill-a-watt; when I was comfortable this was working correctly using light bulbs heaters and such, I tried reactive loads and was really surprised that they matched up equally well. Then I used a 220V resistive load (my water heater) and added the phases as you noticed. That compared favorably with a true rms meter I borrowed from an electrician down the street and around the corner. Then I compared this to a 220V motor (A/C compressor and water pump motor) and was again surprised that it matched. So, I hooked it up and took on the power company. I described this interaction somewhere on this site. It resulted in them replacing the meter and suddenly, my readings and the meter's readings matched each other. After that I, unsuccessfully, tried accumulating values.....
So, while it isn't as accurate as the power company's equipment,it does give me a good indication of my instantaneous power usage. I can turn on a motor and see the increase immediately. I can tell at a glance when the outside security lights have been left on or the dishwasher drying element was not turned off. I have watched the electric oven cycle the element to keep the temperature even and the same with the electric dryer element. I can even see when the vacuum cleaner motor is turned on; amazing how much power a 10 amp vacuum cleaner uses.
In our efforts to lessen our electricity usage, we are severly hampered by not having anything that will tell us what our usage is 'right now'. This little device with all its shortcomings, does this admirably well in my case.
And, it was about a quarter of the cost of the cheapest similar device.
My thoughts for the future of this device follow your suggestions, measuring the voltage for both legs and separating the CTs such that I can actually measure the usage on each leg. The problem with this is the additional isolation transformer and increased load on the tiny processor. This would be added and then my reading would be closer to absolutely correct. If I'm someday lucky enough to get a meter with a pulse output, I may read it and get away from the CTs altogether, but I'm not holding my breath. I do not plan on accumulating values, that will just lead to frustration when my numbers and the power company's don't match. I'm satisfied knowing that I'm using somwhere around 6.8KW when the South A/C kicks on at the same time as the pool motor.
Thanks for bringing this up. I may add a link to your comment and my response to the page so people can get a clearer understanding of the purpose.
And, sorry I rambled on so long.....
I second Dave here. I have to deal with 2 X 120 volts and placed a CT on each leg of the 220 volts coming in. I use the reference voltage from only one leg, since I, too, measured really small (negligible?) differences between the two legs over a period of time. Most heavy loads use 220 volts anyway.
ReplyDeleteContrary to Dave, I don't sum the two reading from the CTs at input, but calculate two separate values and sum the resulting current/watt readings. This tales care of any phase discrepancies between various CTs.
I actually use 5 CTs in my setup, which keeps the Arduino really busy. The Arduino runs at full speed, without delays in the code and outputs one set of values every 3 seconds or so.
OK, you two guys have convinced me...see, I can learn from others as well. I'm going to change the code and hook the other CT to one of the other arduino pins. I have plenty of room for additional resistors and it's not like I have to worry about running out of Arduino pins! It'll be interesting to see how much difference this makes.
ReplyDeleteUnfortunately, it'll mean that I have to recalculate the phase angle difference caused by the CT itself, but if I remember correctly, it's not all that hard to do.
Robert, you convinced me to try this; my hesitation was that I wasn't sure the arduino could keep up with the floating point math.
I suspect it will wait until after Christmas though. Well, and New Year too. And, the hangover after New Year.
Hello!
ReplyDeleteThank you for sharing your awesome creation! I am trying to build a contraption to monitor my well pump. I'm having a tough time understanding how you have your CT's wired. I would pay pal you 10 dollars if you would throw together a little sketch (even in mspaint) of how you have those wired in series and where they connect to your arduino. If you get a chance would you shoot it to me in an e-mail? Cyrus@jandjpumps.com
I would really appreciate your help!
Cyrus, I dropped you a note on how I figured out the wiring. If it doesn't help, write me back and I'll explain it better (well, maybe better). Regarding how it's wired into the Arduino, I just hooked it to an analog input and started reading the voltage. The schematic above shows how I used a voltage divider to get the proper range and which pins I used to do it.
ReplyDeleteHi Dave I am trying also to build a energy monitor and I have some questions about you design. How did you wire the current transducers in series and what kind of circuitry you build for voltage/current measurement? I mean what kind of sensor electronic did you use to connect the current transducers into the arduino.
ReplyDeletemy email is xeneixen@hotmail.com
Thank you
Hi Dave, as you I followed Trystan assembly and made some modifications. I am trying to save the data into a csv file using a memory card connected to the arduino. However I am having a problem in creating the dataString, because it seems like it doesn't support float numbers. I have checked the code that you used to pass the values to pachube but I didn't get how to do it in my case.
ReplyDeleteI want to do something like this: // String dataString = String(emon.realPower) + "," + String(emon.apparentPower) + "," + String(emon.powerFactor) + "," + String(emon.Vrms) + "," + String(emon.Irms); //
because after I want to save this string in the memory card using something like this :
logFile.println(dataString);
logFile.close();
Serial.println(dataString);
but I receive an error when creating the dataString, because of the float numbers and I need them to be float because of the accuracy.
Can you give me some help. my email is pedrosantosg89@gmail.com
Thank you very much
hi DAve thx for sharing . i'm building a power analyzer. i have some questions
ReplyDeletemamadoulamine.deme@uqtr.ca
In case anyone's wondering, the folks above that asked for individual help were contacted directly using email. I'm not sure they got it all working, but I tried to help them directly. Floating point numbers, using the arduino ethernet library are a bit of a pain, but they can be done. Using the arduino String library will cause pretty serious problems over time due to memory fragmentation and the arduino will usually reboot itself. So, use some of the memory tricks that I've detailed on other parts of this site to help with these problems.
ReplyDeleteHello At the entrance you A4 binds directly to the network voltage from 127v. It is not necessary to rectify with diodes? I'm doing a college project and I found a very interesting method of measuring voltage. So I connect the A4 directly into Arduino? How do I convert the reading port A4 (digital) to display the value in voltage? Thank you. Marcelo - marcelocuin@gmail.com
ReplyDeleteNo, the input to both pins on the Arduino is AC. One is for measuring voltage and the other is for measuring current. A4 specifically is for measuring the incoming AC voltage from the mains. I first reduce it to a little over 9VAC with a transformer, then further lower it using a voltage divider so that I can stay inside the tolerance of the Arduino analog input pin. A5 is taken from the current transformers installed inside the mains box. They also go through a voltage divider to keep the voltage inside the Arduino range.
ReplyDeleteYou are measuring the AC power, do not rectify it to DC, use the actual (reduced voltage) in your calculations. To get the voltage from your readings, hook a meter to the mains and adjust the compensation value in your calculations to match it. Since you don't have any capacitors or other active components, it will track just fine for different voltages.
Very nice write up. I was actually just on opensourcenergymonitor trying to figure out the best way to monitor my whole home usage. What I don't understand is why is the step-down transformer necessary? If I know my total voltage coming into the house is 240V, then why couldn't I just use that in my calculations?
ReplyDeleteYou will need a zone controller if you want to turn a single zone HVAC system into a multiple zone system. The zone controller will connect to the HVAC system and control it. The HVAC system sees the zone controller as just one thermostat so it just does what it is told. The Zone controller's job is to take the request from all the connected thermostats and serialize and package the requests to the HVAC system.
ReplyDeleteNo, I don't. Sophisticated zone control is a nice thing to have if you're in a building with multiple floors, lots of rooms with varying heat sources and such. They could even be useful in open warehouse environments. However, they are overkill for a person's home. Unless you live in the White House or have a Spielberg type budget, simply controlling the thermostats you have is good enough.
ReplyDeleteI have two heat pumps with two thermostats I made. These in turn connect via ethernet to a central controller that can set the temps and air circulation based on my desires. The thermostats and controller are shown on this site.
I'm not saying your device isn't useful, just not for a person's home. Actually, your device is a prime example of what I'm working to avoid. A costly solution that might, maybe, save me money amortized over many years.
I want solutions that I can control, I can fix, I can customize, and never ever need to call a repairman out for hundreds of dollars to adjust.
hi dave. i need your help. hmmm. i would like to ask the same question if how you connect current transformer and your voltage transformer?.from a service entrance we have 3 wires. Maybe 220 volts of the two wires and the other is the neutral. csn i ask for sketch. .thank you very much Dave. .Thanks for the help. .
ReplyDeleteDaj, I'm having a little trouble understanding what you're missing. If you are in the US, you will have three wires coming in from the power company and one wire leading to a ground somewhere nearby. The three wires are two hot and one neutral, you want to use the two hot wires and ignore the neutral for the current measurement. The way it works is there is 110 volts from each of the hot wires to the neutral and 220 volts between the two hot wires. This isn't really important for measuring current though, you want to put a CT around both wires. There are drawings all over the web that show this. My picture above of my power distribution box shows where I put the two CTs around the wires; those are the two hot wires. There's not much else to it. Just shut off the power and put them around the wires. Be sure to turn off the power, it's real easy to make a mistake in there and you don't want the power on if you drop a screwdriver or something.
ReplyDeleteMy voltage transformer is just a simple 110V to 9V transformer that you can find from electronic suppliers on the web. An even easier technique is to find a 9VAC wall wart and use it. 9V wall warts are pretty rare, but I've seen them on eBay for 5 dollars a few times.
Thanks for the reply dave..is it safe if i will not turn off the power line? cause i'm planing to put the current transformer above the breaker if it is ok.
ReplyDeletedave . What is the purpose of youre capacitor in your diagram above?
Since i used Two CT for the hot wires. .do i need to use two transformer?
Thank you very much Dave.
i mean two voltage transformer.
ReplyDeleteRegarding safety. I can't actually see your power setup, so I can't say for sure, but if the wires are insulated and you don't go in there with metal tools, loose clothing that can catch on stuff, and long wires that can short something out, you should be OK. Don't do it in the rain.
ReplyDeleteRegarding two voltage transformers. No, you shouldn't need two of them. Just read the voltage on one of them and consider the other leg to be the same. I mentioned above how I measured both legs of the incoming power for a week before I decided only one transformer was good enough. Over the entire week, the voltage never varied by more than a volt between the lines. This makes sense because the power company has a huge reserve and your home shouldn't make one leg change enough to matter.
This may not be true if the power lines to your house are too small for the load your house puts on them. That used to happen to people and they would see the voltage drop as they turned things on because the resistance of the wires was too high. I haven't seen that problem in many years, so unless you notice something very strange, don't be concerned about that.
Regarding the capacitors. Those are to remove high frequency noise from the circuit. The power lines run a long way and electrical noise gets into them and can mess up the readings. These capacitors remove the noise so you don't have to deal with it.
So its safe.!! thanks for the idea dave. it helps me a lot doing my project. but i'm planning to use it in LAN/WLAN . . and dave what is better to use? using ethernet shield or the XBEE Wishield in arduino??what is better??
ReplyDeleteThanks a lot for your reply dave.
I tried both wired and wireless ethernet. The wireless constantly had troubles of various kinds and would sometimes take several minutes to recover from it. The wired worked much better, but my house gets disconnected from the internet a lot and it would require the device to reconnect. Not as bad a problem as it sounds like, but really annoying. These are the reasons I set up a XBee network in the house, it was just too annoying being at the mercy of the phone company for the various devices.
ReplyDeleteFor a long time I had a house clock that grabbed time off the internet and supplied it to the house. This was so darn prone to errors that I got a GPS receiver and hooked the clock to that. It's been about two years now with very, very few problems.
You may have much better luck, so give it a shot. It's not to hard to convert to some other communications method if you need to later, and is actually fun to experiment with. I'd avoid wireless though, it takes a lot more code and eats up a lot of memory on these little boards. Of course, if you're a long ways from a wired connection, you may want to try the wireless and see if it works better for you.
hello dave, im planning to make a power consumption monitoring system for a certain building. This incoming power line to the building is three phase each line is 110Vac and the lines are in phase resulting 220Vac supplied to the appliances. I'm using 3 power analyzer http://www.e-gizmo.com/KIT/PAnalyzer.htm
ReplyDeleteand im planning to hack the power analyzer to insert the Current transformer in the Shunt resistor. And dave i would like to ask how to interface arduino duemilanove with these 3 power analyzer using Newsoftserial. i cant read the output from the power analyzer when i used other digital pins the serial window output is displaying alien characters. but if i used pin 0(rx) its read the output of the power analyzer. .Dave i need your help. Do you have any idea how to fix this problem? or do u have codes that can solve this problem?? thanks a lot dave. .Hoping for your kind consideration.Thanks.
First, thank you very much for pointing out this device (the analyzer) I really like it. I suspect you want to use the CT to increase the rating since it will only go up to 2.2 kW, or you want to make it non-invasive (don't have to cut wires). Take special care during the hack, the CT will put out more voltage than the drop across the coiled wire resistor and that could give you no end of grief.
ReplyDeleteTo make a reasonable suggestion I need to know a couple of things. What kind of LCD display are you using serial with only one data wire, or parallel with a bunch of data wires? Where is the LCD connected, to the analyzer or to the arduino? Which version of the Arduino IDE are you using? If it is greater than 1.0, the software serial library is included with the download, if before that you have to hunt down the library and load it yourself.
But, to get you started troubleshooting, try working with the software serial example. This uses the two ports and you can modify it to test things for comm. You could have a baud rate problem (try 9600 on all ports) or a latency problem; that's where your code is too busy to allow the software serial port to work properly.
Also, I've used software serial to control two serial ports in addition to the hardware serial port, but never more than that. It sounds like you want to have three serial ports and an LCD display running at the same time. This may be too much for the little arduino and moving to an atmega2560 may be necessary. But, play with the one software port and the hardware port first to see what the results are.
Dave thank you for your help. i dont use LCD i planning to connect the power analyzer to the arduino and the arduino is connect to an ethernet Shield ENC28j60 via SPI and the through LAN i will display the data in visual basic 6. .i try the example from software serial using arduino 1.0 and the arduino 0022 but the same results.the serial window display unkwown characters. do you know how to solve this problem dave using the arduino duemilanove. .Thank you for the help dave.
ReplyDeletethe hardware serial can read the output from the arduino but the softserial cannot..:(
That sounds like a baud rate problem. In your setup() routine you need to initialize both the hardware serial and the software serial. Something like this:
ReplyDelete#include
SoftwareSerial mySerial(2, 3);
void setup()
{
// set the hardware serial speed
Serial.begin(9600);
// and the SoftwareSerial port
mySerial.begin(9600);
}
void loop() // run over and over
{
if (mySerial.available())
Serial.print((char)mySerial.read());
if (Serial.available())
mySerial.print((char)Serial.read());
}
I chose 9600 because that is the speed your monitor device runs at (at least that's what I got from the documentation). Hook your monitor to 2,3 and catch characters the output them to something you can see, like the arduino IDE serial monitor.
thanks for the code dave.your right dave. Thanks a lot dave. your a saver.hehe. i used 4800 in mySerial.begin(4800), thats why i cant see the output characters. Thanks dave.
ReplyDeleteDave, do u have idea how can i communicate my arduino using ethernet shield ENC28j60 to VISUAL BASIC 6 in my PC using LAN?. .i dont have any sample code in using arduino to vb using LAN. Thanks a lot dave. Dave here is my yahoomail panot_gomez17@yahoo.com
hello dave i forgot to mension why i post my email. .i post it because i need your expertise.is it okay to you if i'll ask you questions about the communication from ethernet shield enc28j60 to a visual basic.i got problem interfacing the code in ethernet shield enc28j60. maybe you could help me what will i used?.i planning to use ethercard library but i dont know what are the codes to communicate n vb.
ReplyDeleteYou just went beyond my experience range. I've never used the 28j60, I always use the Wiznet chip. There's a reason for this. The 28j60 is a lower level chip and requires a lot more code on the arduino. That means you have less room for your own code and often run out trying to do things. There are very inexpensive versions of the Wiznet board around. To get samples of the ethernet code for the Wiznet chip go to the arduino forum (arduino.cc and click forum up at the top) there are many examples there that are really good.
ReplyDeleteGlad I was able to help. Maybe this conversation will help others as well.
thank you dave.
ReplyDeleteim planning to use w5100 chip but now i have enc28j60. i starting making program for w5100 but i dont know what will i used to communicate to visual basic with winsock,tcp or udp?others said tcp but i dont have any reference for tcp code in w5100 to visual basic. thank you very much for your kind consideration dave. your the best.
Dear all, i use Non Invasive current sensor SCT-013-30 and use ATMega 8535 for the micro controller, could you give some schematic how to make it can be red by ADC micro controller? many thanks
ReplyDeleteArifin, I show the schematic that I used up the page a ways. If you google for your sensor, there are a lot of circuit examples for it on the web. The one you chose is one of the most often used devices.
ReplyDeleteHello Dave, I have my sct-013-000. I got problem in calibrating my ct.
ReplyDelete1.What will be the CT calibration if i used 33ohms with 5% tolerance. ?
2. Also in my voltage transformer (220Vrms -> 12Vrms) i used 10kohms resistor and 680hms resistor for my stepdown voltage divider. I got problem in calibrating it.Our power line has single phase 220Vrms. Do you have any idea will be the calibration of the two transformer?.
Please i need your expertise. Thank you dave.
Hoping for your kind consideration.
I had a lot of trouble getting my setup calibrated. I don't care how much you calculate the various values, it will be wrong when you finish. I finally just gave up trying to calibrate it by calculations and settled for getting the voltage and current in the general area of correct and then calibrating it the rest of the way in the code. As far as tolerance on the components goes, it doesn't really matter if you adjust for it in the code.
ReplyDeleteThe key to this is to get something like the kill-a-watt that we have here in the US. It tells you the current and voltage, so you can adjust the calibration values in the code to match. I don't know what's available where you are and how much the devices cost, but you should be able to find something. Maybe borrow one from someone, or buy it, then take it back a week later for a refund?
You simply use the calculation instructions that Trystan Lea has on his site to get the voltage roughly in the range the arduino can handle, then tweak it by changing the calibration value in the code. Look at the page:
http://openenergymonitor.org/emon/buildingblocks/ct-sensors-interface
This will show you how to get the ct going, then look at:
http://openenergymonitor.org/emon/buildingblocks/measuring-voltage-with-an-acac-power-adapter
For the voltage transformer. Once you get these voltages, compare it to the meter you've somehow acquired and adjust the calibration values in the code to match it.
Trying to get it right by calculation and purchasing the exact right components is almost impossible unless you have extremely good equipment to measure and confirm the components. The commercial devices have a potentiometer in the ct circuit and another one in the voltage circuit. They adjust these to calibrate it to a know source; that's something us experimenters don't have access to.
Another trick is to calibrate it to something that pulls over a Kw of power. Things like toasters electrical heaters, big light bulbs will work. You have to let them run a little while to settle down since the current draw changes as they heat up, but it makes calibration a lot easier.
Hope this helps.
Thank you very much for your help dave. I calibrated the voltage but if there's no power on transformer the program can still read an analoginput eventhough there's no transformer connected. .
ReplyDeleteIf there's nothing connected to the arduino analog input pin, it floats at a high impedance. That means it will pick up stray voltages and give you a reading. If you want to observe it, hook something up to it. I use a voltage divider of two 10K resistors from ground to +5V and hook the pin to the middle of the two. That way I will have close to 2.5V to play with. Remove the resistors before hooking it up to the voltage reference you're going to use for power.
ReplyDeleteThank you very much Dave. Is it ok if i will put a pull down resistor to analog input. ? Thank you.
ReplyDeleteYes, that will give you a low value.
ReplyDeleteso that i cant read any value when transformer is off?
ReplyDeleteI'm having a bit of a problem understanding what is giving you trouble. If you unplug the transformer from the wall, the AC will disappear and you will be reading the voltage at the center of the divider from 5V to ground. If you unplug the arduino input pin, you will be reading a random voltage that the pin happens to be setting at. Let's use Krystan's drawing as the example:
ReplyDeletehttp://openenergymonitor.org/emon/sites/default/files/Arduino%20AC%20voltage%20input_1.png
In his drawing, removing the ac adapter from the mains will still leave the voltage divider from the +5 to ground through R3 and R4. This voltage will be seen by the arduino pin. I suspect this is what is giving you trouble. You're seeing a constant 2.5 volts (or so) on the pin when the transformer is unplugged.
I can't currently think of any way to stop that. You can, however, tell that this is what happened in the code and do something. The code is capable of measuring the frequency of the incoming AC mains. Simply look for the frequency to drop to zero and you'll know that the mains voltage has disappeared.
Ahh, so its normal that my arduino can read random values if i did not put my ct to the Power line and voltage transformer to power plug ?
ReplyDeleteHello Dave. I got trouble in sending Power Parameters in my LAN using ethernet shield. client.write() needs a character value to able to send data to my visual basic.
ReplyDeleteI just want to ask if do you have an idea how to convert Double to charater.
Thank you very much Dave. I really appreciated you help.Thanks.
Yes, I noticed that too. I chose to use sprintf() because it has the ability to format lots of stuff. Make sure you have stdio.h included in your sketch and do something like this:
ReplyDeletesprintf(yourBuffer,"%d.%02d",yourFloatVariable);
client.write(yourBuffer);
You can put more than one variable in and construct an entire string to send. Look at the documentation for printf() online to get an idea what to do.
Thank you for the response Dave. .Is this applicable for integer? sprintf(yourBuffer,"%d.%02d",yourFloatVariable);
ReplyDeleteCrap. Daj, glad you noticed that I made a mistake. I messed up the code fragment when I typed it in. What I should have put down was:
ReplyDeletesprintf(yourBuffer,"%d.%02d",(int)yourFloatVariable,(int, (((yourFloatVariable+0.005) - (int)yourFloatVariable) * 100);
client.write(yourBuffer);
The idea is to take the float value, cut it into integer values for the whole and fractional parts, then print it out as two values on each side of a decimal separator. Adding the .005 is rounding for the hundreths positions. So, if your value is 1.23, the (int)yourFloat variable will give you back 1. Then the (((yourFloatVariable+0.005) - (int)yourFloatVariable) * 100) will give you back the 23. The you print it as two things before and after a '.' to give you '1.23'
Sorry about the mistake. Be sure to look at documentation for printf(), there are a lot of tricks in there to help you.
Thanks a lot dave. That will help me very much. :)
ReplyDeleteDid you try 0.2%lf ?? or %lf for double float and %f for single float?
I did, but I can't remember for certain what happened. I think it failed because the code for that part of sprintf() wasn't ported. It's easy to try, just set up a tiny little test program and use literal values to test it. It may be that when I tried it it didn't work and they have fixed it since.
ReplyDeleteAhh ok. .Thank you for the help Dave.
ReplyDeleteHello Dave .How can i set the current and power factor to 4 decimal places. .
ReplyDeletestrcpy_P(Dbuf2, PSTR("Inrange,%d.%02d,%d.%02d,%d.%02d,0.%d,%d.%02d,%d.%02d,"));
sprintf(Tpadata,Dbuf2,(int)round(realPower),(int)(((realPower+0.005) - (int)realPower) * 100),(int)round(apparentPower),(int)(((apparentPower+0.005) - (int)apparentPower) * 100),(int)round(reactivePower),(int)(((reactivePower+0.005) - (int)reactivePower) * 100),(int)(powerFactor*100),(int)round(Vrms),(int)(((Vrms+0.005) - (int)Vrms) * 100),(int)round(Irms),(int)(((Irms+0.005) - (int)Irms) * 100));
Use something like "%d.%04d" as the format string and then the integer part is:
ReplyDelete(int)(round(realPower))
And the fractional part is something like:
(int)((realPower + .00005) - (int)realPower) * 10000)
But, you're getting into the realm of not having any real meaning to the last digits. 1/10000th is way beyond the accuracy of the devices you're using. It may be better to just set the last couple of digits to zero.
Thank you for the code dave i will try this one. .Thanks for the help. I really appreciate it.
ReplyDeleteDave . i cant find people that can help my problem,only you. I wanna hear your advice if you dont mind. Here is my situation.
ReplyDeleteFirst I made a power monitoring for my study using visual basic. My arduino send data to visual basic through ethernet in my LAN with this string format. .example:
"InRange,-0250.6,0010.0,-0150.2,+0.6228,220.39,0.5,|InRange,-0250.6,0010.0,-0150.2,+0.6228,220.39,0.5,|InRange,-0250.6,0010.0,-0150.2,+0.6228,220.39,0.5,|09:12:42:23:12:11|10000"
and save that data to ms access. The problem of this, is that when i close my visual basic program ,the data from arduino have no destination or in short i can get data when i open my visual basic program.
Now, my adviser told me to use sql.
So therefore dataflow will go like this arduino-->LAN--->database(sql)-->visual basic. .With this i wanna send data to my database and view it in visual basic like what your project is doing.
Dave what database did you use?. .please help. ::(
I send my data over the internet to Cosm, thingspeak, and opencms. They are all web services that store the data online. My actual architecture is that I have several sensors around the house that send data to a central concentrator that bundles them all together and uses ethernet to send the data to these servers. Most of my sensors are based on the Arduino, but there are a couple that are XBee radios with the sensor attached to them. The concentrator is an Arduino Mega2560, that forwards the data and also provides a web interface that I can use to control various devices like my home heating and water heater.
ReplyDeleteI had exactly the same problem you're having when I first started this. I was logging the data to a laptop, but when the laptop was off, nothing worked. This was unacceptable so I added an arduino with an ethernet shield to forward the data to Cosm for storage. This led to more sensors, more Arduinos, etc.
There really is no good solution to logging data on a laptop, since it will get taken on trips, turned off, or just used to play games. Many people use an SD card to log the data, then carry it over to the laptop, retrieve the data, and do their analysis. That may be your best solution if you can get a shield for the arduino that supports an SD card.
If you can get access to a computer that is never taken off line, you can probably work out a way to use it to store the data, but those are hard to find. If you can spend some money, there are many little computers that have ethernet and some storage that you can hook into the system. Maybe borrow someone's old laptop and leave it on 24x7 to collect the data.
Very wonderful blog.I am very sure that this blog is learning blog for the people.transformer testing
DeleteDave .in your case.can you send data without internet? or not?
ReplyDeleteI send it over the internet to web based commercial servers. Take a look at Cosm.com to see what kind of thing I do.
ReplyDeleteMy cousin recommended this blog and she was totally right keep up the fantastic work!
ReplyDeleteTemperature calibration
Hello Dave,
ReplyDeleteI'm having some problems calibrating the Arduino board to show me the real time current and voltage values. I used one 50:1 A CT with a burden resistance of 33ohm and the electric sketch from Trystan Lea's website, and one 230 to 9 V AC-AC power adaptor again with the electric sketch from Trystan Lea's website. Then I used your arduino sketch and interface it with an LCD to show the current and voltage. After that I used some variable resistances with a ampermeter so I can see if it shows the real current value, modified the resistances to show 1A on the ampermeter, then calibrated the value of "double ICAL = 2.85;" so the LCD from the arduino would show 1A and after that I lowered the resistance to give me 2 A on the ampermeter but the LCD would show a current of aprox 2,2 A that is a really big error, 0.2 A... I couldn't lowered the resistance to give more then 2,5A and dident try to connect a bigger consumer to see if the error grows too, but I'm sure that it will. Did you had the same problem ?
Thanks,
Marius
No, I did not. I did it differently though, I used a higher value for the calibration. I borrowed a great big light from a neighbor and pulled about 2kW and set it up at that level. Later, I was able to use a real resistive load that an electrician had that allowed me to set it up for 5kW. In the US, using split 220, I had to do this for both legs to be sure it was actually working correctly. I am not totally sure it's real accurate when the levels get up around 15kW or so, but for my purposes, extreme accuracy isn't as important as knowing that I'm using that much and when.
ReplyDeleteOh, when I tested the calibration at lower currents using light bulbs and such in comparison with a kill-a-watt, it was right on. The symptoms you're describing sort of suggest that the CT is saturating. Try a different burden value to see if things change for the better or worse.
Hello. This is such a great web site and very inspiring! Thank you! I just ordered a SCT-019 so I can build the same type of monitor as you.
ReplyDeleteI have just one question, however, on the code. All the code makes sense except for this section which I don't understand how it works. Could you or someone else explain how this code removes the 2.5 v offset? Thanks.
//Digital high pass filters to remove 2.5V DC offset.
filteredV = 0.996 * (lastFilteredV+sampleV-lastSampleV);
Joe
That code statement drove me nuts also. Basically it's a digital high pass filter that responds to the input in just a few cycles of the power. Unfortunately, that was two years ago and I don't remember any more. However, here's a thread (http://openenergymonitor.org/emon/node/932) that discusses it and there is a pretty good article on Wikipedia under "Digital high pass filter".
ReplyDeleteThe reason it removes the DC offset is because DC is the ultimate low frequency. Can't get much lower than zero.
It's cool to watch it react. When you start experimenting, put together a bit of code to illustrate what it does and play with it a bit. It will remove the DC component of your measurements really well. That means you save on working up hardware and calculating capacitor values.
Thanks for your quick reply. I'll look into that.
ReplyDeleteAnother thing I was wondering about...I too live in the US and have a 200 amp 220 v feed. I ordered just one 2000 amp current sensor as I don't know if I could fit 2 inside my service panel; there is very limited room. But I believe one sensor is enough to measure my 220 volt usage (water heater, baseboard heat, dryer) and I can measure the current draw on one side of the 220v, right? I would need 2 only to measure both sides of the 220. And I would wire both in series. One thing I notice is the current sensors are labeled as to what side points to the source but I have never seen anyone mention this. Seems to be rather important if one is wiring 2 up in series.
Joe
Oops, I meant I ordered one 200 amp...
DeleteBack when I ordered my current sensor it didn't have any indication which end pointed where. The wires were different colors though which turned out to be the same thing. I just oriented them both the same way and it worked.
ReplyDeleteYes, you can measure only one side of the 220, that will tell you the current in that leg of the power. There are things that chew the power up that are not 220 though. Vacuums pull a lot of power, compressors, motors, deep fryers, that kind of thing. If you only want to control the major appliance though, be sure they are all on the leg you're going to measure.
When you get the first CT in, you can look to see if there's room for another in the box somewhere. Just turn the power off to be sure you won't get shocked and check around inside.