accurate Moteino battery voltage monitoring

Started by WhiteHare, September 21, 2015, 01:56:43 PM

WhiteHare

The Mega Moteino is nice, because it would offer access to the 2.56V internal voltage reference for calibrating the ADC.  But for regular 328p Moteino's, what's the best way to ensure accurate readings of battery voltage?

I've read that the gap reference 1.1v is only accurate to 10%.  I'd prefer something more accurate, like 1% or 0.1%.

Are folks using an external voltage reference, such as a zener diode or maybe a temperature compensated precision voltage reference?  Or are you using a dedicated battery monitoring chip?

I'm looking for an ultra low current way of monitoring my battery voltage.  Most likely my power source will be 3 alkaline AA's, so I want them to last as long as possible, but I will also need to check their voltage level from time to time and plot the measured voltages so that I can verify my power budget and Moteino setup are working as planned.  So, I'd prefer to have more than just a low battery indicator (if that's all I needed, it looks as though the HDC1008 actually includes one, which triggers at 2.8v).  This is such a universal problem for battery operated devices that I've got to believe there must be a really good solution.

Felix

You can do it many ways, including what you mentioned.
I've found that just using a high impedance set of resistors to divide the voltage in a range readable by the ADC works great, is highly accurate and super cheap. It's used on my products like: WeatherShield, PowerShield, MotionMote, SonarMote.

TomWS

Quote from: WhiteHare on September 21, 2015, 01:56:43 PM
I've read that the gap reference 1.1v is only accurate to 10%.  I'd prefer something more accurate, like 1% or 0.1%.
I suggest reading the WHOLE 328P spec.  If you do, you will find the table attached below which shows over full usable voltage and temperature range, the Bandgap voltage varies by 0.88%.  It is an unfortunate truth that the datasheet, appearing earlier in the spec DOES, in fact, say that the range is 1.0 to 1.2Volts.   I have to believe there is an errata sheet somewhere that corrects this.

The first clue that the 10% didn't make sense was that it is a 'bandgap' reference, ie, a voltage that is established by the physics of the junction - this is pretty solid...  The second clue is that it is very unlikely that the designers of the ATMega1284P were more 'with it' than the 328P in terms of producing a substantially more accurate reference.  What they had were more transistors to add more ADCMUX selections.

Tom



WhiteHare

#3
Interesting!  It's the first time I've heard of that.  I wouldn't have even known to scour the datasheet regarding this, because the unanimous opinion seemed to be the 10% number.  Koudos for catching that one.

I do get the impression you've read the WHOLE 328p spec sheet, so riddle me this.  The title on that chart refers to a "calibrated" bandgap voltage.  Does that mean factory calibrated, or is there a calibration procedure, or does it somehow self-calibrate transparently?  Or do you basically just measure it once across AREF and ground and then use that number (perhaps adjusting for temperature, if so inclined)?  Obviously, I'm hoping it means factory calibrated.  I've also read there's a temperature sensor within the 328p, so maybe it would be good for calculating a temperature correction factor as I would think that it must be in extremely close proximity to the bandgap (as they're both on the same chip).

joelucid

QuoteI'm looking for an ultra low current way of monitoring my battery voltage

In my recent battery based motes I've always used 2 battery cells without the voltage regulator. This gives you more energy per cell, a nicer form factor and you can use the calibrated internal band gap reference which I've found to be fairly accurate to measure Vcc.

Works well if you don't need 3.3v for your sensors.

TomWS

#5
Quote from: WhiteHare on September 21, 2015, 03:40:26 PM
I've also read there's a temperature sensor within the 328p, so maybe it would be good for calculating a correction factor as I would think that it must be in very close proximity to the bandgap.
Just one of the many pieces of useful information waiting for you as you pour through this wealth of information...

Tom

kobuki

I'd just quickly like to drop these links here. All you need to know is the voltage reference is a bandgap one, so it stays sufficiently precise with varying temperatures. Not a precision reference, though, but I guess for measuring battery voltage, it's more than adequate. In retrospective, I have no idea why they state that 1.0-1.2V range.

https://en.wikipedia.org/wiki/Bandgap_voltage_reference
https://en.wikipedia.org/wiki/Band_gap

WhiteHare

#7
Just now had a look at voltage references on digikey.  It seems 0.88% isn't bad, especially considering it's free and I don't have to order extra parts.   :)

This youtube video sums up how to read bandgap voltage and use it to derive your battery voltage. 
https://www.youtube.com/watch?v=SpDXrhjwbVo

His bottom line is this:

battery voltage = (1.1/analogRead(14))*1023. 

Done!  With 3x AA I won't even need a voltage divider.  :) :) :)

TomWS

#8
Quote from: WhiteHare on September 22, 2015, 10:09:44 AM
This youtube video sums up how to read bandgap voltage and use it to derive your battery voltage. 
Or you could just use the code that's been posted a million places around the web:
/******************************************************************************
*
*   readVcc()
*
******************************************************************************/
unsigned int readVcc(bool restoreMux) {
  unsigned long result;
  byte saveADMUX;
  
  saveADMUX = ADMUX;
  // Read 1.1V reference against AVcc
  // set the reference to Vcc and the measurement to the internal 1.1V reference
  #if defined(__AVR_ATmega32U4__) || defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) || defined(__AVR_ATmega1284P__)
    ADMUX = _BV(REFS0) | _BV(MUX4) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);
  #elif defined (__AVR_ATtiny24__) || defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny84__)
    ADMUX = _BV(MUX5) | _BV(MUX0);
  #elif defined (__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__)
    ADMUX = _BV(MUX3) | _BV(MUX2);
  #else
    ADMUX = _BV(REFS0) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);
  #endif

  #if defined(__AVR_ATmega2560__)
    /****
    it took me a while to figure-out the problem, but on MEGA 2560, immediately after analogRead(A8), ADCL started returning zero. 
    So every attempt to read from A8-A16 on Arduino MEGA will damage the functionality of readVcc().
    I've resolved the problem by adding:
    ADCSRB = 0;
    just before
    delay(2);
    **********************************/
    //ADCSRB &= ~_BV(MUX5); // Without this the function always returns -1 on the ATmega2560

    ADCSRB = 0;
  #endif

  delay(20); // Wait for Vref to settle
  ADCSRA |= _BV(ADSC); // Start conversion
  while (bit_is_set(ADCSRA,ADSC)); // measuring

  uint8_t low  = ADCL; // must read ADCL first - it then locks ADCH
  uint8_t high = ADCH; // unlocks both

  result = (high<<8) | low;
//  Serial.print("BatteryResult="); Serial.print(result);
  result = 1125300L / result; // Calculate Vcc (in mV); 1125300 = 1.1*1023*1000 (because...= 3300*1023/3 since 1.1 is exactly 1/3 of 3.3V)
// Serial.print(", after calculation:"); Serial.println(result);

  if (restoreMux) ADMUX = saveADMUX;

  return result; // Vcc in millivolts
}

Tom

UPDATED: removed PRINT macros and added restoreMux.

WhiteHare

#9
Looks to me as though both ways of doing it are assuming the bandgap voltage is 1.1v.  From looking at your chart, though, a better pick would be 1.13v, which is more  in the middle of the range between super hot and super cold.  Or, if it's for indoor use only, then the 25C curve would be a good pick.

Or, maybe just report the raw value of analogRead(14).  Then the gateway, if it happens to know the ambient temperature, could pick an appropriate constant based on your graph above (a pity it's not more complete).  If done in the dead of night, when ambient temperatues are more settled, that might yield another slight improvement for outdoor sensors.  It would be better than the onchip temp sensor, which the datasheet says is only accurate to +/- 10C.

The ultimate might be measuring the bandgap voltage on the chip under test.  Running a chip the full temperature range and recording the bandgap voltages at each point, over a range of Vcc's, should yield a pretty accurate lookup table for that chip, and maybe all chips depending on whether there's meaningful variation or not.  That lookup table could sit on flash memory in the gateway.  If anyone ever does those measurements, please post them!  It would fill in the gaps missing from the chart.

WhiteHare

#10
BTW, after just now comparing the self discharge rates (see attachment), I think I'll go with lithium rather than Alkaline batteries.

What might change my mind is if the Duracell Quantum version of the Alkaline battery has a much lower self discharge rate than typical alkaline batteries, because I've read that Costco sells the Quantums under their Kirkland brand name and so the price wouldn't be too bad.  However, so far I haven't found any data regarding Quantum self discharge rate.  Anyone happen to know?

Otherwise, what's the best brand (least self discharge rate) for Lithium AA's?  The Energizer Ultimate Lithium's claim they "hold power" for 20 years:

UPDATE TomWS: Unnecessary photo removed.

Even assuming marketing spin, that sounds tough to beat.  On Amazon they'd be about $1.50 each, if purchased in quantity of 20 or more.

kobuki

You're spouting a lot of information commonly available on the net after like 5 minutes of googling. I'm not saying it's not useful at all, but I think your time is better spent looking at readily available information. It's vast, really. As I've said, the bandgap reference is not a precision one in the 328. If you want precision, you pay the price. Calibrating it at the full temperature range is good, but calibration values tend to skew over time, so it's best repeating them every half a year (IIRC it's the norm for most calibrated instruments). You will find that many folks advice a measurement of the bandgap itself chip by chip to achieve more accurate results for AD sampling. You can go that route if you want.

OTOH, choosing a suitable battery is not all about the least self-discharge rate. For small, several mA and sub-mA loads, Alkalines are better suited. They're the fraction of the price of a Lithium one and for that kind of usage they probably last the same. Lithium however is better suited for applications when a higher discarge rate is expected, like in digital cameras, high-performance flashlights, etc. Of course, if your aim is achieving a consumption rate similar to the battery packs' self-discharge rate, Lithium might be a better fit, and the 20 years shelf life might play a role.

joelucid

Problem with the alkalines is the discharge curve is not flat at all - which is why 9V batteries work so great with Moteinos. Lithium is much better in that regard. Also speaking from experience: if you want to power your outdoor Mote during winter do yourself a favor: Lithium, not Alkaline.


kobuki

Yeah, of course. On a case-by-case basis, usage differences help deciding for a given project or even placement. The curve not being flat is not necessarily a problem, though. In case of the (unmodified) Moteino, all needed is they provide at least 3.3V till they run out. According to my calculations, even 3xAA Alkalines can provide 1.5-2 years of operaion for a properly programmed mote. And - for me - it's good enough :)

TomWS

Quote from: joelucid on September 22, 2015, 03:40:34 PM
Also speaking from experience: if you want to power your outdoor Mote during winter do yourself a favor: Lithium, not Alkaline.
I totally agree with Joe here and add, if you want to keep the voltage above 1.5V/battery for the longest amount of time, use Lithium Li-FeS2 primary cells.  Alkaline lose too much of their voltage earlier in their lifecycle.

Tom