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!