Simple Battery management system (BMS) C++ firmware developed in object-oriented way


Introduction

Battery cells represent a vital energy source, making efficient management essential. The Battery Management System (BMS) ensures safe and accurate battery management. Using various sensors, physical quantities are measured, providing necessary data to assess conditions crucial for successful system operation. During the charging and discharging processes, the BMS performs mutual balancing of the batteries to optimize energy consumption and minimize losses.

bms001

System Structure

In order to clarify the system structure hierarchy of the BMS, the following terms have been introduced:

  • Cell: The basic unit of a battery.
  • Module: Battery cells (connected in series and/or parallel) make one module.
  • Pack: Modules (connected in series and/or parallel) make one battery pack.

bms002

We used MH12210 battery cell. To keep things simple, the module level was skipped in this work. Therefore, there are only Cell and Pack classes, where a set of cells represents a battery pack. Here is what the Cell class looks like:

class Cell
{
private :
    float U, I;         // voltage and current
    float C, C_total;   // current and total capacity
    float E, E_total;   // current and total energy
    float E_acc = 0.0;  // accumulated (dis)charging energy over time
    float SOC, SOH;     // states

public :
    Cell( float T , float U , float I );
    ~Cell();

    friend class Pack;
    
    void calc_SOC();
    void calc_SOH();
    
    bool safe_operating( float T );

    //...
};

The concept of a friend class is used so that the Pack has access to the private attributes of the Cell class. SOC and SOH states, as well as safe and secure control, will be discussed later.

An object of the Pack class represents a battery made up of a certain number of cells. It has its own values, which are calculated in a certain way, depending on whether the cells are connected in series or parallel. Monitoring the safe operation of the battery pack is essential. Here is what the Pack class constructor looks like:

Pack :: Pack ( unsigned number_of_cells /* ... */ )
{
    bool flag;
    //...

    v = new std::vector<std::optional<Cell>>[ number_of_cells ];
    
    flag = init( number_of_cells );
    flag = flag && v->size() == number_of_cells;
    
    for ( BYTE i = 0; i < number_of_cells; i++ )
    {
        flag = flag && v->at( i ).has_value();
    };
    
    if( !flag ) // error
};

SOC

State of Charge (SOC) of a battery is a measure of the amount of energy remaining in the battery relative to its full capacity. It is typically expressed as a percentage, where 100% indicates a fully charged battery and 0% indicates a completely discharged battery. SOC is a critical parameter for understanding the current energy status of a battery and is essential for the effective management and operation of battery systems. $$SOC = \cfrac{Current Energy}{Total Energy} \cdot 100\%$$

The relationship between voltage and battery capacity over time is shown on the battery discharge curve. Each battery cell, depending on its chemical composition and other characteristics, has its own discharge curves. The discharge curves of the lithium-ion battery used in this work at different temperatures are shown below.

bms003

According to Joule’s law, electric power can be calculated using the following equation: $$P = U \cdot I$$

By integrating the electric power over time, the amount of electrical energy can be calculated: $$E = \displaystyle \int_{t_n}^{t_{n+1}} P(t) dt$$ $$E = \displaystyle \int_{t_n}^{t_{n+1}} U(t) I(t) dt$$

bms004

The area of each rectangle represents the electric power of the battery at a certain moment t. By summing these areas, an approximate value of the total area under the discharge curve is obtained, which represents the total energy of the battery.

SOH

State of Health (SOH) of a battery represents the ratio of the current total capacity of the battery to its initial total capacity (beginning of life - BOL). SOH is an important parameter for understanding the longevity and reliability of a battery, as it provides insight into how much of the battery’s original capacity and performance remain. $$SOH = \cfrac{Current Capacity}{Capacity BOL} \cdot 100\%$$

The relationship between capacity and the number of battery discharge cycles over time is shown on the battery health curve.

bms005

The range of the electric vehicle D, which the vehicle can cover from the current moment t until the battery charge reaches zero, can be calculated as follows: $$D(t) = SOC(t) \cdot SOH(t) \cdot D_{nom}$$

$$\text{where } D_{nom} \text{ is range when } SOC = SOH = 1$$

Safe Operating and Fault Management

The critical safe operation of the battery management system requires that the values of temperature T and voltage U over time do not exceed the defined limit range: $$T \in (-20.0, 60.0) [^{\circ}C]; \quad U \in (2.85, 4.20) [V]$$

It is also important to determine the limit values that will serve as a warning when the temperature T and voltage U approach critical levels. Safe operation of the system is possible when these values remain within the specified range: $$T \in (-15.0, 55.0) [^{\circ}C]; \quad U \in (3.00, 4.05) [V]$$

It is recommended to connect the battery to the charger if SOC falls below a certain value, or consider replacing the battery if SOH drops below a specified limit: $$SOC(t) > 0.20; \quad SOH(t) > 0.60$$

bms006

Below is a method to check the safe operating and fault management of the cell:

const float T_limits[4] = { -20.00 , 60.00 , -15.00 , 55.00 };
const float U_limits[4] = {   2.85 ,  4.20 ,   3.00 ,  4.05 };

BYTE Cell :: safe_operating( float T )
{
    bool flags[8];
    BYTE indicator = 0, n = 1;
    
    *( flags + 0 ) = SOC > 0.20;
    *( flags + 1 ) = SOH > 0.60;
    
    *( flags + 2 ) = T > T_limits[2];
    *( flags + 3 ) = U > U_limits[2];
    *( flags + 4 ) = T < T_limits[3];
    *( flags + 5 ) = U < U_limits[3];
    
    *( flags + 6 ) = ( T > T_limits[0] ) && ( T < T_limits[1] );
    *( flags + 7 ) = ( U > U_limits[0] ) && ( U < U_limits[1] );
    
    for( BYTE i = 0; i < 8; i++ )
    {
        if( *( flags + i ) ) indicator = indicator | n;

        n = n << 1;
    };
    
    return indicator;
};

If the operation of each cell is safe, then the operation of the battery as a whole is considered safe. If any cell’s operation is unsafe, it is checked whether the duration spent outside the safe operating range exceeds a predefined limit. If it does, the battery operation is halted.

bool Pack :: safe_operating( float t )
{
    unsigned short dim = v->size();
    BYTE csafe;
    bool flag;
    
    for ( unsigned short i = 0; i < dim; i++ )
    {
        csafe = ~( v->at( i )->safe_operating( T ) );
        psafe = psafe && !csafe;
    };
    
    if( ! psafe ) time_spent += t;
    flag = time_spent < limit;
    
    return flag;
};

Prototype

bms007

Sources