Sweden
Offline
Newbie
Karma: 0
Posts: 25
Arduino rocks
|
 |
« on: November 23, 2009, 05:14:45 am » |
I'm so proud! My first Arduino project is a power meter for my house. The power meter that the electric company reads every month and sends me a bill from just states how much power is used since it started. It also sends out a light pulse for every thousands of a kWh used. I use a light dependant resistor to read the pulse and count them and the time between the pulses to get power used since my Aruino started and the current power usage. All this is displayed on a LCD shield. With the buttons on the shield i can display different things: The total power used since the start and current usage. Power used last minute and mean power last minute. Power used last hour and mean power last hour. Power used last day and mean power last day. Power used last week and mean power last week. Power used last four weeks and mean power four. The sketch saves the current usage value every ten seconds and calculates everything. The values are cyclic so last days measyre ment is rather last 24h values and so on. If the Arduino is juststarted it sets a "c" in front of the values that are not correct yet (it takes four weeks to get all values correct). The power meter, fuses and what not. And my Arduino with LCD shield. http://imgur.com/Ub3hh.jpgClose up of the Arduino and shield running showing the default screen: The total power used since the start and current usage . http://imgur.com/rEISz.jpgCode doesnt fit. It's too big. How do I do? It was kind of an anti climax when all was in place and running. What do do now? I have an idea of another shield that kan show graphics so i can draw a graph of power usage. Connecting a memory card so I can import the readings to my computer and analyze the readings more. And... And... Is there evera finished product? /Ulf
|
|
|
|
|
Logged
|
|
|
|
|
Sweden
Offline
Newbie
Karma: 0
Posts: 25
Arduino rocks
|
 |
« Reply #1 on: November 23, 2009, 05:16:13 am » |
And the code part one: #include <LCD4Bit_mod.h> //Acess to LCD library #include <stdio.h> //Needed for sprintf conversion from long to char * #include <string.h> //Needed for getting string lengths LCD4Bit_mod lcd = LCD4Bit_mod(2); //Set LCD as two line display
unsigned int timecounternow; //Timecounters for checking if ten seconds have passed unsigned int timecounterthen=0; volatile unsigned long powercounternow; //Counts time between pulses in milliseconds volatile unsigned long powercounterlasttime=0; byte clocksec, clockmin, clockhour, clockday, clockweek; // to keep track of time volatile byte sensorstate = LOW; //Start with sensor in LOW volatile unsigned long sensortickcounter=0; //Counts powerpulses volatile unsigned int powerrating=0; //Last powermeasurement
unsigned int lastsecondsticks=0; //Last minutes power usage (blinks seen last ten seconds) unsigned long lastsecondstickscounter=0; //Counter to store time we read last ten seconds power usage
char msgs[5][17]={ " Senaste minuten", " Senaste timmen", " Senaste dygnet", " Senaste veckan", "Senaste 4 veckor"}; // Array with messages that can be displayed
int adc_key_val[5]={ 30, 150, 360, 535, 760}; //Array for button readings int NUM_KEYS=5; //Number of keys available int adc_key_in; //To read key with int key=-1; // Key pressed right now, -1 if no key pressed or faulty reading
int seconds[6]; //Array that holds each powerreading done during the last minute int minutes[60]; //Array that holds each powerusage reading done during the last hour int hours[24]; //Array that holds each powerusage reading done during the last day unsigned long days[7]; //Array that holds each powerusage reading done during the last week unsigned long weeks[4]; //Array that holds each powerusage reading done during the last four weeks
unsigned int secondsvalue=0; //Powerusage during the last minute unsigned long minutesvalue=0; //Powerusage during the last hour unsigned long hoursvalue=0; //Powerusage during the last day unsigned long daysvalue=0; //Powerusage during the last week unsigned long weeksvalue=0; //Powerusage during the last four weeks
unsigned int secondsrating[6]; //Array that holds each powerrating reading done during the last minute unsigned long secondspowerrating=0; //Powerrating during the last minute
char powervalue[6]; //Char array to hold power value for printing on LCD char value[6]; //Char array to hold power usage for printing on LCD byte a,b; //Used to get length from strings
byte minutehaspassed=false; //Flag to see if one minute or more has passed since start, set to false from start byte hourhaspassed=false; //Flag to see if one hour or more has passed since start, set to false from start byte dayhaspassed=false; //Flag to see if one day or more has passed since start, set to false from start byte weekhaspassed=false; //Flag to see if one week or more has passed since start, set to false from start byte weekshaspassed=false; //Flag to see if four weeks or more has passed since start, set to false from start byte timingflag=false; //Flag that switches between 0 and 10. Used to get tome to be more accurate
void setup() { attachInterrupt(1, sensortick, RISING); //Set upp interrupt with sensor. If we detect a change from LOW to HIGH we call sensortick timecounterthen=millis(); //Arduino is running, start timing now. clocksec=0; //Clock is 0:0:0:0:0 (w:d:h:m:s)when started clockmin=0; clockhour=0; clockday=0; clockweek=0; lcd.init(); //Initialize LCD lcd.clear(); //Clear LCD
for (int i=1; i<6 ;i++) //Each array that holds powerusage is set to contain 0 in every place seconds[i]=0; for (int i=0; i<60 ;i++) minutes[i]=0; for (int i=0; i<24 ;i++) hours[i]=0; for (int i=0; i<7 ;i++) days[i]=0; for (int i=0; i<4 ;i++) weeks[i]=0; for (int i=0; i<6 ;i++) //Array that holds powerrating is set to contain 0 in every place secondsrating[i]=0; seconds[0]=powerrating; //We have started so set the first seconds powerrating // Serial.begin(9600); }
|
|
|
|
|
Logged
|
|
|
|
|
Sweden
Offline
Newbie
Karma: 0
Posts: 25
Arduino rocks
|
 |
« Reply #2 on: November 23, 2009, 05:18:08 am » |
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); }
|
|
|
|
|
Logged
|
|
|
|
|
Sweden
Offline
Newbie
Karma: 0
Posts: 25
Arduino rocks
|
 |
« Reply #3 on: November 23, 2009, 05:19:16 am » |
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; }
|
|
|
|
|
Logged
|
|
|
|
|
0
Offline
Sr. Member
Karma: 0
Posts: 388
|
 |
« Reply #4 on: November 23, 2009, 06:10:08 pm » |
Nicely done. Thanks for posting pictures and code (and comments in English  ).
|
|
|
|
|
Logged
|
|
|
|
|
Austria
Offline
Sr. Member
Karma: 3
Posts: 261
Arduino rocks
|
 |
« Reply #5 on: November 24, 2009, 02:04:01 am » |
looks nice  can you describe the pickup from the meter a little more?
|
|
|
|
|
Logged
|
|
|
|
|
Sweden
Offline
Newbie
Karma: 0
Posts: 25
Arduino rocks
|
 |
« Reply #6 on: November 24, 2009, 02:37:01 am » |
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.
|
|
|
|
|
Logged
|
|
|
|
|
Copenhagen / Denmark
Offline
Edison Member
Karma: 5
Posts: 2346
Do it !
|
 |
« Reply #7 on: November 24, 2009, 02:50:10 am » |
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 :-)
|
|
|
|
|
Logged
|
|
|
|
|
Sweden
Offline
Newbie
Karma: 0
Posts: 25
Arduino rocks
|
 |
« Reply #8 on: November 24, 2009, 07:31:34 am » |
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.
|
|
|
|
|
Logged
|
|
|
|
|
|
|
Sweden
Offline
Newbie
Karma: 0
Posts: 25
Arduino rocks
|
 |
« Reply #10 on: November 29, 2009, 01:46:33 pm » |
Not a bad idea! Will look into it. Thanks for the idea.
/Ulf
|
|
|
|
|
Logged
|
|
|
|
|
0
Offline
Newbie
Karma: 0
Posts: 2
Arduino rocks
|
 |
« Reply #11 on: November 30, 2009, 03:07:10 am » |
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
|
|
|
|
|
Logged
|
|
|
|
|
Sweden
Offline
Newbie
Karma: 0
Posts: 25
Arduino rocks
|
 |
« Reply #12 on: December 01, 2009, 02:35:17 am » |
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.
|
|
|
|
|
Logged
|
|
|
|
|
0
Offline
Newbie
Karma: 0
Posts: 2
Arduino rocks
|
 |
« Reply #13 on: December 01, 2009, 06:59:21 am » |
Great project
|
|
|
|
|
Logged
|
|
|
|
|
0
Offline
Newbie
Karma: 0
Posts: 1
Arduino rocks
|
 |
« Reply #14 on: January 02, 2010, 08:00:02 pm » |
Nice project  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  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  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...
|
|
|
|
|
Logged
|
|
|
|
|
|