Measuring LiPo supply voltage on AVR-based circuit - Page 1
Forum section: electronics / projects

author: synoptica
author_rank: Contributor
timestamp: January 27, 2017, 05:54:16 am
content: Hi all,On a bit of a proof-of-concept mission before I implement this circuit as a part of a larger project.I've got a fairly basic setup - a cheap lipo charger board with charging/charged status LEDs and a low-voltage cutoff, an Arduino Nano, a little 5V boost converter board and a single 3.7V LiPo cell.I know the usable range of a LiPo is ~3.5 - 4.2V and makes it very easy to measure the percentage of battery remaining, but I'm having a lot of trouble getting a steady reading of this voltage from the Arduino.I'm supplying 5V using the boost converter board, and only supplying the LiPo's voltage to the boost converter and an analog input of the Nano. I've also used a 10K pulldown resistor between the analog pin and ground (this hasn't changed the result.)If I power the Nano from a separate source (while maintaining a common ground), I get steady, accurate readings of the LiPo's voltage.If I power the Nano from the 5V Vout of boost converter and try to measure the voltage at Vin of the boost converter, the readings fluctuate over the range of about a volt and sit (on average) around 0.5V higher than the actual reading.Is there something inherently wrong with the way I'm attempting to do this? Any advice appreciated!

author: sleemanj
author_rank: Super Contributor
timestamp: January 27, 2017, 06:07:30 am
content: Sounds like your boost converter is noisy, and the analog input filtering of your typical arduino is... none.I would start by whacking some bulk capacitance on the output of your boost and see if that helps.Ideally, use a scope to see exactly what you are dealing with.

author: Inflex
author_rank: Regular Contributor
timestamp: January 27, 2017, 07:13:27 am
content: Can you show us a schematic of your voltage measuring setup in to the ADC?I agree with the other post, sounds like a very noisy booster; definitely some bulk caps and make sure you have your 100n close to the Vcc/Vdd on the AVR.What sort of source code are you using to read the ADC value (though the fact that it's stable when run from a separate supply seems to not indicate any issue there).

author: Seekonk
author_rank: Super Contributor
timestamp: January 27, 2017, 08:20:18 am
content: Are you using the 5V supply as the reference for the A/D or the 1.1V internal reference? Got a cap at the input of the A/D with some resistor isolation from the battery? If you haven't noticed, just plugging in the USB will change readings. I use a modified cable with back to back diodes in the 5V line when using 5V as the reference so as not to change the settings.

author: synoptica
author_rank: Contributor
timestamp: January 27, 2017, 09:19:45 am
content: Quote from: sleemanj on January 27, 2017, 06:07:30 amOkay, so I've blindly thrown a 100uf between the analog input and ground, that seems to have calmed it down somewhat! It still jumps around but over a smaller range, and still seems to be reading high. I pulled the pulldown resistor out also; is there any harm in retaining it?No scope, sadly - I should buy a small AliExpress special though (then learn to use it)Quote from: Inflex on January 27, 2017, 07:13:27 amSee previous; I'll seee if I can find a 100n lying about! I do need to rebuild this whole circuit; it grew quickly and is very crammed onto two small breadboards without +/- rails, so that won't be helping with noise etc I wouldn't think. I'll get a schematic going once rebuilt.Code is very simple - as 0-1023 = 5V when reading analog from arduino, I just multiply this by 0.0046 (spec says 0.0045 but I took my own measurements with a meter and corrected it) - I took the measurements when it was powered separately and not exhibiting this current issue!Quote from: Seekonk on January 27, 2017, 08:20:18 amI'm not sure what the 1.1V internal reference is to be honest with you! I've literally got the +ve output on the LiPo charge board (this supplies the battery voltage but will cut out at 3.something volts) to the analog pin. This output line also goes to the input of the boost converter. I've now got a cap which has helped, but I'll try a lower value as suggested by Inflex; I'll have a look into resistor isolation (but no, nothing at the moment).

author: Inflex
author_rank: Regular Contributor
timestamp: January 27, 2017, 09:51:35 am
content: Quote from: synoptica on January 27, 2017, 09:19:45 amYou'll want the 100n (or anything up to 10uF) close to the AVR, but that's more for its own stability. Leave the 100uF bulk on the rails

author: sleemanj
author_rank: Super Contributor
timestamp: January 27, 2017, 10:03:35 am
content: At this point I would put 100nF on the ARef pin, from memory it's tied to AVcc when the Vcc (5v) reference is selected.Then if still too noisy I'd look at filtering on the analog input itself, firstly with a small capacitor.Don't forget that you can of course do some filtering in the software by way of averaging a number of readings, or by reducing the resolution (ie divide by two to get 9 bits resolution instead of 10)Obviously, you are kinda flying blind here so it's just a matter of hitting the obvious things.

author: Siwastaja
author_rank: Super Contributor
timestamp: January 27, 2017, 11:33:56 am
content: Put a 10nF to 100nF small ceramic capacitor (such as 0603 package) directly from the ADC input pin on the AVR chip to the nearby ground pour. If you can't bodge it there, at the header pin is the second best option. This cap creates a low-impedance source for the ADC. To make it an RC filter against noise, add a series resistor (something like 1K is fine); if you are using a voltage divider, it already does the job of being the resistor, and adding another one is unnecessary.

author: Seekonk
author_rank: Super Contributor
timestamp: January 27, 2017, 03:19:32 pm
content: "I'm not sure what the 1.1V internal reference is to be honest with you! I've literally got the +ve output on the LiPo charge board (this supplies the battery voltage but will cut out at 3.something volts) to the analog pin. "Just for future reference. I prefer not to use this i most cases as 5V is common for most sensors. I recently made a lipo battery tester and charger using a phone wall wart for power. Charge mode dragged down the voltage about 250mv and plugging in a laptop USB also changed the voltage. It made it hard to have a precise shutdown voltage for the load. This is a secondary issue. A cap has to be on the A/D input with some isolating resistor. The input voltage divider (if used) can be a high value for low load if a cap is used to provide the charge current to the A/D.// Range is 0 to 1.1V DC with internal 1.1V reference// Use internal 1.1V reference since 5V power can varyanalogReference (INTERNAL); // use with (62K, 5K pot), 15K voltage divider, for .7V input

author: synoptica
author_rank: Contributor
timestamp: January 28, 2017, 02:16:48 am
content: Quote from: Inflex on January 27, 2017, 09:51:35 amCheers mate; I've tried with both a 100n polyester and tantalum without much change. The 100uF on the rails doesn't seem to do much eitherQuote from: sleemanj on January 27, 2017, 10:03:35 amSorry, just to clarify on that first point, I should put a cap on Aref and where? I've Vout of the boost converter (Vin on the Nano) which doesn't do much; bridging Aref and Vin changes the readings (which causes it to under-read by about the same amount as it's over-reading with it disconnected.)I'd like to correct the issue as much in hardware as possible (within reason, a battery reading will only ever be so accurate) but the code is already pushing the upper limits of 32kB so the less soft correction the better!Quote from: Siwastaja on January 27, 2017, 11:33:56 amI'll try this; I don't have any ceramics - only a poly and tantalum. So it'd look something like this?(ADC pin / Vbatt) O----||----/\/\/\----O GNDQuote from: Seekonk on January 27, 2017, 03:19:32 pmYeah, I'm trying to avoid anything that'll put any idle load on the battery, though I honestly haven't crunched the numbers to see what the drain would be like. In any case I agree that 5V is common enough that we shouldn't have to drop it down.!

author: sleemanj
author_rank: Super Contributor
timestamp: January 28, 2017, 05:11:34 am
content: Quote from: synoptica on January 28, 2017, 02:16:48 amground

author: Siwastaja
author_rank: Super Contributor
timestamp: January 28, 2017, 04:10:59 pm
content: Quote from: synoptica on January 28, 2017, 02:16:48 amToo big of caps, especially electrolytics/tants, have ESL and ESR which reduces the filtration. Of course they work somewhat, but I'd recommend you just get some basic components.QuoteNope, use a cap directly between the ADC pin and GND, with minimum wire length possible. Resistor goes between the ADC pin and the measured signal.

author: synoptica
author_rank: Contributor
timestamp: January 29, 2017, 09:02:19 am
content: Quote from: sleemanj on January 28, 2017, 05:11:34 amCool, thanks for clarifying! I can't really see any change from this. I used some code a mate found here to check my Vcc:https://hackingmajenkoblog.wordpress.com/2016/02/01/making-accurate-adc-readings-on-the-arduino/This readout is fluctuating a bit as well, so looks to be the source of the issue.I've incorporated the remaining code from the above link, and by averaging that out over a number of samples I've found it to provide a close enough reading of the voltage to be suitable for my needs so I'll just do that!Here's my working code for anyone interested:Code:#include <Arduino.h>const uint8_t BATT_PIN = A6;const uint8_t SAMPLE_PERIOD = 64;double volts_avg;void setup() {pinMode (BATT_PIN, INPUT);}void loop() {uint8_t batt_percent;double volts;double volts_raw;uint16_t batt_charge = analogRead(BATT_PIN);// read raw and calibrated voltagesvolts_raw = batt_charge * 0.0045;volts = read_volts();// calculate average and percentagevolts_avg = (volts_avg == 0) ? volts : ((volts_avg * (SAMPLE_PERIOD - 1)) + volts) / SAMPLE_PERIOD;batt_percent = (volts_avg - 2.85) / 0.0125; // assumes 4.1 max - 2.85 min = 1.25 differencedelay(500);}long read_vcc() { // code from https://hackingmajenkoblog.wordpress.com/2016/02/01/making-accurate-adc-readings-on-the-arduino/long result;ADMUX = _BV(REFS0) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1); // Read 1.1V reference against AVccdelay(2); // Wait for Vref to settleADCSRA |= _BV(ADSC); // Convertwhile (bit_is_set(ADCSRA,ADSC));result = ADCL;result |= ADCH<<8;result = 1125300L / result; // Back-calculate AVcc in mVreturn result;}double read_volts() {double vcc = read_vcc() / 1000.0;unsigned int adc_value = analogRead(BATT_PIN);return (adc_value / 1024.0) * vcc;}