For what it's worth the guts of my first BMS program for GWBasic and used a Velleman I/O K8000 board and a pic of my old display from 2004!!
- OldBmsDisplay.jpg (110.72 KiB) Viewed 38973 times
REM *******************************************************************
REM SolarVan Control Program Version 2.0 Copyright Peter Perkins 010604
progstart:
REM SET ARRAYS AND VARIABLES
DIM cell(30, 240), bms(30), delay(30), ampstore(30)
count = 0: col = 4: soldata = 0: limit = 160: potlimit = 0: z = 0: ah = 0
REM PWRLEFT IS SET TO 100AH INITIALLY SUGGESTED MAX IS 150AH
pwrleft = 150: kw = 0
REM CONFIG VELLEMAN BOARD
Selecti2cprinterport 1: ConfigIOchipAsOutput 0: ConfigIOchipAsInput 1
REM CONFIG SCREEN FOR 640x480 VGA MODE
SCREEN 12: CLS
REM READ MILEAGE DATA FROM FLOPPY
OPEN "MILEAGE.DAT" FOR INPUT AS #1
total = VAL(INPUT$(5, 1))
CLOSE
REM SET UP SCREEN WITH LI-ION METER SCALES
GOSUB drawlion
REM ***********************************************************************
REM PROGRAM LOOPS AND REDRAWS SCREEN ONCE A SECOND APPROX
mainscreen:
LOCATE 2, 1
PRINT " SV * Trip"; INT(miles); "miles * Total"; INT(total + miles); "miles *";
PRINT INT(counter / 60); "Mins * Test Cell"; count; "* Data"; soldata
PRINT : z = mph * 6.66: y = 2: c = 14: GOSUB hbargraph
PRINT " 00^^05^^10^^15^^20^^25^^30^^35^^40^^45^^50^^55^^60 Speed MPH "; mph; " "
PRINT : z = rpm / 15: y = 4: c = 2: GOSUB hbargraph
PRINT " 00^^05^^10^^15^^20^^25^^30^^35^^40^^45^^50^^55^^60 Motor RPM "; rpm; " "
PRINT : z = ad%(3) * 1.77: y = 6: c = 3: GOSUB hbargraph
PRINT " 00^^^^50^^^100^^^150^^^200^^^250^^^300^^^350^^^400 Motor Amps "; CINT(z); " "
PRINT : camp = ad%(4) * .2225: z = camp * 10: y = 8: c = 4: GOSUB hbargraph
PRINT " 00^^^^05^^^^10^^^^15^^^^20^^^^25^^^^30^^^^35^^^^40 Charge Amps "; CINT(camp); " "
PRINT : btemp = 60 - (ad%(2) / 4): z = btemp * 6.66: y = 10: c = 5: GOSUB hbargraph
PRINT " 00^^05^^10^^15^^20^^25^^30^^35^^40^^45^^50^^55^^60 Battery Temp C"; CINT(btemp); " "
PRINT : z = soc * 4: y = 12: c = 6: GOSUB hbargraph
PRINT " E^^^10^^^20^^^30^^^40^^^50^^^60^^^70^^^80^^^90^^^F Charge State %"; soc; " "
PRINT : z = kw * 20: y = 14: c = 9: GOSUB hbargraph
PRINT " 00^^02^^^04^^^06^^^08^^^10^^^12^^^14^^^16^^^18^^20 Motor Power KW"; LEFT$(STR$(kw), 5); " "
minidist = 0: rpmflag = 0: rpmcounter = 0: starttime$ = TIME$
REM ***********************************************************************
REM FAST PROGRAM LOOP BELOW MAX SPEED REQD TO LIMIT CURRENT AND COUNT RPM
waitforkey:
readiochip 1: ReadADchip (0)
REM NEXT TWO LINES ARE MOTOR RPM COUNTER
IF io%(12) = 1 AND rpmflag = 1 THEN rpmflag = 0: rpmcounter = rpmcounter + 1
IF io%(12) = 0 AND rpmflag = 0 THEN rpmflag = 1
REM MOTOR CURRENT CONTROLLER ROUTINE ECONOMY/NORMAL/PERFORMANCE MODES
motoramps = ad%(3) * 1.77
IF motoramps >= limit THEN GOSUB reduceamps
IF motoramps < limit THEN GOSUB increaseamps
REM LOOP BELOW HAPPENS ONCE A SECOND
IF starttime$ = TIME$ GOTO waitforkey
GOSUB measurecell
GOSUB speedo
REM IF BUTTON (A) PRESSED CHANGE POWER MODE 60-160-400A
IF io%(10) = 1 THEN GOSUB changemode
REM IF BUTTON (B) PRESSED THEN ENTER EXTENDED DATA ANALYSIS MODE
IF io%(11) = 1 THEN GOTO dataanalysis
REM IF RED BUTTON PRESSED SAVE DATA AND EXIT PROGRAM
REM NOTE ON EXIT DOS BATCH FILE WILL RENAME "CELLDATA" TO TODAYS DATE
IF io%(9) = 1 THEN
GOSUB writesoldata: END
END IF
REM CALCULATE MOTOR POWER IN KW
kw = (motoramps * packvoltage) / 1000
GOTO mainscreen
REM *******************************************************************
REM count increments from 0 to 30 as each cell is measured
REM When count = 30 battery pack values are calulated and displayed
measurecell:
IF count = 0 THEN
setiochannel (1): setiochannel (2): cleariochannel (2): cleariochannel (1)
setiochannel (2): cleariochannel (2): count = count + 1: RETURN
END IF
REM ***********************************************************************
REM 30 SECOND LOOP ALL CELLS HAVE BEEN TESTED WHEN COUNT=31
IF count = 31 THEN
packvoltage = 0: highest = 0: lowest = 5: count = 0: col = 4: average = 0
FOR a = 1 TO 30: packvoltage = packvoltage + cell(a, soldata)
IF highest <= cell(a, soldata) THEN highest = cell(a, soldata)
IF lowest >= cell(a, soldata) THEN lowest = cell(a, soldata)
average = average + cell(a, soldata)
NEXT a
average = average / 30: diff = highest - lowest
LOCATE 22, 68: PRINT "PV>"; LEFT$(STR$(packvoltage), 6); " "
LOCATE 23, 68: PRINT "HC> "; LEFT$(STR$(highest), 5); " "
LOCATE 24, 68: PRINT "LC> "; LEFT$(STR$(lowest), 5); " "
LOCATE 25, 68: PRINT "AV> "; LEFT$(STR$(average), 5); " "
LOCATE 26, 68: PRINT "DF> "; LEFT$(STR$(diff), 5); " "
REM ONBOARD/SOLAR CHARGER AND BATTERY HEATER/COOLER STATUS CONTROL
LOCATE 20, 1
REM SOLAR CHARGER CONTROL
IF packvoltage < 124 AND highest < 4.15 AND btemp < 50 THEN
setiochannel (6): PRINT " Solar Charge On! ";
END IF
IF packvoltage > 127.5 OR highest > 4.27 OR btemp > 50 THEN
cleariochannel (6): PRINT " Solar Charge Off ";
END IF
REM MAINS CHARGER CONTROL
IF packvoltage < 124 AND highest < 4.1 AND btemp < 50 AND io%(16) = 0 THEN
setiochannel (7): PRINT " * AC Charge On! ";
END IF
IF packvoltage > 127.5 OR highest > 4.26 OR btemp > 50 OR io%(16) = 1 THEN
cleariochannel (7): PRINT " * AC Charge OFF ";
END IF
REM BATTERY HEATER CONTROL
IF btemp < 20 THEN
setiochannel (8): PRINT " * Bat Heater On!"
END IF
IF btemp > 25 THEN
cleariochannel (8): PRINT " * Bat Heater Off"
END IF
REM FUEL GAUGE CALCULATE AH USAGE/SOC AND DISPLAY
FOR a = 1 TO 30
ah = ah + ampstore(a)
NEXT a
ah = CINT(ah / 30): IF ah = 0 THEN ah = .5
pwrleft = (pwrleft - (ah / 120))
REM calculate miles remaining
pwrused = 150 - pwrleft
pwrpermile = pwrused / (miles + .0001)
milesleft = INT(pwrleft / pwrpermile)
LOCATE 18, 1
PRINT " Est"; milesleft; "miles *"; INT(pwrleft); "ah *"; INT((pwrleft / ah) * 60);
PRINT "mins remaining at"; ah; "ah drain "
cell(0, soldata) = ah: soc = INT((100 / 150) * pwrleft)
REM BMS ROUTINE NOT IMPLEMENTED YET
REM GOSUB bmsroutine
soldata = soldata + 1: RETURN
END IF
REM CELLDATA FOR EACH 2 HOURS IS RECORDED TO DISK
IF soldata = 240 THEN GOSUB writesoldata
REM READ THE CELL VOLTAGE AND DISPLAY USING BARGRAPH THEN INC CELL COUNTER
ReadADchip (0): cell(count, soldata) = .017647 * ad%(1)
bar = cell(count, soldata)
setiochannel (2): cleariochannel (2): GOSUB bargraph
REM STORE MOTOR CURRENT IN AMPSTORE ARRAY THEN INC CELL COUNTER & RETURN
ampstore(count) = motoramps
count = count + 1: RETURN
REM****************************************************************
REM Array bms (30) balancer status for each cell 1=on 0=off
REM Array delay(30) balancer timer status for each cell 120=1 hour
bmsroutine:
FOR a = 1 TO 30
IF cell(a, soldata) > 4.24 AND delay(a) = 0 AND btemp < 50 THEN
bms(a) = 1: delay(a) = 120
END IF
IF cell(a, soldata) < 4.23 AND delay(a) <> 0 AND btemp < 50 THEN
bms(a) = 1: delay(a) = delay(a) - 1
END IF
IF delay(a) = 0 OR btemp > 50 OR cell(a, soldata) < 3.8 THEN
bms(a) = 0: delay(a) = 0
END IF
NEXT a
FOR clock = 30 TO 1 STEP -1
IF bms(clock) = 1 THEN setiochannel (3) ELSE cleariochannel (3)
setiochannel (4): cleariochannel (4): NEXT clock
setiochannel (5): cleariochannel (5): RETURN
REM***************************************************************
REM Display BarGraph with Colour of bar depending on voltage
REM Also displays cell voltage in figures in bar itself!
REM This routine needs calling every time a cell is measured!
REM Variable ("bar" needs loading with cell voltage when called)
REM Variable ("col" is cursor column counter initial value 4)
bargraph:
c = 5: REM Purple Zone <2.8 Volts
IF bar > 2.8 THEN c = 14: REM Yellow Zone 2.8 to 3.4 Volts
IF bar > 3.4 THEN c = 2: REM Green Zone >3.4 Volts
IF bar > 4.27 THEN c = 4: REM Cell overvoltage Red Zone >4.27 Volts
length = 460 - (bar * 30): barwidth = 16 * count: FOR bardraw = 7 TO 16
LINE (bardraw + barwidth, 460)-(bardraw + barwidth, 325), 0
LINE (bardraw + barwidth, 460)-(bardraw + barwidth, length), c
NEXT bardraw: z$ = STR$(bar): LOCATE 26, col: PRINT MID$(z$, 2, 1)
LOCATE 27, col: PRINT MID$(z$, 3, 1): LOCATE 28, col: PRINT MID$(z$, 4, 1)
col = col + 2: RETURN
REM ***********************************************************************
speedo:
rpm = rpmcounter * 120: dist = dist + rpmcounter
minidist = minidist + rpmcounter / 2391
miles = dist / 2391
mph = INT((60 / 4782) * rpm)
counter = counter + 1: starttime$ = TIME$: RETURN
REM ***********************************************************************
REM WRITE 2 HOURLY CELL DATA TO DISK
writesoldata:
LOCATE 19, 45: PRINT "Saving Cell Data"
OPEN "CELLDATA.DAT" FOR OUTPUT AS #1
FOR c1 = 1 TO 240: FOR c2 = 0 TO 30
WRITE #1, cell(c2, c1): NEXT c2: NEXT c1
CLOSE : soldata = 0
REM WRITE MILEAGE DATA TO DISK
writemileage:
OPEN "MILEAGE.DAT" FOR OUTPUT AS #1
total = total + minidist: PRINT #1, STR$(total)
CLOSE : minidist = 0
LOCATE 19, 45: PRINT " "
RETURN
REM ***********************************************************************
REM HORIZONTAL BARGRAPH ROUTINE FOR SPEED,RPM,AMPS ETC
hbargraph:
REM Var Z = Length needs setting in range 0-400
REM Offset = start of graph on screen Horizontal position
REM Graph is 400 pixels long calculate length on screen vs voltage
IF z < 0 OR z > 400 THEN z = 0
offset = 8: length = offset + z
barwidth = 16 * y
FOR bardraw = 4 TO 13
LINE (offset, bardraw + barwidth)-(410, bardraw + barwidth), 0
LINE (offset, bardraw + barwidth)-(length, bardraw + barwidth), c
NEXT bardraw: RETURN
REM ***********************************************************************
REM DRAW LION SCALES ON SCREEN
drawlion:
REM Draw scale on li-ion bargraphs.
LOCATE 21, 64: PRINT "<"
LOCATE 22, 64: PRINT "4"
LOCATE 23, 64: PRINT "<"
LOCATE 24, 64: PRINT "3"
LOCATE 25, 64: PRINT "<"
LOCATE 26, 64: PRINT "2"
LOCATE 27, 64: PRINT "<"
LOCATE 28, 64: PRINT "1"
LOCATE 21, 2: PRINT ">"
LOCATE 22, 2: PRINT "4"
LOCATE 23, 2: PRINT ">"
LOCATE 24, 2: PRINT "3"
LOCATE 25, 2: PRINT ">"
LOCATE 26, 2: PRINT "2"
LOCATE 27, 2: PRINT ">"
LOCATE 28, 2: PRINT "1"
LOCATE 28, 67: PRINT "Normal 160A"
RETURN
REM ***********************************************************************
REM MOTOR CURRENT CONTROLLER ROUTINE
reduceamps:
IF potlimit <= 0 THEN RETURN
clearDACchannel (2): setDACchannel (3): clearDACchannel (3)
potlimit = potlimit - 1: RETURN
increaseamps:
IF potlimit >= 50 THEN RETURN
setDACchannel (2): setDACchannel (3): clearDACchannel (3)
potlimit = potlimit + 1: RETURN
REM ***********************************************************************
REM CHANGES POWER MODE MOTOR CURRENT LIMIT 60-160-400A
changemode:
LOCATE 28, 67
IF limit = 60 THEN
limit = 160: PRINT "Normal 160A": RETURN
END IF
IF limit = 160 THEN
limit = 400: PRINT "Power! 400A": RETURN
END IF
IF limit = 400 THEN
limit = 60: PRINT "Economy 60A": RETURN
END IF
GOTO changemode
REM ***********************************************************************
REM EXTENDED DATA ANALYSIS MODE
dataanalysis:
packvana = 0: highcell = 0: lowcell = 5: avecell = 0: CLS
PRINT " Solar Van Extended Data Analysis Mode. Ver 1.00 010604 P.Perkins"
FOR a1 = 1 TO 30: FOR a2 = 1 TO 240
IF highcell <= cell(a1, a2) THEN highcell = cell(a1, a2)
IF lowcell >= cell(a1, a2) THEN lowcell = cell(a1, a2)
avecell = avecell + cell(a1, a2)
NEXT a2
avecell = avecell / soldata
PRINT "Cell"; a1,
PRINT "HV>"; LEFT$(STR$(highcell), 5); " ";
PRINT "LV>"; LEFT$(STR$(lowcell), 5); " ";
PRINT "AV>"; LEFT$(STR$(avecell), 5); " ";
PRINT "DF>"; LEFT$(STR$(highcell - lowcell), 5); " "
NEXT a1
waithere:
readiochip (1)
IF io%(10) <> 1 THEN GOTO waithere:
CLS
GOSUB drawlion
GOTO mainscreen