My electrical power meter

Code part two:

 void loop() 
{
  char result[17]="                ";  //The empty string that all printing is fed to
  timecounternow=millis();  //Read clock 
  if ((timecounternow-timecounterthen)>=(9918+(timingflag*6))) //Has 10 seconds passed since last clock reading? (9915 is too little and 9916 is too much...)
  {
    updateclock();  //Update clock

      lastsecondsticks=sensortickcounter-lastsecondstickscounter;  //Update last ten seconds pulse counter
    lastsecondstickscounter=sensortickcounter;

    secondsvalue=secondsvalue-seconds[clocksec]+lastsecondsticks;  //Last minutes powerusage is updated
    seconds[clocksec]=lastsecondsticks;  //The array with last minutes powerusage is updated

    secondspowerrating=secondspowerrating-secondsrating[clocksec]+powerrating/6;  //Last minutes powerrating is updated 
    secondsrating[clocksec]=(powerrating/6);  //The array with last minutes powerrating is updated

    minutesvalue=minutesvalue-minutes[clockmin]+secondsvalue;  //Last hours powerusage is updated
    minutes[clockmin]=secondsvalue;  //The array with last hours powerusage is updated

    hoursvalue=hoursvalue-hours[clockhour]+minutesvalue;  //Last days powerusage is updated
    hours[clockhour]=minutesvalue;  //The array with last days powerusage is updated

    daysvalue=daysvalue-days[clockday]+hoursvalue;  //Last weeks powerusage is updated
    days[clockday]=hoursvalue;  //The array with last weeks powerusage is updated

    weeksvalue=weeksvalue-weeks[clockweek]+daysvalue;  //Last four weeks powerusage is updated
    weeks[clockweek]=daysvalue;  //The array with last four weeks powerusage is updated
  }

  adc_key_in=analogRead(0);  //Read value to see if a button is pressed
  key=get_key(adc_key_in);  //Convert reading to a button number

  if (key == -1)  //No button is pressed, display total usage and current rating                         
  {                  
    sprintf(value, "%u", sensortickcounter/1000); //Convert powerusage value to string
    b=strlen(value);  //Get length of string containing powerusage
    for (int i=0; i<b; i++)  // Write powerusage to the output string
      result[i]=value[i];
    if(sensortickcounter<1000000)
    {
      result[b]=',';
      result[b+1]=((sensortickcounter%1000)/100)+48;  //Get first decimal
    }
    if(sensortickcounter<100000)
      result[b+2]=((sensortickcounter%100)/10)+48;  //Get second decimal 
    if(sensortickcounter<10000)
      result[b+3]=(sensortickcounter%10)+48;  //Get third decimal 

    result[5]='k';  //Add kWh to the output string
    result[6]='W';
    result[7]='h';

//    result[b+8]=48+clocksec;  //Print what "10 second" we are at right now. Only for testing

    sprintf(powervalue, "%d", powerrating); //Convert long powerrating to string
    a=strlen(powervalue);  //Get length of string containing powerrating
    for (int i=1; i<=a; i++)  // Write powerrating to the output string
      result[15-i]=powervalue[a-i];
    result[15]='W';  //Add W to the output string

    lcd.cursorTo(1, 0);  //Go to start of line one
    lcd.printIn("Totalt   Just nu");
    lcd.cursorTo(2, 0);  //Set cursor at start of line two
    lcd.printIn(result);  //Print powerusage and powerrating on LCD
  }

  if (key == 0)  //Button corresponding to minutes is pressed, display last minutes usage and rating
  {
    if(minutehaspassed)      
      sprintf(value, "%d", secondsvalue); //Convert powerusage to string
    else
      sprintf(value, "%d", sensortickcounter); //Convert powerusage to string

    b=strlen(value);  //Get length of string containing powerusage
    for (int i=0; i<b; i++)  // Write powerusage to the output string
      result[i]=value[i];  //Add Wh to the output string
    result[b]='W';
    result[b+1]='h';

    result[b+3]=48+(clockmin/10);  //Print what minute we are at right now. Only for testing
    result[b+4]=48+(clockmin%10);

    if(minutehaspassed)
      sprintf(powervalue, "%u", secondspowerrating); //Convert long powerusage to string
    else
      sprintf(powervalue, "%u", ((secondsvalue*360)/(clocksec)));  //Less than on hour has passed so minutesvalue is too small


    a=strlen(powervalue);  //Get length of string containing powerusage
    for (int i=1; i<=a; i++)  // Write powerusage to the output string
      result[15-i]=powervalue[a-i];  //Add W to the output string
    result[15]='W';

    lcd.cursorTo(1, 0);  //Go to start of line one
    lcd.printIn(msgs[0]);     
    lcd.cursorTo(2, 0);  //Set cursor at start of line two
    lcd.printIn(result);  //Print powerusage and powerrating on LCD
  }

  if (key == 1)       //Button corresponding to hours is pressed, display last hours usage and rating. Works the same as for minutes
  {                  
    if(hourhaspassed)
    {
      sprintf(value, "%d", minutesvalue/1000);
      b=strlen(value);
      for(int i=0; i<b; i++)
        result[i]=value[i];

      if(minutesvalue<1000000)
      {
        result[b]=',';
        result[b+1]=((minutesvalue%1000)/100)+48;  //Get first decimal
      }
      if(minutesvalue<100000)
        result[b+2]=((minutesvalue%100)/10)+48;  //Get second decimal 
      if(minutesvalue<10000)
        result[b+3]=(minutesvalue%10)+48;  //Get third decimal 

    }
    else
    {
      sprintf(value, "%d", sensortickcounter/1000); //Convert powerusage to string

      b=strlen(value);
      for(int i=0; i<b; i++)
        result[i]=value[i];

      if(sensortickcounter<1000000)
      {
        result[b]=',';
        result[b+1]=((sensortickcounter%1000)/100)+48;  //Get first decimal
      }
      if(sensortickcounter<100000)
        result[b+2]=((sensortickcounter%100)/10)+48;  //Get second decimal 
      if(sensortickcounter<10000)
        result[b+3]=(sensortickcounter%10)+48;  //Get third decimal 
    }

    result[5]='k';
    result[6]='W';
    result[7]='h';

//    result[b+8]=clockhour+65;  //Only for testing

    if(hourhaspassed)
      sprintf(powervalue, "%u", minutesvalue);  //More than one hour since the start has passed so minutesvalue is ok
    else
    {
      sprintf(powervalue, "%u", ((minutesvalue*60)/(clockmin+1)));  //Less than on hour has passed so minutesvalue is too small
      result[14-strlen(powervalue)]='c';
    }
    a=strlen(powervalue);
    for(int i=1; i<=a; i++)
      result[15-i]=powervalue[a-i];
    result[15]='W';

    lcd.cursorTo(1, 0);
    lcd.printIn(msgs[1]);
    lcd.cursorTo(2, 0);
    lcd.printIn(result);
  }
  if (key == 2)       //Button corresponding to days is pressed, display last days usage and rating. Works the same as for minutes
  {                  
    if(dayhaspassed)
    {
      sprintf(value, "%d", hoursvalue/1000);
      b=strlen(value);
      for (int i=0; i<b; i++)
        result[i]=value[i];

      if(hoursvalue<1000000)
      {
        result[b]=',';
        result[b+1]=((hoursvalue%1000)/100)+48;  //Get first decimal
      }
      if(hoursvalue<100000)
        result[b+2]=((hoursvalue%100)/10)+48;  //Get second decimal 
      if(hoursvalue<10000)
        result[b+3]=(hoursvalue%10)+48;  //Get third decimal 

    }
    else
    {
      sprintf(value, "%d", sensortickcounter/1000); //Convert powerusage to string
      b=strlen(value);
      for(int i=0; i<b; i++)
        result[i]=value[i];

      if(sensortickcounter<1000000)
      {
        result[b]=',';
        result[b+1]=((sensortickcounter%1000)/100)+48;  //Get first decimal
      }
      if(sensortickcounter<100000)
        result[b+2]=((sensortickcounter%100)/10)+48;  //Get second decimal 
      if(sensortickcounter<10000)
        result[b+3]=(sensortickcounter%10)+48;  //Get third decimal 
    }

    result[5]='k';
    result[6]='W';
    result[7]='h';

//    result[b+8]=48+clockday;  //Only for testing

    if(dayhaspassed)
      sprintf(powervalue, "%u", (hoursvalue/24));  //More than one day since the start has passed so hoursvalue is ok
    else
    {
      sprintf(powervalue, "%u", ((hoursvalue*60)/(clockhour*60+clockmin+1)));  //Less than on day has passed so hoursvalue is too small
      result[14-strlen(powervalue)]='c';
    }
    a=strlen(powervalue);
    for (int i=1; i<=a; i++)
      result[15-i]=powervalue[a-i];
    result[15]='W';

    lcd.cursorTo(1, 0);
    lcd.printIn(msgs[2]);
    lcd.cursorTo(2, 0);
    lcd.printIn(result);
  }

Code part three (last part):

  if (key == 3)       //Button corresponding to weeks is pressed, display last weeks usage and rating. Works the same as for minutes                  
  {
    if(weekhaspassed)
    {
      sprintf(value, "%u", daysvalue/1000);
      b=strlen(value);
      for (int i=0; i<b; i++)
        result[i]=value[i];

      if(daysvalue<1000000)
      {
        result[b]=',';
        result[b+1]=((daysvalue%1000)/100)+48;  //Get first decimal
      }
      if(daysvalue<100000)
        result[b+2]=((daysvalue%100)/10)+48;  //Get second decimal 
      if(daysvalue<10000)
        result[b+3]=(daysvalue%10)+48;  //Get third decimal 
    }
    else
    {
      sprintf(value, "%d", sensortickcounter/1000); //Convert powerusage to string
      b=strlen(value);
      for(int i=0; i<b; i++)
        result[i]=value[i];

      if(sensortickcounter<1000000)
      {
        result[b]=',';
        result[b+1]=((sensortickcounter%1000)/100)+48;  //Get first decimal
      }
      if(sensortickcounter<100000)
        result[b+2]=((sensortickcounter%100)/10)+48;  //Get second decimal 
      if(sensortickcounter<10000)
        result[b+3]=(sensortickcounter%10)+48;  //Get third decimal 
    }

    result[5]='k';
    result[6]='W';
    result[7]='h';

//    result[b+8]=48+clockweek;  //Only for testing

    if(weekhaspassed)
      sprintf(powervalue, "%u", (daysvalue/(24*7)));  //More than week hour since the start has passed so daysvalue is ok
    else
    {
      sprintf(powervalue, "%u", ((daysvalue*60)/(clockday*24*60+clockhour*60+clockmin+1)));  //Less than on week has passed so daysvalue is too small
      result[14-strlen(powervalue)]='c';
    }
    a=strlen(powervalue);
    for (int i=1; i<=a; i++)
      result[15-i]=powervalue[a-i];
    result[15]='W';

    lcd.cursorTo(1, 0);
    lcd.printIn(msgs[3]);
    lcd.cursorTo(2, 0);
    lcd.printIn(result);
  }

  if (key == 4)       //Button corresponding to four weeks is pressed, display last four weeks usage and rating. Works the same as for minutes            
  {                  
    if(weekshaspassed)
    { 
      sprintf(value, "%u", weeksvalue/1000);
      b=strlen(value);
      for (int i=0; i<b; i++)
        result[i]=value[i];

      if(weeksvalue<1000000)
      {
        result[b]=',';
        result[b+1]=((weeksvalue%1000)/100)+48;  //Get first decimal
      }
      if(weeksvalue<100000)
        result[b+2]=((weeksvalue%100)/10)+48;  //Get second decimal 
      if(weeksvalue<10000)
        result[b+3]=(weeksvalue%10)+48;  //Get third decimal 
    }
    else
    {
      sprintf(value, "%d", sensortickcounter/1000); //Convert powerusage to string
      b=strlen(value);
      for(int i=0; i<b; i++)
        result[i]=value[i];

      if(sensortickcounter<1000000)
      {
        result[b]=',';
        result[b+1]=((sensortickcounter%1000)/100)+48;  //Get first decimal
      }
      if(sensortickcounter<100000)
        result[b+2]=((sensortickcounter%100)/10)+48;  //Get second decimal 
      if(sensortickcounter<10000)
        result[b+3]=(sensortickcounter%10)+48;  //Get third decimal 
    }

    result[5]='k';
    result[6]='W';
    result[7]='h';

    if(weekshaspassed)
      sprintf(powervalue, "%u", (weeksvalue/(24*7*4)));  //More than four weeks since the start has passed so weeksvalue is ok
    else
    {
      sprintf(powervalue, "%u", ((weeksvalue*60)/(clockweek*7*24*60+clockday*24*60+clockhour*60+clockmin+1)));  //Less than four weeks has passed so weeksvalue is too small
      result[14-strlen(powervalue)]='c';
    }
    a=strlen(powervalue);
    for (int i=1; i<=a; i++)
      result[15-i]=powervalue[a-i];
    result[15]='W';

    lcd.cursorTo(1, 0);
    lcd.printIn(msgs[4]);
    lcd.cursorTo(2, 0);
    lcd.printIn(result);
  }
  timingflag= !timingflag;  //Change flag for next run in the loop
}

int get_key(unsigned int input)  //Converting input reading to corresponding button number (1-5), return -1 if no button pressed
{
  int k;
  for (k=0; k<NUM_KEYS; k++)
  {
    if (input < adc_key_val[k])
      return(k);
  }
  if (k >= NUM_KEYS)
    k=-1;     // No key pressed or an error occurred
  return(k);
}

void updateclock()  //Updating clock 0:0:0:0:0 -> 0:0:0:0:1 and so on
{
  timecounterthen=timecounternow;  //Reset time counter

  if (clocksec>=5)  //if more than 50 (seconds are only stored as 0-5) seconds have passed seconds are set to 0
  {
    clocksec=0;
    minutehaspassed=true;  //One minute has passed since the start so set flag true
    if( clockmin>=59)  //if more than 59 minutes have passed minutes are set to 0
    {
      clockmin=0;
      hourhaspassed=true;  // One hour has passed since the start so set flag true
      if(clockhour>=23)  //if more than 23 hours have passed hours are set to 0
      {
        clockhour=0;
        dayhaspassed=true;  // One day has passed since the start so set flag true
        if (clockday>=6)  //if more than 6 days have passed days are set to 0
        {
          clockday=0;
          weekhaspassed=true;  // One week has passed since the start so set flag true
          if(clockweek>=3)  //if more than 3 veeks have passed weeks are set to 0
          {
            clockweek=0;
            weekshaspassed=true;  // Four week has passed since the start so set flag true
          }
          else clockweek=clockweek+1;  //Less than 4 weeks have passed so update weeks by one
        }
        else
        {
          clockday=clockday+1;  //Less than 7 days have passed so update days by one
        }
      }
      else
      {
        clockhour=clockhour+1;  //Less than 24 hours have passed so update hours by one
      }
    }
    else
    {
      clockmin=clockmin+1;   //Less than 60 minutes have passed so update minutes by one
    }
  }
  else
  {
    clocksec=clocksec+1;   //Less than 60 days have passed so update seconds by ten
  }
}

void sensortick() //The interrupt routine
{
  powercounternow=micros();  //At what time did we se the pulse?
  //Serial.println(powercounternow-powercounterlasttime);
  sensortickcounter=sensortickcounter+1;  //Update number of pulses seen
  powerrating=3600000000/(powercounternow-powercounterlasttime);  //Calculate length of pulse and update current powerrating
  powercounterlasttime=powercounternow;  //Last time we saw a pulse was now
  sensorstate = LOW;
}

Nicely done. Thanks for posting pictures and code (and comments in English :)).

looks nice :slight_smile:

can you describe the pickup from the meter a little more?

Thanks for the positive feedback, in makes me even more proud.
I learned the hard way to comment code when I studied. A professor told me to comment the some code two weeks after I wrote it. It took me four times the time to comment it than to write the code.

The light resistor is connected to 5v and pin 3 and. And there is a 4 Mohm something resistor connected to pin 3 and ground. So if the light resistor sees darkness pin 3 is connected to ground and if it sees light pin 3 is connected to 5v. That vay i can read changes from LOW to HIGH on pin 3.

Really cool project, i guess my meter is the same type. Unfortunately it is in a little box on the outside of my house.

Your next project ?

As a friend of mine always says:

Your project isn't finished before you build the box for it :slight_smile:

Your friend is right. I was looking for a box to make it more finished but instead i screwed i on the wall so it feels pretty finished.

Next project could be to add a thermometer to see how the power usage is correlated to outside temperature. Add some memory and a real time clock to see when the most power is used.

But instead I've started a project to measure the speed of cars passing by on the street.

Add a radio transmitter and send it to your pc . Receiver on that end . Total cost $20 plus an arduino on the pc end
see here

http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1256188633/60#60

Not a bad idea! Will look into it. Thanks for the idea.

/Ulf

Hi Ulf,

that's great project - the one what I searched for.
could you please give me some idea how to connect photoresistor to arduino? some sketch should be great.
I want to read power usage from 3 electric meters - one heating system, one water heating system and air exchanger system.
thanks in advance

Maciek

Sure, here comes a schematic:

The light resistor is connected to 5v and pin 3 and. And there is a 4 Mohm something resistor connected to pin 3 and ground. So if the light resistor sees darkness pin 3 is connected to ground and if it sees light pin 3 is connected to 5v. That vay i can read changes from LOW to HIGH on pin 3.

Great project

Nice project :slight_smile:

I've always wanted to know exactly how much energy I'm using, and now I'm just days away to get my own solution working :slight_smile:

I do not have any experience in Arduinos, but I plan to use an ATmega(8,16,32), I'm not sure what will suit my application best. Those computers do have UART interface and that is good news since the old laptop I uses as home webserver has a RS232 (serial) interface. The plan is to buffer the meter reading in the ATmega and send to laptop (server) and perhaps use another ATmega coupled with a DS1620 digitalthermometer. And then correlate used energy with outdoor temperature :slight_smile:
Python and pySerial is quite easy to use when interfacing with UART.

I tried various solutions before ordering some ATmega.
The first try were 6-7 years when I studied, my apartment had the meter in my office/study, and I used my (only) optical mouse and taped it to the meter with the optical sensor right above the light, and were able to make a Java program that registered changing states in the serial transfer. It was a bad solution since I were a student and a new mouse would cost too much.
Just recently I tried to use a photoresistor and photodiode with a parallel port to do the same trick, but i was not able to get any stable signal through.

Mmmh, should perhaps have something measuring the take wind speed also...

And then correlate used energy with outdoor temperature

Here's one I made earlier :wink:

http://pluggy.is-a-geek.com/gasweek.html

Last hour of everything :

http://pluggy.is-a-geek.com/index.html

Somewhat out of date 'howto'

http://pluggy.is-a-geek.com/arduino/index.html

Thanks for the nice write up. It should help make my project much easier. Our electric Coop has just started a new rate structure where in addition to charging a base rate and Kw charge, they are charging a demand rate for the the maximum 15 minute period of the month.

We have a smart meter like yours that pulses an IR signal for every Watt. I have an old data logger that I connnected to the meter to record the usage and it is working good but due to its age, can only save about 2-3 days of data and has limited connectivity.

After reading your post, I just ordered a Ardunio and LCD display so that I can replicate your setup. The main challenge will be putting together a system that can store the one minute averages and allow me to get the data remotely. My power meter is about 100 meters from the house so it might be hard to reach it with WiFi. I am thinking about trying a ethernet shield and communicating through a Powerline network connection since I have a power outlet available at the meter.

I am looking at using a SD card for the data storage and am trying to figure out the best way to do that. I just saw that Seeedunio has just announced a new board named Stalker that includes a SD card slot, time chip and xBee socket. It looks like this board might meet most of my needs.

I am new to the Ardunio so posts like yours help a lot and I really appreciate the time you took to put it together. :slight_smile:

Find an old laptop and put it in a weatherproof box at the meter, connected to your Arduino. That should be able to hold years of log data. I've gotten nearly a dozen free old laptops by posting want ads on Freecycle.org. I used them for digital picture frames. But I have an extra one that I'm going to use for Arduino-specific stuff - in fact it's going to be a power usage monitor like the OP's.

I received my Arduino about two weeks ago and tried out your sketch and it works good. One question, I reduced the sketch down to a bare minimum so that I could understand how it works and there is one command that I do not understand. In the variable declaration section the following variable is declared:

volatile byte sensorstate = LOW;  //Start with sensor in LOW

Then at the end of the sensortick function it reads:

  sensorstate = LOW;

I can't figure out what this does, maybe somebody could explain it to me. I assume that it should reset the sensor pin state but I can not find a place where it refers to the sensor pin.

Thanks

edit: Once I'm done I will put together a write up in the exhibition section. For now, I will put a couple pictures of what I've done so far here. It took me a while to find a good meter cover to protect the sensor from ambient sunlight. The cover is a 2.5 quart plastic bucket I bought at ACE hardware. I painted the inside black and the outside gray. I first cut the bottom of the bucket off but it was still getting too much light so I taped the bottom back on and made a little window to view the meter.

One small change I made to the sketch was to change the Interrupt condition from RISING to HIGH because the sensor would go crazy when I opened the little door to look at the meter.

The sensor I used is a light to voltage TAOS Photodiode mfg part number TSL267-LF that I purchased from Mouser. Right now the sensor is taped to the top of the meter with the bucket covereing it. Later I will chage it so that the sensor is mounted on the outside of the bucket so that removing the bucket will remove the entire assembly.

Great thread! I just finished my version of this project.

Measuring My Electricity Consumption Cost in Real-Time

My meter is outside so i used pvc piping and lots of silicon to protect it.

Looking forward to your comments!

Andres

You're right CRS8291. the variable sensorstate is obselete. It must be some old stuff left in the code. Im sure its possible to tidy it up even more.
Glad you could use my code for your meter.