Reading Li+ voltage on ADC

I have a single 3.7 Li-Ion battery powering a circuit that I would like to monitor the voltage on. This is then mapped to an LED string and a color gradient displayed (for a brief moment when first powered up.)

So, is this safe to do, or is this going to drain the battery quicker, or something else I’m not considering …? And if not, how should I be doing this properly?

It is safe to do but will not give you a valid measurement. As the battery is both the Vcc and Avcc source voltage then the internal analog reference voltage will also be the battery voltage, thus a analogRead() will always return a value of 1023 counts no matter what the specific battery voltage is.

You could in software switch to the internal 1.1vdc voltage reference and then wire the battery voltage through a two resistor voltage divider to scale down the battery voltage before wiring it to a analog input pin.

Lefty

If you only want a momentary indication, you'll want to take precautions to power down the AVR, not just turn off the LEDs. And ditch the Arduino and just use a bare AVR, because other parts on an Arduino use power. Use as slow a clock as will give you a readout in a time you consider reasonable, as a faster clock means more power consumed.

http://playground.arduino.cc/Learning/arduinoSleepCode

I'd say the same thing as retrolefty, use the internal 1.1V reference and have a voltage divider to scale the battery voltage down. Keeping in mind that the divider resistance cannot be too high or it affects the reading, but lower resistance drains the batteries quicker. Perhaps some kind of switched resistor divider, lift the grounded end and insert a MOSFET that will turn on fully with only 3V of gate drive, then you can shut it off after taking the reading.

retrolefty:
It is safe to do but will not give you a valid measurement. As the battery is both the Vcc and Avcc source voltage then the internal analog reference voltage will also be the battery voltage, thus a analogRead() will always return a value of 1023 counts no matter what the specific battery voltage is.

Odd, I'm getting readings other than 1023 here. When the battery is drained to 2.7V, I'm getting 547 and with it charged to 4.19V, I get 855. Oh wait ... this is using an Uno to test, I think AVcc is isolated ... will have to check schematic for that. The final (production) unit has AVcc isolated with a ferrite bead, as in VCC -> ferrite bead -> AVcc. Would that be enough?

polymorph:
If you only want a momentary indication, you'll want to take precautions to power down the AVR, not just turn off the LEDs. And ditch the Arduino and just use a bare AVR, because other parts on an Arduino use power. Use as slow a clock as will give you a readout in a time you consider reasonable, as a faster clock means more power consumed.

I can't power down the AVR, I need it to display the gradient on the LEDs. But, basically what it's doing (or what I'm planning to do) is this:
a) turn switch to ON which provides power to the AVR - there actually is a Buck/Boost that provides the 3.3V and I completely forgot to add that in the schematic above, that's my bad: battery -> Buck/Boost -> AVR
b) there's a straight line from the battery's VCC to the ADC
c) AVR comes on, with everything else powered down still (uSD card, LEDs and LED drivers), ADC samples in raw battery voltage (20 times over a 2 second period) and calculates the numbers needed for the gradient display.
d) turn on the LED drivers, followed by the LEDs (drivers come on at max output so I need to shut their output off before turning on the LEDs)
e) display gradient on the LEDs for 1 seconds
f) now turn uSD card on and enter main program loop

I realize there's a lot going on, and there WILL be drainage in other places going on, however I'm also not looking for a dead accurate number in the double decimal digits here. Just a rough estimate. I need to know if the battery is at 4.2V or reaching 2.7V ... I will probably cut it off at 3.0V anyway ... but if in one instance the battery was at 3.1V or 2.9V and it got cut off in both situations, I'm okay with it.

There is a library where you measure the Vcc against the internal 1.1V reference. So you get Vcc in Volts as the output. You do not need any analog input connected.

pito:
There is a library where you measure the Vcc against the internal 1.1V reference. So you get Vcc in Volts as the output. You do not need any analog input connected.

Care to provide a link?

No idea where it comes from, this is what runs in my barometer (328p)..

unsigned int VccRead (byte ms = 10 ) {
  ADMUX = 0x4E; //bit(REFS0) | 14;
  delay(ms);
  bitSet(ADCSRA, ADSC);
  while (bit_is_set(ADCSRA, ADSC)) {};
  unsigned int x = ADC;
  return (110L * 1023) / x ;  //1100L for millivolts
  }

It uses Vcc as the reference, and it measures the 1.1V internal reference :astonished:
It returns 329 for 3.29V Vcc, etc..
With (1100L * ..) you get 3297 for 3.297V :slight_smile:

However, since there is no direct connection between the battery and the AVR, it will never measure the battery's voltage with that, only what it's receiving on VCC, which happens to be 3.3V because it's coming from a Buck/Boost. I need the raw voltage from the battery. So I have to read it from somewhere.

In that case, you can just let it use the 3.3V supply as the reference, however the ADC output will be only as clean and stable as the power source. You'll still need to use a voltage divider to bring the max 4.2V below 3.3V.

polymorph:
In that case, you can just let it use the 3.3V supply as the reference, however the ADC output will be only as clean and stable as the power source. You'll still need to use a voltage divider to bring the max 4.2V below 3.3V.

Does it matter how low I bring that raw voltage? Or should I match the 3.3V that the whole unit will be running at?

Does it matter how low I bring that raw voltage? Or should I match the 3.3V that the whole unit will be running at?

Well, not really. You lose resolution as it becomes less full scale as compared to the 3.3V supply, however you've got lots to spare and it is better to ensure that the analog input never goes above 3.3V.

The thevenin equivalent resistance of the voltage divider should be at or below 10k or it may skew the readings. There are some tricks to get around that limitation. However, that is going to be drawing less than 400uA already. So a couple of 18k resistors will divide 4.2 down to 2.1V max and draw less than 250uA, while still presenting about 9k impedance to the analog input.

Edit: ... and draw less than 120uA....

Ok, so with the Buck/Boost in place, with the ON/OFF switch, is this what you’re talking about? Having the two 18K to divide the voltage and feeding into ADC0.

Yes, that should do it.

The exact reading at 4.2V will depend on exactly how close the switching regulator holds to 3.3V. An alternative way is to use the internal 1.1V regulator, and divide down by about 4.5 to 5. I say that instead of 4, because the 1.1V internal Aref is not exact, but should be very time and temperature stable.

A 33k and a 10k will bring it down to just below 1V when the battery is at 4.2V, and present about 7.67k of impedance while drawing about 98uA. The power miser in me wants to tweak those values more. Since you don't need all that resolution, you could use a much larger resistor for the "top" part of the divider and reduce that a lot more.

What does that do for resolution though, going that low with it?

And thanks for all your help so far!

Well, you've got about 1000 levels if you matched 4.2V to the 1.1V internal reference or a resolution of about 4mV per bit. If you go a bit lower, you only get 1/4 or less. But that's still better than 20mV per bit. I'd think that would be good enough for your purposes. You aren't going to display this on a thousand LEDs, after all.

Uh, no ... 48 LEDs only. 1,000 LEDs in the configuration that I have them will translate into a stick that's almost 20 feet long. I don't know anyone with an arm that long. :slight_smile: Even at the current 18" length, it is a bit too long ... I need to shorten the controller part (which is the inner circle in this picture.)

Wow, very nice. Well, 250 to 500 levels (8 to 9 bits) should map nicely to 48 levels.

polymorph:
The exact reading at 4.2V will depend on exactly how close the switching regulator holds to 3.3V. An alternative way is to use the internal 1.1V regulator, and divide down by about 4.5 to 5. I say that instead of 4, because the 1.1V internal Aref is not exact, but should be very time and temperature stable.

Ok, I think I'm going to stick with the two 18Ks only because, for now, I'm going to trust that the Buck/Boost does what it claims it can do, which is to provide a solid 3.3V whether the battery's at 4.2V or 2.0V. But also, because I've never worked with the internal Aref and at this point I've done a code freeze for this particular version. In the future I may very well re-open this can and look inside ...

I don't blame you for not wanting to change it now.