Electricity Usage Monitor

We all know that excessive power use is costly - both in terms of the adverse effect on the environment and also the high prices that we pay for our utility bills. It is quite awkward to keep looking at the electricity meter, usually tucked away in a dark cupboard, to find out how much power has been used. Also, it is hard to be disciplined and write down the dates/times of readings taken and convert this to cash spent.

So it seemed a good idea to have the Cybiko help keep tabs on how much electricity is being used in my home. Fortunately, my electricity meter has a small light on the front. This flashes 800 times for every kWh of power consumed.

The idea is to have a small external circuit count the pulses of light from the LED.

Importantly, this can be done without any physical connection to the electricity meter itself. Whenever asked, the external circuit will pass the number of pulses counted, via RS232, to a Cybiko Classic. This Cybiko will then work out some stats regarding the power use. e.g. total count of kW hours shown on meter, current power 'burn rate' in kW hours, average power use for each hour in the last 24hrs, total cost of power used each day for the last week.

This info will then be sent, wirelessly, to another Cybiko, running RFTerm. This means that we now have a mobile unit that can be viewed easily. A neat feature would be the ability to specify a given power usage rate that, when exceeded, would sound an alarm on the RF Terminal Cybiko. This would be especially handy when living in a house where kids/partners leave lights/TVs/heaters switched on when not needed.

Ideally, the solution could also feed an internet connected PC. This would then allow publishing of the info to the internet. The MRTG software has the ability to generate this entire webpage. It can periodically call a user-supplied 'data gatherer' program. This simply returns one data sample value. The MRTG software then does all the work. Very cool.

Standalone meters can be purchased from from http://www.universalmeterservices.co.uk

A great way of adding MSN alerts to this system would be via the excellent X10Dispatcher program.

Update at 14th Oct - tried a little pulse counter circuit using a TIL81 phototransistor and a resistor. This worked well when illuminated with a LED. However, when tested on an actual meter, it didn't catch the pulses of light. The light source in the meter is rather dimmer than a standard LED. A more sensitive circuit is needed…

Diagram (click image to zoom) Description
pulse_circuit1.jpg Schematic for Pulse Counter.

Incorporating the following sensor detector circuit
http://offog.org/code/electricity.html
pulse1.jpg PCB Layout

Update at 28th Oct - now using an LDR … this works very nicely. Next thing is to hook up an AVR chip to do the counting of the pulses.

The PC executable will be called, periodically, from MRTG. When called, the .exe will send a '?' char to serialrelay. This will be routed to the AVR & will prompt the AVR to reply, via RS232, with the count of pulses received (string).

Idea, flash a LED on the AVR board to show that pulse detection is working.

Now, if SerialRelay was able to selectively route to a given Cybiko, we could also send some 'stats' to an RFTerm.

Update at 7th November - Brett knocked out some AVR code to count the pulses & send out the count over RS232 when a '?' character was received. Ran this up on an AVR 2313 development board. This runs very well. Tested using RFTerminal in 'direct RS232' mode. Great stuff. We are all but done. However, it would be nice to have a dedicated PCB for this project (see design above).

'********************************************************************
'* Pulse counter
'* uP:          AT90S2313
'* Language:    BASCOM-AVR 1.11.7.9
'********************************************************************
$regfile = "2313def.dat"
$crystal = 4000000
$baud = 9600
 
' Configure these for the specific implementation.
Config Pind.4 = INPUT , Pind.5 = OUTPUT
Led ALIAS Pind.5
Sensor ALIAS Pind.4
 
' serial Rx complete interrupt
ON Urxc Readrs232
Enable Interrupts
Enable Urxc
 
DIM Count AS LONG
 
Count = 0
 
' Loop forever
DO
  Bitwait Sensor , RESET                         ' wait until sensor is LOW
  RESET Led                                      ' Turn the LED off
  Count = Count + 1
  Bitwait Sensor , Set                           ' wait until Sensor is HIGH
  Set Led                                        ' Turn the LED on.
LOOP
 
Readrs232:
  IF Udr = 63 THEN                               ' check for ? character
    PRINT Count
    Count = 0
  END IF
RETURN

Here is a config file from MRTG. Note that MRTG is configured here to call an external .exe (getdata.exe) to gather the data to be plotted.

WorkDir: C:\mrtg
RunAsDaemon: yes

Target[hostAdisk]: `getdata.exe`
Title[hostAdisk]:  Electricity Usage at DK Home
MaxBytes[hostAdisk]: 100000
PageTop[hostAdisk]: <H1>Power consumed</H1>
Suppress[hostAdisk]: y
LegendO[hostAdisk]:  Stats
Legend1[hostAdisk]:  % used
Legend2[hostAdisk]:
YLegend[hostAdisk]: Watts
ShortLegend[hostAdisk]: Electricity usage (W)
Options[hostAdisk]: gauge, nopercent, noi

Code shown below is not finished yet.

unit Unit1;
 
{
DK 13 Oct 2005
Designed to be called by MRTG
The idea goes like this ...
an AVR micro is running beside the electricity meter, counting pulses on the LED
 
every 5 mins, MRTG calls this application.
This app sends a '?' character to the serial port.
This is serialrelayed to the cybiko connected to the AVR board.
The AVR receives the '?' and responds with a string which holds the count of
pulses since the last query. The count is then reset.
 
This app receives the pulse count string and passes it back to MRTG.
We probably want a timeout between sending the ? and waiting for a result back ...
  just in case we never get one.
 
Code not finished yet!
}
 
interface
 
uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, OoMisc, AdPort;
 
type
  TForm1 = class(TForm)
    cp1: TApdComPort;
    procedure FormCreate(Sender: TObject);
    procedure cp1TriggerData(CP: TObject; TriggerHandle: Word);
  private
    { Private declarations }
  public
    { Public declarations }
  end;
 
var
  Form1: TForm1;
  myVal : string;
implementation
 
{$R *.dfm}
 
 
procedure TForm1.FormCreate(Sender: TObject);
 
begin
  cp1.AddDataTrigger(':', True);
  cp1.PutChar('?'); //Send a '?' to the AVR
  sleep(100);
  allocConsole;
  writeln('0');   //in - not graphed (noi)
  writeln(myVal); //out - this is the data we want.
  writeln;    //comment1
  writeln;    //comment2
  freeConsole;
  Application.Terminate;
end;
 
procedure TForm1.cp1TriggerData(CP: TObject; TriggerHandle: Word);
const
  LF: Char = chr(10);
var
  ch : char;
begin
  myVal := '';
  repeat
     ch := cp1.GetChar;
     if ch <> LF then myVal := myVal + ch;
  until ch = LF;
end;
 
end.