power monitor with LCD and SD for information storage

Hi

I just wanted to post the code for my Energy Monitor, it is based on Trystan Lea and Eric Sandeen code. i just added a LCD 16x2 menu and a way to storage data in a SD card. For this I used Adafruit Loggin shield, http://www.adafruit.com/products/243 and seedstudio 100 Amp CT. I hope its useful, if you need more informatin please ask. Thanks a lot for your support, this project was very fun to do and I hope to keep evolving it, maybe next phase will be to stream the information to a web server installed in a modiffied apple tv 1 (the only computer that is on all the time at home)

//Basic energy monitoring sketch
//Based on design by: Trystan Lea, Eric Sandeen
//Licenced under GNU General Public Licence
// openenergymonitor.org
// Patricio Duenas

//Sketch measures voltage and current.
//and then calculates useful values like real power,
//apparent power, powerfactor, Vrms, Irms.

// Connections:
// rs (LCD pin 4) to Arduino pin 9
// rw (LCD pin 5) to Arduino pin 8
// enable (LCD pin 6) to Arduino pin 7
// Arduino pin 6 goes to pushbutton for menu
// LCD pins d4, d5, d6, d7 to Arduino pins 5, 4, 3, 2

#include <LiquidCrystal.h>
#include <Wire.h>
#include "RTClib.h"
#include <SD.h>

//initialize lcd
LiquidCrystal lcd(9, 8, 7, 5, 4, 3, 2);
int menupin = 6;
int menusum = 0;
int menu = 0;

//Setup variables
int numberOfSamples = 3000;

//Set Voltage and current input pins
int inPinV = 2;
int inPinI = 1;

//define RTC object
RTC_DS1307 RTC;

//Sanity check calibration method thanks to Eric Sandeen http://sandeen.net/wordpress
//See discussion here: http://openenergymonitor.org/emon/node/59
//Enter the values of your setup below

// Voltage is reduced both by wall xfmr & voltage divider
#define AC_WALL_VOLTAGE        130
#define AC_ADAPTER_VOLTAGE     3.63
// Ratio of the voltage divider in the circuit
#define AC_VOLTAGE_DIV_RATIO   3.3859
// CT: Voltage depends on current, burden resistor, and turns
#define CT_BURDEN_RESISTOR    88
#define CT_TURNS              3030

//Calibration coeficients
//These need to be set in order to obtain accurate results
//Set the above values first and then calibrate futher using normal calibration method described on how to build it page.
double VCAL = 1.111;
double ICAL = 0.62;
double PHASECAL = 2;

// Initial gueses for ratios, modified by VCAL/ICAL tweaks
#define AC_ADAPTER_RATIO       (AC_WALL_VOLTAGE / AC_ADAPTER_VOLTAGE)
double V_RATIO = AC_ADAPTER_RATIO * AC_VOLTAGE_DIV_RATIO * 5 / 1024 * VCAL;
double I_RATIO = (long double)CT_TURNS / CT_BURDEN_RESISTOR * 5 / 1024 * ICAL;

//Sample variables
int lastSampleV,lastSampleI,sampleV,sampleI;

//Filter variables
double lastFilteredV, lastFilteredI, filteredV, filteredI;
double filterTemp;

//Stores the phase calibrated instantaneous voltage.
double shiftedV;

//Power calculation variables
double sqI,sqV,instP,sumI,sumV,sumP;

//Useful value variables
double realPower,
       apparentPower,
       powerFactor,
       Vrms,
       Irms;

//SD writing related variables
const int chipSelect = 10;   //SD CS for adafruit shield
File logfile;   // the logging file
#define SYNC_INTERVAL 60000   //interval to write data to SD, here is set to 1 munite in miliseconds
                              //minus 1 sec for the V and I sampling time 
uint32_t syncTime = 0;  //time of last sync

void setup()
{
  Serial.begin(57600);
  Wire.begin();
  RTC.begin();
  RTC.adjust(DateTime(__DATE__, __TIME__));  //set RTC to computer's clock at the time of compilation
 
  // make sure that the default chip select pin is set to
  // output, even if you don't use it:
  pinMode(10, OUTPUT);
  pinMode(menupin, INPUT);
  // see if the card is present and can be initialized:
  lcd.begin(16,2);
  lcd.clear();
  lcd.setCursor(0,0);
  if (!SD.begin(chipSelect)) {
    lcd.print("Card failed, or");
    lcd.setCursor(0,1);
    lcd.print("not present");
    delay(5000);
    lcd.clear();
  }else{
  lcd.print("card initialized");
  delay(2000);
  // create a new file
  char filename[] = "LOGGER00.CSV";          //Every time the Arduino starts, we will create
  for (uint8_t i = 0; i < 100; i++) {        //a new file named LOGGERnn.csv
    filename[6] = i/10 + '0';
    filename[7] = i%10 + '0';
    if (! SD.exists(filename)) {
     // only open a new file if it doesn't exist
     logfile = SD.open(filename, FILE_WRITE);
     break;  // leave the loop!
    }
  }
  logfile.println("date,time,realPower,apparentPower,powerFactor,Vrms,Irms");  //this is the header of the csv file
  }
}

void loop()
{
menusum = digitalRead(menupin);
if ( menusum == HIGH) {
  menu = menu+1;
}
for (int n=0; n<numberOfSamples; n++)
{
   //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.
   shiftedV = lastFilteredV + PHASECAL * (filteredV - lastFilteredV);
 
   //Root-mean-square method voltage
   //1) square voltage values
   sqV= filteredV * filteredV;
   //2) sum
   sumV += sqV;
  
   //Root-mean-square method current
   //1) square current values
   sqI = filteredI * filteredI;
   //2) sum
   sumI += sqI;

   //Instantaneous Power
   instP = shiftedV * filteredI;
   //Sum
   sumP +=instP;
}

//Calculation of the root of the mean of the voltage and current squared (rms)
//Calibration coeficients applied.
Vrms = V_RATIO*sqrt(sumV / numberOfSamples);
Irms = I_RATIO*sqrt(sumI / numberOfSamples);

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

//output to serial  i, v, real p
Serial.print(Vrms);
Serial.print("  ");
Serial.print(Irms);
Serial.print("  ");
Serial.println(realPower);

//output to lcd
DateTime now = RTC.now();

if (menu == 0) {
lcd.clear();                  // start with a blank screen
lcd.setCursor(4,0);           // set cursor to column 0, row 0 (the first row)
lcd.print(now.day(), DEC);
lcd.print("/");
lcd.print(now.month(), DEC);
lcd.print("/");
lcd.print(now.year(), DEC);
lcd.setCursor(5,1);
lcd.print(now.hour(), DEC);
lcd.print(":");
if(now.minute() < 10){   //little trick to print 0 to 9 minutes
  lcd.print("0");
}
lcd.print(now.minute(), DEC); 
}

if (menu == 1){
lcd.clear();                  // start with a blank screen
lcd.setCursor(0,0);           // set cursor to column 0, row 0 (the first row)
lcd.print(realPower);
lcd.setCursor(6,0);           //main menu, will have to change to a more complex one
lcd.print("Real Power");
lcd.setCursor(0,1);
lcd.print(Vrms);
lcd.setCursor(4,1);
lcd.print("Vrms");
lcd.setCursor(9,1);
lcd.print(Irms);
lcd.setCursor(12,1);
lcd.print("Irms");
}

if (menu == 2){
lcd.clear();                  // start with a blank screen
lcd.setCursor(0,0);           // set cursor to column 0, row 0 (the first row)
lcd.print(powerFactor);
lcd.setCursor(6,0);           //main menu, will have to change to a more complex one
lcd.print("Pwr Factor");
lcd.setCursor(0,1);
lcd.print(apparentPower);
lcd.setCursor(7,1);
lcd.print("Aprnt Pwr");
}

if (menu > 2){
    menu = 0;
  }
 
//Reset accumulators
sumV = 0;
sumI = 0;
sumP = 0;

//Save data to disk once every minute
if ((millis() - syncTime) < SYNC_INTERVAL) return;
syncTime = millis();

//write data to SD
  logfile.print(now.year(), DEC);
  logfile.print("/");
  logfile.print(now.month(), DEC);
  logfile.print("/");
  logfile.print(now.day(), DEC);
  logfile.print(" ");
  logfile.print(",");
  logfile.print(now.hour(), DEC);
  logfile.print(":");
  logfile.print(now.minute(), DEC);
  logfile.print(":");
  logfile.print(now.second(), DEC);
  logfile.print(", ");   
  logfile.print(realPower);
  logfile.print(", ");   
  logfile.print(apparentPower);
  logfile.print(", ");   
  logfile.print(powerFactor);
  logfile.print(", ");   
  logfile.print(Vrms);
  logfile.print(", ");   
  logfile.println(Irms);
  logfile.flush();

}
 

glyn.hudson's picture

Re: power monitor with LCD and SD for information storage

Hi Mexican Watt,

Thanks for posting your code. I am sure people will find it very useful. It would be really cool if you could post a few photos of your setup. It would bring the code alive!

All the best, Glyn.  

MexicanWatt's picture

Re: power monitor with LCD and SD for information storage

Hi, Sorry for my late response, here are some photos of the exterior. I will post some of the interesting part, the circuitry when I reopen the box for some troubleshooting with the clock unit

 

TrystanLea's picture

Re: power monitor with LCD and SD for information storage

That looks really neat, nice work!

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.