DIY Lithium Packs, Proposal and Discussion


Well-known member
I would be curious if you can read the part numbers. I am certain they use off the shelf parts. Like any design, the details make or break it.

I bet they are using a standalone balance chip inside the battery itself (with fets and resistors). I would be curious if it uses bi-directional balancing, or if its simple shunt style.
Victron batteries have internal balancing.

Watch you don't void that nice warranty!

I bet digging through their user forums much is revealed
Nope, I won't do that, but I will keep rooting around their forum. It's hit or miss on information.

dont listen to him.. void, void, void.

seriously tho, warranty void if removed stickers are unenforceable, as long as you can crack it open w/out physically damaging it.. you'll be fine.. void, void, void.. you kno u wanna :p
I'm pretty good at non-destructive investigations, so we'll see what I can see.

Recommended books for Overlanding

Overlanding the Americas: La Lucha
by Mr Graeme Robert Bell
From $20
Into Africa
by Sam Manicom
From $21.02
Road Fever (Vintage Departures)
by Tim Cahill
From $12.99


Engineer In Residence
I spent a few days going through commercially available BMS. I can't seem to find one with the options I want.

External relay/contactor support
Ability to control a heater
Ability to report data over bluetooth, LCD display, or similar
200mA balancing.

The Orion JR is nice, but it doesn't support heater control, and requires an expensive (and almost pathetic) external screen, or CANbus interface for monitoring.
The EMUs BMS requires a specific external shunt, and is designed around direct control of dumb charge sources, and has some behavior that is undesirable.
The various commerical grade BMS units either don't support 4 cell packs, or require extensive software or hardware to monitor, and I don't want to pull out my PC every time I need to check cell voltages. Nor do I want to program a front end either.

The simple chinese bluetooth bms (they all share the same core hardware) may work, but I need them to support an external shunt and contactor. Not sure if they could be modified for that.

I took a look at the arduino BMS posted previously. The code is well written, and I should be able to modify for my simple needs. There is not display though. So I would need to work out something with a bluetooth module, or rig up a basic character style LCD display to show vital stats. Linear tech (BMS chip maker) provides a decent library which makes interfacing fairly straightforward for newbs like me. Manually setting registers is not very intuitive.

I am going to keep looking to see whats available though, searching for this stuff on the web, and trolling through forums is not very expedient.

#include <Arduino.h>
#include <stdint.h>
#include <SoftwareSerial.h>
#include <SPI.h>
#include "Linduino.h"
#include "LT_SPI.h"
#include "UserInterface.h"
#include "LTC68042.h"
#include "Average.h"

#define TOTAL_IC  1            // Number of ICs in the isoSPI network LTC6804-2 ICs must be addressed in ascending order starting at 0.
/***** Pack and sensor characteristics *****/
const float MAX_CURRENT = 50.;        // Maximum battery current(amps) before relay opens
const float MAX_TEMP  = 30. ;        // Maximum pack temperature (deg C) before relay opens
const float LEM_RANGE =  50.;        // Rated range of LEM hall sensor.
const float MIN_CELL_V = 2.00;              // Minimum allowable cell voltage. Depends on battery chemistry.
const float MAX_CELL_V = 3.60;              // Maximum allowable cell voltage. Depends on battery chemistry.
/***** Xbee serial *****/
#define xbRxD 4
#define xbTxD 5
SoftwareSerial XbeeSerial(xbRxD, xbTxD);
/******** Arduino pin definitions ********/
int chargeRelayPin = 8;           // relay output for overcharge conditions
int dischargeRelayPin = 9;           // relay output for undercharge conditions
int currentPin = A0;           // LEM Input should be Vcc/2 + I * 1.667 / LEM_RANGE
int currentBiasPin = A1;       // for comparing with LEM output (currentPin) since Vcc/2 may change as aux battery discharges.
int tempPins[] = {A2};
/********  variables for tracking cell voltages and states ***************/
float VBalanceThreshold = 3.5;  // cell balancing occurrs when voltage is aboce this value
int overCharge_state = LOW;             //Over charge state. HIGH = relay on, LOW = relay off
int underCharge_state = LOW;             //Under charge state. HIGH = relay on, LOW = relay off
int overTemp_state = LOW;       //Over temperature state. HIGH = relay on, LOW = relay off
int overCurrent_state = LOW;    //Over current state. HIGH = relay on, LOW = relay off
int cellMax_i;                  // temporary variable for holding index of cell with max voltage
int cellMin_i;                  // temporary variable for holding index of cell with min voltage
float  cellMin_V;               // temporary variable for holding  min measured cell voltage
float  cellMax_V;               // temporary variable for holding  max measured cell voltage
unsigned long  tstart;
float minV1 ;
float maxV1 ;
int error = 0;
/********   Current and temperature variables ***********************/
const uint16_t imax = 100;                 // size of arrays for averaging read measurements
Average<float> lemHistory(imax);
Average<float> lemBiasHistory(imax);
float lem = 0;
float lemBias = 0;
float lemZeroCal = 0;
float current = 0;
float temp[sizeof(tempPins)];
*** Global Battery Variables received from 6804 commands
  These variables store the results from the LTC6804
  register reads and the array lengths must be based
  on the number of ICs on the stack
uint16_t cell_codes[TOTAL_IC][12];
  The cell codes will be stored in the cell_codes[][12] array in the following format:
  |  cell_codes[0][0]| cell_codes[0][1] |  cell_codes[0][2]|    .....     |  cell_codes[0][11]|  cell_codes[1][0] | cell_codes[1][1]|  .....   |
  |IC1 Cell 1        |IC1 Cell 2        |IC1 Cell 3        |    .....     |  IC1 Cell 12      |IC2 Cell 1         |IC2 Cell 2       | .....    |
uint16_t aux_codes[TOTAL_IC][6];
  The GPIO codes will be stored in the aux_codes[][6] array in the following format:
  |  aux_codes[0][0]| aux_codes[0][1] |  aux_codes[0][2]|  aux_codes[0][3]|  aux_codes[0][4]|  aux_codes[0][5]| aux_codes[1][0] |aux_codes[1][1]|  .....    |
  |IC1 GPIO1        |IC1 GPIO2        |IC1 GPIO3        |IC1 GPIO4        |IC1 GPIO5        |IC1 Vref2        |IC2 GPIO1        |IC2 GPIO2      |  .....    |
uint8_t tx_cfg[TOTAL_IC][6];
  The tx_cfg[][6] stores the LTC6804 configuration data that is going to be written
  to the LTC6804 ICs on the daisy chain. The LTC6804 configuration data that will be
  written should be stored in blocks of 6 bytes. The array should have the following format:
  |  tx_cfg[0][0]| tx_cfg[0][1] |  tx_cfg[0][2]|  tx_cfg[0][3]|  tx_cfg[0][4]|  tx_cfg[0][5]| tx_cfg[1][0] |  tx_cfg[1][1]|  tx_cfg[1][2]|  .....    |
  |IC1 CFGR0     |IC1 CFGR1     |IC1 CFGR2     |IC1 CFGR3     |IC1 CFGR4     |IC1 CFGR5     |IC2 CFGR0     |IC2 CFGR1     | IC2 CFGR2    |  .....    |
uint8_t rx_cfg[TOTAL_IC][8];
  the rx_cfg[][8] array stores the data that is read back from a LTC6804-1 daisy chain.
  The configuration data for each IC  is stored in blocks of 8 bytes. Below is an table illustrating the array organization:
  |rx_config[0][0]|rx_config[0][1]|rx_config[0][2]|rx_config[0][3]|rx_config[0][4]|rx_config[0][5]|rx_config[0][6]  |rx_config[0][7] |rx_config[1][0]|rx_config[1][1]|  .....    |
  |IC1 CFGR0      |IC1 CFGR1      |IC1 CFGR2      |IC1 CFGR3      |IC1 CFGR4      |IC1 CFGR5      |IC1 PEC High     |IC1 PEC Low     |IC2 CFGR0      |IC2 CFGR1      |  .....    |
  \brief  Inititializes hardware and variables
void setup()
  pinMode(chargeRelayPin, OUTPUT);
  pinMode(dischargeRelayPin, OUTPUT);
  pinMode(currentPin, INPUT);
  pinMode(currentBiasPin, INPUT);
  for (int i = 0; i < sizeof(tempPins); i++)
    pinMode(tempPins[i], INPUT);
  digitalWrite(dischargeRelayPin, LOW);
  digitalWrite(chargeRelayPin, LOW);
  overCharge_state = HIGH; //HIGH = relay on, LOW = relay off
  underCharge_state = HIGH; //HIGH = relay on, LOW = relay off
  LTC6804_initialize();  //Initialize LTC6804 hardware
  init_cfg();        //initialize the 6804 configuration array to be written
  lemZeroCal = zeroCurrentCalibrate();
  tstart = millis();
  \brief main loop
void loop()
  // read current:   
  overCurrent_state = HIGH;
  current = readCurrent();
  if(current > MAX_CURRENT) overCurrent_state= LOW;

  // read temperatures:
  overTemp_state = HIGH; 
  for (int i = 0; i < sizeof(tempPins); i++)
    temp[i] = (analogRead(tempPins[i]) * 5. / 1024 - 0.5) / 0.01;
    if(temp[i]> MAX_TEMP) overCurrent_state= LOW;
  // read cells:
  LTC6804_adcv(); // do cell AD conversion and fill cell registers
  error = LTC6804_rdcv(0, TOTAL_IC, cell_codes); // read cell voltages from registers
  if (error == -1)
    Serial.println("A PEC error was detected in the received data");
  // print to serial outputs:
  // test for over charge/undercharge states:
  minV1 = MIN_CELL_V;
  maxV1 = MAX_CELL_V;
  if (overCharge_state == LOW) { // add hysteresis
    maxV1 = maxV1 - .2;
  if (underCharge_state == LOW) { // add hysteresis
    minV1 = minV1 + .2;
  // get maximum and minimum cells:
  cellMax_i = -1;
  cellMin_i = -1;
  cellMin_V = 100.;
  cellMax_V = 0.;
  for (int i = 0; i < 12; i++)
    float V = cell_codes[0][i] * 0.0001;
    if (V < cellMin_V) cellMin_V = V; cellMin_i = i;
    if (V > cellMax_V) cellMax_V = V; cellMax_i = i;
  underCharge_state = HIGH;
  overCharge_state = HIGH;
  if (cellMin_V <= minV1)
    underCharge_state = LOW;
    // Serial.println("V <= MIN_CELL_V");
  if (cellMax_V >= maxV1)


Well-known member
The Victron VE.Bus BMS is completely sealed in a plastic case, so someone else will have to spend $118 if they want to crack one open. It'll do high/low/temp cutoff, but I don't know how much work it'd take to get it to work with a non-Victron battery.

If you can get a BMV-712 to work with your battery, I think it'll do some of the things you want, like triggering a heater via a programmable relay.

Would a Venus GX be useful? The Venus OS is open source.


Well-known member
Venus gives you alot more customization and flexibility, you can go dig through its docs..

but ultimately it didint do everything I wanted, so I'mna go with standard debian, setup the dbus interface and write my own safety layers.. and a simple WebUI for status display.. but most of that is due to the need to run anything I want on the lil SoC, like a media server too.


Engineer In Residence
There is something to be said for a standalone embedded control of the BMS, though if the hardware has a failover ,or fail safe state is matters less. I am always leering of putting to much control into a single piece of hardware, especially one I can screw up myself.

I like the victron hardware. I just am having trouble finding any previous applications that don't use their battery modules.

The issue of course, is that I want to avoid the slippery slope of completely redoing my electrical system, new hardware, new wires, testing it all... Trade offs.

It depends on how the cost pans out, but drop ins like the Trillium (unless they down rate their current limits again) may be the best approach.


Again, I am skeptical of the idea of single monolithic "a BMS".

I see it as a collection of functions. LVC to prevent discharging too low, HVC in case a charge source fails, are the two core ones.

By regularly balancing, and avoiding both voltage shoulders, pack-level is enough for main layer, cell-level only for failsafe redundancy, if at all.

Temperature issues, IMO should separate out, so few, even very reliable and capable BMS do anything at all.

AFAICT any BMS can be adapted to control external contactors.

So make a list of those functions, use the BT enabled one to let you monitor cell / group voltage for example. If cheap enough use it just for that.

Multiple layers on some critical functions for redundancy.

Some functions only need to be attached periodically, not 24*7.

Some don't need to be fully automated, human attention can substitute.

Look closely at REC BMS, many from the master thread chose that.

well worth parsing through the whole thing, taking summary notes, quickly learn who has real credibility.


Trillium are 100% unproven

Made by K2, Trojan label slapped on

will be years before we know, if they're great, best case

don't actually save money over known-good bare cells like Winston/Thundersky, CALB, GBS, Sinopoly and A123 (now Lithium Werks)


Engineer In Residence
A single BMS does the following.

  • monitor cell and pack voltage/temps (very reliable hardware based)
  • compare voltages and balance if needed (again, very reliable and simple hardware)
  • disconnect pack if the temps or voltages exceed the limits (nothing special here).

In fact some 80% of the above functions are built into a single IC chip. The chances of failure are much reduced that way.

That arduino BMS has a very small component count. No big banks of fets, etc. The code is simple, and with a watchdog timer and some careful design it can be made reliable, and fail-safe. Personally I see minimal risk. Most solar controllers have higher component counts.

Looping back around this BMS. I think it has potential, and for the cost, may be worth looking into. It doesn't appear to support pack heaters, but has the other requirements met.



I been using the chargery bms8t almost 6 months now on my 220ah 4s lifepo4. It has proven very reliable, once setup you don't have to mess with it again. The ability to use mechanical contactors/relays to shutoff power is a big plus when use with solar. Though it is capable of 1.2 amp balancing I have it disable and rely on active balancers for that. I tested it fully at different battery/cell voltage cutoffs, and worked everytime.

The unit is failsafe, the contactors are always on. If the chargery loses power the contactors are turned off (stopping power flow). When the sun goes down I have a toggle switch to cutoff power to the contactor.

It doesn't do pack heaters. It does counts amps in/out of battery but since it uses watthours to track power, its worthless as a coulombmeter.

I only use on my 240 watt solar system, the contacter is between the solar panel and controller. If the contactor is deenergized the controller goes to sleep. To fast charge my lifepo4 and because of voltage drop I have the controller bulk setting set to 15.5 volts. I have the chargery and also an overvoltage protection relay monitoring battery voltage. Even at bulk setting of 15.5 volts its rare to get the lifepo4 up to 14.6 volts, the bms triggers a shutdown maybe 2 or 3 times a week. When I have a full battery and don't require fast charging I set the bulk to 15.3 volts, at that setting the battery never gets to 14.6 volts, the controller goes into float mode too soon and trickle charges at 1 or 2 amps all day long.

94 dollars is a good deal for the 300 amp model, I paid 86 for the 100 amp model. You have to buy your own contactors (I use a 4 dollar 30a automotive relay) but you can get 200a relays in the 20 dollar range. As far as something to control a heater, there are 12 volt temperature relays that cost less then 5 dollars, the only drawback is they only do celcius as a measurement. I don't trust any bms to be 100 percent everytime, even the chargery, thats why I always have an overvoltage protection relay as a backup.


I'm not saying do not use "a BMS" just do not rely on one product to do it all.

Never heard of on the controls heating / cooling, TMS is a separate system.

Overvoltage also has (should have) the additional layer of the charge source regulator.

Undervoltage LVC is the other function I'd want the additional layer, at pack level, if the BMS is doing


Engineer In Residence
The orion BMS supports driving an external output based on pack temperature (high, low or both). Pack preheating is pretty common on vehicle BMS, and some Ebikes. Makes sense, as they are often used/charged in cooler weather.

I understand your concern, but spreading the control features around doesn't necessarily mean more reliable. Though I would be more prone to go that way if I was using a 90$ bms as opposed to the commercial grade ones. In the design I deal with typically any down time is a significant issue. I think that needs to be weighed in the risk assessment along with the "total pack destruction" scenarios.

And really, with properly configured load/charge sources, and an audible alarm, you already have a first level of protection. The BMS is the second. The third is a human who checks that everything is working as it should ever hundred cycles or so.

Recommended books for Overlanding


Just be aware of failure modes.

Going dead flat or charging when too cold doesn't just lose "longevity" thousands of cycles off the back end, can render the whole bank as instant scrap.

BMSs actually are also the active causes of a high percentage of failures, thousands of owners rely on the human factor only or at least primarily.

If you're talking a cheap system no worries, but when the cells alone cost tens of thousands it's a different story.


Engineer In Residence
If you're talking a cheap system no worries, but when the cells alone cost tens of thousands it's a different story.
I agree, but this thread is about vehicle based systems (as is this forum). It would be useful to modify your advice based on the audience/context. If I was talking about a 30kw-hr system I would not be concerned about spending another 1,000 on a bms and contactors.