Arduino Lithium-ion Battery Charger

In this post we are going to build a smart 3.7V Li-ion / Li-Po battery charger using Arduino, which is equipped with tons of features with minimal hardware setup. We will learn how to correctly charge a lithium-ion cell using CC / CV methods in easy words and in the end of this post you will be able to make a very reliable, feature rich yet simple smart li-ion battery charger.

We will see:

  • Main features of this Li-ion / Li-Po battery charger circuit.
  • How to charge a 3.7V Li-ion / Li-Po cell correctly.
  • Circuit diagram of Li-ion / Li-Po battery charger.
  • Circuit description.
  • Program code for Li-ion / Li-Po battery charger.
  • How to operate the proposed battery charger.

Features of the proposed Li-ion / Li-Po battery charger:

Li-ion Battery charger using Arduino
Li-ion Battery charger using Arduino
  1. Full battery detection and auto cut-off.
  2. Auto timer cut-off.
  3. Overcharging current cut-off.
  4. Auto current sensor calibration.
  5. Real time current consumption on display.
  6. CC / CV modes indication on display.
  7.  Charging current recommendation.
  8. Current sensor – Reverse polarity detection and cut-off.
  9. Battery reverse polarity protection.
  10. Charging range from 1000mAh to 5000mAh.

We will explore each and every feature mentioned here in later part of the post. Now let’s learn the fundamentals of Li-ion / Li-Po charging method. We concluded the following explanations from Battery university and designer’s guide to Li-ion battery charging from Digi-Key Electronics.

How to charge 3.7V Li-ion /Li-Po cell properly? 

It is very crucial to know lithium-ion cell / battery’s charging parameters like voltage, current and cut-off current before we start to build the charger so that charging process can be done correctly. As you might know Li-ion and Li-Po batteries are known for its violet character (if mishandled), so it is essential to know the correct charging procedure.

What is the nominal charging voltage for 3.7V Li-ion / Li-Po Cell?

Short answer: 4.20V

For a 3.7V Li-ion / Li-Po battery the charging voltage is 4.20V (+/- 50mV) meaning we should ideally apply 4.20V to the battery, but may have deviation within the range from 4.15V to 4.25V, but not more than 4.25V strictly.

What is the charging current for a typical Li-ion / Li-Po cell?

Short answer: 0.5C or 0.5 x Ah (Universally) 

The charging current is ultimately decided by the battery’s capacity Ah (C -rating). The charging current can be between 0.5C to 1C and this also depends on your battery’s datasheet / manufacturer. Just remember “C” rating = Ah of the battery.

  • For example a 2000mAh or 2Ah battery’s “C” rating is 2 and we can charge the battery between 0.5 x 2 (Ah) = 1 A (to) 1 x 2 (Ah) = 2A.
  • For 1000mAh or 1Ah capacity, we can charge battery from 0.5 x 1 (Ah) = 0.5A (to) 1 x 1 (Ah) = 1A.

Some manufactures say not to charge the battery above 0.7C and some say not above 0.8C and some say not above 1C. This could be because manufacturer to manufacturer do some chemistry tweaks to their batteries and due to this some batteries can accept more current than others. If you apply more current than manufacturer’s recommendation your battery will degrade soon.

Since our charger must suit batteries from all manufacturers we have to fix some charging rate where all the batteries can be charged and we found 0.5C to be optimal. Charging Li-ion / Li-Po battery below the recommended current will not harm the battery; instead it actually improves the battery life span.

When to Cut-off the battery from charger?

Full charge occurs when battery voltage reaches its threshold i.e. 4.20V and when the charging current reaches 0.1C that is 10% of the battery capacity.

For example: A 2000mAh battery can be disconnected when the voltage reaches as mentioned and also when the current reaches 200mA, similarly a 5000mAh battery can be disconnected when charging current reaches 500mA and so on.

Please note that voltage alone cannot tell the state of charge of a battery, the voltage can reach 4.20V when the battery is just charged around 70%, so it is the charging current that indicates the state of charge.   

When the battery is disconnected after a full charge, open circuit voltage settles around 4.0V after some time.

Can I float charge Li-ion / Li-Po batteries?

BIG NO!

Li-ion / Li-Po battery chemistry cannot accept float charging – that is applying current equal to self-discharge rate of the battery to keep it at full voltage like lead-acid type. The Li-ion battery has to be disconnected completely from the charger.

Topping charge:

However topping charge can be done. Topping charge is a charging process done when the battery falls around 4.05V or below after a full charge and the battery is still connected to charger, the charger kicks in to fill the battery till 4.20V and the cycle repeats. But it is nowhere applying continuous current to the battery.

What are “Pre-conditioning” and “CC / CV” charge stages?

Pre-conditioning charge: If the battery is over-discharged to like below 3V, charging the battery at 0.5C may cause overheating which could lead to permanent damage. To prevent such thermal dissipation, the battery is charged with current at 0.1C or 10% of the capacity till the battery can accept full recommended charging current.   

CC / CV charging stage:

Li-ion / Li-Po batteries are charged with strict constant current (CC) and constant voltage (CV) parameters. Any deviation will lead to premature expiration of the battery. 

What is CC Mode?

When a discharged battery is connected to a charger the battery will pull maximum current limited by the charger. During this charging stage if we measure the current it will stay constant with time but the voltage will be equal to discharged battery voltage and slowly rising. Here current is constant but voltage is varying / increasing. 

What is CV Mode?

During CC mode the battery terminal voltage was rising slowly and the current was constant, but once the battery reaches 4.20V current starts to drop and voltage will stay constant till the end of full charge. Here the voltage is constant but current is varying / decreasing.     

Note: The current limit is the tip over point between CC and CV mode of a charger. If the battery consumes less than the current limit, the charger is said to be in CV mode and if the battery is consumes current equal to the current limit, the charger is said to be in CC mode. 

Li-ion / Li-Po battery charger circuit:

Li-ion Battery charger circuit
Li-ion Battery charger circuit

Circuit description:

The circuit relatively simple for a Li-ion / Li-Po battery charger and even a beginner in Arduino can accomplish with ease.

CC / CV buck converter:

CC / CV Buck Converter
CC / CV Buck Converter

The heart of this project is the above shown CC / CV power supply which is actually a buck converter. This buck converter like any other voltage buck converter but this has an additional feature which can control current. This module has two control potentiometers one for voltage and another for current. Once the current is limit is set to (say) 1A, the short circuit current will not exceed 1A.

You can find this “CC / CV buck converter” on any e-commerce sites and commonly available. The above shown exact model is not necessary.

How to set current limit:

To set current limit on this type of buck converter we need a multimeter.

  • Set the multimeter at 10A mode.
  • Power the buck converter with 9V supply.
  • Touch the multimeter leads at buck converter output’s which will short circuit. Note that the output is current limited so it won’t fry the buck converter.
  • Using a Phillips screw driver rotate the current potentiometer anti-clockwise to reduce the current and vice-versa to the desired current limit.
  • Set current to 0.5C of the battery capacity. (0.5 x Ah)

How to set voltage?

  • Set the multimeter to 20VDC range.
  • Connect the leads at the output and rotate the voltage potentiometer clockwise to increase voltage and vice-versa.
  • Set voltage to 4.20V precisely.

Now you buck converter module is ready for CC and CV operations.

ACS712 5A current sensor:

ACS712 current sensor 5A
ACS712 current sensor 5A

We are using ACS712 (ACS712-05B) 5A (AC/DC) current sensor module which will measure charging current in the circuit. This current sensor works on the principal of magnetic effect where a current carrying conductor will generate magnetic field around it and the magnetic field around the conductor is directly proportional to current flow.

The IC in the breakout board measures the magnetic field and gives out a proportion analogue voltage between 0 to 5V. When no input current is applied the output voltage will be at 2.5V, if the current is applied the voltage will go from 2.5 to 5V or 2.5V to 0V since it can measure AC and DC.

But the problem with this sensor is that the output is rich in noise and picks up stray magnetic field from its surrounding, so it practically never centres at 2.5V and also deviates from the actual current reading by a lot.

To overcome this issue we came up with an algorithm to calibrate this sensor aggressively every 10 minute during its operation.

To improve accuracy further more you can add 0.47uF capacitor of any type as shown (in parallel with the existing SMD component):

ACS712 current sensor 5A
ACS712 current sensor 5A

Also use twisted pair wires to reduce noise from measuring wires as shown:

ACS712 current sensor 5A
ACS712 current sensor 5A

Important note: ACS712 comes in 3 variants 5A, 20A and 30A and all look identical. Make sure you got 5A one and 20A and 30A will not work with this project.  

LCD Display:

LCD with I2C Adapter
LCD with I2C Adapter

We are using a ‘16×2’ LCD display to show battery charging status and several important information on charging. We are using I2C LCD adapter module to reduce wiring work with the project.

Relay:

We are using a 9V relay (12V relay also works fine) which is activated by a low power NPN transistor. A diode will arrest high voltage back EMF while switching the relay ON and OFF.

Please note that 5V relay (which is available with breakout board) is NOT used here. If we use 5V relay, we usually connect to Arduino’s 5V output pin and when the coil is energized the Arduino’s 5V output pin drops to 4.4V which mess with current sensor voltage reference and gives incorrect readings and cut-off point.

Fuse and diode:

The fuse and diode act as simple reverse polarity protection. When the battery is connected in correct polarity the diode is reverse biased and does not short circuit the battery to blow the fuse. If the polarity is reversed the diode conducts and makes a short circuit which will blow the fuse instantly and prevents any further damage to battery or to the circuit.

Push buttons:

In this circuit two push buttons are provided, one for entering the battery capacity that you are going to connect (INC – increment button) and one for starting the charging process (START button).  

Input power supply:

In our test setup we used 9V 600mA wall adapter for a 2000mAh battery which was charged with 1A current limit (at buck converter). If you want to charge a higher capacity battery like 3000mAh or above use 9V at 1 – 2A as input supply.

That concludes the hardware setup.

Library files to be downloaded before compiling the code:

ACS712 current sensor library: Click here

I2C LCD library: Click here

Program code for Li-ion / Li-Po battery charger:

// ----------© Electronics-Project-Hub----------- //
#include "ACS712.h"
#include <EEPROM.h>
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27, 16, 2);
#define sensorInput A0
ACS712 sensor(ACS712_05B, sensorInput);

//------ Time out Setting --------//
int h_lt = 4; // in hrs
int m_lt = 20; // in min
// -------------------------------//

const int relay = 5;
const int inc = 4;
const int ok = 3;
int address = 0;
int batt_cap;
int current_lt = 0;
float peak_I_lt = 0;
float cut_off = 0;
boolean set_batt = true;
boolean var = true;
int i = 0;
int hrs = 0;
int Min = 0;
int sec = 0;
float currentReading;
float CV_current = 0;
void setup()
{
  pinMode(relay, OUTPUT);
  digitalWrite(relay, LOW);
  pinMode(inc, INPUT_PULLUP);
  pinMode(ok, INPUT_PULLUP);
  lcd.init();
  lcd.backlight();
  EEPROM.get(address, batt_cap);
  if (batt_cap < 1000)
  {
    EEPROM.put(address, 1000);
  }
  lcd.clear();
  while (set_batt)
  {
    lcd.setCursor(0, 0);
    lcd.print("Enter capacity:");
    lcd.setCursor(0, 1);
    EEPROM.get(address, batt_cap);
    lcd.print(batt_cap);
    lcd.print(" mAh");
    if (digitalRead(inc) == LOW)
    {
      while (var)
      {
        if (digitalRead(ok) == LOW) var = false;
        if (digitalRead(inc) == LOW)
        {
          lcd.setCursor(0, 1);
          batt_cap = batt_cap + 100;
          if (batt_cap > 5000) batt_cap = 1000;
          lcd.print(batt_cap);
          lcd.print(" mAh");
          delay(250);
        }
      }
    }
    if (digitalRead(ok) == LOW)
    {
      EEPROM.put(address, batt_cap);
      lcd.clear();
      lcd.setCursor(0, 0);
      lcd.print("Your battery");
      lcd.setCursor(0, 1);
      lcd.print("is ");
      lcd.print(batt_cap);
      lcd.print(" mAh.");
      delay(2000);
      lcd.clear();
      lcd.setCursor(0, 0);
      lcd.print("Set current");
      lcd.setCursor(0, 1);
      lcd.print("limit = ");
      current_lt = batt_cap * 0.5;
      peak_I_lt = batt_cap * 0.7 * 0.001;
      cut_off = batt_cap * 0.1 * 0.001;
      lcd.print(current_lt);
      lcd.print(" mA");
      delay(3000);
      set_batt = false;
    }
  }
  current_calib();
  CCCV();
}

void loop()
{
  for (i = 0; i < 10; i++)
  {
    currentReading = sensor.getCurrentDC();
    delay(100);
  }
  timer();
  lcd.clear();
  lcd.setCursor(0, 0);
  if (currentReading <= CV_current)
  {
    lcd.print("MODE:CV");
  }
  if (currentReading > CV_current)
  {
    lcd.print("MODE:CC");
  }
  lcd.setCursor(0, 1);
  lcd.print("CURRENT: ");
  lcd.print(currentReading);
  lcd.print(" A");
  if (currentReading <= cut_off)
  {
    for (i = 0; i < 10; i++)
    {
      currentReading = sensor.getCurrentDC();
      delay(100);
    }
    if (currentReading <= cut_off)
    {
      digitalWrite(relay, LOW);
      lcd.clear();
      lcd.setCursor(0, 0);
      lcd.print("BATTERY FULLY");
      lcd.setCursor(0, 1);
      lcd.print("CHARGED.");
      while (true) {}
    }
  }
  currentReading = sensor.getCurrentDC();
  if (currentReading >= peak_I_lt)
  {
    digitalWrite(relay, LOW);
    current_calib();
    digitalWrite(relay, HIGH);
    delay(3000);
    currentReading = sensor.getCurrentDC();
    if (currentReading >= peak_I_lt)
    {
      while (true)
      {
        digitalWrite(relay, LOW);
        lcd.clear();
        lcd.setCursor(0, 0);
        lcd.print("Overcharging");
        lcd.setCursor(0, 1);
        lcd.print("current detected");
        delay(2000);
        lcd.clear();
        lcd.setCursor(0, 0);
        lcd.print("Charging halted.");
        lcd.setCursor(0, 1);
        lcd.print("Press reset.");
        delay(2000);
      }
    }
  }
}

void current_calib()
{
  lcd.clear();
  lcd.print("Auto Calibrating");
  lcd.setCursor(0, 1);
  lcd.print("Current Sensor.");
  sensor.calibrate();
  delay(1000);
  currentReading = sensor.getCurrentDC();
  if (currentReading >= 0.02 || currentReading <= -0.02 )
  {
    sensor.calibrate();
    delay(5000);
    currentReading = sensor.getCurrentDC();
    if (currentReading >= 0.02)
    {
      current_calib();
    }
  }
}

void timer()
{
  sec = sec + 1;
  if (sec == 60)
  {
    sec = 0;
    Min = Min + 1;
    re_calib();
  }
  if (Min == 60)
  {
    Min = 0;
    hrs = hrs + 1;
  }
  if (hrs == h_lt && Min == m_lt)
  {
    digitalWrite(relay, LOW);
    while (true)
    {
      lcd.clear();
      lcd.setCursor(0, 0);
      lcd.print("Time out !!!");
      lcd.setCursor(0, 1);
      lcd.print("Charge Completed");
      delay(2000);
      lcd.clear();
      lcd.setCursor(0, 0);
      lcd.print("  Press reset");
      lcd.setCursor(0, 1);
      lcd.print("****************");
      delay(2000);
    }
  }
}

void re_calib()
{
  if (Min == 10 || Min == 20 || Min == 30 || Min == 40 ||
      Min == 50 || Min == 60 && sec == 0)
  {
    digitalWrite(relay, LOW);
    current_calib();
    digitalWrite(relay, HIGH);
  }
}

void CCCV()
{
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("Analyzing CC/CV");
  lcd.setCursor(0, 1);
  lcd.print("Modes...");
  digitalWrite(relay, HIGH);
  for (i = 0; i < 20; i++)
  {
    currentReading = sensor.getCurrentDC();
    delay(100);
  }
  if (currentReading <= -0.1)
  {
    while (true)
    {
      digitalWrite(relay, LOW);
      lcd.clear();
      lcd.setCursor(0, 0);
      lcd.print("Reverse current");
      lcd.setCursor(0, 1);
      lcd.print("detected.");
      delay(2000);
      lcd.clear();
      lcd.setCursor(0, 0);
      lcd.print("Flip current");
      lcd.setCursor(0, 1);
      lcd.print("sensor polarity.");
      delay(2000);
    }
  }
  CV_current = currentReading * 0.8;
}  
// ----------© Electronics-Project-Hub----------- //

Timeout setting:

If the battery is taking too long to charge fully this is an indication of a malfunctioned battery and such batteries must not put to use in future. If you charge such battery there is risk of safety and the charging battery must be cut-off if it exceeds expected charging time.

Such function is built-in to this charger and you can set / change the timing in the code after which the charging will get terminated even if the battery is not fully charged.  

//------ Time out Setting --------//
int h_lt = 4; // in hrs
int m_lt = 20; // in min
// -------------------------------//

You can set hours and minutes. By default it is set at 4 hour and 20 minutes. A discharged battery (not over discharged) should fully charge around 3 to 3.5 hours at 0.5C charging current.

Prototype:

Li-ion / Li-Po battery charger circuit
Li-ion / Li-Po battery charger circuit
Li-ion / Li-Po battery charger circuit
Li-ion / Li-Po battery charger circuit
Li-ion / Li-Po battery charger circuit
Li-ion / Li-Po battery charger circuit

How to operate this charger and its functions:

With your completed hardware setup, power ON the charger and you will be asked to enter the battery capacity that you are going to connect.

  • Press INC button to increment the battery capacity on the screen. For every press, it will increase by 100mAh on display. The minimum battery capacity that can be charged is 1000mAh and maximum is 5000mAh with this charger.
  • If you reach 5000mAh any further increment will reset to 1000mAh.
  • Now press start button, the screen will display the required charging current that you should have set to the CC / CV buck converter beforehand (0.5C).
  • After this Arduino starts to calibrate the current sensor and compensates for the stray magnetic field. This can take 5 to 15 seconds.
  • The charger starts to analyze CC and CV modes. CV mode will be indicated if the charging current is reduced by 20% of the initial current.  
  • The actual charging process starts now.
  • When the charging current reaches 0.1C, charging is terminated and you may disconnect the battery from charger.
  • If you set charging current more than 0.7C on buck converter, the charger will detect as over-current and halt charging. You need to bring it below 0.7C (ideally 0.5C).
  • If the charging process did not complete with in the designated time period, timeout process will kick in and terminate charging. You can change timeout setting in the code if you want.

Note 1: If the display shows “Reverse current detected” just reverse / flip the wires that are connected to screw terminals on ACS712 sensor.

Note 2: The charger will auto calibrate the current sensor every 10 minute during which relay will switch multiple times, which is completely normal.

If you have any questions regarding this smart Li-ion / Li-Po battery charger project, feel free to ask us in the comments, you will get a guaranteed reply from us.