3-192S Arduino BMS (using BQ76PL536A)

dmwahl

10 kW
Joined
May 6, 2009
Messages
614
Location
Madison, WI
This thread is to document my R&D efforts surrounding the Texas Instruments (TI) BQ76PL536A battery management IC. After following the thread matterrr started about the BQ76PL536A Arduino shield he put together, I started looking into it myself. Since the chips themselves aren't super cheap and the eval board is hundreds of dollars, I put together an Arduino shield with a single IC on it for development work. Is anybody interested in one for their own entertainment? I'll initially sell them basically at cost, ready to through on an arduino and start experimenting. It's not suitable to put on a bike yet and I left most of the filter caps off the design to keep it simple (so it will likely not work at the full rated speed), but I'd like to eventually have an open source, arduino compatible BMS come out of it. I did include balancing fets and resistors.

Thoughts?

bq76pl536a-shield-top.png

[EDIT] Schematics and other relevant files here: https://github.com/dmwahl/BQ76PL536A-Arduino-Development-Shield

Current status of the project here.
 
How much for the current prototype shield David? I'd be interested in two or three to play with and if your looking for assistance with this shield I would be happy to offer some help coding / testing.

-Mike
 
I'm thinking $60 for an assembled prototype shield plus whatever shipping costs come to, I'm going to do some comparison shopping but that's what it looks like for now. That would include all the surface mount parts placed plus the headers to plug onto an Arduino Uno or compatible. You will need to solder on cell wires to the VC0-6 terminals, which are spaced 0.100" apart. If that's a problem I can add a header, but I'm trying to keep the cost/complexity down for now.

Although I'm using what's basically a stripped down reference schematic from TI, there's still a risk that it won't work 100% the first time around. I was only initially planning on building 3 to begin since there is a risk of not functioning properly. Unfortunately they won't be stackable in the current revision, but I did include header locations for the north/south chip connections. All that aside, if you want to buy more of them I'd be willing to build up extras.

I'd appreciate any coding/testing help you could offer, the only thing I ask is that you're willing to make all code public when complete.
 
David,

The only issues with your PCB or design at this point is lack of filtering, I doubt it will work in an eBike environment due to the noise but for a first run it should be fine on the bench.

If youve got a solder pot or rework station then by all means I'll take em assembled, otherwise I would be happy to slap them together myself... which ever.

Yes, sharing the code as open source... a working bms shield for arduino is sadly missing in the quiver, it's long overdue :)

-Mike
 
Sounds like we're on the same page here. I'm going to sit on the schematic and layout until the weekend and then order some boards (general practice for me, cuts down on errors). OSHpark has been a bit slower lately, but hopefully I'll have the boards by early-mid may and then I can get them assembled fairly quickly.

I fully expect it not to work yet in an ebike environment. For actual ebike use I would like to put together an "Arduino compatible" bms that would allow use of the Arduino development environment while not being an actual Arduino shield.

Any suggestions on code sharing methods? I was thinking github or something similar.
 
github or google code work for me :)
-Mike
 
Hey all,

I am working on a similar project, except for the Arduino Mega board. I produced boards and code that work. If you have any questions about code, I have a working implementation that I can share. I haven't converted it to a library, but that might be a good next step.
 
Matt,

share anything applicable, the Arduino Mega board isn't too far different from the normal AtMega with the exception of 2 serial ports, this can be done in SoftSerial with an interrupt vector handler. Please feel free to contribute Code (we don't mind spaghetti, we will classify it and make it useable), hardware (especially working is appreciated).

-Mike

PS Hope I speak for all :)
 
I had a reply all written out to the same effect. I'm setting up a github project for this as well, I'll get a basic directory structure set up and share the info here.

Is anyone interested in me building up some dev hardware? I was going to send my single chip shield off to fab, but if Matt already has working hardware it's probably smart to compare first.
 
If anyone wants to take a look, I attached a program that includes a few useful functions. I can edit it a bit later, but it has the basics, including the CRC check that you need to write bytes to registers on the BQ76PL536A.

https://docs.google.com/file/d/0B8XAfMAgWNMBSXA4NFdHeW0xX0U/edit?usp=sharing

Here is some of the code if you don't want to download the file (this is not a working program). The file also includes code that isn't relevant to BQ76PL536A (the Arduino also communicated with an Android tablet). These are the functions I used for the chip. Enjoy and feel free to use what you like.

Code:
void setup() {
  // Delay for programming
  delay(2000);

  // Set pin modes and interrupts
  pinMode(VOLT_A, INPUT);
  pinMode(VOLT_B, INPUT);
  pinMode(CURRENT_A, INPUT);
  pinMode(CURRENT_B, INPUT);
  pinMode(TEMP_A, INPUT);
  pinMode(TEMP_B, INPUT);
  pinMode(THROTTLE_SIM, OUTPUT);
  pinMode(RELAY_OUT, OUTPUT);
  pinMode(CRUISE_BUTTON, INPUT);
  pinMode (SLAVE_SELECT_PIN, OUTPUT);
  pinMode(CRUISE_EN, OUTPUT);
  pinMode(FAULT_PIN, INPUT);
  pinMode(ALERT_PIN, INPUT);
  pinMode(DRDY_PIN, INPUT);
  pinMode(CONV_PIN, OUTPUT);
  pinMode(LED_A, OUTPUT);
  pinMode(LED_B, OUTPUT);
  attachInterrupt(0, magnetInterrupt, FALLING);

  // Write initial pin states
  digitalWrite(RELAY_OUT, HIGH);
  digitalWrite(CRUISE_EN, LOW);
  digitalWrite(CONV_PIN, LOW);
  digitalWrite(SLAVE_SELECT_PIN, HIGH);

  // Begin serial communication for serial debug
  Serial.begin(57600);
  delay(100);

  // Set SPI preferences
  SPI.begin();
  SPI.setClockDivider(SPI_CLOCK_DIV32);
  SPI.setBitOrder(MSBFIRST);

  // Connect to Android device
  acc.powerOn();
  delay(1000);

  // Connect to BMS shield
  setupDevices();
}

void setupDevices() {
  resetDevices();
  delay(200);
  setAddresses(); //0xa5 reset 0x3c reset register 0x3f broadcast all devices
  delay(10);
  bqWrite(0x01,ADC_CONTROL,0x04);
  delay(10);
  bqWrite(0x02,ADC_CONTROL,0x04);
  delay(10);
}

void bqWrite(byte deviceAddress, byte regAddress, byte regData) {
  SPI.setDataMode(SPI_MODE1);

  // Shift the device bit and set bit 0
  byte logicalAddress = deviceAddress << 1;
  logicalAddress |= 1 << 0;  // Set bit 0, test bitSet(x,n)

  byte crcInput[3] = {
    logicalAddress, regAddress, regData                          };

  // Take the SS pin low to select the chip
  digitalWrite(SLAVE_SELECT_PIN,LOW);

  // Send and receive SPI
  SPI.transfer(logicalAddress);
  SPI.transfer(regAddress);
  SPI.transfer(regData);
  SPI.transfer(pec(crcInput));

  // Take the SS pin high to de-select the chip
  digitalWrite(SLAVE_SELECT_PIN,HIGH);

  SPI.setDataMode(SPI_MODE0);
}

byte *bqRead(byte deviceAddress, byte regAddress, byte length)  {
  SPI.setDataMode(SPI_MODE1);

  // Shift the device bit and clear bit 0
  byte logicalAddress = deviceAddress << 1;
  logicalAddress &= ~(1 << 0);  // Clear bit 0, test bitClear(x,n)

  // Create buffer for receivedData and clear it
  static byte receivedData[20];
  memset(receivedData, 0, sizeof(receivedData));

  // take the SS pin low to select the chip:
  digitalWrite(SLAVE_SELECT_PIN,LOW);

  //  Send and receive SPI
  SPI.transfer(logicalAddress);
  SPI.transfer(regAddress);
  SPI.transfer(length);
  for (int i = 0; i < length + 1; i++) {
    receivedData[i] = SPI.transfer(0x00);
  }

  // take the SS pin high to de-select the chip:
  digitalWrite(SLAVE_SELECT_PIN,HIGH); 

  SPI.setDataMode(SPI_MODE0);

  // Return data read from registers
  return receivedData;
}

int resetDevices() {
  // Write reset code to reset register
  bqWrite(0x3F,RESET,0xA5); 
}

void setAddresses() {  
  // Set bottom address to 1
  bqWrite(0x00,ADDRESS_CONTROL,0x01);
  delay(10);

  // Set and clear AR bit of ALERT_STATUS
  byte alertSet = 0x00;
  bitSet(alertSet, 7);
  bqWrite(0x01,ALERT_STATUS,alertSet);
  delay(10);
  bqWrite(0x01,ALERT_STATUS,0x00);
  delay(10);

  // Set top address to 2
  bqWrite(0x00,ADDRESS_CONTROL,0x02);
  delay(10);

  // Set and clear AR bit of ALERT_STATUS
  bqWrite(0x02,ALERT_STATUS,alertSet);
  delay(10);
  bqWrite(0x02,ALERT_STATUS,0x00);
  delay(10);
}

int getCellVoltage(byte cellNumber) {

  byte deviceAddress;
  byte cellAddress;

  switch (cellNumber) {
  case 0x01:
    deviceAddress = 0x01;
    cellAddress = VCELL1;
    break;
  case 0x02:
    deviceAddress = 0x01;
    cellAddress = VCELL2;
    break;
  case 0x03:
    deviceAddress = 0x01;
    cellAddress = VCELL3;
    break;
  case 0x04:
    deviceAddress = 0x01;
    cellAddress = VCELL4;
    break;
  case 0x05:
    deviceAddress = 0x01;
    cellAddress = VCELL5;
    break;
  case 0x06:
    deviceAddress = 0x01;
    cellAddress = VCELL6;
    break;
  case 0x07:
    deviceAddress = 0x02;
    cellAddress = VCELL1;
    break;
  case 0x08:
    deviceAddress = 0x02;
    cellAddress = VCELL2;
    break;
  case 0x09:
    deviceAddress = 0x02;
    cellAddress = VCELL3;
    break;
  case 0x0A:
    deviceAddress = 0x02;
    cellAddress = VCELL4;
    break;
  case 0x0B:
    deviceAddress = 0x02;
    cellAddress = VCELL5;
    break;
  case 0x0C:
    deviceAddress = 0x02;
    cellAddress = VCELL6;
    break;

  }

  bqWrite(deviceAddress,ADC_CONVERT,0x01);
  delay (100);
  byte *cellRaw = bqRead(deviceAddress,cellAddress,0x02);
  delay(10);

  int cellVoltage = (cellRaw[0] * 256) + cellRaw[1];

  prevCellVoltages[cellNumber - 1] = convertCellVoltage(cellVoltage);

  return cellVoltage;
}

double convertCellVoltage(int cellVoltageInt) {
  return ((double) cellVoltageInt) * (6250.0 / 16383.0);
}


byte *getCellVoltages() {
  byte *lowCells = bqRead(0x01, VCELL1, 0x0C);
  delay(10);
  byte *highCells = bqRead(0x02, VCELL1, 0x0C);
  delay(10);

  // Create buffer for cell data and clear it
  static byte cellData[12];
  memset(cellData, 0, sizeof(cellData));

  for (int i = 0; i < 6; i++) {
    cellData[i] = lowCells[i];
  }

  for (int j = 0; j < 6; j++) {
    cellData[6+j] = highCells[j];
  }

  return cellData;
}





double convCellVoltage(byte hibyte, byte lobyte) {
  // Use given conversion equation to convert 14 bit cell voltage to double
  return (((double) hibyte * 256.0) + (double) lobyte) * (6250.0 / 16383.0);
}

byte pec(byte crcBuffer[]) {
  byte crc = 0;
  int temp = 0;
  for (int i = 0; i < sizeof(crcBuffer) + 1; i++) {
    temp = crc ^ crcBuffer[i]; 
    crc = crcTable[temp];
  }
  return crc;
}
 
Just got a chance to look through your code, that's good stuff, thanks for posting. It will no doubt save us a lot of time.

What was the problem you mentioned earlier in a PM about the chip select pin? Did you get it resolved?
 
The chip select pin I assigned on board was being used for the USB host chip to communicate with Android. I recruited that signal to a different pin with sky wires and it worked fine. I would just double check whether there are any signals assigned in the Arduino Uno already.
 
Quick update: First version of this board is built up. Mike (mwkeefer) is working on the code. After we're happy with the code it will be released as open source, along with the board schematic. A larger (probably 24S) version will follow. If anybody is interested in one or more of these for development/experimentation use PM me.
 

Attachments

  • IMG_0883-small.JPG
    IMG_0883-small.JPG
    143 KB · Views: 16,765
Hello All,

I'm making good progress with a first beta release of firmware for the Arduino BMS Shield but it's taking a little longer to code up than I had anticipated - the codebase as of now is comprised of an Arduino Library for the TI bq76PL536 which handles all the device stack communication and other processing of data (ie: cell voltages, alerts and faults) and a Sketch which provides the actual BMS functionality such as balancing, etc.

This seemed to be the most logical manor to code support for the Shield (and stacked upper bus shields used for expanding beyond 6S).

As of now the first version released should be Wednesday / Thursday to allow sufficient time to test and it will not implement cell balancing yet (it's coded but will not be enabled in the first beta releases).

By the weekend I should also have an open source and as yet unnamed "Parameter Designer" with beginner (set chemistry type) and advanced (set HVC/LVC/Balance Voltage/Balance Timer, etc.) - this is coded in .net 2.0 framework so it will compile for Windows, Mac OSX and Linux (using Mono Support on OSX and Linux). The first version of the software will only use the USB port or TTL serial IO of the MCU of an Arduino Uno (or compatible) to communicate but future releases will implement use of the SparkFun SPI shortcut and probably I will code up a dedicated Arduino Firmware to act as an ISP/SPI interface to the PC.

Shield Firmware Progress / Features so far and Status:
1.) Automatic Detection and Addressing of up to 62 stacks (bq76PL536 chips attached in series to the North Bus) - Done
2.) Automatic Detection of connected cells (this will allow less than 6S to be connected to each module enabling packs to be configured of any cell count) - Done
3.) Minimizing current draw when active (when ADC conversion is done, only attached cells are measured and frequency of cell voltage polling on ADC is adjustable) - In Progress
4.) PC based configuration software - In progress
5.) Interrupt driven response to Faults and Alarms ensuring FAST actions by the Arduino MCU to disconnect throttle signal (send to GND) or operate a FET bank disconnect circuit I shamelessly stole from Methods.
6.) Logging of cell data to FAT/Fat32 SD Card - works but needs tuning and refining.
7.) Crude balancing - needs work, currently only enables when charging and cell voltages are above .1v above nominal for the chemistry, so for LiPo > 3.8v is when balancing takes place. Hoping this will prevent long taper / low current balance at end of charge cycle by keeping the cells in balance to the CV point at 4.15v per cell (default coded for LiPo chemistry).

Just thought I'd post an update to be sure everyone has reasonable expectations for the first version of firmware and some idea where I'm heading with the codebase.

-Mike
 
When you program the settings for the LVC and nominal voltage of the cell do you parametrize such values. Say for example later on you wanted to use these boards for LIFEPO4 or a higher voltage Li-ion (4.35V) or future cell voltages is this something that is going to be a quick reflash setting?
 
migueralliart said:
When you program the settings for the LVC and nominal voltage of the cell do you parametrize such values. Say for example later on you wanted to use these boards for LIFEPO4 or a higher voltage Li-ion (4.35V) or future cell voltages is this something that is going to be a quick reflash setting?

Yes.
 
Actually David,

The answer is yes and NO.

While the firmware does allow reconfiguration, it's based purely on Charging Chemistry Profiles:

Take LiPo Chemistry for Example;

Out of box chemistry configuration for Soft Cell Level HVC (this is also when Balancing is triggered when Balancing is Enabled) is 4.1v and the HVC is 4.15v, similarly the LVC is set at 3.3 volts HARD and 3.5v soft cutout (reduce power).

So it's a bit more complicated than just re-initializing properties but it also offers features the TI chip alone can't - Soft and Hard HVC/LVC and MCI (Maximum cell imbalance) which will handle conditions where a CELL is beginning to deteriorate.

Please don't mis-understand, much of what I'm doing is to ensure full BMS (HVC/LVC/MCI) using the analog front end but declaring as few un-needed parameters as needed to ensure it will (the shield) work with the minimum processor requirement of ATMega328.

In a next version - we can use perhaps an Atmega1280 for more program memory or even an XMEGA to add some seriously cool features.

After doing some quick math, it seems the SPI bus can onlt be run at 1MHZ up to 6 ti chips - or 36 cells in series. I have a chart of speeds supported per devices and plan to automatically set the maximum SPI after enumerating devices on the bus, initial SPI is set at 250 khz to ensure compatibility with maximum stack size.

I hope this helps to explain better... Details are limited because I'm going through a HUGE learning curve on the TI Chip and in writing the Arduino specific library but it's coming along.

-Mike
 
I'm dying to get my hands on a compact microcontroller system to allow me to monitor a 14S ebike pack. Right now I'm just going with opto-isolated alarm outputs from 2x CellLog 8M's that I can easily unplug when not in use. But I am a data hound and want to monitor everything, plus maybe eventually have an efficient on-bike charging system.

I guess I *could* spend my time and money buying a couple CellLog 8S, hacking them to fix battery drain, isolating the USB outputs, and figuring out the protocol to read data from a Raspberry Pi. It would eventually work. But I like the look of this project a whole lot more! For some dumb reason I started my search for DIY BMS on Google rather than E-S but of course I ended up back here before long.

Even if you may not have a working on-bike system yet, I think it's awesome that you've gotten this far so quickly. PM sent to see how I can get a board to hack on and get involved!
 
Hello all,

Just a quick update:

There are some isolation issues with the shield which David and I are working on resolving now, I won't get into specifics but suffice to say we should have a solution for this v1 of the Shield and likely design the issue out in v2.

I received the "official" evaluation platform today (finally) which for firmware development is instrumental to getting done in timely fashion with required features and minimum function.

I have no ETA (not weeks, if that helps) on details of isolation issue resolution or firmware first release (full source) via github but it shouldn't be too long :)

-Mike
 
matterrr said:
If anyone wants to take a look, I attached a program that includes a few useful functions. I can edit it a bit later, but it has the basics, including the CRC check that you need to write bytes to registers on the BQ76PL536A.

matterrr,

I'm sure that you have figure out by now to add the CRC checksum on the final byte of CRC8 on response to read to ensure contents are valid - if not I can supply you a quick replacement function which calculates CRC.

This is useful when changing the SPI divide by - at 6 devices stacked or less you can use 1MBit but above that the SCK frequency on the SPI bus must go lower.... while I am perfectly happy to leave support at 36 cells and addressing the expansion of my addressing algo I think perhaps you will too find it useful.

Just a quick .02.... worth about that.

-Mike
 
If anyone is still watching this thread, it's not dead, just slowed down due to life outside of ES. Namely that my wife and I are expecting our first child any day now. Anyway, the boards I built up earlier this year are working and I've got some basic code running on them. Cell voltage measurement and balancing are the only items I've included at the moment, but they are working well and now that I've got the basic communication worked out the rest will be relatively easy.

Here is the output from a 4S LiFePO4 pack I'm using to debug. Reported voltages are within 1-2mV of what I measure with my DMM.
Code:
Cell 1: 3302.00mV
Cell 2: 3332.72mV
Cell 3: 3305.64mV
Cell 4: 3310.60mV
 
dmwahl said:
Anyway, the boards I built up earlier this year are working and I've got some basic code running on them.
Can you post the code on GitHub?
 
That's the long term plan, although I'd rather keep it semi-private right now since it's changing constantly. Are you doing something similar to my project? The code won't be much use to you unless you're using the same chip. If you are, PM me and I can email it to you.
 
I am definitely still watching! I'm hugely excited about this project. Congrats on the new addition, and best of luck. Let me know if I can help out with the shield development.
 
What's the setup of your current pack/bms? At the moment I'm planning on a 9-18S version (my own bike uses a 16S LiFePO4 pack) as the first edition, I wanted to make it a modular design in 6S modules, but the schematic for that turned out to be a nightmare to implement since the bottom, middle, and top BQ76 chips all have different connections. 18S should cover a lot of people, but a 24S would cover a lot more and wouldn't be too much worse.
 
Back
Top