Li-ion Discharge Charge Circuit

Hello everyone, Im a computer science student who is fairly new to working with arduino and microcontrollers. Ive been tasked with designing a li-ion battery charging and discharging circuit. The circuit is required to discharge the li-ion battery to say a minimum voltage of 2.75 V and then charge it back to 3.75 V and this cycle repeats for as many times as specified in the input. Ive made a GUI using python in order to interact with the application as there is a requirement of graphing the characteristic curves for all the cycles of the battery.

Background : The hardware circuit Ive designed from looking into videos and forums is one consisting of a constant current load made using an Op-Amp (LM358) , 1ohm resistor , n-Mosfet (IRLZ44n) and a DAC(MCP4725) .The DAC sets the non-inverted input voltage for the discharge current required. Im using an arduino UNO. The basics as I understand it is that the op-amp here compares the non-inverted input (from the DAC) and the inverted input (across the resistor) and the output to the gate of the mosfet is such that it makes sure there is a constant current across the source. (Please correct me if im wrong here) . I measure the current across the 1ohm resistor and the voltage across the terminals of the battery. Im using a TP4056 to charge the li-ion battery and Im using the enable pin on the tp4056 to control when it charges the battery and when it's off so that battery discharges. Please let me know if I should include the schematic.

Code : I am only attaching the arduino code here as the python code is around 700 lines (due to the GUI) . Im using pyserial for communicating with arduino. I take inputs from the GUI for discharge current (can set between 10mA and 1000mA with steps of 10mA for current setup), min volt to discharge to , max volt to charge to and number of cycles. Then I read the voltage and current from the arduino and apply a median filter (tried using MAF, curve wasnt satisfactory) with a window size of 30 and graph using matplotlib.

#include <Wire.h>              //Module  to initiate I2C communications with required devices
#include <Adafruit_MCP4725.h>  //DAC module for communication with the DAC from arduino
Adafruit_MCP4725 dac;          //DAC object containing in-built functions , here the setVoltage function

#define voltmin 2.75  // Minimum voltage battery can discharge to
#define voltmax 3.75  // Maximum voltage battery can charge to
#define supplyVoltage 5.00
#define batteryVoltagePin A0
#define batteryCurrentPin A1
#define chargePin 11
#define enablePin 12

double battery01Capacity = 0;  // Battery capacity
float state = 0;             //To control discharge current values

double volt_min=0.0f;
double volt_max=0.0f;

//The below function is used to convert the values read by the arduino into voltages . 0V corresponds to 0 and 5V corresponds to 5V

double readVoltage(uint8_t voltagePin) {
  return (supplyVoltage * analogRead(voltagePin)) / 1024.0;  // This is used to find the voltage that the battery is at currently. We convert into a digital value by multiplying by 5 and diving by 1024 as 0-1024 is 0-5V in arduino
}

//The below function is used to monitor the voltage and capacity of the battery as it is being discharged

void logMilliampHoursForTheLastSecond(double *capacity, uint8_t state, uint8_t voltagePin) {
  double voltage = readVoltage(voltagePin);         // Voltage of the battery is monitored here.
  double current = readVoltage(batteryCurrentPin);  // Value of current at which the battery is being discharged at
  *capacity += current / 3600;                      // 3600 as we are converting As to Ahr , if we change the delay we will have to change this value
  Serial.print(*capacity,2);
  Serial.print(" ");
  Serial.print(voltage,7);
  Serial.print(" ");
  Serial.println(current,7);
}

//The below function is used to prevent the battery from over discharging i.e discharging below 2.7V

void protectFromOverDischarge(uint8_t voltagePin) {
  double voltage = readVoltage(voltagePin);
  if (voltage < voltmin)  //This value is the minimum value of voltage
  {
    dac.setVoltage(0, false);
    digitalWrite(enablePin, HIGH);
  }
}

void protectFromOverCharge(uint8_t voltagePin) {
  double voltage = readVoltage(voltagePin);
  if (voltage > voltmax) {
    digitalWrite(enablePin, LOW);
    decideVoltage(state);
  }
}

//The below function is used to decide the voltage value of the DAC to control the current at which the battery discharges based on the state/value of current required.

void decideVoltage(uint8_t state) {
  if (state >= 0 && state <= 10)
    dac.setVoltage((((state + 1.0) / 100) * 4095) / 5, false);
  else if (state >= 11 && state <= 51)
    dac.setVoltage((((state + 1.0) / 100) * 4095) / 5, false);
  else if (state >= 52 && state <= 61)  
    dac.setVoltage((((state + 1.0) / 100) * 4095) / 5, false);
  else if (state >= 62 && state <= 71)
    dac.setVoltage((((state + 1.0) / 100) * 4095) / 5, false);
  else if (state >= 72 && state <= 81)
    dac.setVoltage((((state + 1.0) / 100) * 4095) / 5, false);
  else if (state >= 82 && state <= 102)
    dac.setVoltage((((state + 1.0) / 100) * 4095) / 5, false);
}

//The below function is run once and is used to initialize the I2C communication devices, here DAC

void setup() {
  Serial.begin(9600);
  dac.begin(0x62);
  pinMode(chargePin, INPUT);
  pinMode(enablePin, OUTPUT);
  delay(2000);
}

//The below function loops performing the necessary operations required for charging/discharging the battery

/*

We can set the voltage to the DAC which is used to control the current at which the battery is charged with the help of the OP-AMP which works
as a voltage comparator and maintains the constant voltage between input and output. This is then fed to the n-MOSFET which is a logic level MOSFET.
The MOSFET is used to obtain a constant current for discharging the Battery

Different current values possible are : 10mA - 1000mA in steps of 10 i.e 10,20,30,40 etc

*/

void loop() {
  double control = readVoltage(batteryVoltagePin); 
  if (control > voltmin)  
  {
    digitalWrite(enablePin,LOW);
    if (Serial.available()) {
    String inputString = Serial.readStringUntil('\n');
    
    // Split the string using ',' as a delimiter
    int index = 0;
    char *ptr = strtok(const_cast<char*>(inputString.c_str()), ",");
    float values[3];

    while (ptr != NULL && index < 3) {
      values[index] = atof(ptr);
      ptr = strtok(NULL, ",");
      index++;
    }

    // Store values in specific variables
    if (index >= 3) {
      state = values[0];
      volt_min = values[1];
      volt_max = values[2];
    }
  }
    decideVoltage(state);
    if (state >= 0 && state < 101) {
      Serial.print("DISCHARGING ");
      protectFromOverDischarge(batteryVoltagePin);
      logMilliampHoursForTheLastSecond(&battery01Capacity, state, batteryVoltagePin);
      delay(1000);  //This is the time delay between successive intervals for noting/plotting the values of voltage and current
    }
  }else
  {
    digitalWrite(enablePin, HIGH);
    dac.setVoltage(0, false);
    protectFromOverCharge(batteryVoltagePin);
    double volt = readVoltage(batteryVoltagePin);
    Serial.print("CHARGING ");
    Serial.print("Voltage :");
    Serial.print(volt);
    Serial.println();
    delay(1000);
  }

}

My problem : While measuring the voltage and currents across the terminals of the battery using the arduino Im getting a lot of fluctuation . Also the measured potential differs from the no current potential (im assuming due to internal resistance of the components) but I have no way to correct it as Ive heard the internal resistance of the battery increases with use. Here is an image of the graph for discharge at a discharge current of 0.5A Ive gotten for reference :

The shape of the curve seems right till the drop as Ive seen from different curves but the next part does not seem to make sense. I have not looked into the curve of charging yet

I would like help on how to fix the fluctuation and the characteristic curve. I would appreciate any help with the hardware as well or general pointers

Its my first post on the forum so sorry if Ive violated any guidelines

TIA!

Of course.

It's not too late to read the sticky. The second from top, actually.

Here is the schematic :

Thank you for linking the sticky, I will go through it

Unless this is a course assignment, stick with something designed by a professional. The TP4056 does not handle load sharing, overdischarge protection, or work properly when a load is attached to the battery.

It's a project under a teacher but it's not graded. Is there any other way of designing the circuit? All the sources I found were using the tp4056 module and I'm afraid I don't have the electronics background to design one myself from scratch.

It is OK to use the TP4056 to charge the battery in isolation, but it makes no sense to use it in conjunction with any other active circuitry, because you have no control over it.

A workable approach is to isolate the charge and discharge circuits completely, with a double pole, double throw switch, if you like. The task then is to get the discharge function working properly.

The LM358 is a poor choice for this application, because the output is not rail-to-rail (Vout is limited to Vcc-1.5V). The op amp supply voltage should be considerably greater than 5V.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.