Timothy_Hennessy
Member
- Joined
- Aug 10, 2017
- Messages
- 42
Here's the quick and dirty bms i've thrown together from a handful of arduino pro-mins. okay, at the moment it's just a cell monitor, but adding a master node that collects the data and controls the charge/discharge mosfets as well as the balance resistors is the easy part. I first posted on the facebook group, but since I can't upload code there, I've added it here. Yes, I know this is sort of reinventing the wheel, and the version by Stewart Piscataway and Colin Hickey does the same thing, only better. The difference is that mine can read up to 8 temperature sensors and doesn't use that expensive digital isolator chip. All that's needed here are two optos, one pnp transistor, and three resistors. you will also need one 10k resistor for each thermistor.
Code:
/* This code is a total kludge, thrown together on a rainy day
* Thanks to Adafruit, Kevin Darrah, and Tinkerit for allowing me to lift some useful bits
*/
#define MODULENUM 5
#define POWERPIN 13
#define TRIGGERPIN 12
#define NUMSENSORS 3
// resistance at 25 degrees C
#define THERMISTORNOMINAL 10000
// temp. for nominal resistance (almost always 25 C)
#define TEMPERATURENOMINAL 298.15
// how many samples to take and average, more takes longer
// but is more 'smooth'
#define NUMSAMPLES 5
// The beta coefficient of the thermistor (usually 3000-4000)
#define BCOEFFICIENT 3950
// the value of the 'other' resistor
#define SERIESRESISTOR 10000
float readVcc()
{
long result; // Read 1.1V reference against AVcc
ADMUX = _BV(REFS0) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);
delay(5); // Wait for Vref to settle
ADCSRA |= _BV(ADSC); // Convert
while (bit_is_set(ADCSRA,ADSC));
result = ADCL; result |= ADCH<<8;
result = 1100000L / result; // Back-calculate AVcc in
return result/1000.0;
}
float steinhart(float average)
{
float result;
average = 1023 / average - 1;
average = SERIESRESISTOR / average;
result = average / THERMISTORNOMINAL; // (R/Ro)
result = log(result); // ln(R/Ro)
result /= BCOEFFICIENT; // 1/B * ln(R/Ro)
result += 1.0 / (TEMPERATURENOMINAL); // + (1/To)
result = 1.0 / result; // Invert
return result;
}
void setup(void)
{
Serial.begin(9600);
pinMode(POWERPIN, OUTPUT);
pinMode(TRIGGERPIN, OUTPUT);
}
void loop(void)
{
uint8_t i,n;
uint16_t samples[NUMSAMPLES];
float averages[NUMSENSORS];
float highest, lowest;
for (n=0; n < NUMSENSORS; n++)
{
// take N samples in a row, with a slight delay
for (i=0; i< NUMSAMPLES; i++)
{
digitalWrite(POWERPIN, HIGH);
delay(2);
samples[i] = analogRead('A' + n);
digitalWrite(POWERPIN, LOW);
delay(2);
}
// average all the samples out
averages[n] = 0;
for (i=0; i< NUMSAMPLES; i++)
{
averages[n] += samples[i];
}
averages[n] /= NUMSAMPLES;
}
highest = 0.0;
lowest = 1023.0;
for(i=0; i< NUMSENSORS; i++)
{
if(averages[i] > highest) highest = averages[i];
if(averages[i] <= lowest) lowest = averages[i];
}
// highest and lowest are swapped after steinhart
Serial.print(MODULENUM);
Serial.print(' ');
Serial.print(readVcc(),3);
Serial.print("V ");
Serial.print(steinhart(lowest),1);
Serial.print("K ");
Serial.print(steinhart(highest),1);
Serial.print("K ");
Serial.println();
digitalWrite(TRIGGERPIN, HIGH);
delay(10);
digitalWrite(TRIGGERPIN, LOW);
delay(20);
ADCSRA &= ~(1 << 7); //shut down adc
SMCR |= (1 << 2); //power down mode
SMCR |= 1;//enable sleep
MCUCR |= (3 << 5); //set both BODS and BODSE at the same time
MCUCR = (MCUCR & ~(1 << 5)) | (1 << 6); //then set the BODS bit and clear the BODSE bit at the same time
__asm__ __volatile__("sleep");//in line assembler to go to sleep
}