LiPo for the Currie USPD

Joined
Feb 27, 2008
Messages
79
I'm in the final testing stages of my LiPo system for the 24V Currie.
I'm using Hobby King 3s and 4s 5AH Turnigy packs configured 7s3p 15AH.
Fits in the Currie box.

BMS schematic and PCB attached. Photos to come.



Uses an Arduino Nano microcontroller.
Here are initial limits I've set:
High cell V charge cutoff: 4.2V
Low cell V charge disable: 3.0V
Low cell V discharge cutoff: 3.2V
Low pack V discharge cutoff: 24V (3.4V per cell)
Low pack V discharge warning light: 24.5V
Will normally charge to 29V (4.15V per cell)

Schematic doesn't show 0.25A PTC polyswitches on the balance lines and 9A PTC on each string output.
Parasitic drain is 100uA. PWM Shunt balancing during charging to 35mV. Shunt current ~400mA max.
The PCB has jacks for the balance connectors and main lines.
Anderson connectors to the charger and motor.

Questions or suggestions welcome.

Any custom builders interested in this?
 

Attachments

  • bms.gif
    bms.gif
    39.4 KB · Views: 1,887
  • bms_pcb.gif
    bms_pcb.gif
    81.4 KB · Views: 2,020
Attached is a photo of the BMS board.
Missing are motor & charger cables, polyswitch fuses for the battery main lines .
Added are LEDs to visually monitor balance channels.

PICT1892_s.jpg
 
Sweet ! 35 mV resolution is kind of rough... but other than that not bad at all!

I would be worried about the inputs to the micro not being buffered at all though.

How much testing have you performed?


PTCs on the balance lines is a solid idea.
 
The resistive divider for the charger voltage goes to a digital input and only signals the presence of the charger for a software branch.
 
Ahh gotcha! Don't you hate when random Bozos on the internet try to pick apart your design?

Any chance you'll release the boards assembled or as a kit? The cell balancing implementation is enticing. It would be cool to scale it up with discrete SPI A/Ds or something so you can get up to like 20s by stacking :)

Thanks for letting me play along at home :mrgreen:
 
grindz145 said:
Ahh gotcha! Don't you hate when random Bozos on the internet try to pick apart your design?

No worse than the non-random Bozos at my job!

I would be interested in releasing assembled boards.
That way I know it works right (there's software calibration for cell voltages).

I really tailored this design to the USPD specifically, after not seeing anything out there that fit well.
 
It's great to see someone just go out there and do it. I think the arduino BMS design has been tossed around 10 times without anyone actually producing anything, so kudos for making the jump!
 
Here is the Arduino code (sorry, the indentation was lost):

Revised 4-8-2014

const int LowCellV = 3200, HiCellV = 4210, LowBatV = 24000; // millivolts
const byte ChargePin = 2, LoadPin = 8, LEDpin = 7;

byte i = 0, j = 0, BalPwm [7];
byte BalOut [7] = {12, 11, 10, 9, 6, 5, 3}; // balance drive pins

int BatV = 0, BotCell = 4300;
long Cal [7] = {4828, 4840, 4828, 4834, 4817, 4834, 4831}; // cell voltage calibration

unsigned int CellV [7];
unsigned long PwmMicros = 0, ReadMillis = 0, Timeout = 0, LedMinis = 0;

boolean Charge = false, Vlimit = false, LED = false;
boolean Warn = false, Balancing = false, Charging = false, Discharge = false;

void setup() {
for (i=0; i < 7; i++) pinMode (BalOut, OUTPUT);
pinMode (2, OUTPUT);
pinMode (4, INPUT);
pinMode (7, OUTPUT);
pinMode (8, OUTPUT);

Serial.begin(9600);
}

void loop() {
if ((millis() - ReadMillis) > 500) { // check cell voltages twice a second
ReadMillis = millis(); // reset 2X per sec timer
if (digitalRead(4) == HIGH) Charge = true; // check for charger voltage
if (digitalRead(4) == LOW) Charge = false;

if (Vlimit == true) {
if (Charge == false) return;
if (LED == false) { // blink LED
LED = true;
digitalWrite(LEDpin, HIGH);
}
else {
LED = false;
digitalWrite(LEDpin, LOW);
}
return;
}

if (Balancing == true) {
for (i=1; i < 7; i++) digitalWrite (BalOut, LOW); // stop balancing
delay(50); // settle down
}

for (i=0; i < 7; i++) CellV = 0; // clear old values
for (j=0; j < 5; j++) { // read cell V 5 times
for (i=0; i < 7; i++) CellV = CellV + analogRead(i) * Cal / 1000;
}

for (i=0; i < 7; i++) CellV = CellV / 5; // average of 5

if (Balancing == true) {
for (i=1; i < 7; i++) analogWrite(BalOut, BalPwm); // resume balancing
}

BatV = CellV[6] * 7; // overall bat voltage

for (i=6; i > 0; i--) CellV = (CellV * (i+1)) - (CellV[i-1] * i); // individual cells

if (Charge == false) { // discharge protection

if (Discharge == false) Discharge = true;
for (i=0; i < 7; i++) { // low cell cutoff
if (CellV < LowCellV) {
Vlimit = true;
digitalWrite(LoadPin, LOW);
return;
}
}

if (BatV < LowBatV) { // LV cutoff
Vlimit = true;
digitalWrite(LoadPin, LOW);
return;
}

if (BatV < (LowBatV + 500)) Warn = true; // LV warning
if (Warn == true) {
if (LED == false) { // blink LED
LED = true;
digitalWrite(LEDpin, HIGH);
}
else {
LED = false;
digitalWrite(LEDpin, LOW);
}
}

digitalWrite(LoadPin, HIGH); // turn on load
return;
} // end of discharge protection

BotCell = 4300;
digitalWrite(LoadPin, LOW); // turn off output for short charge
for (i=0; i < 7; i++) { // check cell voltages
Serial.print(CellV);
Serial.print(" ");

if ((CellV < (LowCellV - 200)) || (CellV > HiCellV) || (((millis() - Timeout) > 12000000UL) && (Balancing == true)) || ((CellV > 4100) && (Discharge == true))) {
// cell V limits, short charge or timeout
Vlimit = true;
for (j=1; j < 7; j++) digitalWrite (BalOut[j], LOW); // stop balancing
BalPwm[0] = 0;
digitalWrite(ChargePin, LOW); // stop charging
digitalWrite(LEDpin, LOW);
LED = false;
return;
}

if (CellV < BotCell) BotCell = CellV; // find lowest cell
if ((CellV > 4100) && (Balancing == false)) { // enable balancing
Balancing = true;
Timeout = millis();
digitalWrite(LEDpin, LOW);
LED = false;
}
}

Serial.println("");
Serial.println(BatV);

if (Charging == false) {
digitalWrite(ChargePin, HIGH); // turn on charge
digitalWrite(LEDpin, HIGH);
LED = true;
Charging = true;
}

if (Balancing == false) return;

for (i=0; i < 7; i++) { // adjust balance currents
if ((BalPwm < 250) && ((CellV - BotCell) > 20)) BalPwm = BalPwm + 10; // turn up high cell balance
if ((BalPwm > 9) && ((CellV - BotCell) < 20)) BalPwm = BalPwm - 10; // turn down low cell balance

if (i > 0) analogWrite(BalOut, BalPwm);
Serial.print(BalPwm);
Serial.print(" ");
}
Serial.println("");
}

if ((BalPwm[0] != 0) && (Vlimit == false)) { // last PWM channel
if ((micros() - PwmMicros) > 4000) { // 4ms cycle time
PwmMicros = micros(); // reset cycle
digitalWrite(12, HIGH);
delayMicroseconds(BalPwm[0] * 15);
digitalWrite(12, LOW);
digitalWrite(LEDpin, HIGH); // dim ON light
delayMicroseconds(800);
digitalWrite(LEDpin, LOW);
}
}

if (((Balancing == true) || (Charge == false)) && (Warn == false) && (BalPwm[0] == 0) && (Vlimit == false)) { // dim ON light
if ((millis() - LedMinis) > 5) { // 5ms cycle time
LedMinis = millis(); // reset cycle
digitalWrite(LEDpin, HIGH);
delay(1);
digitalWrite(LEDpin, LOW);
}
}
}
 
Just an update:

I've assembled the pack and put it on the bike.
It weighs 8.5lb vs 10lb with NiMH, not bad considering it's 15AH vs 11AH.
Maximum voltage sag with the pack ~80% full is ~1V vs 4V with the NiMH.
Next step is to see the real world range: how many days I can ride to work on a charge (3 with NiMH).
 
Well, I've ridden the equivalent of to work 4 times and the pack was still 1/2 full.
This would have more than emptied the NiMH pack (good for 3 rides).
Plus, it was only ~ 90% full when I started.
So twice the range of my "broken in" 11AH NiMH pack!
Right now I'm charging the pack for the first time fully assembled in the Currie box.
 
I've found that there's enough noise in the cell voltage readings to act as a dither.
That combined with averaging multiple samples allows the system to resolve better than the theoretical 35mV on the top cell.
So I can balance the cells to a tighter tolerance.
 
Diy BMS hmm very cool.
 
Alan B said:
How is it working out?

I occasionally plug into the USB on the Arduino to check the cell voltages and balancing action.
The balancing activates when a cell reaches 4.1V and keeps the cells within +/- 20mV until they finish at 4.15V.
The balance shunts are capable of ~400mA, but they only come on to ~100mA for a minute or less.
I'm using a rather slow 2.8A charger from BatterySpace.
I charge once or twice a week at work when the pack is down to ~50% (3.75V/cell).
A 4 hour charge brings it up to ~4.0V per cell, then I head out to lunch.
If I charge on a Friday I'm good to go over the weekend.
My plan is to fully charge every 10th cycle or so, so it can balance.
 
In order to enhance safety I've added a "crowbar" circuit to the charger input.
Normally the external charger tops out at 29.4V.
If it exceeds ~33V an SCR is triggered and the charger output is shorted to ground.
A PTC polyfuse limits the current drawn from the charger.
So now there's hardware and software protection from over-voltage.
I'll update the schematic posted above soon...
 
How is the 100 uA parasitic drain? acceptable or a pain in the ass when you don't use the battery?
 
jaumebalust said:
How is the 100 uA parasitic drain? acceptable or a pain in the ass when you don't use the battery?

It takes 10,000 hours to drain 1AH from the 15AH battery. That's over a year.
 
It's been almost 18 months since I started using this pack.
I use it every day riding to work and running errands on the weekends.
I charge it typically 3X a week, so that's about 200 moderate cycles.
Generally keep it in the 3.85-4.15vpc, 27-29V (resting) range.
The Turnigys have held up well.
Voltage sag has increased from 1V to 1.2V max (total pack) at room temp.
 
I've updated the software.
The indicator light on the battery case is more informative.
There is now the option for a "short charge":
Charging stops when any cell reaches 4.1V, which is where balancing starts during a full charge cycle.
 
Back
Top