[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
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!
You’re absolutely right Kevin. Color me embarrassed, I missed the clock line there. I’ve corrected the article. Thanks for the catch!
Your ampersand amps are showing, quasic didn’t need its ampersands escaped, and the pre tag overrides the html ampersand tags.
You’re displaying
when I’m guessing you
meant to display
.
Ah, rats! No edit button!
What I see, without the pre tag, is 10 W = INP(&H379)
Whatever I did in my comment seems to have fixed the display in either case.
Boy that’s annoying. Not sure why WordPress hoses code like this sometimes but I fixed it.
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! :-)
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!
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
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.
Putting aside the ungodly energy usage implied by Adam typing kWh instead of Wh, how exactly does a 10ms pulse pose a problem for detection?
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.
Erroneous ‘k’ removed. Thanks.
Editing note:
ms = milliseconds
mS = millisiemens
http://www.ewh.ieee.org/soc/ias/pub-dept/abbreviation.pdf
It’s not an uncommon mistake, but it drives me nuts especially in electrical documentation.
Thanks, fixed.
a Toshiba Libretto running Windows 95…. pulls how many watts, even with the screen off?
Far out man! goto’s, gosubs, line numbers Qbasic rocked !
The irony is that Qbasic has function calls, so all that goto salad could have been condensed to something much neater and readable.
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
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.
“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.
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
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.