BVolts : Battery Voltage read from Pin A0. Read as 1/6th of real voltage via 6:1 resistive divider.BVArray(29) : Battery Volts 30 element array - 1 each 2 secs.BatCharge : Battery state of charge as a percentage where 12.75V or higher is 100%, down to 12.05V, being 35% - danger level.AverageBV : Average battery voltage over last 60 seconds.BVAArray(59) : 1 hours worth of minute average battery volts.DeltaBV : Battery voltage variation over 60 seconds. The difference between the first and the last used to calculate the "Time to 30% discharge point".Time2Discharge : Time to 30% discharge.SVolts : Solar Array voltage from Pin A1. Read as 1/10th of real voltage by 10:1 resistive divider.SVArray(29) : Solar Volts 30 element array - 1 each 2 secs.AverageSV : Average Solar Panel volts over last 60 seconds.SVAArray(59) : 1 hours worth of minute average solar volts.InputCurrent : Charging current into battery/load read from Pin A2 2.5V reading = 50mA across the shunt, which = 50A through shunt. This can be both battery charging current and load current.ICArray(29) : Input Current 30 element array.AverageIC : Average input (generator) current over last 60 seconds.ICAArray(59) : array with 1 hours worth of minute average input currents.LoadCurrent : Load current of caravan - can be supplied from the batteries and the charging systems (solar, AC Mains or generator). Pin A3.LCArray(29) : Load Current 60 element array.AverageLC : Average load current over last 60 seconds.LCAArray(59) : array to hold 1 hours worth of average load currents.BatCurrent : A calculated value; if positive, = a charging current to the battery; if negative, = a discharge current from the battery.AverageBC : Average charge/discharge current over last min calculated by AverageLC-AverageIC.InputCurrentAH : Input current as an amp hour calculated value - this is an accumulating value over 24 hours.PreviousICAH : yesterdays Input Current AHs.LoadCurrentAH : Load current as an amp hour calculated value - this is an accumulating value over 24 hours.PreviousLCAH : yesterdays Load Current AHs.BatCurrentAH : Bat charge (+value) or discharge (-value) as an amp hour calculated value - accumulating value over 24 hours.PreviousBCAH : yesterdays Bat Current AHs.LogArray$ : String to use for plugging into log arrayNewDataFlag : Two second flag - set every 2 seconds by the interrupt routine when new voltage and current values are collected.MinFlag : Minute flag - set once every min at 00 seconds by sec interrupt process and cleared by min processing code.HourFlag : Hour Flag - set on the hour to enable writing the 60 average readings in the log array to be written to fileEODFlag : End of Day Flag - set at 6AM to enable daily log file to be closed and new log file created by LogFileCreate subroutine
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' ' Battery Management Program for Caravan 12V System ' ' Copyright Doug Pankhurst May 2012 ' ' Written for MMBasic V4.3a, copyright Geoff Graham 2012 ' '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' ' File Version: 2.04 May 2013 ' ' For a detailed explanation of the program, it's ' construction and rationale, please read the ' CVBMREAD.TXT file. ' Main Entry to Program EntryPoint: SetTick 0,0 ' Disable timer interrupt ' Global Variables - initialised to default values BVolts = 12.8 ' Battery Voltage read from Pin A0. Read as ' 1/6th of real voltage via 6:1 resistive divider Dim BVArray(29) ' Battary Volts 30 element array - 1 each 2 secs BatCharge = 100 ' Battery state of charge as a percentage where ' 12.75V or higher is 100%, down to 12.05V being 35% - danger level AverageBV=12.8 ' Average battery voltage over last 60 seconds Dim BVAArray(59) ' 1 hours worth of minute average battery volts DeltaBV = 0.1 ' Battery voltage variation over 60 seconds. The ' difference between the first and the last used to ' calculate the "Time to 30% discharge point" Time2Discharge = 20 ' Time to 30% discharge SVolts = 20 ' Solar Array voltage from Pin A1. Read as 1/10th ' of real voltage by 10:1 resistive divider Dim SVArray(29) ' Solar Volts 30 element array - 1 each 2 secs AverageSV = 21 ' Average Solar Panel volts over last 60 seconds Dim SVAArray(59) ' 1 hours worth of minute average solar volts InputCurrent = 18 ' Charging current into battery/load read from Pin A2 ' 2.5V reading = 50mA across the shunt, which equals 50A through the shunt. ' This can be both battery charging current and load current. Dim ICArray(29) ' Input Current 30 element array AverageIC = 18 ' Average input (generator) current over last 60 seconds Dim ICAArray(59) ' array with 1 hours worth of minute average input currents LoadCurrent = 12 ' Load current of caravan - can be supplied from the ' batteries and the charging systems (solar, AC Mains or generator). Pin A3 Dim LCArray(29) ' Load Current 60 element array AverageLC = 12 ' Average load current over last 60 seconds Dim LCAArray(59) ' array to hold 1 hours worth of average load currents BatCurrent = 6 ' A calculated value; if positive, = a charging current to ' the battery; if negative, equals a discharge current from the battery. AverageBC = 6 ' Average charge/discharge current over last min ' calculated by AverageLC-AverageIC InputCurrentAH = 0 ' Input current as an amp hour calculated value - ' this is an accumulating value over 24 hours PreviousICAH = 0 ' yesterdays Input Current AHs LoadCurrentAH = 0 ' Load current as an amp hour calculated value - ' this is an accumulating value over 24 hours PreviousLCAH = 0 ' yesterdays Load Current AHs BatCurrentAH = 0 ' Bat charge (pos value) or discharge (neg value) ' as an amp hour calculated value - accumulating value over 24 hours PreviousBCAH = 5 ' yesterdays Bat Current AHs LogArray$ = "" ' String to use for plugging into log array NewDataFlag = 0 ' Two second flag - set every 2 seconds by the ' interrupt routine when new voltage and current values are collected. MinFlag = 0 ' Minute flag - set once every min at 00 seconds ' by sec interrupt process and cleared by min processing code. HourFlag = 0 ' Hour Flag - set on the hour to enable ' writing the 60 average readings in the log array to be writtent to file EODFlag = 0 ' End of Day Flag - set at 6AM to enable daily log file ' to be closed and new log file created by LogFileCreate subroutine ''''''''''''''''' ' Carry out initialisation routines here - enable interrupt last Entry: Mode 3,7 ' 8 colour mode white on black Colour White,Black Cls ' Display all static data Option USB Off ' only to VGA DisplayStatic BarGraph ' Create Log File on program entry and then at start of new day - 6AM CreateLogFile ' Set up all initial values to default for orderly entry. PreviousICAH = 50 PreviousLCAH = 45 PreviousBCAH = 5 OldABV = 13 LFAPointer = 0 ' Fill the arrays For Ax=0 To 29 BVArray(Ax) = 12.8 SVArray(Ax) = 19 ICArray(Ax) = 18 LCArray(Ax) = 12 Next Ax ' Wait until 00 seconds to start for clean entry. Do While Val(Mid$(Time$,7,2)) <> 58 Loop ' Enable timer tick interrupts to start data collection at 00 seconds SetTick 2000,GetData ' Every other second, get battery voltage and current ' data through the GetData interrupt routine. ' end of initialisation routines '''''''''''''''''''' ' Main program loop Main: Do While NewDataFlag = 0 ' wait until interrupt provides fresh data Loop '''''''''''''''''''' ' 2 second processing code ' Write out time and date to display Print @(1,1)Time$ + " " + Date$ ' Push most recent values into arrays for minute averaging For Ax = 29 To 1 Step -1 BVArray(Ax) = BVArray(Ax-1) SVArray(Ax) = SVArray(Ax-1) ICArray(Ax) = ICArray(Ax-1) LCArray(Ax) = LCArray(Ax-1) Next Ax BVArray(0) = BVolts SVArray(0) = SVolts ICArray(0) = InputCurrent LCArray(0) = LoadCurrent ' Charge/Discharge Current is InputCurrent-LoadCurrent - if positive ' it is a charging current into battery, if negative, it is a ' discharging current out of battery BatCurrent = InputCurrent-LoadCurrent ' Convert both input, output & battery instantaneous currents into Amp Hours InputCurrentAH = InputCurrentAH + (InputCurrent/1800) ' use 2 sec interrupt LoadCurrentAH = LoadCurrentAH + (LoadCurrent/1800) ' rate for calculation BatCurrentAH = InputCurrentAH-LoadCurrentAH ' Note: All AH accumulating totals are plugged into last day values ' then reset to zero at 6AM - now update terminal display with all values Font #2 Colour 2 Print @(372,25)Format$(BatCharge,"%3.0f")+"%" Font #1 Colour 7 Print @(123,45)Format$(BVolts,"%5.2fV") Print @(123,56)Format$(SVolts,"%5.2fV") Print @(123,67)Format$(InputCurrent,"%5.2fA") Print @(123,78)Format$(LoadCurrent,"%5.2fA") Print @(312,45)Format$(AverageBV,"%5.2fV") Print @(312,56)Format$(AverageSV,"%5.2fV") Font #2 Colour 7 Print @(420,184)Format$(AverageBV,"%4.1f") Colour 3 Print @(420,232)Format$(AverageSV,"%4.1f") Colour 7 Font #1 Print @(376,149)Format$(Time2Discharge,"%4.0f min") Print @(312,67)Format$(AverageIC,"%5.1fA") Print @(312,78)Format$(AverageLC,"%5.1fA") Colour 6 Font #2 Print @(420,285)Format$(AverageIC,"%4.1f") Colour 5 Print @(420,340)Format$(AverageLC,"%4.1f") Colour 7 Font #1 Print @(117,127)Format$(InputCurrentAH,"%5.1fahrs") Print @(117,138)Format$(LoadCurrentAH,"%5.1fahrs") If (Sgn(BatCurrentAH) = +1) Then Colour 2 Print @(117,149)Format$(BatCurrentAH,"%+5.1fahrs") Else Colour 4 Print @(117,149)Format$(BatCurrentAH,"%+5.1fahrs") EndIf Colour 7 Print @(312,127)Format$(PreviousICAH,"%5.1fahrs") Print @(312,138)Format$(PreviousLCAH,"%5.1fahrs") If (Sgn(PreviousBCAH) = +1) Then Colour 2 Print @(312,149)Format$(PreviousBCAH,"%+5.1fahrs") Else Colour 4 Print @(312,149)Format$(PreviousBCAH,"%+5.1fahrs") EndIf Colour 7 If (Sgn(AverageBC) = +1) Then Colour 2 Print @(312,89)Format$(AverageBC,"%+5.1fA") Font #2 Print @(418,410)Format$(AverageBC,"%+4.1f") Font #1 Else Colour 4 Print @(312,89)Format$(AverageBC,"%+5.1fA") Font #2 Print @(418,410)Format$(AverageBC,"%+4.1f") Font #1 EndIf If (Sgn(BatCurrent) = +1) Then Colour 2 Print @(123,89)Format$(BatCurrent,"%+5.2fA") Else Colour 4 Print @(123,89)Format$(BatCurrent,"%+5.2fA") EndIf Colour 7 NewDataFlag = 0 ' clear new data flag ready for next interrupt ' ' End of two second processing code, now check for minute, hour or end of ' day flags and process accordingly ''''''''''''''''''''''''''''''''''' ' Check for minute flag, calculate the average of the last 60 seconds of ' readings, plug them into the log file array at the log file array pointer If MinFlag = 1 Then ' calculate average for last minute OldABV = AverageBV OldASV = AverageSV AverageBV = 0 AverageSV = 0 AverageIC = 0 AverageLC = 0 For Ax = 0 To 29 'don't forget only sampled every 2 secs AverageBV = AverageBV + BVArray(Ax) AverageSV = AverageSV + SVArray(Ax) AverageIC = AverageIC + ICArray(Ax) AverageLC = AverageLC + LCArray(Ax) Next Ax AverageBV = AverageBV/30 AverageSV = AverageSV/30 AverageIC = AverageIC/30 AverageLC = AverageLC/30 AverageBC = AverageIC - AverageLC ' Calculate battery change as percentage and fill charge state graph ' now calculate charge and paint capacity graph ' 12.1V equates to 30% charge BatCharge = Int((AverageBV - 11.75) * 100) If BatCharge < 30 Then BatCharge = 30 If BatCharge > 100 Then BatCharge = 100 FillBarGraph(BatCharge) ' Calculate time to 30% discharge of battery DeltaBV = OldABV - AverageBV If Sgn(DeltaBV) = +1 Then DVRate = BVArray(0) ' set initial values for rate of Time2Discharge = 0 ' discharge and time to discharge Do While DVRate > 12.1 ' 12.1 volts equates to 30% discharge DVRate = DVRate - DeltaBV ' loop through calculating the amount ' of time to get the current voltage ' down to the 30% discharge point Time2Discharge = Time2Discharge + 1 ' inc minutes to discharge count Loop Else Print @(376,149)"Charging " ' if current voltage is greater than EndIf ' previous minute BVAArray(LFAPointer) = AverageBV ' update average arrays SVAArray(LFAPointer) = AverageSV ICAArray(LFAPointer) = AverageIC LCAArray(LFAPointer) = AverageLC LFAPointer = LFAPointer + 1 ' LFA pointer reset by eod routine ' Now paint out running graph ' - first work out the current minute hour combo in minutes rg_mins = Val(Mid$(Time$,1,2)) Mod 6)*60))+(Val(Mid$(Time$,4,2 rg_mins = rg_mins + 30 ' offset to start of graph rg_abv = 412 - Int((AverageBV-8) * 10) If rg_abv > 412 Then rg_abv = 412 rg_asv = 412 - Int((AverageSV-8) * 10) If rg_asv > 412 Then rg_asv = 412 If rg_asv < 163 Then rg_asv = 163 rg_aic = 412 - Int(AverageIC * 5) rg_alc = 412 - Int(AverageLC * 5) If (Sgn(AverageBC) = +1) Then rg_abc = 412 - Int(AverageBC * 5) Else rg_abc = 412 + Int(AverageBC * 5) EndIf For yrg = 163 To 412 Step 1 If rg_abc = yrg Then If (Sgn(AverageBC) = +1) Then Pixel(rg_mins,yrg) = 2 Else Pixel(rg_mins,yrg) = 4 EndIf Else Pixel(rg_mins,yrg) = 0 EndIf If rg_abv = yrg Then Pixel(rg_mins,yrg) = 7 EndIf If rg_asv = yrg Then Pixel(rg_mins,yrg) = 3 EndIf If rg_aic = yrg Then Pixel(rg_mins,yrg) = 6 EndIf If rg_alc = yrg Then Pixel(rg_mins,yrg) = 5 EndIf Line(rg_mins+1,yrg)-(rg_mins+3,yrg),0 Next yrg MinFlag = 0 ' reset the min flag EndIf ' End of minute code '''''''''''''''''''' ' Check for hour flag - if true then append log array out to file ' (we want to limit log file writes to 24 per day to keep ' SD card writes as low as possible). If HourFlag = 1 Then ' set each hour in interrupt routine LogArray$ = "" For x=0 To 59 Step 1 ' get the average values from each average array LogArray$ = LogArray$+Format$(BVAArray(x),"%+6.2fV")+Chr$(44) LogArray$ = LogArray$+Format$(SVAArray(x),"%+6.2fV")+Chr$(44) LogArray$ = LogArray$+Format$(ICAArray(x),"%+5.1fA")+Chr$(44) LogArray$ = LogArray$+Format$(LCAArray(x),"%+5.1fA") Print #1,LogArray$ LogArray$ = "" Next x LFAPointer = 0 ' reset array pointer HourFlag = 0 ' clear hour flag until next hour EndIf ' End of hour code '''''''''''''''''' ' Check for end of day - if true then close log file and create new file ' for new day starting at 6AM If EODFlag = 1 Then Close #1 ' close out last log file, save amp hour data PreviousICAH = InputCurrentAH InputCurrentAH = 0 PreviousLCAH = LoadCurrentAH LoadCurrentAH = 0 PreviousBCAH = BatCurrentAH BatCurrentAH = 0 CreateLogFile ' create new log file with todays date EODFlag = 0 ' clear end of day flag until next 6AM EndIf ' End of day code ''''''''''''''''' GoTo Main ' back to Main ready for next interrupt ' End of main program loop '''''''''''''''''''''''''' '''''''''''''''''''''''''' ' Interrupt Routine Handling GetData: ' Timer Interrupt handler ' Arduino analogue input pins SetPin a0,1 ' pin 35 - battery voltage - 0 to 2.5V = 0 to 15V SetPin a1,1 ' pin 36 - solar volts - 0 to 2.5V = 0 to 25V SetPin a2,1 ' pin 37 - input current shunt - 0 to 2.5V = 0 to 50A SetPin a3,1 ' pin 38 - load current shunt - 0 to 2.5V = 0 to 50A ' Get the battery voltage ic=0 BVolts = 0 GetPin35 = 0 For ic = 1 To 20 GetPin35 = Pin(a0) BVolts = BVolts + GetPin35 Next ic BVolts = BVolts/20 ' Average out Battery voltage BVolts = BVolts*6 ' 15 battery volts = 2.5 MM volts ' Get the Solar Array voltage ic=0 SVolts=0 GetPin36 = 0 For ic = 1 To 20 GetPin36 = Pin(a1) SVolts = SVolts + GetPin36 Next ic SVolts = SVolts/20 ' same as for BVolts SVolts = SVolts*10 ' 25 solar volts = 2.5 MM volts ' Get the input current as a voltage InputCurrent = 0 GetPin37 = 0 For ic = 1 To 20 GetPin37 = Pin(a2) - 0.5817 InputCurrent = InputCurrent + GetPin37 Next ic InputCurrent = InputCurrent/20 ' Average out input current InputCurrent = Abs(InputCurrent*16.667) ' Correct for opamp amplification ' current by 16.667 now gives a value 50mV = 1A ' Get the load current as a voltage LoadCurrent = 0 GetPin38 = 0 For ic = 1 To 20 GetPin38 = Pin(a3) - 0.5803 LoadCurrent = LoadCurrent + GetPin38 Next ic LoadCurrent = LoadCurrent/20 ' Average out load current LoadCurrent = Abs(LoadCurrent*16.667) ' Correct for opamp amplification ' current by 16.667 now gives a value 50mV = 1A ' Get the current time, extract the minutes component If Mid$(Time$,7,2) = "00" Then MinFlag = 1 EndIf ' Get the current time, test for hour AND minute tunover If Mid$(Time$,7,2) = "00" And Mid$(Time$,4,2) = "00" Then HourFlag = 1 EndIf ' Get the current time, test for 0600:00 If MinFlag And HourFlag And Mid$(Time$,1,2)="06" Then EODFlag = 1 EndIf NewDataFlag = 1 ' set to indicate fresh data IReturn ' Returns with the following values:- ' BVolts = 0 to 15 equalling battery volts ' SVolts = 0 to 25 equalling solar Array volts ' InputCurrent=0 to 50 equalling 0 to 50Amps ' LoadCurrent=0 to 50 equalling 0 to 50Amps 'End of Interrupt code ''''''''''''''''''''' ' Defined Subroutines ''''''''''''''''''''' ' Set up terminal display with static data (field names etc.) ' - uses the PRINT @ to print at defined positions Sub DisplayStatic Local xrg,yrg,volts,amps Cls Print @(150,1)"CARAVAN POWER MONITOR V2.04" Line(150,11)-(312,11) Print @(1,25)"Instantaneous Readings" Print @(198,25)"Minute Average Readings" Line(1,36)-(152,36) Line(198,36)-(346,36) Print @(376,1)"Battery" Print @(1,45)"Battery Volts =" Print @(198,45)"Battery Volts =" Print @(379,12)"Charge" Print @(1,56)"Solar Array Volts =" Print @(198,56)"Solar Array Volts =" Print @(1,67)"Input Current =" Print @(198,67)"Input Current =" Print @(1,78)"Load Current =" Print @(198,78)"Load Current =" Print @(1,89)"Bat Chg/Dis Curr =" Print @(198,89)"Bat Chg/Dis Curr =" Print @(1,108)"Daily Accumulating Readings" Print @(198,108)"Previous Days Total Readings" Line(1,120)-(170,120) Line(198,120)-(362,120) Print @(1,127)"Input Charge =" Print @(198,127)"Input Charge =" Print @(384,127)"Time to" Print @(1,138)"Load Discharge =" Print @(198,138)"Load Discharge =" Print @(400,138)"30%" Print @(1,149)"Battery Chg/DisChg=" Print @(198,149)"Battery Chg/DisChg=" Line(0,162)-(479,431),1,B Colour 7,0 Print @(424,164)"Battery" Print @(430,174)"Volts" Colour 3,0 Print @(430,210)"Solar" Print @(430,220)"Volts" Colour 6,0 Print @(430,260)"Input" Print @(433,270)"Amps" Colour 5,0 Print @(433,315)"Load" Print @(433,325)"Amps" Colour 2,0 Print @(427,375)"Battery" Print @(436,385)"Amps" Print @(430,395)"In/" Colour 4,0 Print @(449,395)"Out" Colour 7,0 volts=22 amps = 48 For yrg =167 To 387 Step 10 Print @(2,yrg)Format$(volts,"%2.0fV") Print @(400,yrg)Format$(amps,"%2.0fA") amps=amps-2 yrg=yrg+10 Print @(400,yrg)Format$(amps,"%2.0fA") volts=volts-1 amps=amps-2 Next yrg Line(29,163)-(29,413) Line(393,163)-(393,413) For yrg = 163 To 413 Step 10 'minor ticks Pixel(28,yrg)=7 Pixel(394,yrg)=7 Next yrg For yrg = 173 To 413 Step 20 'major ticks Pixel(27,yrg)=7 Pixel(395,yrg)=7 Next yrg Line(30,414)-(390,414) 'now for the bottom For xrg = 30 To 390 Step 5 Pixel(xrg,415)=7 Next xrg For xrg = 30 To 390 Step 15 Line(xrg,415)-(xrg,416),7 Next xrg For xrg = 30 To 390 Step 30 Line(xrg,415)-(xrg,417),7 Next xrg For xrg = 30 To 390 Step 60 Line(xrg,415)-(xrg,418),7 Next xrg Print @(100,419)"6 Hour Graphing period - starts 6AM" End Sub ''''''' ' Bar Graph - Draw Static ' top left x=432, y=0, bottom right x=479, y=162 ' This equates to 2 steps per percentage for the range of 35% to 100% Sub BarGraph Local xtl,ytl,xbr,ybr,percent,xpos,ypos,y xtl=432:ytl=0:xbr=xtl+47:ybr=ytl+162 ' First draw the box and values Line(xtl,ytl)-(xbr,ybr),1,B percent=100 Print @(xtl+3,ytl+2)"Battery" Print @(xtl+13,ytl+13)100 Print @(xtl+40,ytl+13)"%" percent=percent-5 For y=ytl+22 To 142 Step 10 Print @(xtl+19,y)percent Print @(xtl+40,y)"%" percent = percent-5 Next y End Sub ''''''' Sub CreateLogFile LogFileName$="B:"+Mid$(Date$,9,2)+Mid$(Date$,4,2)+Left$(Date$,2)+".CSV" Open LogFileName$ For Output As #1 Print #1, "Battery Voltage";Chr$(44); Print #1, "Solar Array Voltage";Chr$(44); Print #1, "Input Current";Chr$(44); Print #1, "Load Current" End Sub ''''''' ' Bar Graph - Fill ' top left x=432, y=0, bottom right x=479, y=162 ' To fill, start at x=434 thru 443 and y=150 back up to y=12 ' This gives 138 steps, equates to 1.97 steps per % for 30% to 100% ' Enter with actual battery charge as a percent Sub FillBarGraph(BatCharge) Local xtl,ytl,bat_chg,xpos,ypos,yper,xright,xleft,ybot,ytop bat_chg=BatCharge xtl=432:ytl=0:xbr=xtl+47:ybr=ytl+162 ' Now fill the bar graph If bat_chg < 31 Then bat_chg=30 Colour 4,7 Print @(xtl + 4,ytl + 151,2)"DANGER" Else Colour 2,7 Print @(xtl + 4,ytl + 151,2)" OK " EndIf Colour 7,0 If bat_chg > 100 Then bat_chg=100 ytop=12 yper=Int(((100 - bat_chg) * 1.97) + 12) ybot=150 xleft=437 xright=446 For ypos=ybot To ytop Step -1 If yper < ypos Then For xpos=xleft To xright Step 1 Pixel(xpos,ypos)=2 Next xpos Else For xpos=xleft To xright Step 1 Pixel(xpos,ypos)=0 Next xpos EndIf Next ypos End Sub ' End program. ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''