The following code uses a mega to measure the voltage of 16 cells in a lithium battery, one by one, maintaining independent grounding (via solid state relays), and can instruct to discharge any cell that is above average. Data outputs via bluetooth to an android device.
I would prefer a discharge to last 3 seconds but Im not going to do the enormous amount of blinkwithoutdelay coding for that. There is a small (unknown) bug, every now and then ALL alarms trigger for no reason. I have attempted to patch this bug by checking how many alarms are triggered before triggering any.
The “impossible” BMS:
#include <Wire.h>
int del = 30;
int Pins[] = {
A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, 2
}; // an array of pin numbers to which relays are attached
int BP[] = {
23, 25, 27, 29, 31, 33, 35, 37, 39, 41, 43, 45, 47, 49, 51, 53
}; // an array of pin numbers to which Resistors are attached
int bat[] = {
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 //cells to measure
};
int batV[] = {
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 //stored cell voltage
};
int pinCount = 16; // the number of cells
int VB = 0;
int b48, ba;
int lead = 10;
String inputString = ""; // a string to hold incoming data
boolean stringComplete = false; // whether the string is complete
int top = 0;
int bot = 0;
int all = 0;
int alarms = 0;
void setup()
{
for (int thisPin = 0; thisPin < pinCount; thisPin++) {//set up each pin
pinMode(Pins[thisPin], OUTPUT);
pinMode(BP[thisPin], OUTPUT);
pinMode(BP[thisPin], HIGH);
}
pinMode(A0, INPUT);//set up A0 pin for reading voltage
Serial.begin(9600);
inputString.reserve(10);
}
void serialEvent() {
while (Serial.available()) {//listen for a command from the android
char inChar = (char)Serial.read();//store the character
inputString += inChar;//add the characters together
if (inChar == 'A') {//if A command, it is changing top calibration number
top = Serial.parseInt();
top = top - 50;
top = top / 5;
Serial.print("*>");
Serial.print(top);
Serial.print("*");
}
if (inChar == 'B') {//if B command, it is changing all calibration number
all = Serial.parseInt();
all = all - 50;
all = all / 5;
Serial.print("*|");
Serial.print(all);
Serial.print("*");
}
if (inChar == 'C') {//if C command, it is changing bottom calibration number
bot = Serial.parseInt();
bot = bot - 50;
bot = bot / 5;
Serial.print("*<");
Serial.print(bot);
Serial.print("*");
}
if (inChar == 'D') {//if D command, it is changing lead number
lead = Serial.parseInt();
lead = lead / 5;
Serial.print("*~");
Serial.print(lead);
Serial.print("*");
}
if (inChar == '\n') {//check for end of command
stringComplete = true;
}
}
}
void loop()
{
if (stringComplete) {
Serial.println(inputString);
inputString = "";
stringComplete = false;
}
for (int thisPin = 0; thisPin < pinCount; thisPin++) { //this loop will go through each cell one by one
digitalWrite(Pins[thisPin], HIGH); //turn on the relay so we can measure the cell
delay(del);
for (int i = 0; i < 20; i++) { //measure the voltage of each cell 20x
int V = analogRead(A0); //measure the voltage on the analog pin
VB = VB + V; //add the 20 measurements together
}
bat[thisPin] = VB / 20; //take average of the 20 measurements
VB = 0;
bat[thisPin] = constrain(bat[thisPin], 400, 900); //check for error measurements
batV[thisPin] = bat[thisPin];//store the voltage reading
batV[thisPin] = map(bat[thisPin], 434, 867, (200 + bot), (400 + top)); //convert the analog reading into mv
batV[thisPin] = (batV[thisPin] + all); //calibrate the voltage
b48 = b48 + batV[thisPin]; //add the cell voltages together to give pack voltage
digitalWrite(Pins[thisPin], LOW); //turn off the relay, we have finished measuring that cell
} //check for an abnormal number of alarms, first count how many alarms
if (batV[0] - lead > ba) { //is battery voltage above average? if yes, track the alarms
alarms++;
}
if (batV[1] - lead > ba) {
alarms++;
}
if (batV[2] - lead > ba) {
alarms++;
}
if (batV[3] - lead > ba) {
alarms++;
}
if (batV[4] - lead > ba) {
alarms++;
}
if (batV[5] - lead > ba) {
alarms++;
}
if (batV[6] - lead > ba) {
alarms++;
}
if (batV[7] - lead > ba) {
alarms++;
}
if (batV[8] - lead > ba) {
alarms++;
}
if (batV[9] - lead > ba) {
alarms++;
}
if (batV[10] - lead > ba) {
alarms++;
}
if (batV[11] - lead > ba) {
alarms++;
}
if (batV[12] - lead > ba) {
alarms++;
}
if (batV[13] - lead > ba) {
alarms++;
}
if (batV[14] - lead > ba) {
alarms++;
}
if (batV[15] - lead > ba) {
alarms++;
}