details
Home energy monitoring system
Basically the circuit uses 2 current transformers install in my circuit breaker box; one on each line. They feed an Arduino which measures the current the house is using through induction. It also knows the voltage. With current and voltage know, it figures out power usage. It then uploads the measurements to a super site called Emoncms. Emoncms houses the data and presents the data in graphical form. The screen shot below shows my dashboard on Emoncms. I plot current, power, KW usage and outside temp. I am working on adding cost display too.
Hardware and Sofrware is ready
Code
#include <SD.h> #include <SPI.h> #include <Ethernet.h> #include <JeeLib.h> // https://github.com/jcw/jeelib #include <OneWire.h> //for one wire temp sensor #include <DallasTemperature.h> #define ONE_WIRE_BUS 7 // Data wire is digital pin 7 on the Arduino #define TEMPERATURE_PRECISION 12 // Setup a oneWire instance w/any OneWire devices OneWire oneWire(ONE_WIRE_BUS); // Pass our oneWire reference to Dallas Temperature. DallasTemperature sensors(&oneWire); // arrays to hold device addresses – replace with your sensors addresses //DeviceAddress insideThermometer = { 0x28, 0x37, 0x38, 0xB7, 0x03, 0x00, 0x00, 0x3F}; DeviceAddress outsideThermometer = { 0x28, 0x45, 0x1F, 0x61, 0x03, 0x00, 0x00, 0x68}; // code used to show free sram /* int freeRam () { extern int __heap_start, *__brkval; int v; return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval); } */ File myFile; // used to store total kilowatt reading int bc = 0; // used to bypass first time thru loop int j; //loop counter char kwh[10]; //temp array used to convert string to float byte mac[] = { }; // my second ethernet mac address byte ip[] = { 192,168,1,99 }; //I am using ip=99, port 1234 typedef struct { int power1, power2, power3, voltage; } PayloadTX; PayloadTX emontx; // Enter your apiurl here including apikey: char apiurl[] = "http://emoncms.org/api/post.json?apikey=YOURAPIKEY&json="; //char timeurl[] = "http://emoncms.org/time/local.json?apikey=YOURAPIKEY"; // For posting to emoncms server with host name, (DNS lookup) comment out if using static IP address below // emoncms.org is the public emoncms server. Emoncms can also be downloaded and run on any server. //char server[] = "emoncms.org"; //byte server[] = { 213.138.101.177 }; // emnocms.org ip IPAddress server(213,138,101,177); // emoncms server IP for posting to server without a host name, can be used for posting to local emoncms server //------------------------------------------------------------------------------------------------------ // The PacketBuffer class is used to generate the json string that is send via ethernet - JeeLabs //------------------------------------------------------------------------------------------------------ class PacketBuffer : public Print { public: PacketBuffer () : fill (0) { } const char* buffer() { return buf; } byte length() { return fill; } void reset() { memset(buf,NULL,sizeof(buf)); fill = 0; } virtual size_t write (uint8_t ch) { if (fill < sizeof buf) buf[fill++] = ch; } byte fill; char buf[150]; private: }; PacketBuffer str; //-------------------------------------------------------------------------------------------------------- //-------------------------------------------------------------------------- //datastreams char kilowatt[] = "Kilowatt"; char RealPower[] = "Power"; char RMSCurrent[] = "RMSCurrent"; const int voltageSensor = A0; const int currentSensor = A1; //CT #1 const int currentSensor2 = A2; //CT #2 const int numberOfSamples = 3000; // Calibration constants const float AC_WALL_VOLTAGE = 122.0; const float AC_ADAPTER_VOLTAGE = 7.40; const float AC_VOLTAGE_DIV_VOUT = 1.23; const float CT_BURDEN_RESISTOR = 55; const float CT_TURNS = 6060.6; const float CT_BURDEN_RESISTOR2 = 55; // Calibration coefficients const float VCAL = 1.00; const float ICAL = 0.86; const float PHASECAL = 0.9; const float ICAL2 = 0.86; // Calculated ratio constants, modified by VCAL/ICAL const float AC_ADAPTER_RATIO = AC_WALL_VOLTAGE / AC_ADAPTER_VOLTAGE; const float AC_VOLTAGE_DIV_RATIO = AC_ADAPTER_VOLTAGE / AC_VOLTAGE_DIV_VOUT; const float V_RATIO = AC_ADAPTER_RATIO * AC_VOLTAGE_DIV_RATIO * 5 / 1024 * VCAL; const float I_RATIO = CT_TURNS / CT_BURDEN_RESISTOR * 5 / 1024 * ICAL; const float I_RATIO2 = CT_TURNS / CT_BURDEN_RESISTOR2 * 5 / 1024 * ICAL2; // Sample variables int lastSampleV, lastSampleI, sampleV, sampleI; int lastSampleI2, sampleI2; //using same V for both CTs // Filter variables float lastFilteredV, lastFilteredI, filteredV, filteredI; float lastFilteredI2, filteredI2; //using same V for both CTs // Power sample totals float sumI, sumV, sumP, sumI2, sumV2, sumP2; float instantcost, kilowattcost, tempF, tempC; const float crate = 0.0544; // current electric rate per kilowatt/hour // Phase calibrated instantaneous voltage float calibratedV; float calibratedV2; // Calculated power variables float realPower, apparentPower, powerFactor, voltageRMS, currentRMS; float realPower2, realPowert, apparentPower2, powerFactor2, currentRMS2, currentRMSt; unsigned long last_kWhTime, kWhTime; float kilowattHour = 0.0; float kilowattHour2 = 0.0; float kilowattHourt = 0.0; EthernetClient client; void setup() { Serial.begin(9600); //Serial.println("Begin"); sensors.begin(); // Start up the library for temp sensor delay(1000); // set the resolution sensors.setResolution(outsideThermometer, TEMPERATURE_PRECISION); delay(1000); Ethernet.begin(mac, ip); // On the Ethernet Shield, CS is pin 4. It's set as an output by default. // Note that even if it's not used as the CS pin, the hardware SS pin // (10 on most Arduino boards, 53 on the Mega) must be left as an output // or the SD library functions will not work. pinMode(10, OUTPUT); if (!SD.begin(4)) { // SD card uses pin 4 as CS because ethernet uses pin 10 Serial.println("SD fail"); return; } } void loop() { //Serial.print(F("free SRAM ")); //Serial.println(freeRam()); //Serial.println(F("Loop")); sensors.requestTemperatures(); tempC = sensors.getTempC(outsideThermometer); //get outside temp tempF = (DallasTemperature::toFahrenheit(tempC)); calculatePower1(); calculatePower2(); realPowert = realPower + realPower2; kilowattHourt = kilowattHour + kilowattHour2; currentRMSt = currentRMS + currentRMS2; //Serial.print(F("KWHT is ")); //Serial.println(kilowattHourt); instantcost = realPowert * (crate / 1000); //instant power cost myFile = SD.open("killog.txt"); if (myFile) { // check to see if killlog.txt exists and open //Serial.println(F("Open killog")); if (kilowattHourt < 2.0) { // either just starting or Arduino has reset //myFile = SD.open("killog.txt"); //killog.txt holds kilowatt hour running total j = 0; do { // test for kwh full if (j == sizeof(kwh)) { Serial.println(F("line too long")); break; } kwh[j] = myFile.read(); } while (kwh[j++] != '\r'); kilowattHourt = atof(&kwh[0]); //convert read string to float //Serial.print(F("KWH from SD ")); //Serial.println(kilowattHourt); myFile.close();} else { myFile = SD.open("killog.txt", FILE_WRITE); //open file for writing //Serial.print(F("Write KWH ")); //Serial.println(kilowattHourt); myFile.seek(0); //set to write to first byte of file myFile.println(kilowattHourt); myFile.close(); } //save kilowattHour to file } // end of if myfile code else { // if the file didn't open, print an error: Serial.println(F("error killog")); } kilowattcost = kilowattHourt * crate; //Serial.print(F("V ")); //Serial.println(voltageRMS); //Serial.print(F("I ")); //Serial.println(currentRMSt); //Serial.print(tempF); //Serial.println(F(" F")); /*Serial.print("Instant $ "); Serial.println(instantcost); Serial.print("Total KW $ "); Serial.println(kilowattcost); */ if (bc > 0) { str.reset(); // Reset json string str.print("{kilowattHour:"); str.print(kilowattHourt); // Add power reading str.print("},{realPower:"); str.print(realPowert); // Add power reading str.print("},{currentRMS:"); str.print(currentRMSt); // Add power reading //str.print("}"); //comment out this line when adding cost lines str.print("},{InstantCost:"); str.print(instantcost); // Add power reading str.print("},{TotalCost:"); str.print(kilowattcost); str.print("},{OusideTemp:"); str.print(tempF); str.print("}"); //end string if (client.connect(server, 80)) { str.print("}\0"); //Serial.println(); //Serial.print(F("Send ")); //Serial.println(str.buf); client.print(F("GET ")); client.print(apiurl); client.print(str.buf); client.println(); delay(1000); } else { //Serial.println(F("Done")); delay(500); client.stop(); } } //wdt_reset(); //watchdog timer delay(60000); bc = 1; } // LOOP end //========================================================================= void calculatePower1() { // calculate line 2 of 240v for (int i = 0; i < numberOfSamples; i++) { // Used for voltage offset removal lastSampleV = sampleV; lastSampleI = sampleI; // Read voltage and current values sampleV = analogRead(voltageSensor); sampleI = analogRead(currentSensor); // Used for voltage 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 calibratedV = lastFilteredV + PHASECAL * (filteredV - lastFilteredV); // Root-mean-square voltage sumV += calibratedV * calibratedV; // Root-mean-square current sumI += filteredI * filteredI; // Instantaneous Power sumP += abs(calibratedV * filteredI); } // Calculation of the root of the mean of the voltage and current squared (rms) // Calibration coeficients applied voltageRMS = V_RATIO * sqrt(sumV / numberOfSamples); currentRMS = I_RATIO * sqrt(sumI / numberOfSamples); //Serial.println(voltageRMS); //Serial.println(currentRMS); // Calculate power values realPower = V_RATIO * I_RATIO * sumP / numberOfSamples; apparentPower = voltageRMS * currentRMS; powerFactor = realPower / apparentPower; // Calculate running total kilowatt hours // This value will reset in 50 days last_kWhTime = kWhTime; kWhTime = millis(); // Convert watts into kilowatts and multiply by the time since the last reading in ms kilowattHour += (realPower / 1000) * ((kWhTime - last_kWhTime) / 3600000.0); // Reset sample totals sumV = 0; sumI = 0; sumP = 0; } // end of CT#1 calculations //============================================================ void calculatePower2() { // calculate line 2 of 240v for (int i = 0; i < numberOfSamples; i++) { // Used for voltage offset removal lastSampleV = sampleV; lastSampleI2 = sampleI2; // Read voltage and current values sampleV = analogRead(voltageSensor); sampleI2 = analogRead(currentSensor2); // Used for voltage offset removal lastFilteredV = filteredV; lastFilteredI2 = filteredI2; // Digital high pass filters to remove 2.5V DC offset filteredV = 0.996 * (lastFilteredV + sampleV - lastSampleV); filteredI2 = 0.996 * (lastFilteredI2 + sampleI2 - lastSampleI2); // Phase calibration calibratedV = lastFilteredV + PHASECAL * (filteredV - lastFilteredV); // Root-mean-square voltage sumV += calibratedV * calibratedV; // Root-mean-square current sumI2 += filteredI2 * filteredI2; // Instantaneous Power sumP2 += abs(calibratedV * filteredI2); } // Calculation of the root of the mean of the voltage and current squared (rms) // Calibration coeficients applied voltageRMS = V_RATIO * sqrt(sumV / numberOfSamples); currentRMS2 = I_RATIO2 * sqrt(sumI2 / numberOfSamples); //Serial.println(voltageRMS); //Serial.println(currentRMS); // Calculate power values realPower2 = V_RATIO * I_RATIO2 * sumP2 / numberOfSamples; apparentPower2 = voltageRMS * currentRMS2; powerFactor2 = realPower2 / apparentPower2; // Calculate running total kilowatt hours // This value will reset in 50 days last_kWhTime = kWhTime; kWhTime = millis(); // Convert watts into kilowatts and multiply by the time since the last reading in ms kilowattHour2 += (realPower2 / 1000) * ((kWhTime - last_kWhTime) / 3600000.0); // Reset sample totals sumV = 0; sumI2 = 0; sumP2 = 0; }
http://www.joecool.org/joe_home_energy_power_monitor_pr.htmзайм 20000 без отказа
COMMENTS