Wattmeter Program

Declarations

#rem This is the green box with a sloping front and a line receptacle on top

Wattmeter.bas. Modified to provide an output for the Universal Transmitter

John Sainders 10/19/2021

#endrem


#picaxe 14M2


symbol RS232_out = B.0        'SEROUT, also used for the transmitter output

symbol Disp_clk  = B.4        '74HC164 clocks on low to high

symbol Disp_en   = B.5        'Positive pulse clocks the data & RS into the QC1602 display

symbol Disp_data = C.1        'MSB first


'input ports

symbol Watt_Port = B.1

symbol Curr_Port = B.2

symbol Volt_Port = B.3

symbol Two_Hz    = pinC.0    'An interrupt port for the 14M2

symbol RangeX10  = pinC.2    'These 3 go low when the position named is selected

symbol Range100  = pinC.3

symbol Range300  = pinC.4



'Constants

DATA 0,(128,64,32,16,8,4,2,1)                'Mask for shifting into the 74HC164

DATA 8,($30,$30,$30,$38,$0C,$01,$06,$14,$80)    'Display Initialization string

DATA 17,($80,"Overload!        ",0)            '$80 is first line, $C0 2nd line

DATA 35,($85," W ",0)

DATA 40,($8D," MA",0)

DATA 45,($C5," V ",0)

DATA 50,($CD," WH",0)

DATA 55,($8D," A ",0)

DATA 60,($84," W  ",0)

symbol Volt_off = 751        'There is a bias on the voltage circuit so that the count is 10/volt


'variables

symbol RS        = bit0        'Low for command, High for data

symbol BLZ       = bit1        'Blank leading zeroes, (not last one)

symbol Sec_phase = bit2        'Divides a second ito two halves

symbol Char      = b1        'Used to bit-bang to the 74HC164 shift register

symbol Mask      = b2        'Used to bit-bang to the 74HC164 shift register

symbol Mask_Addr = b3        'Used to bit'Used to bit-bang to the 74HC164 shift register

symbol Scratch   = b4        'Used to bit'Used to bit-bang to the 74HC164 shift register

symbol Data_addr = b5        'Location of the data character in un-named memory

symbol Range     = b6        '0=20W,1=60W,2=200W,3=600W,4=2000W, 5=overload

symbol Disp_loc  = b7        'Position in the display to place a data item

symbol DP_loc    = b8        'Location of the decimal point 0 = no DP, 1=X.XXX, etc

symbol Factor    = b9        'For scaling watts and current

symbol Cycle_cnt = b10        'Increments every half second to 128 to transmit logging every minute

symbol Iter      = b11        'General use

symbol Dummy     = b12        'General use

symbol String_addr = b13    'Location of string in EEPROM

symbol CkSum    = b14        'For Transmit

symbol MsgStartAddr = b15    'For Transmit

symbol LenChar    = b16        'For Transmit

symbol MsgEndAddr    = b17        'For Transmit

symbol Watt_sec  = W11        'Watts is added every second, each minute 3600 is repeatdly subtracted 

symbol Watt_hrs  = W12        'Increments for each watt-hour

symbol Disp_val  = W13        'Value to be displayed

Init  Main

init:

SETFREQ m16                'Lowest frequency to write one line in less than 500 ms

LOW Disp_en            

LOW Disp_clk

FOR Data_addr = 8 TO 16

    READ Data_addr,Char

    GOSUB Disp_cmd

NEXT

SETINT %00000001,%00000001    'Interrupt when pin C.0 goes high

FOR Iter = 28 TO 67        'The fixed values of the logging output are pre-loaded

    POKE Iter,","

NEXT

POKE 28,"H"                'If this order is reversed, you get blank lines in between logging rows

POKE 36,"W"

POKE 50,"C"                'If this order is reversed, you get blank lines in between logging rows

POKE 57,"V"


main:                        

PAUSE 1000                'This is always cut short by the interrupt at 500 ms

GOTO main

Interrupt

interrupt:                            'every 500 ms

LET Range = 2*Range100 + Range300

LET Range = 2*RangeX10 + Range

LET Range = 5 - Range

INC Cycle_cnt

IF Cycle_cnt >= 128 THEN

    LET Cycle_cnt = 0                    '1 minute

ENDIF

IF Range = 5 THEN                        'Overload takes preference

    LET String_addr = 17

    GOSUB Disp_String

ELSE

    LET Sec_Phase = Cycle_cnt & %00000001    'Splits flow into two 500 ms sets

    IF  Sec_Phase = 0 THEN                '300 ms

        GOSUB Disp_Volts

        GOSUB Disp_WH    

    ELSE

        GOSUB Disp_Watts                '150 ms

        IF Cycle_Cnt = 63 OR Cycle_Cnt = 123 THEN

            GOSUB Transmit        

        ELSE        

            GOSUB Disp_Current        '150 ms

        ENDIF

    ENDIF

ENDIF

DO

    PAUSE 1 

LOOP WHILE Two_Hz = 1 

SETINT %00000001,%00000001        'Interrupt when pin C.0 goes high

RETURN

Display Fields

Disp_Watts:

READADC10 Watt_Port,Disp_val

LOOKUP Range,(12,8,12,8,12),factor

LET Disp_val = factor * Disp_val

LOOKUP Range,(5,1,5,1,5),factor

LET Disp_val = Disp_val/Factor

LOOKUP Range,(1,1,2,2,5),DP_Loc

LET Disp_loc = $80

LET Scratch = Range + "0"

POKE 38,Scratch

LET Data_addr = 40

GOSUB Disp_4dec

LOOKUP Range,(100,100,10,10,1),factor

LET Disp_val = Disp_val/factor

LET Watt_sec = Watt_sec + Disp_val

DO WHILE Watt_sec >= 3600

    INC Watt_Hrs

    LET Watt_sec = Watt_sec - 3600

LOOP

IF Range < 4 THEN

    LET String_addr = 35

ELSE

    LET String_addr = 60

ENDIF

GOSUB Disp_string

RETURN


Disp_Current:

READADC10 Curr_Port,Disp_val

LOOKUP Range,(12,8,12,8,12),factor

LET Disp_val = factor * Disp_val

LOOKUP Range,(5,1,5,1,5),factor

LET Disp_val = Disp_val/Factor

LOOKUP Range,(2,2,0,0,1),DP_Loc

LET Disp_loc = $88

LET Data_addr = 52

GOSUB Disp_4dec

IF Range < 2 THEN

    LET String_addr = 40

ELSE

    LET String_addr = 55

ENDIF

GOSUB Disp_string

RETURN


Disp_Volts:                    '157 ms @ 16 Mhz

READADC10 Volt_Port,Disp_val

LET Disp_val = Disp_val + Volt_off

LET Disp_loc = $C0

LET DP_loc = 2

LET Data_addr = 59

GOSUB Disp_4dec

LET String_addr = 45

GOSUB Disp_String

RETURN


Disp_WH:                    'Recycles at 65535, which is 65 KWH, about $10

LET Disp_loc = $C8

LET Data_addr = 30

GOSUB Disp_5dec

LET String_addr = 50

GOSUB Disp_String

RETURN

Display Numbers

Disp_4dec:                'Watts, Current or Volts    

'Displays number in Disp_val at location Disp_loc. 

'Data starts at Data_addr, decimal point in DP_val

LET Char = Disp_loc    'Place cursor at desired location on the display

GOSUB Disp_Cmd

LET bptr = Data_addr

BINTOASCII Disp_val,Dummy,@bptrinc,@bptrinc,@bptrinc,@bptr    'MSD first, does not exceed 8184

LET bptr = Data_addr

FOR Iter = 0 TO 3

    LET Char = @bptrinc

    SELECT Iter

        CASE 0

            IF Char = "0" THEN

                LET BLZ = 1

            ELSE

                LET BLZ = 0

            ENDIF

        CASE 1 TO 2

            IF BLZ = 1 AND Char <> "0" THEN

                LET BLZ = 0

            ENDIF

        CASE 3

            LET BLZ = 0

    ENDSELECT

    IF BLZ = 1 AND Iter < DP_loc THEN

        LET Char = " "

    ENDIF

    GOSUB Disp_Char

    IF DP_loc = Iter THEN

        LET Char = "."

        GOSUB Disp_Char

    ENDIF

NEXT

RETURN


Disp_5dec:        'Watt - Hours only, no dP    

'Displays number in Disp_val at location Disp_loc. 

'Data starts at Data_addr

LET Char = Disp_loc    'Place cursor at desired locatio on the display

GOSUB Disp_Cmd

LET bptr = Data_addr

BINTOASCII Watt_hrs,@bptrinc,@bptrinc,@bptrinc,@bptrinc,@bptr    'MSD first

LET bptr = Data_addr

FOR Iter = 0 TO 4

    LET Char = @bptrinc

    SELECT Iter

        CASE 0

            IF Char = "0" THEN

                LET BLZ = 1

            ELSE

                LET BLZ = 0

            ENDIF

        CASE 1 TO 3

            IF BLZ = 1 AND Char <> "0" THEN

                LET BLZ = 0

            ENDIF

        CASE 4

            LET BLZ = 0

    ENDSELECT

    IF BLZ = 1 THEN

        LET Char = " "

    ENDIF

    GOSUB Disp_Char

NEXT

RETURN

Low-level Sub-programs

Disp_Cmd:

LET RS = 0

GOSUB Shift_164

RETURN



Disp_Char:

LET RS = 1

GOSUB Shift_164

RETURN


'Picaxe has no shiftout command! Slow, but the REV-ED algorithim is worse

Shift_164:                '11.4 ms @ 16 mhz

FOR Mask_Addr = 0 to 7

    READ Mask_Addr,Mask

    LET Scratch = Char & Mask

    LOW Disp_data

    IF Scratch = 0 THEN BitisLow

    HIGH Disp_data

BitisLow:

    PULSOUT Disp_clk,2    'measures  8 microseconds

NEXT

IF RS = 0 THEN

    LOW Disp_data

ELSE

    HIGH Disp_data

ENDIF

PULSOUT Disp_en,5            'measures  16 microseconds

RETURN


Disp_string:            'First entry is the display location, last is null

READ String_Addr,Char

GOSUB Disp_Cmd

DO

    INC String_addr

    READ String_addr,Char

    IF Char = 0 THEN

        EXIT

    ENDIF

    GOSUB Disp_Char

LOOP

RETURN

Logging

Transmit:

#rem Message 0 is sent on Cycle_Cnt = 123 and Message 1 on Cycle_Cnt = 63

Message 0: bptr=28 is "H", Watt-Hour Data = 30-34. 36 = "W", Range = 38, Watt Data = 40-43. 45&46 are checksum

Message Length = 16, Msg-Len character = "L"

Message 1: bptr=50 is "C", Current Data = 52-55. 57 = "V", Volts Data = 59-62. 64&65 are checksum

Message Length = 13, Msg-Len character = "K"

#endrem


rem Customize for differences in the 2 messages

IF Cycle_Cnt = 123 THEN

    LET MsgStartAddr = 28

    LET MsgEndAddr = 43

    LET LenChar = "L"

ELSE

    LET MsgStartAddr = 50

    LET MsgEndAddr = 62

    LET LenChar = "I"

ENDIF

 

rem Calculate the checksum    

LET CkSum = 0

FOR bptr = MsgStartAddr TO MsgEndAddr

    LET CkSum = CkSum + @bptr

NEXT

                                

rem convert the checksum into hex and store it

LET bptr = MsgEndAddr + 2

LET Scratch = CkSum / 16 + "0"

IF Scratch > "9" THEN

    LET Scratch = Scratch + 7

ENDIF

LET @bptrinc = Scratch

LET CkSum = CkSum & $0f + "0"

IF CkSum > "9" THEN

    LET CkSum = Cksum + 7

ENDIF

LET @bptr = Cksum


rem Now transmit the buffer - the receivers are incompatable with HSEROUT

SETFREQ m4

HIGH RS232_out

PAUSE 20

LOW RS232_out

PAUSE 10

SEROUT RS232_out,N2400_4,("14L1776w,",LenChar,",")

LET Scratch = MsgEndAddr + 3        'To add the checksum

FOR bptr = MsgStartAddr TO Scratch

    SEROUT RS232_out,N2400_4,(@bptr)

NEXT 

SEROUT RS232_out,N2400_4,(13,10)

SETFREQ m16

RETURN