Internal resistance estimation in a LiFePO4 battery

Hello,

I am trying to estimate the internal resistance of a LiFePO4 Li-ion battery, using Arduino Uno. The schematic I use for the estimation, is as shown below:

Schematic: Schematic (1).png - Google Drive

The schematic is quite simple. A single LiFePo4 (3.2V, 1800mAh) battery cell is connected through the 1 Ohm shunt resistance to the 5V, 1-channel relay. The "NO" terminal of the relay is connected to the 4 ohm load resistor and inturn to the ground. The battery cell is not connected to the load by default. I also use a 5V, 8-Channel, 24-bit ADS1256 Data Acquisition Board to measure the required voltages.

Logic:
I have used the pulse-discharge technique for the internal resistance estimation and it goes as follows. In the code, the logic to estimate the internal resistance is implemented towards the end (please ignore starting part of the code, as that is just to configure the ADC in different modes). The ADC is configured in the required part of the code to measure single-ended inputs. Before the relay is closed, the open circuit voltage of the battery is measured and displayed (voltageValue1). Once the relay is closed and the load is connected to the battery, voltage of the battery is measured and displayed again (voltageValue2). It goes without saying that voltageValue1 > voltageValue2. I also measure and display the voltage across the "battery+shunt" as voltageValue3. As this is a 1 ohm shunt resistance, the discharge current can be estimated using the formula (voltageValue2-voltageValue3)/1ohm. After this, the internal resistance at that particular point in time is estimated using the formula (voltageValue1 - voltageValue2)/dsichargeCurrent.

The issue I'm facing here is:
I use a DMM in parallel with the ext ADC, to verify the accuracy of the readings/measurements. Before closing the relay, the "voltageValue1" readings of both DMM and the ADC show the same values. But once the relay is closed, both DMM and ADC show different "voltageValue2" readings. There is some leakage current with the ADC, when the load is connected to the battery through the relay. The readings of voltageValue3 also differ significantly. The following sample output from the serial monitor shows the difference in the readings:

ADC readings:
Voltage across the battery without load: 3.3094V
Relay closed.
Voltage across the battery with load: 3.1793V
Voltage drop across the battery+shunt: 2.3104V
Discharge current: 0.8689A
Internal Resistance of the battery: 0.1498Ohms
Relay opened.

Multimeter readings:
Voltage drop across the battery without load: 3.318V
Relay closed.
Voltage drop across the battery with load: 3.270V
Voltage drop across the battery+shunt: 2.370V
Discharge current: 0.900A
Internal Resistance of the battery: 0.0533Ohms
Relay opened.

Please check the code below:

#include <Wire.h>
#include <ADS1256.h>

#define RELAY_PIN 7  // Relay signal pin connected to digital pin D7

ADS1256 Adc(2, 0, 5, 9, 2.500); //DRDY, RESET, SYNC(PDWN), CS, VREF

int singleEndedChannels[8] = {SING_0, SING_1, SING_2, SING_3, SING_4, SING_5, SING_6, SING_7}; //Array to store the single-ended channels
int differentialChannels[4] = {DIFF_0_1, DIFF_2_3, DIFF_4_5, DIFF_6_7}; //Array to store the differential channels
int inputChannel = 0; //Number used to pick the channel from the above two arrays
char inputMode = ' '; // single-ended or differential 
char relayStatus = ' '; //close or open

void setup() {
  // put your setup code here, to run once:
  Serial.begin(115200); //Baud Rate
  pinMode(RELAY_PIN, OUTPUT);

  while (!Serial)
  {
    Serial.println("ADC initialization failed! Try again!"); //Wait until the serial becomes available
  }

  Serial.println("Estimating the internal resistance of a LiFePO4 Li-ion battery");

  Adc.InitializeADC(); // Initialize ADC

  //Freeze the display for 2 sec
  delay(2000);
}

void loop() {
  // put your main code here, to run repeatedly:

  float voltageValue1, voltageValue2, voltageValue3, dischargeCurrent, internalResistance;
  long rawValue1, rawValue2, rawValue3 = 0; //24-bit raw value
  if (Serial.available() > 0)
  {
    char commandChar = Serial.read(); //we use characters for controlling the switch-case

    switch (commandChar) //based on the command character, we decide what to do
    {
      case 's': //SDATAC - Stop Reading Data Continously
        Adc.stopConversion();
        break;
      //--------------------------------------------------------------------------------------------------------
      case 'L': //Perform a self calibratin
        Adc.sendDirectCommand(SELFCAL);
        break;
      //--------------------------------------------------------------------------------------------------------
      case 'G': //Read a single input continuously
        while (Serial.read() != 's') //The conversion is stopped by a character received from the serial port
        {
          Serial.println(Adc.convertToVoltage(Adc.readSingleContinuous()), 6);
          //The conversion is printed in Volts with 6 decimal digits
          //Note: Certain serial terminals cannot keep up with high speed datastream!
        }
        Adc.stopConversion();
        break;
      //--------------------------------------------------------------------------------------------------------
      case 'C': //Cycle single ended inputs (A0+GND, A1+GND ... A7+GND)
        while (Serial.read() != 's')//The conversion is stopped by a character received from the serial port
        {
          for (int i = 0; i < 8; i++)
          {
            Serial.print(Adc.convertToVoltage(Adc.cycleSingle()), 4); //print the converted single-ended results with 4 digits
            Serial.print("\t");//tab separator to separate the 4 conversions shown in the same line
          }
          Serial.println();//Printing a linebreak - this will put the next 8 conversions in a new line
        }
        Adc.stopConversion();
        break;
      //--------------------------------------------------------------------------------------------------------
      case 'D': //Cycle differential inputs (A0+A1, A2+A3, A4+A5, A6+A7)
        while (Serial.read() != 's') //The conversion is stopped by a character received from the serial port
        {
          for (int j = 0; j < 4; j++)
          {
            Serial.print(Adc.convertToVoltage(Adc.cycleDifferential()), 4);//print the converted differential results with 4 digits
            Serial.print("\t"); //tab separator to separate the 4 conversions shown in the same line
          }
          Serial.println(" ");//Printing a linebreak - this will put the next 4 conversions in a new line
        }
        Adc.stopConversion();
        break;
      //--------------------------------------------------------------------------------------------------------
      case 'a': //Testing a single conversion - Only one single result is returned

          rawValue1 = Adc.readSingle(); 
          voltageValue1 = Adc.convertToVoltage(rawValue1);
          Serial.print("Voltage drop: ");
          Serial.print(voltageValue1, 4);
          Serial.println("V");
        break;
      //--------------------------------------------------------------------------------------------------------
      case 'M': //set MUX
        {
          while (!Serial.available());
          inputMode = Serial.read(); //Read the input mode

          if (inputMode == 's') //single-ended
          {
            while (!Serial.available());
            inputChannel = Serial.parseInt();
            Adc.setMUX(singleEndedChannels[inputChannel]);
            Serial.print("ADC is set as single-ended and the channel is: ");
            Serial.println(Adc.readRegister(MUX_REG));
            //Example: "Ms1" selects the SING_1 as input channel
          }

          if (inputMode == 'd') //differential
          {
            while (!Serial.available());
            inputChannel = Serial.parseInt();
            Adc.setMUX(differentialChannels[inputChannel]);
            Serial.print("ADC is set as differential and the channel is: ");
            Serial.println(Adc.readRegister(MUX_REG));
            //Example: "Md0" selects the DIFF_0_1 as input channel
          }
        }
        break;
      //--------------------------------------------------------------------------------------------------------
      case 'R': // Close or open the relay
      {
        while (!Serial.available());
        relayStatus = Serial.read(); // Read the relay modes
        
        if (relayStatus == 'c' || relayStatus == 'C') 
        {
          while (!Serial.available());
          Adc.setMUX(SING_1); //MUX set to measure 7th channel
          delay(100);
          rawValue1 = Adc.readSingle(); 
          voltageValue1 = Adc.convertToVoltage(rawValue1); 
          Serial.print("Voltage across the battery without load: ");
          Serial.print(voltageValue1, 4);
          Serial.println("V");
          delay(100);
          digitalWrite(RELAY_PIN, HIGH); // Close the relay
          Serial.println("Relay closed.");
          delay(8000); //allow some settling time
          rawValue2 = Adc.readSingle(); 
          voltageValue2 = Adc.convertToVoltage(rawValue2); 
          Serial.print("Voltage across the battery with load: ");
          Serial.print(voltageValue2, 4);
          Serial.println("V");
          delay(100);
          Adc.setMUX(SING_7);//MUX set to measure 1st channel
          delay(100);
          rawValue3 = Adc.readSingle(); 
          voltageValue3 = Adc.convertToVoltage(rawValue3);
          Serial.print("Voltage across battery+shunt: ");
          Serial.print(voltageValue3, 4);
          Serial.println("V");
          dischargeCurrent = abs(voltageValue2-voltageValue3);
          Serial.print("Discharge current: ");
          Serial.print(dischargeCurrent, 4);
          Serial.println("A");
          internalResistance = (abs(voltageValue1 - voltageValue2))/dischargeCurrent;
          Serial.print("Internal Resistance of the battery: ");
          Serial.print(internalResistance, 4);
          Serial.println("Ohms");
        }
      
        else if (relayStatus == 'o' || relayStatus == 'O') 
        {
          while (!Serial.available());
          digitalWrite(RELAY_PIN, LOW); // Open the relay
          Serial.println("Relay opened.");
          delay(1000);
        }
        
      }
      break;
      //--------------------------------------------------------------------------------------------------------
    }
  }
}

Library used for ext ADC: https://curiousscientist.tech/blog/ADS1256-custom-library
For building the circuit above, I have used a breadboard. I have checked for extra ground loops and there are none. The grounds of the battery, ADC and Arduino Uno are all shorted and the ADC is self-calibrated also. I'm unable to find out what is going wrong here and why are the DMM and ADC values different after closing the relay. Any assistance in this matter would be greatly appreciated and thank you in advance for all the suggestions and opinions!

Please share a schematic and a picture of your setup.
One picture can tell more than a thousand words...

I have shared the link to the schematic above. Please find it here: Schematic (1).png - Google Drive

Maybe you should take a lower shunt resistor value...
Now you try to estimate a very small resistor with a large one as refetence. Small errors will blow up in your calcs.
Ideally the shunt should be equal to the internal reference (from an accuracy point of view). But that may very well damage your battery.
But usually it is pretty OK to take 1.8A from a 1.8 Ah battery... some special designed batteries can do far more...

The cheapy songle relay may also contribute to the total resistance and may burn at 1.8A...

Consider this:
Let’s assume the DMM is correct.
DMMreading-ADCreading
3.270V - 3.1793V = 90.7mV
90.7mV/0.9A = 101mΩ

So if there is a resistance difference of ~100mΩ bewteen where the ADC is connected and where the DMM is connected you will see a voltage difference.

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