Arduino 4S BMS (Version 7)


Like my previous post about this project, this is a customizable arduino based 4S BMS system. This is now version 7 of hardware and version 21 of software.

The benefit of using this over other low cost solutions is that gives a lot more feedback about what exactly its doing and control over how its doing it. For example setting the limit for the over voltage protection and under voltage protection value.


Here is a couple pictures of my BMS in action on a 4S11P 18650 pack.


Now to the key features that makes this project different from the rest:

  1. Adjustable voltage tolerance (maximum difference between cells)
  2. Display individual voltage cell values on an oled i2C display and/or serial print monitor over UART (TX, RX, GND)
  3. Set under-voltage protection value and over-voltage values for series MOSFET
  4. Adjustable balance current with a swapping of drain resistor up to 1A safely
  5. Serial commands for adjusting variables and feedback:
    1. Under voltage protection value
    2. Over voltage protection value
    3. Cell voltage tolerance
    4. Request calibration values
    5. Request status of UVP and OVP event and reset if triggered
    6. Enter calibration mode to re-calibrate if needed for ADC inputs


Schematic and original kicad files

Arduino Code

NOTE: Arduino BMS A7 hardware is supported only with the current arduino software


Now lets look into the hardware for this. There are 5 important sections in this board:

  1. Power Section
  2. MCU: Arduino ATMEGA328P running at 8MHz with minicore bootloader
  3. Reference 1.25V voltage for ADC
  4. Voltage dividers for cell measurement into ADC
  5. Balance Section
  6. Series MOSFET Section


One of the most important sections in this project is the power section. Key features I was looking for was low Quiescent current because this will be battery operated.

Quiescent current is the amount of current needed to operate an IC.

The current power IC I’m using is a buck converter RT6208G. I chose a buck converter over an LDO because of efficiency, again trying to keep the current draw from the battery low. Key features of this converter is:

  1. Low quiescent current: less than 500nA or 0.5mA
  2. High input voltage: 36Vin max
  3. Small and compact
  4. Efficient: around 80-90%

One thing to mention is that the buck converter I chose is currently out of stock and will not be available until November. I’m currently working on choosing an alternative but any buck converter will work with this.

Figure 1: Power Section

Anything here is that instead of stepping down to 3V3, I stepped down the voltage to 2V7. The image above is wrong and the FB resistor values are picked for an output voltage of 2V7 but it will function the same.

The reason for going with this voltage is because I was trying to minimize the current draw from the battery. I found that 2V7 is the minimum voltage I can use while still being able to use the I2C OLED screen.


The next section is the Arduino MCU and the external 1.25V reference.

Figure 2: MCU and Reference Section

Here you can see where the ADC pins are attached to the cell pins for measurements. And the digital pins for the control of the mosfets for the balance section. Its pretty straight forward here

On the bottom you can see the external 1.25V voltage reference. I’m sure there are different values I could have gone with but I figured that with a 1.25V I could try and increase resolution even after the voltage dividers. Without accounting for the voltage dividers for measuring the cells, we have a 1.22mV resolution. Not too bad but obviously it will increase with the voltage dividers.


The next section is the voltage divider section which is used to step down the cell voltages to within the voltage reference value, in our case 1.25V max.

My values calculated were based stepping down the voltage value to within 1.20V, that is when a cell is at 4.2V then the divider will be 1.20V. This gives some margin in case the cells get overcharged.

Here you can see the divider circuit. Since the measurements are single-ended measurements and not differential, then everything is reference to ground and there for the higher in cell measurements we go the bigger the voltage divider we need.

I used 100k as the base resistor as I tried to minimize current consumption. The measurement cycle will be slower due to the arduinos 14pF capacitor but thats ok because we sample about 100 measurements to offset this.


The last section is the balance section. Here the controllers turns off and on the mosfets corresponding to the cell its attached to. When the software determines that ex. cell 2 is higher than the lowest cell, then D1 is triggered and an 18 ohm resistor will start draining some current to let the rest of the cells catch up.

Figure 3: Balance Section

Looking at the bottom section of this circuit ( BAT0 / BAT1 ) you can see resistor value R424, this controls how much current is drained. At full charge of the cell 4.2V, you will see a current drain of 0.23A and then a minimum drain of 0.16A when the cell is at 3.0V.

Since the series mosfets are capable of 4.2A, you can theoretically lower the resistor value to drain more current but I do not suggest draining more than 1A at least with this board.


This section was added for an extra layer of protection and is not needed for the bms balancing functionality to operate.

I’ve added a mosfet switch that utilizes the arduinos digital pin 9 and 10 to control power mosfets that are in series on the positive line of the battery.


Theoretically each mosfet can handle around 10A without a heatsink and about 40A per mosfet with a heatsink for a total of 120A total. But here you can add more in parallel for higher current or change the mosfet for a higher current capable one. The mosfets used here are P_Channel mosfets for high side switching.


Now to cover the software side of this board, here I will only cover the important sections in which you might want to adjust for your own purposes. Most of the variables you would want to change can be accessed and changed via serial monitor using an FTDI while only connecting the TX, RX, and GND pins.

Here I will list the important sections:

  1. Sampling frequency
  2. Tolerance adjustment
  3. Serial Commands


// Sampling number for analog read values.
// Will use this sample size to calculate a
// better estimation of the analog value
int samples = 100;

Here in the samples variable you can change the value for how many samples the adc takes before creating an averaging of the cells. If you want a smoother value you can increase but I would not go past 250 samples. Remember this will also effect the amount of time it takes to display voltage values.


// This is the max difference that the cells
// can achieve in respect to each other
float tol = 0.01; // 10mV max voltage difference

Here in the tol variable, it is used to as a reference for when the balancing of the cells should stop. The cells will balance until all cells are within 10mV of each other. This might not be exact because with the last 2 cells the register voltage different might be greater than 10mV due to a higher voltage divider but the measured voltage is around +/- 25mV of the actual cell voltage.


The most important part is the serial commands because this allows you to make changes without having to reflash the arduino.

Below we have a list of 6 serial commands that I will explain in details on how to enter the values into the serial monitor.

/* This is the table for entering into different modes and how to 
 *  enter commands within serial console
 *  modes:
 *  1. Display ADC calibration values = 1001
 *  2. Enter adc auto calibration     = 1002
 *  3. Adjust minimum and maximum     = 1003
 *     voltage threshold for OVP
 *     and UVP
 *  4. Used to set all digital outputs
 *     to low                         = 1004
 *  5. Erase all EEPROM values        = 1005
 *  6. Reset series switch value      = 1006
 *  7. Display status for Reset, UVP
 *     OVP state condition            = 1007
 *  1.Example of displaying calibration values
 *    Serial window: mode
 *    Serial window: 1001
 *  2.Example of entering into auto calibration mode
 *    Serial window: mode,vref1,vref2,vref3,vref4
 *    Serial window: 1002,4.00,8.00,12.00,16.00
 *  3.Example of entering UVP and OVP values
 *    Serial window: mode,UVP,OVP
 *    Serial window: 1003,3.10,4.15

Lets look at the first command entry.

  • 1001
    • This will display the calibration values that is stored in EEPROM
  • 1002
    • This will enter calibration mode. Since there might be some offset from what the adc reads vs the actual value, I added a calibration mode that will add the offset to each adc value to try and get the values closer to the actual.
    • To enter calibration mode you will need to enter:
      • 1002,vef1,vef2,vef3,vef4
      • The vef1-4 values are the actual measurements of the battery cells
    • Calibration setup:
      • Enter 1004 first to turn off all balancing MOSFETS
      • Measure each cell value with a multimeter to get the actual value
      • Then enter calibration mode by entering 1002,3.54,3.76,3.20,3.56 as an example for vef values
  • 1003
    • This will adjust the values set for under voltage protection and over voltage protection. So if lets say 3.1V is set value for under-voltage protection then if any one of the cells drop below this value then the series FET will trigger if you have it attached.
    • This command is only valid if you use digital pin 9 and 10 for the series FET switch.
    • Enter 1003,UVP,OVP
    • 1003,3.2,4.2
  • 1004
    • Used to set all balancing values to low so that no cell is draining current. This is usually used for testing purposes.
    • Enter 1004,1 for setting all values to low
    • Enter 1004,0 to go back to normal balancing operation
  • 1005
    • This is used to erase all eeprom values
  • 1006
    • If UVP or OVP is triggered then you will need to reset this fault condition
    • Enter 1006,0
    • NOTE: If values are above or below the UVP and OVP values then resetting the 1006 command will not allow current to pass


I’m very happy on how this project has progressed but its far from perfect and I will continue to optimize both the software and hardware to make it easier to integrate into a battery.

Future updates in my roadmap:

  1. Optimize the Power MOSFET Switch circuit to handle higher currents
  2. Possibly combine the mosfet control to one pin as using two might not be needed. UVP and OVP conditions open circuit the mosfet bidirectional switch
  3. Add current sense capability for overcurrent protection.
  4. My next big protection is creating a 7-8S BMS as well as a separate UVP, OVP, and Over-current protection board that will operate independently from the 8S BMS board but will still communicate via I2C to pass current data to the BMS board

If you have any questions or any suggestions or improvements please feel free to ask or comment.

Thank you and hope you enjoyed this project.

OCP and UVP Protection

I strive to provide my content for free without using ads as I personally do not like ads in most website. If you can afford to donate anything via Paypal i would great appreciate it. Thanks:


This project was created in order to provide protection for my battery projects. I didn’t really trust the cheap low cost protection boards you find online so I decided to try and create my own.

The protection board I’ve designed has bidirectional current detection (can detect charging and discharging currents) which in turn means you can set independent OCP( over-current protection) for discharging current and charging current.

One thing to note before hand is that the current maximum current that can flow is about 5A due to lack of heatsink on the powerFETS. Adding heatsink is necessary to allow more current

Main features:

  1. Over current protection with output latch open when tripped
  2. Under voltage protection for the whole battery pack
  3. Theoretically 45-50A pass-through current ( will require heatsink)

In the next sections I will discuss in more detail this project.


Here are some more technical details of this project

Minimum input voltage (V)10
Maximum input voltage (V)34
Maximum current (A)50A*
Quiescent current (mA)**
Undervoltage protectionYes (adjustable)
Overcurrent protection Yes (adjustable)
Short circuit protectionYes***
Overvoltage protectionNo

*Maximum current passing through this board will depend on sizing and adding a heatsink to the power mosfets

**Quiescent current has not been measured yet but it is below 1mA during normal operation with a 4S battery. This will change slightly when using more cells in series

***Theoretical the supervisor IC will latch when the overcurrent protection section detects its threshold. This will need testing to confirm


Here in this section I will discuss the project in more detail by talking about the different sections used to create this whole circuit.

Lets first discuss the over-current/short-circuit protection sections of the circuit:

Bidirectional Current sensor

Here we have the heart of the over-current detection. We have the MAX4081FASA which is a bidirectional current sense IC that can handle a max VCC of 76V which is more than enough for our applications. The FASA has an internal gain of 5V/V coupled with a 10 milliohm sense resistor value ( I have two 20 milliohm in parallel for size reduction) this will allow the sense current of up to 50A.

REF1A is tied to 5V reference IC and REF1B to ground. With this we have a bias point of 2.5V meaning that when no current is passing the voltage on the OUTPUT pin 5 is 2.5V. When current is discharging into a load the output will rise up to 5V being the 50A max. When current is flowing the other direction to charge the batteries then it will go below 2.5V to 0V being 50A max.

In order to both detect charging and discharging current since I will be using this for a battery pack, I had to create a circuit that detects both an upper limit and lower limit. Hence I went with a window comparator to detect charge and discharge values.

Window comparator with latching supervisor IC

Here is where you can adjust your own over-current detection points for charging and discharging and they can be set independently from each other. The idea here is when the output from the max IC is within the window of the voltage values you set from the voltage divider values on pin 3 and pin 6, the output of the opamp will be high. As soon as the output value goes above or below those set points, it will pull the output of the op-amp low and trigger the supervisor IC.

The supervisor IC is very important because this is what controls the power mosfets allowing current to pass through. This one is different from normal supervisor ICs because when this triggers, the output is latched low and this pulls the power mosfets high causing them to stop current flow. The only way to clear the fault condition is to pull the clear pin 1 to 5V. What I have done is add the option for a push button on the board directly or pads to run the clear pin trigger somewhere else.

Here is a simple equation to calculate the discharge current detection resistor values on PIN3 of the op amp:

(0.05 V/A * X) + 2.5V = VdetectHigher ; X = current trigger desired

For charging current detection here is the equation for the lower voltage detection of the op amp PIN 6

(0.05 V/A * X) – 2.5V = Vdetectlower; X = current trigger desired

Here is the power mosfets controlled by the supervisor IC. When no fault condition is triggered then the supervisor IC pulls Q4 to high causing the 10k resistor to be tied to ground. This then creates a voltage difference between the gate and source for the powerFETS allowing current to flow. When an overcurrent fault condition is triggered the supervisor IC is then pulled low and Q4 stops conducting which in turns unties the 10k resistor from ground and makes the gate and source the same potential.

Now the under-voltage protection scheme works by using a different supervisor IC measuring the input voltage. When the voltage falls below the threshold then the output pulls low and stops current flow in the powerFET

Here you see this circuit in action. A voltage divider is used to bring down the input voltage of the pack to a range the supervisor can handle. I have defined some voltage divider resistor values for different size battery packs in which it will trigger when the threshold is hit. I only used one MOSFET for this because this will stop discharging current but will allow charging current to flow via the body diode until the input voltage is above the threshold and then turn on the mosfet.

Here you have to take some precautions not to kill the mosfet when an event triggers an under-voltage event and you try to charge the pack.

When an event happens, start charging the pack with 0.5A-1A until there is about 1V above the under-voltage value. Then maximum current charging can be applied. This part of the circuit will need more tweaking in the future


Hopefully this project will help someone out. I created this to help protect my batteries and to allow the flexibility to adjust as much values as I can and have as much control of my protection circuitry as possible. Of course this is not perfect and I am still working on improvements.


  1. Figure out a way to protect Under-voltage powerFET from getting damaged when charging through the body diode initially until the supervisor IC allows current to flow through the FET and not the body diode
  2. Use DPAK powerFETS instead of IPAK due to cost. Size increase is negligible or non-existent
  3. Create a heatsink to provide maximum current discharge through mosfets. Currently the maximum current discharge without heatsink is roughly 5A without getting too hot. Will do testing after its completed.
  4. Perform thorough testing and post results.


Download Here:

Arduino 4S BMS: UPDATE (Version 5)


This is an updated post regarding my 4S arduino based lithium ion balance management system (BMS).

If you click here, you will see the original post with details on the project.


  1. Replaced external voltage reference to a more stable one. Upgraded to a REF3012AIDBZT from Texas instrument.  This does not require an output capacitor for stability.
  2. Modified Buck converter RT6208GE.  Grounded pin 4 to set the max peak current to 50mA which is an average current of 25mA.  This was done to eliminate audible oscillation from the buck converter
  3. Increased copper traces for ADC pins A0-A3
  4. Better layout to increase continuous copper plane on internal layer

Minicore bootloader

updated 01/13/2019: For the new arduino version, load bootloader with 8MHz clock. I did more testing and this was more stable and accurate on the adc then the 1MHz clock setting.

I will be doing testing on these new versions and see how they perform.  I will post results as soon as I can.  Also below I will attach all files needed (schematics, gerber, bom) if you wish to make your own. 

Arduino Code Download

updated 01/13/2019: Download arduino code version A15.

Schematic and Gerber Download