Digital Electric Meter Monitor Goes Old School

powerMeter

[Sal] sent us his digital electric meter monitor, which immediately made us nostalgic for some of Forrest Mims’ books. Sal’s schematic and circuit description are similar to Forrest’s style, and we mean that as a compliment. Even in today’s world of CAD and EDS packages, sketching out a circuit by hand is sometimes both easier and faster. The schematic isn’t the only classic aspect of [Sal’s] design. He’s collecting data using a parallel port on an unused PC: in this case, a Toshiba Libretto running Windows 95. Before cheap flash-based microcontrollers and dev boards were available, the PC parallel port was the go-to hardware hacking interface for many of us. Plenty of the software running those old hacks was written in basic, and [Sal’s] meter is no exception. His software runs on Microsoft QBasic, which shipped with Windows 95.

The circuit takes advantage of the digital meter’s output: a 10 ms pulse for every 1 Wh of energy used. An IR photo detector from RadioShack detects the meter pulses, which are amplified by an LM324 Op Amp. An NPN transistor then shifts the output to send it to two 74LS73 JK flip flops. The first flip flop uses a transistor to drive an LED for visual output. The second JK flip flop sends the data to the PC. The flip flop has the effect of dividing the number of meter pulses by two, creating a much longer toggled signal that a PC can better detect.

Although using an AVR or PIC would consume less power, [Sal’s] setup has already more than paid for its power usage. By monitoring and adapting his electrical usage, [Sal] is saving $20 a month on his electric bill. We’ve included [Sal’s] circuit diagram and source code after the break (apologies to our readers on RSS).

First [Sal’s] test program:

CLS

SCREEN 12

10 W = INP(&H379)

IF W <= 127 THEN GOTO 10 R! = TIMER 20 W = INP(&H379) IF W > 127 THEN GOTO 20

30 W = INP(&H379)

IF W <= 127 THEN GOTO 30

S! = TIMER

T! = S! – R!

IF T! = 0 THEN GOTO 10

KW = 7.2 / T!

PRINT KW;

ON KEY(1) GOSUB 40

KEY(1) ON

GOTO 10

40 END

Here is the main application:

CLS

REM C:\METER011.BAS

1 SCREEN 12

LOCATE 1, 1: INPUT "INPUT ELECTRICAL METER KWH ", QQ

LOCATE 1, 1: FOR X = 1 TO 40: PRINT " "; : NEXT

DD1$ = MID$(DATE$, 4, 2) 'DAYS

TH1$ = LEFT$(TIME$, 2) 'HOURS

TM1$ = MID$(TIME$, 4, 2) 'MINUTES

TS1$ = RIGHT$(TIME$, 2) 'SECONDS

4 A = 0: B = 0: C = 0: D = 0: F = 0: G = 0: K = 0: L = 0: M = 0: N = 0

O = 0: P = 0: Q = 0: R = 0: V = 32: LA = 0: MB = 0: NC = 0: OD = 0: NN = 0

DD = 1: U = 1

DIM A(120): DIM B(120): DIM C(25): DIM D(32)

B = VAL(TM1$)

C = VAL(TH1$)

D = VAL(DD1$)

GOSUB 2010

GOSUB 500

8 Q! = TIMER

10 W = INP(&H379)

IF W <= 127 THEN GOTO 10 R! = TIMER 20 W = INP(&H379) IF W > 127 THEN GOTO 20

AA& = AA& + 1

BB& = BB& + 1

30 W = INP(&H379)

IF W <= 127 THEN GOTO 30

S! = TIMER

IF S! < Q! THEN S! = S! + 86400 T! = S! - R! IF T! >= 0 AND T! <= .5 THEN GOTO 10 K = (7.2 / T!) LOCATE 1, 5: PRINT USING "##.###"; K; LOCATE 1, 11: PRINT " KW" ON KEY(1) GOSUB 1000: KEY(1) ON 100 A = A + 1: L = L + K: LA = L / A A(A) = K IF K >= 7 THEN A(A) = 7

V = 32 + A * 8: Y = A(A) * 16: LINE (V, 135)-(V, 135 - Y), 10

TM2$ = MID$(TIME$, 4, 2)

IF TM2$ = TM1$ THEN 110 ELSE GOSUB 1005

110 IF S! - Q! >= 60 THEN GOSUB 600

IF S! - Q! >= 60 THEN 125

GOTO 10

125 A = 0: B = B + 1: L = 0: M = M + LA: MB = M / B

B(B) = LA

IF LA >= 5 THEN B(B) = 5

V = 32 + B * 8: Y = B(B) * 16: LINE (V, 247)-(V, 247 - Y), 10

TH2$ = LEFT$(TIME$, 2)

IF TH2$ = TH1$ THEN 126 ELSE GOSUB 700

IF TH2$ = TH1$ THEN 126 ELSE GOTO 150

126 GOTO 8

150 TH1$ = TH2$: B = 0: C = C + 1: M = 0: N = N + MB: NC = N / C

C(C) = MB

IF MB >= 3 THEN C(C) = 3

V = 32 + C * 8: Y = C(C) * 16: LINE (V, 327)-(V, 327 - Y), 10

151 IF C >= 24 THEN GOSUB 800

IF C >= 24 THEN 175

GOTO 8

175 C = 0: D = D + 1: N = 0: O = O + NC: OD = O / D

D(D) = NC

IF NC >= 4 THEN D(D) = 4

V = 32 + D * 8: Y = D(D) * 38.4: LINE (V, 423)-(V, 423 - Y), 10

IF D >= DA THEN GOSUB 900

IF D >= DA THEN 180

GOTO 8

180 D = 0: O = 0: E = E + 1: P = P + OD * 24: PE = P / E

E = 0: P = 0

GOTO 4

500 REM ONE MINUTE KW PLOT

LOCATE 2, 1

FOR G = 7 TO 0 STEP -1: PRINT USING "##.#"; G; : PRINT CHR$(45): NEXT

FOR GG = 2 TO 9

LOCATE GG, 6: FOR T = 1 TO 60: PRINT CHR$(45); : NEXT

NEXT

LOCATE 10, 6: PRINT " KW/1 MINUTE"

REM ONE HOUR KWH PLOT

LOCATE 11, 1

FOR G = 5 TO 0 STEP -1: PRINT USING "##.#"; G; : PRINT CHR$(45): NEXT

FOR GG = 11 TO 16

LOCATE GG, 6: FOR T = 1 TO 60: PRINT CHR$(45); : NEXT

NEXT

LOCATE 17, 6: PRINT " KW/1 HOUR"

REM ONE DAY KWH PLOT

LOCATE 18, 1

FOR G = 3 TO 0 STEP -1: PRINT USING "##.#"; G; : PRINT CHR$(45): NEXT

FOR GG = 18 TO 21

LOCATE GG, 6: FOR T = 1 TO 24: PRINT CHR$(45); : NEXT

NEXT

LOCATE 22, 6: PRINT " KWH/1 DAY"

REM ONE MONTH TOTAL KWH

LOCATE 23, 1

FOR G = 40 TO 0 STEP -10: PRINT USING "##.#"; G; : PRINT CHR$(45): NEXT

FOR GG = 23 TO 27

LOCATE GG, 6: FOR T = 1 TO DA: PRINT CHR$(45); : NEXT

NEXT

LOCATE 28, 6: PRINT " KWH/1 MONTH"

REM DATE AND TIME

LOCATE 1, 17: PRINT DATE$

LOCATE 1, 30: PRINT TIME$

LOCATE 1, 41: PRINT USING "#####.#"; QQ;

PRINT " KWH"

RETURN

600 FOR HH = 2 TO 9: LOCATE HH, 6: FOR H = 1 TO 60: PRINT " "; : NEXT H: NEXT HH

FOR GG = 2 TO 9

LOCATE GG, 6: FOR T = 1 TO 60: PRINT CHR$(45); : NEXT

NEXT

RETURN

700 FOR HH = 11 TO 16: LOCATE HH, 6: FOR H = 1 TO 60: PRINT " "; : NEXT H: NEXT HH

FOR GG = 11 TO 16

LOCATE GG, 6: FOR T = 1 TO 60: PRINT CHR$(45); : NEXT

NEXT

RETURN

800 FOR HH = 18 TO 21: LOCATE HH, 6: FOR H = 1 TO 24: PRINT " "; : NEXT H: NEXT HH

FOR GG = 18 TO 21

LOCATE GG, 6: FOR T = 1 TO 24: PRINT CHR$(45); : NEXT

NEXT

RETURN

900 FOR HH = 23 TO 28: LOCATE HH, 6: FOR H = 1 TO DA: PRINT " "; : NEXT H: NEXT HH

FOR GG = 23 TO 27

LOCATE GG, 6: FOR T = 1 TO 31: PRINT CHR$(45); : NEXT

NEXT

RETURN

1000 LOCATE 1, 1

INPUT "QUIT? YES OR NO ", ANS$

IF ANS$ = "YES" THEN END

RETURN

REM 1005 LOCATE 28, 37

1005 LOCATE 28, 38

DA$ = (LEFT$(DATE$, 5))

TI$ = (LEFT$(TIME$, 5))

PP = ((7.2 * BB&) / 3600)

TT = QQ + PP

PRINT DA$; " "; TI$; " ";

PRINT USING "###.#"; PP;

PRINT " KWH"

LOCATE 1, 54

PRINT USING "#####.#"; TT;

PRINT " KWH"

REM IF PP >= 0 AND PP < 303 THEN XX = PP * .13: GOTO 1006 REM IF PP >= 303 AND PP < 394 THEN XX = 39.52 + (PP - 304) * .16: GOTO 1006 REM IF PP >= 394 AND PP < 606 THEN XX = 54.08 + (PP - 395) * .26: GOTO 1006 REM IF PP >= 606 AND PP < 648 THEN XX = 109.46 + (PP - 608) * .29: GOTO 1006

REM 1006 PRINT " "; "$";

REM PRINT USING "###.##"; XX

RETURN

1007 LOCATE U, 67

VV = ((7.2 * AA&) / 3600)

PRINT DD2$; " ";

PRINT USING "###.##"; VV

AA& = 0

U = U + 1

IF U = 29 THEN U = 1

PP = (7.2 * BB&) / 3600

TT = QQ + PP

RETURN

2010 C$ = LEFT$(DATE$, 2)

ON VAL(C$) GOTO 2101, 2102, 2103, 2104, 2105, 2106, 2107, 2108, 2109, 2110, 2111, 2112

2101 DA = 31: GOTO 3000

2102 E$ = MID$(DATE$, 9, 2)

IF VAL(E$) = 16 THEN DA = 29 ELSE DA = 28

IF VAL(E$) = 20 THEN DA = 29 ELSE DA = 28

IF VAL(E$) = 24 THEN DA = 29 ELSE DA = 28

IF VAL(E$) = 28 THEN DA = 29 ELSE DA = 28

GOTO 3000

2103 DA = 31: GOTO 3000

2104 DA = 30: GOTO 3000

2105 DA = 31: GOTO 3000

2106 DA = 30: GOTO 3000

2107 DA = 31: GOTO 3000

2108 DA = 31: GOTO 3000

2109 DA = 30: GOTO 3000

2110 DA = 31: GOTO 3000

2111 DA = 30: GOTO 3000

2112 DA = 31: GOTO 3000

3000 RETURN

3010 LOCATE 28, 24

T1$ = LEFT$(TIME$, 5)

PRINT T1$

RETURN

22 thoughts on “Digital Electric Meter Monitor Goes Old School

  1. A super neat device, and I do really love the Mimsian documentation.

    However, the article needs correction. The two flip-flops are not fed into each other at all. The transistor just provides a clock signal to both JK latches at the same time. One latch drives an LED through a transistor, and the other drives the output transistor. The divide by two action is due the fact that the J and K pins are held high, which means at each clock transition the output toggles from its previous state.

    I only point this out because the JK latch or flip-flop is so rarely seen, so when it gets used we should make sure people understand how clever its use is in those cases.

    Keep up the great work HaD!

  2. Your ampersand amps are showing, quasic didn’t need its ampersands escaped, and the pre tag overrides the html ampersand tags.

    You’re displaying

     10 W = INP(&H379) 

    when I’m guessing you
    meant to display

     10 W = INP(&H379) 

    .

      1. Thanks. I hope I didn’t come off as snarky or anything. Just tryin’ to help. :-)
        In fact, I was kind of surprised that the ampersand tag got rendered inside a pre tag!
        In any case, the article really was a very pleasant trip down nostalgia lane – when I was a kid, all the hobbyist projects used toobz!

        Please keep up the good work! :-)

  3. Nitpicks aside, this article did give me a pleasant stroll down nostalgia lane! Especially the parallel port part. I used to LOVE to do I/O by parallel port, albeit under DOS.

    Thanks!

  4. Using line numbers and goto [number] in Qbasic/QuickBasic was/is a horrible archaic practice from the 8 bit era that only served to make the code next to impossible to read and debug, because it didn’t require line numbers and can use plain text labels for the goto/gosub commands.

    http://www.svatopluk.com/qbtutor/tut6.htm

    And structures like ON VAL(C$) GOTO 2101, 2102, … could be replaced with a SELECT CASE structure to make the code simpler to read, because then you wouldn’t have to write all the goto labels and return structures.

    http://www.angelfire.com/scifi/nightcode/prglang/qbasic/function/control/select_case.html

    1. Especially when the code appears to be taking the first two characters of a date string, converting that to an integer, and then jumping to a different part of the code based on that number, where the SELECT CASE structure could do direct string comparison.

      It’s hard to say what that piece of code is supposed to accomplish, because the only way into that subroutine seems to be through a label named “2010” which doesn’t tell us anything about what it’s about. It seems to be modifying a global variable DA that contains how many days there are in a month.

      No wonder people thought basic was horrible spaghetti code.

    1. I think you would need to be polling the parallel port constantly to detect transient signals, so doing anything else would risk missing it. It’s designed for parallel data, which means there’s some sort of syncronization going on where host/guest toggle the lines and signal each other when to read them.

  5. First off, thanks to Sal/HaD for posting this.

    All you folk complaining about SELECT over GOTO et al, should offer up some streamlined code using your suggestions. Lead, follow or get out of the way. Be grateful that Sal thought up the design, built it, photographed it, created + tested the code and posted it ALL for FREE.

    Just this last month a Smart Meter replaced our spinning disk so I would like to know how to save money on power usage by monitoring. I am not suggesting it is not possible, but I can’t see any way I could benefit from monitoring.

    We have a single rate for power in all-electric home. The heaters have to be on when we are awake and they use about 50% of our total monthly usage. The water heater runs when we take showers.

    Sal – care to explain if we/I can save some money by building this brilliant project? Thanks

    1. i have a finished product, that does the same job, with just a couple og AAA’s that i havent evem replaced yet ( http://www.norgo.dk/da/produkter/norgo-energy-wireless/ )

      It helps me save money, because i now know what my house uses “at rest” meaning when all but the fridge, heater and other “always on” things are turned off. of course this value varies a little bit, but i know the usual interval.
      So whenever the meter shows a higher number, i know i have forgotten to turn off something.

      Also its an eye opener to try and turn something off you don’t really need on when not using it (stereo, projector and such) and see how that affects the “at rest” usage. Now i installed switches on a lot of things that used to go on standby, and it really saves a significant amount of money.

      of course this only works because of my geekish side that makes me look at the display a few times every day.

    2. “should offer up some streamlined code using your suggestions”

      I’m not going to start reverse-engineering that code salad just to prove a point. Of course you could write a spec of what it should be doing and how, and I could re-implement that if it’s not too complicated.

      1. For example, the whole month to days-in-month conversion could have been replaced in a single line using a lookup table.

        DIM days_in_month(1 TO 12) AS INTEGER
        days_in_month(1) = 31
        days_in_month(2) = 29

        And the actual routine would condense to two lines.

        DA = days_in_month(month)
        IF year MOD 4 = 0 AND month = 2 THEN DA = 28

    3. Depending on your utility, your smart meter may provide a Zigbee connection. I know that those Itron OpenWay meters have a zigbee radio in them that provides a data stream to In Home Displays and sends peak usage alerts to smart appliances. It may be a bit of a pain to get your custom device provisioned, so you might be better off hacking an existing in home display to send the data wherever you want it.

Leave a Reply

Please be kind and respectful to help make the comments section excellent. (Comment Policy)

This site uses Akismet to reduce spam. Learn how your comment data is processed.