ArduinoBMS/ArduinoBMS.h

211 lines
12 KiB
C
Raw Normal View History

2024-08-22 09:26:48 +00:00
/*
* License
* -------
* ArduinoBMS, a very basic Lithium Battery Management System.
* Copyright 2024 Frans Veldman
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
// ********* Switches ***********
#define LCD 1 // Set this to 0 if you don't have an LCD attached
// ********* Device settings **********
#define VER "0.1"
// ********* IO ports ***********
#define BUTTON 2 // Push button input port
#define LED 13 // Status LED output port
#define DISPBRIGHT 10 // Display brightness output PWM port
#define BUS_RELAY_ON 11 // Latching relay pulse output / mono relay power output port for the BUS circuit. Must be a PWM port for mono-stable relay power saving
#define BUS_RELAY_OFF 12 // Latching relay pulse output port for the BUS circuit. Set to 0 if a mono-stable relay is used
#define CHG_RELAY_ON 3 // Latching relay pulse output / mono relay power output port for the CHARGE circuit. Must be a PWM port for mono-stable relay power saving
#define CHG_RELAY_OFF 19 // Latching relay pulse output port for the CHARGE circuit. Set to 0 if a mono-stable relay is used
// These are port numbers, without the prefix 'A'. We use our own low level port routines for interaction with the analog ports, and they want to see just the number.
#define CELL1 0 // Analog input port
#define CELL2 1 // Analog input port
#define CELL3 2 // Analog input port
#define CELL4 3 // Analog input port
#define BUSV 6 // Analog input port, can be 6 on the Arduino pro-mini (leaving the I2C port available), or 4 or 5 on the Arduino Uno
// These are the pinouts for use with an LCD
#define LCD_RS 9 // IO pin for the RS line
#define LCD_EN 8 // IO pin for the EN line
#define LCD_4 4 // IO pin for the D4 line
#define LCD_5 5 // IO pin for the D4 line
#define LCD_6 6 // IO pin for the D4 line
#define LCD_7 7 // IO pin for the D4 line
// Alternatively, the same pins can be used for status LED's when you don't use an LCD.
#define FULL_LED 5 // IO pin for the FULL LED
#define EMPTY_LED 6 // IO pin for the EMPTY LED
// ********* Relay settings ************
// Set the following to 1 if you use a DUALBUS configuration
#define DUALBUS 0
// To select a mono-stable relay instead of a bi-stable relay, set its respective OFF pin to zero.
// In case of dualbus, you might think that you could use a mono-stable relay "just for charging",
// but note that ArduinoBMS will activate the charge relay as soon as it *can* accept a charge, *regardless whether there is a charge voltage present*.
// This means that the charge relay will be closed (and consume power!) during the entire *dis*charge curve.
// Using mono stable relais is not recommended, but if you have nothing else available, it will work.
// The RELAYHOLD setting defines for the bi-stable relays the time in milliseconds that the coil should be powered when transitioning. In case of mono-stable relays,
// it defines the time that the relay will receive full power before receiving reduced PWM power. Most mono-stable relays need only full power to close the contact,
// but once closed, a lower voltage is acceptable to keep the contacts in the closed position. This is achieved with PWM, which can be configured with the RELAYPWM setting.
// Try this with the lowest possible battery voltage, and after that increase the PWM setting somewhat to create a safe margin.
// Note that using the PWM feature (any value less than 255) will prevent the processor to enter sleep mode. But the power savings by using PWM will outweigh the increased processor consumption.
#define RELAYHOLD 300 // Time in milliseconds
#define RELAYPWM 255 // PWM power saving value for mono-stable relay, after RELAYHOLD has expired (0-255)
// **** Various settings **********
#define UARTBAUD 115200 // Baudrate of the serial interface
#define MAX_ADC_SKIPS 4
#define MAX_ADC_VARIATION 4 // Max number of ADC steps difference between two measurements
#define ADC_RES 13 // The ADC resolution in bits. Any value of 10 is achieved by oversampling. 13 is the maximum we can do before overflowing counters and accumulators.
#define AVG 4 // 1-4 Samples to use in the averaging filter (AVG can not be higher than 4, overflow will occur!)
#define RECONTIMEOUT 30000 // Wait time in ms to reconnect after a high or low voltage disconnect
#define MINCVOLTAGE 2.70 // Limit where we stop discharging. Most manufacturers state 2.50V is the absolute minimum. I like to remain on the safe side with this though
#define MAXCVOLTAGE 3.45 // Limit where we stop charging. Most manufacturers state 3.60V as the upper limit. Again, I like to remain on the safe side.
#define CELLOVERVOLT 3.60 // Above this voltage the cell gets damaged
#define CELLDEFECT 2.50 // Cells with a voltage below this value are defect
#define VDEADBAND 0.15 // The voltage difference we want to see with the bus before deciding to reconnect
#define RYVOLTLOSS 0.40 // The maximum measured voltage difference between the bus voltage and battery voltage when the relay is closed
#define RYINRUSHTIME 10000 // Time to wait after the relay closes to measure the voltage difference
// For DUALBUS installations only:
#define DUAL_HI_RECONV 3.30 // Dual bus reconnect cell voltage after a high voltage disconnect
#define DUAL_LO_RECONV 3.15 // Dual bus reconnect cell voltage after a low voltage disconnect
// ********* Eprom settings ***********
#define ROMSIGNATURE 0xA55AC33C // Signature to see whether the data has been initialized already
#define EEPROM_CAL_ADDR 0 // We store the calibration data at offset 0
// ********** Calibration settings ***********
// Cell voltage resistor dividers. This sets the *approximate* resistor divider ratio, so the automatic calibration routine
// has some clue what voltage range to expect.
#define R1 22 // See schematic
#define R2 10 // See schematic
#define RPRECISION 15 // Combined resistor precision percentage (plus some margin). This defines the calibration window.
// The calibration routine measures the cell voltages and busvoltages multiple times to obtain a good precision.
// A higher value of CALSAMPLES might give a better precision but takes more time.
// Note that these are complete readings, so if oversampling is enabled (ADC_RES>10) each sample will consist of multiple readings, up to 64 in case of ADC_RES=13.
// Each voltage will then be sampled 32 * 64 times. Seems good enough to me
#define CALSAMPLES 32 // Number of samples taken to calibrate the cell voltage sensors. Max 64.
#define MAXCALATTEMPTS 32 // In case the voltage fluctuates too much, the reading will be rejected and a new reading takes place. If it happens too often, something is wrong.
#define REFVOLTAGE 5.0 // Approximate voltage of the REF input of the processor
#define CALVOLTAGE 5.000 // Voltage used for resistor calibration
#define CALIBRATION 1.000 // Manual calibration factor. Start with 1.0 and adjust until the voltage is correct. This is in addition to the automatic resistor calibration.
#endif
// ******** User interface settings **********
// Button presses in milliseconds
#define SHORTPRESS 50 // Minimum time required to register as a short button press
#define LONGPRESS 1000 // Minimum time required to register as a long button press
// LCD configuration
// Note that regarding the LCD brightness, any other values than 0 or 255 will prevent the processor to enter sleep mode when the PWM signal is active.
// If the display is too bright, it is better to use a higher value resistor instead of dimming it with PWM.
// If you want the display to give a low brightness instead of shutting it off, it is better to use a bypass resistor instead of a low duty cycle PWM signal.
// Also note that if you use the PWM feature, the respective timer will not be shut down, increasing power consumption slightly.
#define LCD_HI_BRIGHT 255 // L2: Default active display brightness. 0 - 255
#define LCD_LO_BRIGHT 0 // L2: Default sleep display brightness. 0 - 255
#define LCDBACKLIGHTTMR 5 // L2: Automatic backlight shut off in seconds, 0 to disable
#define LCDREFRESH 600 // L2: interval in ms between LCD updates
// The LCD dimensions are used for some calculations, but if you use a larger display, no extra information will show up.
#define LCD_COLS 16
#define LCD_ROWS 2
// The flags below are used for flagging "nogo" events.
// This ArduinoBMS version uses a subset of the features available in my main BMS project.
// Some of the flags below are inherited from that project but are not used in this version.
// Non-recoverable (fatal) status codes:
#define X_SYSTEM 0x8000 // Charge and Discharge
#define X_UNDERVOLT 0x4000 // Charge and Discharge
#define X_OVERVOLT 0x2000 // Charge and Discharge
#define X_FATALTEMP 0x1000 // Charge and Discharge
// Recoverable status codes requiring a keypress:
#define X_HITEMP 0x0800 // Charge and Discharge
#define X_STOPBUTTON 0x0400 // Charge and Discharge
#define X_CONNERROR 0x0100 // Charge and Discharge
// Recoverable status codes that are automatically recovered or cleared after a keypress
#define X_XFEED 0x0200 // Discharge
#define X_TRICKLE 0x0008 // Charge
#define X_TARGET 0x0004 // Charge
#define X_LAPRIO 0x0002 // Charge
#define X_SOFTFUSE 0x0001 // Charge and Discharge
// Automatically recoverable status codes:
#define X_LOWTEMP 0x0080 // Charge
#define X_LOVOLTLIMIT 0x0040 // Discharge
#define X_HIVOLTLIMIT 0x0020 // Charge
#define X_MEASURE 0x0010 // Charge and Discharge
struct cal_t {
uint32_t romsignature; // Just a marker so we know the EPROM content is valid
uint16_t arefv; // Calibration setting for AREF
uint16_t cell1; // Calibration setting for Cell1
uint16_t cell2; // Calibration setting for Cell2
uint16_t cell3; // Calibration setting for Cell3
uint16_t cell4; // Calibration setting for Cell4
uint16_t busv; // Calibration setting for Bus Voltage
uint32_t ttrv; // Measured time to read all voltages at the requested resolution
};
struct adc_t {
const uint8_t admux;
const bool delay;
uint16_t raw;
uint8_t resolution;
uint16_t *cal;
float voltage;
adc_t* stacksum;
uint16_t totalreads;
uint16_t totalresol;
uint8_t samples;
uint16_t avg;
uint16_t sum;
};
struct button_t {
const uint8_t button;
bool longpressed;
uint32_t pressed;
};
struct relay_t {
const uint8_t pin_on;
const uint8_t pin_off;
bool state;
uint32_t timer;
const uint16_t holdtmr;
const uint8_t pwm;
};