'------ Li-Ion Battery Management System Master by Peter Perkins Module ------- '------ Picbasic Pro Compiler version PIC16F886 - 111010 - V0.19 Alpha ------- '------ This code requires Master board V2 Please report errors or problems. '------------------------------------------------------------------------------ 'Note this code is for the Pic Basic Pro v2.60A+ internal PM compiler not MPASM! '**************************** General Information ****************************** 'The BMS modules carry no warranty or guarantee of any kind! They are used at 'your own risk, and I make no claims as to their suitability for a particular 'function. Prospective users must evaluate the system before using it, and no 'liability will be entertained by myself in any shape or form whatsoever. 'The modules and software have been produced at low cost for the benefit of 'the EV & electronic community. The software is available free via the internet. 'Users may modify or adapt the system as they see fit. If you are not fully 'competent to work on potentially lethal battery systems and high voltages, 'then do not experiment with or use this system. Be aware that vehicle 'modifications can lead to invalidated insurance and warranty issues. You the 'end user remain fully liable for any modifications made to your vehicle. '************************** Master PIC 16F886 Pinout ************************** ' Top ' _____ '(Pic Reset) MCLR -01| ^ |28- RB7 ICSP Data & (433mhz Txd Out) '(Current Sensor Adc In) RA0 -02| |27- RB6 ICSP Clk & (Chg Relay Out) '(Buttons Adc In) RA1 -03| |26- RB5 (Video Display Out) '(Analog Temp1 Adc In) RA2 -04| |25- RB4 (Controller Cutback Out) '(Analog Temp2 Adc In) RA3 -05| 1 |24- RB3 (WatchDog Led Out) '(Spare) RA4 -06| 6 |23- RB2 (Slave Data Bus Out) '(Spare) RA5 -07| F |22- RB1 (Charger Cutback Out) '(- Supply) Gnd -08| 8 |21- RB0 (Audible Alarm Out) '(Spare) RA6 -09| 8 |20- +Ve (+5V Supply) '(Spare) RA7 -10| 6 |19- Gnd (- Supply) '(Interlocks) RC0 -11| |18- RC7 (Master Data Bus In) '(VSS Speed & CCP2 PWM) RC1 -12| |17- RC6 (Dash Led 1 Out) '(I2C Temp & CCP1 PWM) RC2 -13| |16- RC5 (Dash Led 2 Out) '(I2C Eeprom Clock) RC3 -14| |15- RC4 (I2C Eeprom Data) ' ----- '************************ Other IC's On Master Pcb ***************************** '************************* Watchdog PIC 12F683 Pinouts ************************* ' Top ' _____ '(+ Supply) +Ve -1| ^ |8- -Ve (- Supply) '(Not Used) Output 5 -2| 6 |7- Output 0 (Not Used) '(Watchdog Led Out) Output 4 -3| 8 |6- Output 1 (Audible Alarm Out) '(Pulse Count In) Input 3 -4| 3 |5- Output 2 (Master Reset Out) ' ----- '**************** SV2000 Serial to Composite RCA Video IC ********************** ' Top ' _____ '(Sync Out) Syn -1| ^ |8- +Ve (+5V Supply) '(Video Out) Vid -2| V |7- Nc (No Connection) '(Serial Data In) Rxd 4 -3| I |6- Inv (Serial Data Invert Mode) '(- Supply) Gnd -4| D |5- Txd (No Connection) ' ----- '******************* AT24C512B 64K Byte I2C Serial EEprom IC ******************* ' Top ' _____ '(Address A0) A0 -1| ^ |8- +Ve (+5V Supply) '(Address A1) A1 -2| M |7- WP (Write Protect) '(Address A2) A2 -3| E |6- Scl (I2C Clock) '(- Supply) Gnd -4| M |5- Scd (I2C Data) ' ----- '************************ Master Module Specification ************************** 'Board Supply Voltage 8.00-30.00V DC or as limited by 5.00V 78L05 regulator 'CPU Supply Voltage 5.00V 'Master Pic 16F886 CPU Speed 8mhz with internal resonator 'Watchdog Pic 12F683 CPU Speed 4mhz with internal resonator (Timeout 65 Seconds) 'Average Board Supply Current at 12.00v <100ma 'Serial Master Bus data rate 9600 baud 'Maximum Cell Capacity 650ah but adjustment will be reqd for Soc routines. 'Maximum Pack Voltage 655.35v (65535) (Limit of 16bit Word Variable) 'Maximum Charge/Discharge rate 100A (Allegro Current Sensor limit) (This can be increased to 650A) 'Maximum 95 Slave Modules per Master Module. (95 Cells in series) 'Maximum OdoMeter Reading 65535 Miles 'Maximum TripMeter Reading 6553.5 Miles to 1/10th Mile 'DS18B20 I2C Digital Battery Pack Temp Sensor Range (-55 to +127C) 'LM335 Analog Pack Temp Sensor Range (0 to +100C) 'LM35 Analog Pack Temp Sensor Range (-20 to +100C) 'Composite Video Display data rate 9600 baud (SV2000 Serial to Video Chip) 'Composite RCA Video Monitor Output 1V 75ohm PAL/NTSC 'The Main display is limited to 16x9 characters in a 0-15 & 0-8 format. 'The Remote display is limited to 16x2 characters in a 2 line format. 'Charger relay output is board supply voltage max 30v at 500ma (Normally this will be about 12V) 'Opto Isolated Charger/Controller Cutback outputs max 50ma 50v '***************************** Slave Commands ********************************** ' Command 01 = Send Cell Voltage on Master Bus ' Command 02 = Turn Off Loads ' Command 03 = Turn On Loads as Reqd by Load CutIn Voltage ' Command 04 = Increase Load CutIn Voltage by 50mv ' Command 05 = Decrease Load CutIn Voltage by 50mv ' Command 06 = Set Slave Load CutIn Voltage Default ' Command 07 = Turn On Slave Load for 0.5 seconds (Flash Led) ' Command 08 = Report Slave Software Version Number ' Command 09 = Increase FailLow Cut Off Voltage by 50mv ' Command 10 = Decrease FailLow Cut Off Voltage by 50mv ' Command 11 = Increase FailHigh Cut in Voltage by 50mv ' Command 12 = Decrease FailHigh Cut in Voltage by 50mv ' Command 13 = Set FailHigh/Low Voltages to Default '******************************************************************************* '****************** Program Size 6373 Words out of 8192 Words ****************** '******************************************************************************* 'Variables Constants and I/O definitions 'Note Global variables must be preserved throughout the program 'Note Local variables may be used at will within subroutines 'Variables 16bit (Word) CellV var Word 'Local = Cell Voltage 0-1023 10bit (0-5v) PackV var Word 'Global = Calculated pack voltage (Voltage of all Cells added together) BatCurrent var Word 'Global = Current Sensor ADC value 0-1023 10bit (0-5v) Approx 200ma resolution Offset var Word 'Global = Current Sensor Zero Offset (0 Amp point) Should be around 512 I2CAddr var Word 'Global = I2CAddress byte (0-65535) address of data byte to be written WhMile var Word 'Global = Watt Hours per mile (Power Consumed) Odo var Word 'Global = Vehicle odometer miles travelled (0-65535) Trip var Word 'Global = Vehicle trip meter miles travelled (0-6553.5) Soc var Word 'Global = Calculated pack capacity (Soc State of Charge) Dist var Word 'Global = Distance (feet) travelled (5280ft = 1 mile) HighPackV var Word 'Global = High Pack voltage Seconds var Word 'Global = Time in seconds BMS active Max 65535 (18hrs) Charge var Word 'Global = [Charge] Discharge var Word 'Global = [Discharge] DistRem var Word 'Global = [DistRem] VarA var Word 'Local = General 0-65535 16bit Word Variable VarB var Word 'Local = General 0-65535 16bit Word Variable VarC var Word 'Local = General 0-65535 16bit Word Variable VarD var Word 'Local = General 0-65535 16bit Word Variable VarE var Word 'Local = General 0-65535 16bit Word Variable R_Temp var Word 'Local = RAW Temperature readings TempC var Word 'Local = Temp in deg C Float var Word 'Local = Holds remainder for + temp C display 'Variables 8bit (Byte) Alarms var Byte 'Global = Alarm Type Byte (0-255 number of different Alarms) VData var CellV.LowByte 'Local = Voltage data byte (8 bit value) (Rxd from Slave via serial link) Cells var byte 'Global = Number of cells in the battery pack (Max 95) ErrCell var Byte 'Global = [Error Cell] Cell with error HighCell var Byte 'Global = [HighCell] Cell with Highest Voltage LowCell var Byte 'Global = [LowCell] Cell with Lowest Voltage TSeconds var Byte 'Global = [TSeconds] Thirty second elapsed Timer Temp1Data var Byte 'Global = [Temp1] Temperature data Temp2Data var Byte 'Global = [Temp2] Temperature data Tenths var Byte 'Global = [Tenths of a mile] VoltsData var Byte 'Global = [PackV] SocData var Byte 'Global = [Soc] AmpSign var Byte 'Global = [AmpSign + or -] Dummy var Byte 'Local = [Dummy for Div32 temp conversion] 'Control var Byte 'Global = [BMS Control Byte 8 bits used for BMS Configuration] Key var Byte 'Local = [Returns Menu Key/Button pressed] BD var BYTE[95] 'Define BD as a byte array (95 Cells) (BD = Battery Data) I2C1 var BYTE[8] 'Define I2C1 as a byte array (8 Bytes) (I2C = I2C Temp sensor) VarA1 var VarA.LowByte 'Local = General 0-255 8bit Byte Variable VarA2 var VarA.HighByte 'Local = General 0-255 8bit Byte Variable VarB1 var VarB.LowByte 'Local = General 0-255 8bit Byte Variable VarB2 var VarB.HighByte 'Local = General 0-255 8bit Byte Variable VarC1 var VarC.LowByte 'Local = General 0-255 8bit Byte Variable VarC2 var VarC.HighByte 'Local = General 0-255 8bit Byte Variable VarD1 var VarD.LowByte 'Local = General 0-255 8bit Byte Variable VarD2 var VarD.HighByte 'Local = General 0-255 8bit Byte Variable 'Variables 1bit (Bit) Busy var BIT 'Local = I2C Temp conversion Busy Status-Bit Cold_Bit var R_Temp.Bit11 'Local = Sign-Bit for +/- Temp. 1 = Below 0 deg C ChgBit var BIT 'Global = [ChgBit] Charge in progress (0=Not Charging 1=Charging) FullBit var BIT[95] 'Global = FullBit is a bit array (Max 95 Cells) (0 = Cell Not Full) (1 = Cell Full) RetBit var BIT 'Local = Forces return from MenuButtons Routine if Set 'Control Byte (Bit Assingment) 'TempBit var Control.Bit0 'Global = Temperature Routine (1 = I2C Sensors 0 = Analog Sensors) 'Unallocated var Control.Bit1 'Global = 'TxdBit var Control.Bit2 'Global = Remote Display and EEprom Alarm Logging bit (1 = Disabled 0 = Enabled) 'PcBit var Control.Bit3 'Global = PCLogger Enable bit (1 = Disabled 0 = Enabled) 'Unallocated var Control.Bit4 'Global = 'Unallocated var Control.Bit5 'Global = 'Unallocated var Control.Bit6 'Global = 'Unallocated var Control.Bit7 'Global = 'Alarms Additional Information (255 possible Alarms) 0 = No Alarms 'If Alarms = 0 then (No Alarms Set) 'If Alarms = 1 then (Cell over AbsMax V) 'If Alarms = 2 then (Cell under AbsMin V) 'If Alarms = 3 then (Cell data serial transfer timeout error) 'If Alarms = 4 then (Battery Pack over AbsMax Temp) 'If Alarms = 5 then (External End Charge OR Battery Fault Condition) (Pack Voltage Drop Detector) 'If Alarms = 6 then (Internal End Charge) (Cells have all reached balancing voltage during this charge cyle) 'If Alarms = 7 then (Pack Voltage > Maximum permitted Pack Voltage) 'If Alarms = 8 then (Pack Voltage < Minimum permitted Pack Voltage) 'Constants MaxPackV con 18500 'Maximum pack voltage = 185v (18,500 as 16 bit value) Res 10mv (Max 650v) MinPackV con 0 'Minimum pack voltage = 0v (0 as 16 bit value) Res 10mv AbsMaxCellV con 375 'Abs Maximum permitted cell voltage = (3.75V) (Alarm & Shutdown point) AbsMinCellV con 230 'Abs Minimum permitted cell voltage = (2.30V) (Alarm & Shutdown point) MaxCellV con 370 'Normal Maximum permitted cell voltage = (3.70V) (Charger/Regen cutback point) MinCellV con 240 'Normal Minimum permitted cell voltage = (2.40V) (Controller/Assist cutback point) CutInVD con 365 'Balancing load/bypass default cut in Voltage (Must match value set in Slaves) MaxTemp con 40 'Maximum permitted pack temperature = (40C) (Alarm & Shutdown point) SocMax con 1000 'Max cell capacity = 100% (1000 as 16 bit value) SocMin con 100 'Min cell capacity = 10% (100 as 16 bit value) SocUnit con 72 'SocUnit constant for Soc calculations. (For 20ah cells = 72) DiscardLow con 175 'Cell correction value 175 or 1.75V added to CellV to recreate correct V TimeOut con 100 'Serial Data Receive Timeout value 100ms VoltageDrop con 50 'Voltage Drop = 50 Pack must drop (500mv) when charging before charger cuts off FailSafeV con 335 'Fail Safe Minimum Load voltage Cut off (<3.35V) Cell resting voltage AT24C512B con %10100000 'Control byte for AT24C512B eeprom %1010ddd0 (ddd) = device select bits 'Pins used for I/O and designations '*** Digital high/low Outputs on PORT B - Outputs 0-7 *** Alarm var PORTB.0 'Audible Alarm warning on Output 0 Charger var PORTB.1 'Charger Cutback output on Output 1 (Opto conducts when cell V > 3.75V) SlaveBus var PORTB.2 'Slave Data Bus Output on Output 2 WatchDogLed var PORTB.3 'Watchdog flashing Green Led on Output 3 (Flashes every other program loop) Controller var PORTB.4 'Controller Cutback output on Output 4 (Opto conducts when cell V < 2.40V) Video var PORTB.5 'Dashboard Video Display on Output 5 ChargerOnOff var PORTB.6 'Charger Relay On/Off control on Output 6 (5.00v Max 500ma) DriveInhibit var PORTB.7 'Drive inhibit Opto on Output 7 '*** Digital high/low Inputs & Outputs on PORT C *** Interlocks var PORTC.0 'Interlocks and additional safety/security switch inputs on Input 0 SpeedSensor var PORTC.1 'VSS Speed sensor pulse sensor on Input 1 (Measures length of a single pulse) DigitalTemp var PORTC.2 'I2C DS18B20 Battery Pack Temp Sensor on Input 2 (-55 to +127C) Sckeeprom var PORTC.3 'I2C EEprom Clock (Symbol not used in program added for completeness only) Scdeeprom var PORTC.4 'I2C EEprom Data (Symbol not used in program added for completeness only) Led2 var PORTC.5 'Dashboard Led 2 on Output portc 5 Led1 var PORTC.6 'Dashboard Led 1 on Output portc 6 MasterBus var PORTC.7 'Master Data Bus Input on Input 7 '*** Analogue ADC Inputs on PORT A *** CurrentSensor var PORTA.0 'Battery Current Sensor 0-5V ADC on Input 0 (+100 to -100A) 2.5v = 0 Amps ButtonsADC var PORTA.1 'Menu Buttons ADC Input 0-5V ADC on Input 1 (0-5v = 1 of 6 values) (0,1,2,3,4,5) AnalogTemp1 var PORTA.2 'Analog Temp1 Sensor LM335 ADC Input (-55 to +150C) on Input 2 AnalogTemp2 var PORTA.3 'Analog Temp2 Sensor LM335 ADC Input (-55 to +150C) on Input 3 'Unallocated4 var PORTA.4 'Spare Output PCLogger var PORTA.5 'Serial BMS Data Output to Pc Logger 'Unallocated6 var PORTA.6 'Spare Output 'Unallocated7 var PORTA.7 'Spare Output '************************************ Memory Allocations for 16F886 ****************************************** '************************************************************************************************************* '************************************** 368 bytes of Ram available ******************************************* '************************************** 256 Bytes Eeprom [0 to 255] ****************************************** '************************************************************************************************************* '(Peek & Poke used to access RAM) (Read & Write used for EEprom) '****************** Eeprom Data Storage 0-255 bytes (This does not affect program memory) ******************* '*** Eeprom addresses constants OdoStore0 con 252 'EEprom address Word data [Odo] (Dec 252) Stored every 10 seconds OdoStore1 con 253 'EEprom address Word data [Odo] (Dec 253) Stored every 10 seconds TripStore0 con 254 'EEprom address Word data [Trip] (Dec 254) Stored every 10 seconds TripStore1 con 255 'EEprom address Word data [Trip] (Dec 255) Stored every 10 seconds '************************************************************************************************************* 'Below used with PM Assembler '@ DEVICE PIC16F886,LVP_OFF 'Turn off low voltage programming '@ DEVICE PIC16F886,FCMEN_OFF '@ DEVICE PIC16F886,IESO_OFF '@ DEVICE PIC16F886,CPD_OFF '@ DEVICE PIC16F886,MCLR_ON 'Turn on Master Clear '@ DEVICE PIC16F886,WDT_OFF 'Turn off internal Watchdog timer '@ DEVICE PIC16F886,INTRC_OSC_NOCLKOUT 'Set internal oscillator with no clock out pins '@ DEVICE PIC16F886,PROTECT_OFF 'Turn off code protect 'Below used with MPASM Assembler @ __config _CONFIG1, _LVP_OFF & _FCMEN_OFF & _IESO_OFF & _CPD_OFF & _CP_OFF & _MCLRE_ON & _PWRTE_OFF & _WDT_OFF & _INTRC_OSC_NOCLKOUT Define OSC 8 'Set PicBasic Pro processor speed to 8 Mhz (Must match oscillator value) OSCCON = %01110101 'Internal 8 mhz Osc and stable CM1CON0 = 0 'Comparator Off CM2CON0 = 0 'Comparator Off T1CON = %00111001 'Enables and Starts Timer1 prescale 1:8 internal clock source 262ms at 8mhz TRISA = %11111111 'SET PORTA AS INPUTS TRISB = %00000000 'SET PORTB AS OUTPUTS TRISC = %10011111 'SET PORTC AS INPUTS EXCEPT PORTS C5,C6 ANSEL = %00001111 'SET INPUTS AN0-AN3 AS ANALOG INPUTS ANSELH = %00000000 'Disable PortB AD ADCON0 = %01000001 'SETUP ADC CONVERTER MODULE FOSC/8 & ENABLE ADC MODULE on AD0 ADCON1 = %10000000 'SETUP ADC RIGHT JUSTIFY SET REFV to VDD & VSS 'Debug is used as it is the fastest and smallest serial routine to drive the video display DEFINE DEBUG_REG PORTB 'Set Debug pin port DEFINE DEBUG_BIT 5 'Set Debug pin bit DEFINE DEBUG_BAUD 9600 'Set Debug baud rate DEFINE DEBUG_MODE 0 'Set Debug mode: 0 = true, 1 = inverted DEFINE PULSEIN_MAX 20000 'Set PULSIN maximum count to 20,000 (100ms) (Range 0-65535 x 5us units) include "modedefs.bas" 'Allows the use of the serin/serout command '************************************************************************************************************* Start: 'Initialise Program. Load Variables CLEAR 'Clear all variables to 0 PORTB = %00100000 'Set PortB outputs to off except PortB.5 PORTC.5 = 0 'Set PortC.5 output to off PORTC.6 = 0 'Set PortC.6 output to off I2CREAD Scdeeprom,Sckeeprom,AT24C512B,I2CAddr,[Cells,Odo.Byte0,Odo.Byte1,Trip.Byte0,Trip.Byte1,_ Dist.Byte0,Dist.Byte1,Soc.Byte0,Soc.Byte1] 'Read data from I2c eeprom starting at addr 0 'Line from above I2Caddr = 14 'Set to first available byte of I2C EEprom (First 14 bytes [0-13] reserved for BMS data) 'Manual I2C Temp Sensor initiate routine avoids start up temperature error owout DigitalTemp,1,[$55,$28,$B9,$11,$16,$01,$00,$00,$A4,$44] 'Match ROM & Convert Temperature owout DigitalTemp,1,[$55,$28,$CD,$11,$EA,$00,$00,$00,$28,$44] 'Match ROM & Convert Temperature 'Display Splash Screen, Software Version, CPU Speed & Number of Cells in System. serout PcLogger, N9600, [13,"MSG, BMS Data,",13] 'Send Data to Port Initialise Excel spreadsheet DEBUG 27,70,27,46,27,67,"BMS Master V0.19www.150mpg.co.uk",10,13,"8mhz (",#Cells,") Cells",10,13 gosub CheckCurrent 'Gosub CheckCurrent Routine to calculate sensor zero amps offset Offset = BatCurrent 'Set Current sensor Offset to BatCurrent (0 Amps) Around 512 DEBUG "Offset ",#Offset,10,10,13 'Video Display pause 4000 'Pause for 4 seconds VarA = 14 'Set I2C Address variable to 14 Start of Alarm Data AlarmCheck: 'Checks EEprom for stored Alarms from previous BMS cycle. I2CREAD Scdeeprom,Sckeeprom,AT24C512B,VarA,[VarB1,VarB2,VarC] 'Read data from I2c eeprom starting at VarA if VarB1 = 255 then 'Eof (End of file) Detected DEBUG 27,67 'Clear Screen goto MainLoop 'Goto Main Loop endif DEBUG "Alarm ",#VarB1," Cell ",#VarB2,10,13,"Sec ",#VarC," Adr ",#VarA,10,10,13 'Video Display VarA = VarA + 4 'Increment VarA gosub MenuButtons 'Gosub MenuButtons gets button. Push = 0 Up = 1 Right = 2 Down = 3 Left = 4 if Key = 0 then 'If Menu button pressed exit Alarms DEBUG 27,67 'Clear Screen goto MainLoop 'Goto Main Loop endif goto AlarmCheck '************************************************************************************************************* '************************************************************************************************************* MainLoop: 'Main program loop RetBit = 1 'set RetBit to 1 forces MenuButtons routine to return gosub Buttons 'Gosub Buttons gets button. Push = 0 Up = 1 Right = 2 Down = 3 Left = 4 if Key = 0 then gosub SubMenu1 'If Menu button pressed gosub SubMenu1 serout PcLogger, N9600, [13,"DATA,TIME,"] 'Txd BMS Data bytes to Spreadsheet toggle WatchDogLed 'Keeps watchdog happy, flashes every other time through loop gosub CheckVoltage 'Gosub CheckVoltage routine to collect/display Cell V data gosub CheckCurrent 'Gosub CheckCurrent routine to get Current Data from sensor gosub CalculateSoc 'Gosub CalculateSoc routine to calculate/display Current & Soc gosub CheckI2CTemp 'Gosub I2C Temp routine to collect/display battery temperature gosub CheckSpeed 'Gosub CheckSpeed routine to calculate/display speed/distance serout DriveInhibit, N2400, [85, 85, 85, 85, 85] 'Txd 5 byte preamble to clear Rxd pause 10 'Pause for 10ms to allow serial rxd to clear serout DriveInhibit, N2400, ["bms",Alarms,ErrCell] 'Txd data (2400 baud at 8mhz) if Alarms > 0 then gosub DisplayAlarms 'If an Alarm is set then Gosub DisplayAlarms if TSeconds > 29 then 'If Thirty seconds have elapsed then store data VarA = 0 I2CWrite Scdeeprom,Sckeeprom,AT24C512B,VarA,[Cells,Odo.Byte0,Odo.Byte1,Trip.Byte0,Trip.Byte1,_ Dist.Byte0,Dist.Byte1,Soc.Byte0,Soc.Byte1] 'Write data to I2c eeprom starting at addr 0 'Line from above TSeconds = 0 'Reset 30 second counter endif TSeconds = TSeconds + 1 'Increment Thirty Second Timer Seconds = Seconds + 1 'Increment One Second Timer goto mainloop 'Goto main program loop '************************************************************************************************************* CheckVoltage: 'Check Voltage subroutine, receives all cell V data VarB1 = 255 'Reset VarB1 to 255 (Used to accumulate lowest Cell V) VarB2 = 0 'Reset VarB2 to 0 (Used to accumulate highest Cell V) PackV = 0 'Reset PackV Total to zero = 0V serout SlaveBus,N9600, [2] 'Send Command 2 (Turn off all Loads) for VarA1 = 1 to Cells 'Start for/next loop to wait until previous command complete serin MasterBus, N9600, TimeOut, DataError, VData 'Check Commands If VData <> 2 then DataError 'If Command is not 2 then error! next VarA1 'repeat loop serout SlaveBus,N9600, [1] 'Send Command 1 (Report Cell Voltages) 'Get cell data into BD byte array for VarA1 = 1 to Cells 'Start for/next loop to get cell voltage data into array serin MasterBus, N9600, TimeOut, DataError, BD[VarA1] 'Receive Cell Voltage Data next VarA1 'repeat loop serout SlaveBus,N9600, [3] 'Send Command 3 (Turn on Loads if Reqd) for VarA1 = 1 to Cells 'Start for/next loop to wait until previous command complete serin MasterBus, N9600, TimeOut, DataError, VData 'Check Commands If VData <> 3 then DataError 'If Command is not 3 then error! next VarA1 'repeat loop 'Evaluate returned cell data 'Evaluates and calculates cell voltages for VarA1 = 1 to Cells 'Start for/next loop to receive cell CellV = 0 'Clear CellV Variable serout PCLogger, N9600, [#BD[VarA1],","] 'Send Cell Voltage Data to PC Logger if VarB2 < BD[VarA1] then 'If VData > VarB2 then (Current Cell > Highest Cell) VarB2 = BD[VarA1] 'VarB2 = VData (Store Current High Cell V in VarB2) HighCell = VarA1 'Store High V Cell number in HighCell Variable endif if VarB1 > BD[VarA1] then 'If VData < VarB1 then (Current Cell < Lowest Cell) VarB1 = BD[VarA1] 'VarB1 = VData (Store Current Low Cell V in VarB1) LowCell = VarA1 'Store Low V Cell number in LowCell Variable endif CellV = BD[VarA1] + DiscardLow 'CellV (w1) = CellV (w1) (1-255 0.01-2.55V) + (175 1.75V) if CellV > CutInVD then 'If Cell V > Load cut in point means this cell is charged FullBit [VarA1] = 1 'Set FullBit for this cell to 1 = Charged endif if CellV > AbsMaxCellV then 'If Cell V > AbsMaxCellV then set Alarms to 1 Alarms = 1 'Set Alarms to 1 (Indicates Cell over AbsMax Voltage condition) ErrCell = VarA1 'Store Error Cell number in ErrCell Variable endif if CellV < AbsMinCellV then 'If Cell V < AbsMinCellV then set Alarms to 2 Alarms = 2 'Set Alarms to 2 (Indicates Cell under AbsMin Voltage condition) ErrCell = VarA1 'Store Error Cell number in ErrCell Variable endif PackV = PackV + CellV 'Add Cell voltage to accumulated Pack voltage next VarA1 'Increment for/next loop and move to next cell 'End Charge and/or Battery Faults Detection. 'Detects battery out of range conditions. 'Detects end of charge by keeping record of highest pack voltage, when voltage falls more than (500mv Variable) 'means charge has terminated or battery/bms fault so switches off charger relay. 'Detects end of charge and cuts charger when all cells have reached balancing voltage. if PackV > MaxPackV then 'If PackV > Maximum pack voltage then set Alarm Alarms = 7 'set Alarms to 7 (Pack over Maximum Voltage) endif if PackV < MinPackV then 'If PackV < Minimum pack voltage then set Alarm Alarms = 8 'set Alarms to 8 (Pack under Minimum Voltage) endif if ChgBit = 1 then 'If Charger is operating (1) then Chg End FullBit Detection routine VarA2 = 1 'Charge End test Byte all Cells full = 1 for VarA1 = 1 to Cells 'Check FullBit array status (0 = Cell Not Full) (1 = Cell Full) if FullBit [VarA1] = 0 then 'Check if FullBit for Cell is set VarA2 = 0 'if it isnt set, clear Charge End Temporay Byte VarA2 endif next VarA1 'Check Next Cell if VarA2 = 1 then 'If VarA2 is 1 then all cells have reached balancing voltage during cycle Alarms = 6 'Set Alarms to 6 (Indicates End of Charge) endif endif if ChgBit = 1 then 'If Charger is operating (1) then Chg End & Batt Chg Fault Detection if PackV < HighPackV then 'If Pack Voltage is less than stored Pack Voltage then HighPackV = HighPackV - PackV 'Calculate Voltage Drop result stored in HighPackV if HighPackV > VoltageDrop then 'If Pack Voltage Drop > than permitted Voltage Drop then Alarms = 5 'Set Alarms to 5 (Indicates Pack Voltage dropping) endif else HighPackV = PackV 'Load HighPackV from PackV endif endif 'Display Pack Voltage VarA = PackV / 100 'Get first part of decimal value (Before decimal point) VarC = PackV // 100 / 10 'Get second part of decimal value (After decimal point) VoltsData = VarA1 'Store VarA1 in VoltsData Variable (Note 0-255V Max) DEBUG 27,83,0,0,"Vol ",#VarA,".",#VarC,"V " 'Video Display 'Display Highest Cell Voltage VarA = VarB2 + DiscardLow 'Highest Cell V Correction factor if VarA > MaxCellV then 'If VarA > MaxCellV then activate Charger Opto high Charger 'Activate Charger pull down opto high Led1 'Activate dash Over-V Led1 else low Charger 'De-Activate Charger pull down opto (Note may need to remove this) low Led1 'De-Activate dash Over-V Led1 endif VarC = VarA / 100 'Get first part of decimal value (Before decimal point) VarD = VarA // 100 'Get second part of decimal value (After decimal point) VarA1 = HighCell 'Load VarA1 with data from HighCell Variable DEBUG 27,83,0,1,"HiV [" 'Video Display if VarA1 < 10 then 'If Leading zero reqd in display due to Value <10 then DEBUG "0" 'Video Display endif if VarD <10 then 'Check if leading zero reqd in display DEBUG #VarA1,"] ",#VarC,".0",#VarD,"V " 'Video Display else DEBUG #VarA1,"] ",#VarC,".",#VarD,"V " 'Video Display endif 'Display Lowest Cell Voltage VarA = VarB1 + DiscardLow 'Lowest Cell V Correction factor if VarA < MinCellV then 'If VarA < MinCellV then activate Controller Opto high Controller 'Activate Controller pull down opto high Led2 'Activate dash Under-V Led2 else low Controller 'De-Activate Controller pull down opto low Led2 'De-Activate dash Under-V Led2 endif VarC = VarA / 100 'Get first part of decimal value (Before decimal point) VarD = VarA // 100 'Get second part of decimal value (After decimal point) VarA1 = LowCell 'Load VarA1 with data from LowCell Variable DEBUG 27,83,0,2,"LoV [" 'Video Display if VarA1 < 10 then 'If Leading zero reqd in display due to Value <10 then DEBUG "0" 'Video Display endif if VarD <10 then 'Check if leading zero reqd in display DEBUG #VarA1,"] ",#VarC,".0",#VarD,"V " 'Video Display else DEBUG #VarA1,"] ",#VarC,".",#VarD,"V " 'Video Display endif return 'Return to main program loop DataError: 'Serial Slave Data Rxd TimeOut, If no Data within (TimeOut = 100ms) ErrCell = VarA1 'Store Error Cell number in ErrCell Variable Alarms = 3 'Set Alarms to 3 (Indicates Cell Serial Data Timeout Error) return 'Return to main program loop '************************************************************************************************************** CheckI2CTemp: 'Check Battery Pack temperature routine (Multiple DS18B20 I2C Sensors) 'The owout lines become ($55,xx,xx,xx,xx,xx,xx,xx,xx,$44) where xx = decimal serial no's for the I2C chips DEBUG 27,83,0,3,"Tem " 'Setup Display for Temp Sensors 'Sensor 1 (Front Battery Block) owout DigitalTemp,1,[$55,$28,$B9,$11,$16,$01,$00,$00,$A4,$BE] 'Match ROM & Convert Temperature gosub calculatetemp 'Gosub Calculate Temperature routine DEBUG 27,83,0,3,"Tem ",VarB1,#VarD1,".",#VarD2," " 'Display Temp Sensor 1 owout DigitalTemp,1,[$55,$28,$B9,$11,$16,$01,$00,$00,$A4,$44] 'Match ROM & Convert Temperature Temp1Data = VarD1 'Store Front Pack Temp in Temp1Data (Note 0-100C Max) 'Sensor 2 (Rear Battery Block) owout DigitalTemp,1,[$55,$28,$CD,$11,$EA,$00,$00,$00,$28,$BE] 'Match ROM & Convert Temperature gosub calculatetemp 'Gosub Calculate Temperature routine DEBUG VarB1,#VarD1,".",#VarD2," " 'Display Temp Sensor 2 owout DigitalTemp,1,[$55,$28,$CD,$11,$EA,$00,$00,$00,$28,$44] 'Match ROM & Convert Temperature Temp2Data = VarD1 'Store Rear Pack Temp in Temp2Data (Note 0-100C Max) serout PCLogger, N9600, [#Temp1Data,",",#Temp2Data,","] 'Send Pack Temp Data to PC Logger return 'Return to main program loop CalculateTemp: owin DigitalTemp, 2, [VarA.Lowbyte, VarA.Highbyte] 'Read two bytes / end comms 'Convert 12bit DS18B20 value to a useable Temperature range -55 to +127C VarB1 = 43 'Display + (43) IF VarA > 64655 THEN '- 55 degrees = 64656 VarB1 = 45 'Display - (45) VarA = - VarA 'if - ie w1=100 display 10.0 -c ENDIF VarA = VarA * 5 / 8 '+ ie w1=850 display 85.0c VarD1 = VarA / 10 'Calculate Temp prior to decimal point VarD2 = VarA // 10 'Calculate Temp after decimal point if VarD1 > MaxTemp AND VarD1 < 127 then 'If Temp > MaxTemp set Alarms (Also checks if value is within sensor range) Alarms = 4 'Set Alarms to 4 (Indicates Temp over MaxTemp condition) endif return 'Return to CheckTemp '************************************************************************************************************** CheckCurrent: 'Accumulate (Current in Amps) charge/discharge data VarB = 0 'Reset Current oversampling accumulator to 0 (Zero) ADCON0 = %01000001 'SETUP ADC CONVERTER MODULE FOSC/8 & ENABLE ADC MODULE on AD0 for VarA2 = 1 to 10 '10 x Current Oversampling ADCON0.1 = 1 'Start ADC conversion while ADCON0.1 = 1 'Wait for ADC DONE wend BatCurrent.highbyte = ADRESH 'Move HIGH byte of result to Variable (0-1023 10bit)(-100A to +100A 0-5V) BatCurrent.lowbyte = ADRESL 'Move LOW byte of result to Variable (0-1023 10bit)(-100A to +100A 0-5V) VarB = VarB + BatCurrent 'Add Current to accumulated Current Next VarA2 'Repeat loop until 10 samples obtained BatCurrent = VarB / 10 'Divide VarB by 10 to get average current from last 10 samples return 'Return to main program loop '************************************************************************************************************** CalculateSoc: 'Calculate Soc routine 'The below comment lines can be swapped for the existing lines below them depending on orientation of current sensor ' if BatCurrent < Offset then 'If BatCurrent is < Offset means system is Charging ' BatCurrent = ((Offset - BatCurrent) * 2) / 10 'Subtract offset to get a positive number (0-510 = 0-100A+) Max 99 ' AmpSign = 43 'Set ascii Character 43 (+) to be displayed if charging ' goto ExitCurrent ' endif ' if BatCurrent > Offset then 'If BatCurrent is > Offset means system is Discharging ' BatCurrent = ((BatCurrent - Offset) * 2) / 10 'Subtract offset to get a positive number (0-512 = 0-100A-) Max 99 ' AmpSign = 45 'Set ascii Character 45 (-) to be displayed if discharging ' goto ExitCurrent ' endif if BatCurrent < Offset then 'If BatCurrent is < Offset means system is Discharging BatCurrent = ((Offset - BatCurrent) * 2) / 10 'Subtract offset to get a positive number (0-510 = 0-100A+) Max 99 AmpSign = 45 'Set ascii Character 45 (-) to be displayed if Discharging goto ExitCurrent endif if BatCurrent > Offset then 'If BatCurrent is > Offset means system is Charging BatCurrent = ((BatCurrent - Offset) * 2) / 10 'Subtract offset to get a positive number (0-512 = 0-100A-) Max 99 AmpSign = 43 'Set ascii Character 43 (+) to be displayed if charging goto ExitCurrent endif BatCurrent = 0 'If BatCurrent = (508 to 512) then set BatCurrent to 0 (Zero) AmpSign = 61 'Set ascii Character (=) to be displayed if no charge/discharge ExitCurrent: if BatCurrent > 99 then 'Limits BatCurrent to 99 Amps BatCurrent = 99 endif serout PCLogger, N9600, [#AmpSign,",",#BatCurrent,","] 'Send Amp Sign & Battery Current Data to PC Logger 'Display Battery Amps & Motor Power Kw VarA = (PackV / 100) * BatCurrent VarB = (VarA // 1000) / 100 VarA = VarA / 1000 DEBUG 27,83,0,4,"Amp ",AmpSign,#BatCurrent," K ",#VarA,".",#VarB," " 'Video Display '1ah = 3600 Amp Seconds '10ah = 36000 Amps Seconds (SocUnit 36) '20ah = 72000 Amp Seconds (SocUnit 72) '100ah = 360000 Amp Seconds (SocUnit 360) 'Percentage counter is represented by 1,000 = 100% Soc Each 0.1% for a 20ah battery is represented by 72 Amp Seconds 'Soc New Routine for 20ah Cells 72000 Amp Seconds (SocUnit = 72) Gives resolution to 0.1Ah if AmpSign = 43 then 'If AmpSign = "+" then Charge calculations Charge = Charge + BatCurrent 'Add latest Current data to running Charge total while Charge > SocUnit 'If Charge > SocUnit then execute code in loop Soc = Soc + 1 'Increment Soc % by one unit Maximum 1000 (100%) Charge = Charge - SocUnit 'Subtract (SocUnit) from Charge WEND 'Repeat loop until Charge < SocUnit endif if AmpSign = 45 then 'If AmpSign = "-" then Discharge calculations Discharge = Discharge + BatCurrent 'Add latest Current data to running Discharge total while Discharge > SocUnit 'If Discharge > SocUnit then execute code in loop Soc = Soc - 1 'Decrement Soc % by one unit Minimum 1 (0.01%) If Soc < 1 then 'Limit Soc to 1 (00.01%) Soc = 1 endif Discharge = Discharge - SocUnit 'Subtract (SocUnit) from Discharge WEND 'Repeat loop until Discharge < SocUnit endif If Soc > 1000 then 'Limit Soc to 1000 (100%) Soc = 1000 endif 'Display Battery State of Charge SOC in % VarC = Soc / 10 'Get first part of value (Before decimal point) serout PCLogger, N9600, [#VarC1,","] 'Send Soc Data to PC Logger SocData = VarC1 'Store Soc % in SocData Ram Byte (Note 0-100%) VarA = Soc // 10 'Get second part of value (After decimal point) DEBUG 27,83,0,5,"Soc ",#VarC,".",#VarA," % " 'Video Display ' if VarA < 10 then 'If Leading zero reqd in display due to Value <10 then ' DEBUG "0",#VarA," % " 'Video Display ' else ' DEBUG #VarA," % " 'Video Display ' endif return 'Return to main program loop '************************************************************************************************************** '************************************************************************************************************** CheckSpeed: 'Pulsin measures VSS pulse. If no pulse result will be 0 pulsin SpeedSensor,1,VarB 'Measure length of a VSS pulse in 5us (8mhz) units (Timeout 100ms) if VarB = 0 then JumpSpeed 'If VarB = 0 pulsin has timed out and vehicle is moving at < 1.5mph VarB = VarB / 11 'Divide VarB by 11 to enable results to fit into 16 bit Integer Maths VarB = (7908 / VarB) + 1 'Returns speed in mph accurate to 1 mph Approx! JumpSpeed: 'Jump here to avoid calculation errors serout PCLogger, N9600, [#VarB1,","] 'Send vehicle Speed Data to PC Logger 'Display Speed DEBUG 27,83,0,6,"Mph ",#VarB," wh/m ",#WhMile," " 'Video Display 'CheckDistance 'Check Distance routine needs to execute once per second. '528ft per 1/10th mile / Tyre circumference 5.8ft = 910 revs per mile '1mph = 1.4667ft per second. 1mile = 5280ft VarB = VarB * 146 'Work out feet travelled in last second (1mph = 1.466ft) Dist = Dist + VarB 'Add Distance travelled to any saved from previous loop while Dist >= 52800 'If distance travelled > 528ft 1/10th mile then execute code in loop Tenths = Tenths + 1 'Increment by 1 /10th Mile Trip = Trip + 1 'Increment TripMeter by 1/10th mile Max 999.9 miles if VarA1 = 10 then 'If VarA1 = 10 one mile has been travelled Odo = Odo + 1 'Increment OdoMeter by 1 mile 'Calculate Miles Remaining ' read PrevOdo0, VarA.Byte0 'Load last saved PrevOdo (Miles) reading from eeprom storage ' read PrevOdo1, VarA.Byte1 'Load last saved PrevOdo (Miles) reading from eeprom storage ' read PrevSoc0, VarC.Byte0 'Load last saved PrevSoc (Miles) reading from eeprom storage ' read PrevSoc1, VarC.Byte1 'Load last saved PrevSoc (Miles) reading from eeprom storage VarA = Odo - VarA 'Subtract Previous Odo from Current Odo (VarA = Dist in whole miles) VarB = VarC - Soc 'Subtract Current Soc from Previous Soc (VarB = Cap Used) VarC = VarB / VarA 'Divide Capacity used by distance travelled (VarC = Cap/Dist) VarD = Soc / VarC 'Divide Soc by Cap/Dist (VarD = Miles Remaining) DistRem = VarD 'Load DistRem from VarD 'Calculate wh/mile WhMile = ((VarC * 4) * 16) / 100 'Calculate wh/mile MAX 999 if WhMile > 999 then 'Limit WhMile to 999 WhMile = 999 endif Tenths = 0 'Reset (Tenths) counter endif Dist = Dist - 52800 'Subtract (528ft 1/10th mile) from Distance WEND 'Repeat loop until Distance < 52800 If Trip > 9999 then 'Limit tripmeter to 999.9 miles Trip = 0 endif If Odo > 9999 then 'Limit Odometer to 9999 miles Odo = 0 endif 'Display Odometer, Tripmeter and Charge Flag Status DEBUG 27,83,0,7,"Odo ",#Odo," Rem ",#DistRem," " 'Video Display VarA = Trip / 10 'Get first part of decimal value (Before decimal point) VarB = Trip // 10 'Get second part of decimal value (After decimal point) DEBUG 27,83,0,8,"Tri ",#VarA,".",#VarB," Chg ",#ChgBit," " 'Video Display return 'Return to main program loop '************************************************************************************************************** '************************************************************************************************************** DisplayAlarms: 'Alarms turns on led/audible alarms and turns off charger etc 'Action taken depends on Alarms (255 different Alarms max!) DEBUG 27,94 'Video Display Save Current Screen to SV2000 Flash pause 100 'pause for 100ms at 8mhz DEBUG 27,33,27,67," * BMS Alarms * ",10,13 'Display Negative, Clear Screen & Display Message high Alarm 'Activate audible alarm high Charger 'Activate Charger pull down opto high Controller 'Activate Controller pull down opto low ChargerOnOff 'Turn off Charger main relay ChgBit = 0 'Set ChgBit to 0 indicates Charging Off If Alarms = 1 then '(Cell over AbsMax V) DEBUG "Cell ",#ErrCell," >AMaxV" 'Video Display endif If Alarms = 2 then '(Cell under AbsMin V) DEBUG "Cell ",#ErrCell," Max Temp" 'Video Display endif If Alarms = 5 then '(Pack Voltage Dropped) DEBUG "Voltage Drop" 'Video Display endif If Alarms = 6 then '(All Cells Reached Balancing Voltage) DEBUG "Charge End OK" 'Video Display endif If Alarms = 7 then '(Pack over Maximum Voltage) DEBUG "Pack V > Max V" 'Video Display endif If Alarms = 8 then '(Pack under Minimum Voltage) DEBUG "Pack V < Min V" 'Video Display endif I2CWRITE Scdeeprom,Sckeeprom,AT24C512B,I2CAddr,[Alarms,ErrCell,Seconds,255] 'Write data to I2c eeprom I2CAddress I2CAddr = I2CAddr + 4 'Increment I2CAddress word variable pause 3000 'Pause for 3 seconds low Alarm 'Deactivate audible alarm DEBUG 27,95 'Video Display Restore Current Screen from SV2000 Flash Alarms = 0 'Reset Alarms to 0 (Clears any Alarm Flags) return 'Return to main program loop '************************************************************************************************************** '************************************************************************************************************** MenuButtons: 'Decodes button Inputs and returns 5 values (0,1,2,3,4) in Key (Wait 500ms) pause 500 'Pause for 500ms Buttons: 'Decodes button Inputs and returns 5 values (0,1,2,3,4) in Key (No wait) ADCON0 = %01000101 'SETUP ADC CONVERTER MODULE FOSC/8 & ENABLE ADC MODULE on AD1 pause 10 'Pause 10ms until ADC Module ready ADCON0.1 = 1 'Start ADC conversion while ADCON0.1 = 1 'Wait for ADC DONE wend VarD.highbyte = ADRESH 'Move HIGH byte of result to Variable (0-1023 10bit) VarD.lowbyte = ADRESL 'Move LOW byte of result to Variable (0-1023 10bit) Key = ((VarD + 6) / 170) - 1 'Convert 10 bit to result and return value in Key If RetBit = 1 then 'If RetBit is set then exit MenuButtons after one loop RetBit = 0 'Clears RetBit return endif if Key > 4 then MenuButtons 'If Invalid Key or No Key (5) then repeat return 'Return to main program '************************************************************************************************************** '************************************************************************************************************** StartChg: 'Start Charging Cycle Routine high Alarm 'Activate audible alarm If ChgBit = 0 then 'If Charge Flag is not set then set Charge Flag ChgBit = 1 'Set ChgBit = 1 low Charger 'De-activate Charger pull down opto low Controller 'De-activate Controller pull down opto high ChargerOnOff 'Turn on Charger main relay HighPackV = 0 'Load HighPack Voltage data with 0 for VarA1 = 1 to Cells 'Start for/next loop to clear Charged/Not Charged Bit Array (FullBit) FullBit [VarA1] = 0 'Clear Cell Full Bit Next VarA1 'Repeat Loop until all cleared else 'If Charge Flag is set then clear Charge Flag ChgBit = 0 'Set ChgBit = 0 low ChargerOnOff 'Turn off Charger main relay endif pause 250 'Pause for 250ms at 8mhz low Alarm 'Deactivate audible alarm goto ExitMenu 'Return to main program via restore screen routine '************************************************************************************************************** SubMenu1: 'Sub Menu 1 routine for extended BMS functions Debug 27,94 'Video Display Save Current Screen to SV2000 Flash pause 100 'Pause for 100ms at 8mhz Debug 27,67,"* Sub Menu 1 *",10,13,"Start Charge = UReset Soc = LReset Trip = DReset Odo = RSub Menu 2 = C" gosub MenuButtons 'Gosub MenuButtons gets button. Push = 0 Up = 1 Right = 2 Down = 3 Left = 4 if Key = 0 then SubMenu2 'If Key = 0 then goto Sub Menu 2 if Key = 1 then StartChg 'If Key = 1 then goto Start Charge Routine if Key = 2 then 'If Key = 2 then Reset Odometer to 0 miles Odo = 0 'Set Odo to Zero ' write OdoStore0, Odo.Byte0 'Write Odo reading to eeprom for storage when Master Off ' write OdoStore1, Odo.Byte1 'Write Odo reading to eeprom for storage when Master Off endif if Key = 3 then 'If Key = 3 then Reset Tripmeter to 0 miles Trip = 0 'Set Tripmeter to Zero ' write TripStore0, Trip.Byte0 'Write TripMeter reading to eeprom for storage when Master Off ' write TripStore1, Trip.Byte1 'Write TripMeter reading to eeprom for storage when Master Off endif if Key = 4 then 'If Key = 4 then Reset Soc to 100% Soc = 1000 'Set Soc to 1000 (100%) ' write SocStore0, Soc.Byte0 'Write Soc reading to eeprom for storage when Master Off ' write SocStore1, Soc.Byte1 'Write Soc reading to eeprom for storage when Master Off endif ExitMenu: Debug 27,67,27,95 'Video Display Restore Current Screen from SV2000 Flash return 'Return to main program '************************************************************************************************************** SubMenu2: 'Sub Menu 2 routine for extended BMS functions Debug 27,67,"* Sub Menu 2 *",10,13,"Cell Voltage = USlave Update = LNum Of Cells = DTemp Rom = RExit Menu = C" gosub MenuButtons 'Gosub MenuButtons gets button. Push = 0 Up = 1 Right = 2 Down = 3 Left = 4 if Key = 0 then ExitMenu 'If Key = 0 then goto ExitMenu if Key = 1 then Display 'If Key = 1 then goto Display Cell Voltages if Key = 2 then TempRom 'If Key = 2 then goto Discover I2C Temp Sensor Serial Number if Key = 3 then CellNumber 'If Key = 3 then goto Cell Number if Key = 4 then UpdateSlaves 'If Key = 4 then goto Update Slaves '************************************************************************************************************** CellNumber: 'Set number of cells in pack routine (Max 95) DEBUG 27,83,0,8,"Cells ",#Cells," " 'Video Display gosub MenuButtons 'Gosub MenuButtons gets button. Push = 0 Up = 1 Right = 2 Down = 3 Left = 4 if Key = 1 then 'If Button pressed is up then increment cells Cells = Cells + 1 'Increment Cells Variable if Cells > 95 then 'Prevent Cells > 95 being selected Cells = 1 'Set Cells to 1 endif endif If Key = 3 then 'If Button pressed is down then decrement cells Cells = Cells - 1 'Decrement Cells Variable if Cells < 1 then 'Prevent Cells < 1 being selected Cells = 95 'Set Cells to 95 endif endif If Key <> 0 then CellNumber 'If Centre button not pressed then goto loop1 VarA = 0 I2CWRITE Scdeeprom,Sckeeprom,AT24C512B,VarA,[Cells] 'Write data to I2c eeprom at addr 0 goto ExitUpdate 'Goto Exit Update '************************************************************************************************************** Display: 'Display Individual Cell Voltages and Cell Full Bit Data DEBUG 27,67,"Scroll Cells = D",10,13 'Video Display for VarA1 = 1 to Cells 'Start for/next loop to retrieve cell data CellV = BD[VarA1] + DiscardLow 'CellV (w1) = CellV (w1) (1-255 0.01-2.55V) + (175 1.75V) VarB = CellV / 100 'Get first part of decimal value (Before decimal point) VarC = CellV // 100 'Get second part of decimal value (After decimal point) if VarC < 10 then 'If Leading zero reqd in display due to Value <10 then DEBUG #VarA1,") ",#VarB,".0",#VarC,"V FB",#FullBit[VarA1],10,13 'Video Display else DEBUG #VarA1,") ",#VarB,".",#VarC,"V FB",#FullBit[VarA1],10,13 'Video Display endif gosub MenuButtons 'Gosub MenuButtons gets button. Push = 0 Up = 1 Right = 2 Down = 3 Left = 4 If Key <> 3 then ExitUpdate 'If Down Button is pressed then next cell next VarA1 'Display Next Cell goto ExitUpdate 'Goto ExitUpdate '************************************************************************************************************** '*********************************************** Slave Commands *********************************************** UpdateSlaves: 'Update Slaves in situ (Non Picaxe Serial command slaves only) VarC1 = 7 'Set variable VarC1 to Command 7 (Turn on Load for 0.5 seconds, flash Led) Loop5: 'Loop here to wait for button press DEBUG 27,83,0,8," Command ",#VarC1," ! " 'Video Display gosub MenuButtons 'Gosub MenuButtons gets button. Push = 0 Up = 1 Right = 2 Down = 3 Left = 4 if Key = 1 then 'If Button pressed is up then increment command varC1 = varC1 + 1 'Increment Variable VarC1 if varC1 > 13 then 'If varC1 > 13 then set varC1 to 1 varC1 = 1 'Prevents sending unsupported commands endif endif If Key = 3 then 'If Button pressed is down then decrement command varC1 = varC1 - 1 'Decrement Variable VarC1 if varC1 = 0 then 'If varC1 = 0 then set varC1 to 13 varC1 = 13 'Prevents sending unsupported commands endif endif If Key <> 0 then Loop5 'If Centre button not pressed then goto loop5 DEBUG 10,10,13,"Confirm (Y/N) ? Up=Yes Down=No" 'Video Display Loop6: 'Loop here to wait for button press gosub MenuButtons 'Gosub MenuButtons gets button data (0,1,2,3,4,5 = no button) in Key If Key = 1 then ConfirmUpdate 'If Button pressed is up then update slaves If Key = 3 then ExitUpdate 'If Button pressed is down then cancel update Goto Loop6 'Goto Loop6 ConfirmUpdate: DEBUG 27,67,"Command ",#VarC1," Sent" 'Video Display serout SlaveBus,N9600, [VarC1] 'Send Command (VarC1) SlaveCheck: 'Checks Slaves have received and acknowledged commands for VarA1 = 1 to Cells 'Start for/next loop to wait until previous command complete serin MasterBus, N9600, 1000, CommError, BD[VarA1] 'Check Commands next VarA1 'repeat loop for VarA1 = 1 to Cells 'Start for/next loop to receive data if VarC1 = 8 then 'If Command sent was 8 (report slave software version number) then DEBUG 10,13,"Slave ",#VarA1," V",#BD[VarA1]," " 'Video Display pause 250 'Pause for 1/4 second goto CheckLoop 'Goto CheckLoop endif if VarC1 <> BD[VarA1] then 'If bytes don't match then command error DEBUG 10,13,"Slave ",#VarA1," Error!" 'Video Display high Alarm 'Activate audible alarm pause 3000 'Pause for 3 seconds low Alarm 'Deactivate audible alarm goto ExitUpdate 'Goto ExitUpdate endif CheckLoop: 'Slave Commands Check Loop label next VarA1 'Increment for/next loop and move to next cell DEBUG 10,13,"Acknowledged OK" 'Video Display ExitUpdate: 'Jump here to exit updates DEBUG 10,13,"Any Key To Exit" 'Video Display gosub MenuButtons 'Gosub MenuButtons gets button data (0,1,2,3,4,5 = no button) in Key DEBUG 27,67,27,95 'Video Display Restore Current Screen from SV2000 Flash return 'Return to main program loop CommError: 'Command serial comms error DEBUG 10,13,"* Comms Error! *",10,13," Cell ",#VarA1," " 'Video Display high Alarm 'Activate audible alarm pause 3000 'Pause for 3 seconds low Alarm 'Deactivate audible alarm goto ExitUpdate 'Goto Exit Update '**************************************************************************** TempRom: 'Read I2C DS18B20 Temp Sensor Rom Code debug 27,67,"Ins Temp Sensor",10,13,"Any key to scan",10,13 'Video Display gosub MenuButtons 'Gosub MenuButtons gets button data (0,1,2,3,4,5 = no button) in Key OWOUT DigitalTemp, 1, [$33] 'Issue Read ROM command OWIN DigitalTemp, 0, [STR I2C1\8] 'Read 64-bit device data into the 8-byte array "I2C1" Pause 100 'Pause for 100ms For VarA1 = 0 to 7 'Display Sensor 1 data DEBUG HEX2 I2C1[VarA1] 'Video Display Next VarA1 goto ExitUpdate 'Goto Exit Update