Voltage divider not being pulled to GND when left floating [Solved]

Hi all,

I have designed a custom board based on a 32u4 chip, designed to check voltages of a connected 6s lipo. I have used a voltage divider but when some of the pins are left "floating" I still get an analog read of around 700. From a technical POV, is this stray capacitance, too high value a voltage divider or something else?

At the moment I am reading in the ADC and just printing the raw value to serial monitor. Most of the code below isnt used but is there for reference.

// These constants won't change. They're used to give names to the pins used:

int analogPins[6]  = {A5, A4, A3, A2, A1, A0};

int RawAnalogReads[6] = {0, 0, 0, 0, 0, 0};        // value read from the pot
float ActualVoltages[6] = {0, 0, 0, 0, 0, 0};
float CellVoltages[6] = {0, 0, 0, 0, 0, 0};
int scaled = 0;

int valueread = 0;

int adc_value = 0;
float adc_voltage = 0.0;
float in_voltage = 0.0;
float ref_voltage = 5.0;
float R1 = 0.0;
float R2 = 0.0;

float readval(int var) {
  switch (var) {
    case 0:
      in_voltage = (analogRead(A5) * (5.0 / 1023)); // * (560 / 330 + 560);
      Serial.print("Cell 1 ");
      Serial.print(in_voltage);
      Serial.print(" Raw = ");
      Serial.println(adc_value);
      break;

    case 1:
      R1 = 560000.0;
      R2 = 330000.0;
      adc_value = analogRead(A4);
      adc_voltage  = (map(adc_value, 0, 1024, 0, 1024) * ref_voltage) / 1024.0;
      in_voltage = adc_voltage / (R2 / (R1 + R2));
      Serial.print("Cell 2 ");
      Serial.print(in_voltage);
      Serial.print(" Raw = ");
      Serial.println(adc_value);
      break;

    case 2:
      R1 = 560000.0;
      R2 = 330000.0;
      adc_value = map(analogRead(A3), 855, 1024, 0, 1024); 
      adc_voltage  = (adc_value * ref_voltage) / 1024.0;
      in_voltage = adc_voltage / (R2 / (R1 + R2));
      Serial.print("Cell 3 ");
      Serial.print(in_voltage);
      Serial.print(" Raw = ");
      Serial.println(adc_value);
      break;

    case 3:
      R1 = 560000.0;
      R2 = 150000.0;
      adc_value = map(analogRead(A2), 764, 1024, 0, 1024); // 806 resting
      adc_voltage  = (adc_value * ref_voltage) / 1024.0;
      in_voltage = adc_voltage / (R2 / (R1 + R2));
      Serial.print("Cell 4 ");
      Serial.print(in_voltage);
      Serial.print(" Raw = ");
      Serial.println(adc_value);
      break;

    case 4:
      R1 = 560000.0;
      R2 = 150000.0;
      adc_value = map(analogRead(A1), 763, 1024, 0, 1024); // 805 resting
      adc_voltage  = (adc_value * ref_voltage) / 1024.0;
      in_voltage = adc_voltage / (R2 / (R1 + R2));
      Serial.print("Cell 5 ");
      Serial.print(in_voltage);
      Serial.print(" Raw = ");
      Serial.println(adc_value);
      break;

    case 5:
      R1 = 560000.0;
      R2 = 100000.0;
      adc_value = map(analogRead(A0), 695, 1024, 0, 1024); // Resting 731
      adc_voltage  = (adc_value * ref_voltage) / 1024.0;
      in_voltage = adc_voltage / (R2 / (R1 + R2));
      Serial.print("Cell 6 ");
      Serial.print(in_voltage);
      Serial.print(" Raw = ");
      Serial.println(adc_value);
      break;

    default:
      // statements
      Serial.println("Nothing happened");
      break;
  }

  int cellVoltage = 0;
  return in_voltage;
  R1 = 0.0;
  R2 = 0.0;

}


void setup() {
  // initialize serial communications at 9600 bps:
  Serial.begin(115200);
}

void loop() {
  // read the analog in value:
  for (int i = 0; i <= 5; i++) {
    valueread = analogRead(analogPins[i]);
    Serial.print(valueread);
    Serial.print(" ");
    
    // ActualVoltages[i] = readval(i);    
  }
  
//  for (int i = 0; i <= 5; i++) {
//    CellVoltages[i] = ActualVoltages[i] - ActualVoltages[i-1];    
//    Serial.print(i);
//    Serial.print(" ");
//    Serial.println(CellVoltages[i]);
//  }
//  Serial.print(analogRead(A10));
//  Serial.println();

  Serial.println();
  delay(200);
}

And out in the serial monitor I get:

16:52:56.564 -> 0 0 910 806 805 730 
16:52:58.188 -> 0 0 910 806 806 730 
16:52:59.761 -> 0 0 910 806 805 730 
16:53:01.391 -> 0 0 910 806 805 730 
16:53:02.987 -> 0 0 910 806 805 730 
16:53:04.624 -> 0 0 910 806 805 730 
16:53:06.218 -> 0 0 910 806 805 730 
16:53:07.845 -> 0 0 910 806 805 730 
16:53:09.431 -> 0 0 910 806 805 730 
16:53:11.066 -> 0 0 910 806 805 730 
16:53:12.658 -> 0 0 910 806 805 731 

And this is my schematic:

And my Board layout:


100K should pull down the input, but it needs to be connected to ground.

The schematic does not reveal where either of the resistors R26/R27 are connected (other than to the analog input 13).

Source impedance should be less than 10K for the analog input. To reduce the divider impedance, add some capacitance from the tap to ground.

Even when directly attaching the pin to GND, the ADC still reads about 600. And shouldnt the resistors in the divider do this already?

"LipoCell*" is input voltage, with each cell being between 3.5 and 4.3v, wired in series. Given that the inclusion of a capacitor will need a new PCB anyway, is it better to reduce the voltage divider resistor values to bring it more into range?

As @jremington stated you need to verify the input divider resistors are there and connected to the correct circuit.

I would (without the Lipo's connected) short out the inputs that the Lipo's would normally be connected to. The Raw readings should all go to 0 (or at least close).

Do you have a multimeter? If not you need to get one. Even a low cost $20 unit is better than nothing. Keep in mind you won't need to make precision measurements.

If you have one. Without power to the board, measure resistance from the Lipo input to a ground you KNOW is the same as the Processor.

Remember, this is a custom board, at this point you cannot assume anything about the board. Mfg defects like a via not being internally connected are a distinct possibility.

How many layers does your board have? It looks like 2 but I can't be sure.

Sorry, I was looking at the left side of the schematic, not the box on the right side.

Analog input A5 should not be floating, given the 150K to ground. So, either than analog input is not actually connected to anything, or the resistor to ground connection is open.

Check continuity of all connections to the MCU pins, and measure resistance from the LiPo pins to ground.

You do need capacitors from the voltage divider taps to ground. 10 nF is fine.

Based on the schematic of n voltage dividers, with no lipo connected there is NO
electrical reason for any voltage to be on those analog inputs. You can easily verify
this by get two resistors , connect them in series and measure the voltage at the
point where they are connected. (WTF ?) Exactly. Makes no sense. clearly there is
an issue with the pcb. What to do ? No clue. Tracing the source of the voltage is
very difficult unless the traces are clearly visible in which case it is simple. Just follow the traces to where they come from and measure there, but no doubt you
have already done this.

This isn't connected properly...
image

Did you mean this?
image

I think what dlloyd is trying to say is that a BMS should measure the voltage of each
cell , NOT the voltage of that cell with respect to the negative terminal of the pack. The correct voltage divider (AFAIK) should be single series divider with
multiple resistors with each cell across a single resistor with the ONLY GND connection being at the NEG terminal of the PACK , NOT the cell.
An example that shows what the resistors would be like is attached.
Imaging the schematic with the switches and R6 REMOVED and each analog input
connected to the tap between resistors and the resistor values selected such
that when the POS terminal of the PACK is connected at the bottom of R1,
the MAXIMUM voltage read by the analog input does not exceed Vcc of the
arduino. (obviously you need more resistors because you have 6 cells).
The voltage divider "R2" (bottom resistor) value for cell #6 is the SUM of all
the resistors below R1 (schematic R1, NOT voltage divider R1, although, coincidently
in this case they are the same) .Vo=Vin x R2/R1 (voltage divider formula)
The R2 value (voltage divider schematic R2 NOT attached schematic R2) for cell
#5 is the SUM of all the resistors below R3 and so forth, yielding 6 voltages
(if you add the needed resistors) to 6 analog inputs, all < Vcc.

Whoops ... please disregard reply#7. I was mistakenly thinking the Arduino was at the left side of the circuit.

I still think the totem pole resistor divider is more consistent with most BMS circuits. The OP's original schematic would still work but I think that doing the
math to subtract one analog input from another to obtain a cell voltage is elementary and the totem pole approach might draw less current. It's just
my personal preference. There is nothing 'wrong' with the OP's original schematic
as far as that goes.

I think I missed this in my earlier post (it was the same time as your post).

Per your schematic the analog input readings should all = 0
Now you two that go to 0 and 4 that do not. I would connect the input of the two that are currently 0 to the Arduino Vcc. They should now read something.

The first thing I would do is to inspect the solder connections looking for opens or shorts. I find a 4X or 5X eye loop to be a good start.

As a test, try this:

int analogPins[6]  = {A5, A4, A3, A2, A1, A0};
float temporaryRead, valueRead;

void setup() {
  Serial.begin(115200);
}

void loop() {

  for (int i = 0; i <= 5; i++) {
    temporaryRead = analogRead(analogPins[i]); // switched to adc[i], discard reading
    temporaryRead = analogRead(analogPins[i]); // ensure internal cap is fully charged/discharged
    temporaryRead = 0; // clear
    for (int j = 0; j < 10; j++) {
      temporaryRead += analogRead(analogPins[i]);
    }
    valueRead = temporaryRead * 0.1; // average of 10 readings
    Serial.print(valueRead);
    Serial.print(" ");
  }
  Serial.println();
  delay(200);
}

This is either SW issue (reading other pin than intended) or the pin is damaged.

Just to test, I would write a simple program that reads a single ADC channel and prints out the RAW number. The absolute minimum of code.

AND, if you have the ability to connect the analog input pin (at the chip) to ground with a wire and maybe some sort of Pin (even a safety pin would work), you should try that.

Be careful of ESD. Ground your body (i.e. touch it to the board ground) before performing the above test.

What's Vcc voltage ?

Thanks For all the responses.

I have run the basic code (with cap charge) suggested above and the same problem. When the "input" side of the voltage divider is set to gnd, It still reads around 600-700. Shorting the 32u4 side of the voltage divider (ie directly the input) to gnd does lower the read ADC to 0.

Using a multimeter and testing for voltage on the voltage divider input shows voltages ranging from 4.5 down to 3 (on the larger voltage dividers).

My Voltage in is 5.2v.

I've given the board a good visual inspection and probed to ensure they are the correct value resistors, all good. I've tried another 2 boards that I hadn't soldered anything to and still the same.

I now have this code that maps the input (from the ADC value when GND) which works, but obviously isn't the solution.

I also put together the same circuit in tinkerCAD and got the results I was expecting (ADC from 0-1023)

  valueread = analogRead(analogPins[0]);
  valueread = map(valueread, 0, 1024, 0, 5000) * 1.04270109235;
  Serial.print(valueread);
  Serial.print(" ");

  valueread = analogRead(analogPins[1]);
  valueread = map(valueread, 860, 1024, 0, 13485) * 1.053291536;
  Serial.print(valueread);
  Serial.print(" ");

  valueread = analogRead(analogPins[2]);
  valueread = map(valueread, 860, 1024, 0, 13485) * 1.063014748132;
  Serial.print(valueread);
  Serial.print(" ");

  valueread = analogRead(analogPins[3]);
  valueread = map(valueread, 767, 1024, 0, 23667) * 1.04250698107;
  Serial.print(valueread);
  Serial.print(" ");

  valueread = analogRead(analogPins[4]);
  valueread = map(valueread, 767, 1024, 0, 23667) * 1.0460772104607;
  Serial.print(valueread);
  Serial.print(" ");

  valueread = analogRead(analogPins[5]);
  valueread = map(valueread, 700, 1024, 0, 33000) * 1.041845309552;
  Serial.print(valueread);
  Serial.print(" ");

Next step to cut the traces between the 32u4 and voltage dividers and have another probe around?

It looks like your part to part spacing is only 6-8mil ...
image

but recommended is usually around 50mil (1.25mm) ...
image

I believe leakage between the smt parts (resistors) is causing the non-zero analog readings. Also, if possible, increasing the track-track and track to component spacing would help.

I wouldn't cut anything yet.

Did you measure the voltage across the various 150k resistors? Comparing the channels that read 0 with those that do not read 0.

Can you measure the voltage at the IC Analog input pins at the chip to ground.

Can you measure the resistance from the IC Analog input pin to the non ground side of the 150k's.

Ok so a bit more investigation leads to this:

Cutting the traces and soldering the voltage divider to an official uno has the expected results (full range of adc being used). But I do agree about moving the resistors apart.

I don't think I have a 32u4 based arduino to test with unfortunately. Measuring the voltage shows each analog pin outputting 5v from my 32u4. The same test on the uno only showed about 2.2v floating for comparison.

I have tried using digitalWrite(a*, LOW) to see if it was the internal pullups being activated, but still not using the full range. I currently am building for a "Leonardo" board in arduino before using DFU-Programmer to flash the HEX. I have also not flashed an arduino bootloader, in case that has an effect on pin pullups? I also desoldered the thermistor on the board to split the 5v from the gnd with no change.

I think you are getting side tracked. You have this connection on (for example ADC7)

If you set PF7 as an input, then the voltage at that pin will be zero or near Zero. Period !