3S Lipo battery monitor - unstable values

3S Lipo battery monitor

Hi,

I’m tring to setup a lipo Battery monitor using an Arduino Nano and an I2C 128x64 Display OLED.
I have surf and found many example to do it, and I have implement this code :

#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_RESET 4
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

const int analogInput1 = A0;
const int analogInput2 = A1;
const int analogInput3 = A2;

const float R1 = 13.2;  // (100K + 8.2K) / 8.2K
const float R2 = 13.2;  // (100K + 8.2K) / 8.2K
const float R3 = 13.2;  // (100K + 8.2K) / 8.2K

float readCellVoltage(int inputPin) {

  float vCell = analogRead(inputPin);
  if (vCell < 0.09) vCell = 0.0;
  float returnValue = (vCell * 1.1) / 1024;
  return returnValue;

}

void setup() {

  analogReference(INTERNAL);
  display.begin(SSD1306_SWITCHCAPVCC, 0x3C);

  display.display();
  display.clearDisplay();
  display.setTextColor(WHITE);
  display.setTextSize(1);

}

void displayLipoMeter() {

  float B1Voltage = (readCellVoltage(analogInput1) * R1);
  float B2Voltage = (readCellVoltage(analogInput2) * R2) - B1Voltage;
  float B3Voltage = (readCellVoltage(analogInput3) * R3) - ( B1Voltage + B2Voltage );

  display.clearDisplay();
  display.setTextSize(2);
  display.setCursor(0, 0);
  display.println("LiPo Meter");
  display.setTextSize(1);
  display.setCursor(0, 12);
  display.println("---------------------");
  display.setCursor(0, 20);
  display.println("- V Cells -- V LiPo -");

  display.setCursor(0, 30);
  display.println("Cel1=     |");
  display.setCursor(31, 30);
  display.println(B1Voltage);

  display.setCursor(0, 40);
  display.println("Cel2=     |");
  display.setCursor(31, 40);
  display.println(B2Voltage);

  display.setCursor(0, 50);
  display.println("Cel3=     |");
  display.setCursor(31, 50);
  display.println(B3Voltage);

  display.setCursor(65, 36);
  display.setTextSize(2);
  display.println(B1Voltage + B2Voltage + B3Voltage);

  display.display();
  delay(5000);
}

void loop() {

  displayLipoMeter();
  
}

The code work but I have a problem that the read value is not stable, I mean the values have a window from 4.14V to 4.25V but for all the cells the voltage measured with a multi meter is 4.18V-4.19V.

I 'm became crazy with un-useful small code refinements or voltage divider value correction >:( , but nothing change the results. The values are still be from 4.14V to 4.25V :confused: .
Any suggestions?

Li-Po Voltage Monitor.JPG

float readCellVoltage(int inputPin) {

  float vCell = analogRead(inputPin);
  if (vCell < 0.09) vCell = 0.0;
  float returnValue = (vCell * 1.1) / 1024;
  return returnValue;

}

Given that analogRead returns an integer, that first if statement is pretty useless.

void displayLipoMeter() {

  float B1Voltage = (readCellVoltage(analogInput1) * R1);
  float B2Voltage = (readCellVoltage(analogInput2) * R2) - B1Voltage;
  float B3Voltage = (readCellVoltage(analogInput3) * R3) - ( B1Voltage + B2Voltage );

Sometimes when you’re changing pins between reads you get slightly unstable readings. Some folks will read the pin twice and only keep the second reading if they just changed pins. That might be worth trying.

While troubleshooting this it might be helpful to print out the analog readings as well. They’ll be numbers between 0 and 1023. Sometimes it’s easy to lose sight of things like precision and resolution when we’re doing calculations. If the analog reading is only changing by 1 or 2 then you may just be at the limit of the ADC. If it is changing by more than that then it may be worth looking further.

Hi Delta,
In the first piece of code there was an error, now I think is ok:

float readCellVoltage(int inputPin) {

  int vCell = analogRead(inputPin);
  float returnValue = (vCell * 1.1) / 1024;
  if (returnValue < 0.09) returnValue = 0.0;
  return returnValue;

}

For the second part I have update it to see also the analog read:

/*
  float B1Voltage = (readCellVoltage(analogInput1) * R1);
  float B2Voltage = (readCellVoltage(analogInput2) * R2) - B1Voltage;
  float B3Voltage = (readCellVoltage(analogInput3) * R3) - ( B1Voltage + B2Voltage );
*/
  int vCell1 = analogRead(analogInput1);
  int vCell2 = analogRead(analogInput2);
  int vCell3 = analogRead(analogInput3);

  float B1Voltage = (((vCell1 * 1.1) / 1024) * R1);
  float B2Voltage = (((vCell2 * 1.1) / 1024) * R2) - B1Voltage;
  float B3Voltage = (((vCell3 * 1.1) / 1024) * R3) - ( B1Voltage + B2Voltage );

The analog values change of max 5 points.

Delta_G:
Sometimes when you're changing pins between reads you get slightly unstable readings. Some folks will read the pin twice and only keep the second reading if they just changed pins. That might be worth trying.

Not worth trying as the impedance of the divider is less than 10k. The issue is for high impedance sources the
sample capacitor in the ADC doesn't have enough chance to charge up - for source impedances of 10k or less this is never an issue. The impedance of a 8k2/100k divider is 7k6 as the resistors are effectively in parallel for this use (battery has a low impedance).

My question is what is Vcc? If its 5.0V, then a 8k2/100k divider won't work. And for a 3S LiPo pack that's certainly not going to work.

A 100k/8k2 divider I could believe, especially given the INTERNAL ADC reference setting. Perhaps the circuit diagram should be corrected, and extended to include all the cells...

MarkT:
A 100k/8k2 divider I could believe, especially given the INTERNAL ADC reference setting. Perhaps the circuit diagram should be corrected, and extended to include all the cells...

That greatly ups the impedance too and brings the double read back into play does it not?

I only bring that back up because I am fighting the same issue right now on my robot. I had been using only one analog pin with a 1.1 internal reference and almost the same divider (100K/5K6) to read a 5S battery and I always noticed that when I first fired the bot up it took about three or four seconds for the voltage reading to rise all the way up to the real voltage but once there it would read fine ever after.

Now I'm adding another component that will use an analog pin so this slow reading will become a problem and I am redesigning with a different divider to lower the impedance. If I had more room on my board I might even consider an OpAmp buffer to help.

Hi,

I have attached the schema of the wring of the full LiPo.
I make a video of my analog read, but is not possible to upload it.

The values read are every 2sec volt-analog:

  • Cell1=4.18-295 | 4.24-299 | 4.24-299 | 4.18-295 | 4.20-296 | 4.18-295 | 4.25-300 | 4.18-295
  • Cell2=4.28-597 | 4.15-592 | 4.14-591 | 4.20-591 | 4.18-591 | 4.20-591 | 4.20-596 | 4.20-591
  • Cell3=4.13-888 | 4.14-884 | 4.15-884 | 4.15-884 | 4.17-885 | 4.27-889 | 4.08-884 | 4.15-884

I have read somewhere that the resistor from the cell must be around 100K.

I have try with other dividers like 10K/100K or 15K/100K or 20K/100K, but nothing change.

Do you suggest different values ?

agomax:
I have read somewhere that the resistor from the cell must be around 100K.

In that case I'm going back to the read it twice idea. Just try it, what can it hurt. It's one added line and try it. If it doesn't work take it back out.

Post a picture showing the grounding scheme.
To measure that battery correctly, Arduino ground and battery negative must be short and not carry other significant currents.
Leo…

Hi
OPs circuit;

Thanks.. Tom... :slight_smile:

Couple of things ...
Write your own code !
I would use the internal voltage reference of the Arduino, without that you are comparing the battery voltage to the arduino supply , which might be wandering about.
I would do all the maths as integers , there is no extra accuracy gained by using floats -Work all your values in mV ( look at map) and add the decimal point only when you display it .

You can buy quite cheap Lipo meters with balancing , discharge to storage voltage etc
Don’t forget if 0-5v gives 0-1023 ; then you can’t get more accurate than 5mV. +/- 5mV accounts for some of the error you are seeing, eg: 4.20v , 4.19v. If you are “ “mapping”. The analog reading to a voltage , then you will get some additional rounding errors .

Hi,
Have you tried this edit;

/*
  float B1Voltage = (readCellVoltage(analogInput1) * R1);
  float B2Voltage = (readCellVoltage(analogInput2) * R2) - B1Voltage;
  float B3Voltage = (readCellVoltage(analogInput3) * R3) - ( B1Voltage + B2Voltage );
*/
  float vCell1 = analogRead(analogInput1);
  float vCell2 = analogRead(analogInput2);
  float vCell3 = analogRead(analogInput3);


  float B1Voltage = (((vCell1 * 1.1) / 1023.0) * R1);
  float B2Voltage = (((vCell2 * 1.1) / 1023.0) * R2) - B1Voltage;
  float B3Voltage = (((vCell3 * 1.1) / 1023.0) * R3) - ( B1Voltage + B2Voltage );

Keeping everything floating.
1023.0

Tom... :slight_smile:

The following link is a good read on measuring LiPo cells in series when we want to read the individual cell voltages.

The author points out some of the pitfalls of using voltage divider circuits.

Ron

Hi Ron,

your link is very interesting, and I will use it for my project. I understand read errors but what I don't understand, why I have this variability on the reads with a difference of 5 points:

  • Cell1=4.18-295 | 4.24-299 | 4.24-299 | 4.18-295 | 4.20-296 | 4.18-295 | 4.25-300 | 4.18-295
  • Cell2=4.28-597 | 4.15-592 | 4.14-591 | 4.20-591 | 4.18-591 | 4.20-591 | 4.20-596 | 4.20-591
  • Cell3=4.13-888 | 4.14-884 | 4.15-884 | 4.15-884 | 4.17-885 | 4.27-889 | 4.08-884 | 4.15-884

For the other contributors, I have try to make multiple reads and do the average of it, trash the first 10 reads, I have try to use the INTERNAL Aref and the EXTERNAL using the 3.3V pin, but at te end nothing solve the variability of the read values.

M.

Not sure why you are seeing that. The Arduino with a 5 volt reference and 2^10 A/D will give a 4.88 mV (figure 5 mV) resolution so that is as good as it gets and a little noise is normal Using an Arduino Uno to measure a DC voltage I normally get pretty decent stability. Glad you found the link interesting.

Ron

Hi,
Did you try my edited bit of code?
post #10.

Tom... :slight_smile: