powering a pro mini 3.3v and Measure the voltage

Hello all
i have a Arduino pro mini 3.3v
i power it with a 1cell lipo battery with 4.2v (fully charged) into the RAW pin .
I also want to monitor that lipo.

Can i directly plug the + wire of the lipo into the A0 pin of arduino and read the voltage?
Any code examples?

Remember i have other devices that requires 3.3v exactly from VCC

gc9n:
Hello all
i have a Arduino pro mini 3.3v
i power it with a 1cell lipo battery with 4.2v (fully charged) into the RAW pin .
I also want to monitor that lipo.

Can i directly plug the + wire of the lipo into the A0 pin of arduino and read the voltage?
Any code examples?

Remember i have other devices that requires 3.3v exactly from VCC

You can't connect a voltage higher than Vcc to any Arduino pin.
You'll need to use a voltage divider.

The nominal voltage of a Li-Po cell is 3.7V, which goes lower during discharge, until it reaches the point where the dropout voltage of the regulator will begin to affect the voltage at Vcc, (making it useless as a reference), so it's best to use the ATMega328P's internal 1.1V reference rather than the Vcc voltage, as a reference, and set up the voltage divider accordingly. (You'll get an analogue value of 1023 when 1.1V is applied to the pin.)

There's more info on the internal 1.1V reference here:- analogReference()

you mean the AREF ?

like this?? maybe?

long readVcc() {
  // 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__)
    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  

  delay(2); // 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

  long result = (high<<8) | low;

  result = 1125300L / result; // Calculate Vcc (in mV); 1125300 = 1.1*1023*1000
  return result; // Vcc in millivolts
}

I meant use 'analogReference(INTERNAL)', which sets the reference to 1.1V, then 'analogRead()', which will return 1023 when 1.1V is present on the analogue input pin, as I said earlier.
And, of course, a suitable voltage divider consisting of two resistors, with the top connected to the battery, and the junction of the resistors connected to the analogue input pin.

Why mess with registers directly? You're making things hard for yourself.

Did you just write that, or copy it from somewhere?
(I guess you copied it, since it's written to cover a wide range of processors, not just your ATMega328P.)

Take another look at the page I linked earlier. It explains the 'analogReference()' function.

OldSteve:
<…>
… ATMega328P’s internal 1.1V reference rather than the Vcc voltage, as a reference, and set up the voltage divider accordingly. (You’ll get an analogue value of 1023 when 1.1V is applied to the pin.)

Exactly!
The exact same principle applies to using a thermistor to measure temperature; a voltage divider is required.

One can use a handheld calculator for such things, but in this day of so many web-toys, I would suggest the approach I used in this article and use a Circuit Simulator.

Ray

mrburnette:
One can use a handheld calculator for such things, but in this day of so many web-toys, I would suggest the approach…

I love my old calculator. :slight_smile:

@gc9n, needless to say, anything you use to monitor the battery voltage will also add to the current drawn from it.

Atmel recommend that the impedance of a source measured by an analogue input should be about 10K or less. You can get away with higher if you have a capacitor from the pin to ground.

This circuit would do what you want, (and hardly needs the cap, but it helps). :-
Li-Po Voltage Monitor.JPG

It would apply the full 1.1V to the analogue pin if the battery was at 4.675, so all calculations must be based on that value.
As shown on the diagram, You can calculate the battery voltage with this:-float volts = (analogRead(A0) * 4.675)/1024;

The analogRead() value would be 920 at Vbatt = 4.2V, 810 at Vbatt = 3.7V, or 723 if the battery is as low as 3.3V.
To calculate the raw analogRead() value for a given voltage, value = (Vin * 1024) / 4.675

With the values shown, the voltage divider will draw 72.5uA when the battery is at 3.7V. That’s not much, since the power LED at 20mA would draw 275 times that much.

And as I mentioned earlier, the internal 1.1V reference can be enabled with:-analogReference(INTERNAL);

I don't trust magic numbers :frowning:

The input voltage to the given voltage divider will be
(analogRead(pin)*ARef/1024.0) * (39.0+12)/12
with ARef being either 1.1 or 3.3V, just as selected.

DrDiettrich:
I don’t trust magic numbers :frowning:

The input voltage to the given voltage divider will be
(analogRead(pin)*ARef/1024.0) * (39.0+12)/12
with ARef being either 1.1 or 3.3V, just as selected.

And, of course, the result will be exactly what I said.

OldSteve:
And, of course, the result will be exactly what I said.

:o

My HP67 is charging next to me... a gift from 1975. I think HP programmed a zillion magic numbers in the darn thing! The bulk of the trig is CORDIC.

But I respect DrDiettrich statement. Code 'reads' better when Magic Numbers are not utilized. And self-documenting code strictly condemns such usage. Oh, well... it's been 44 years since I received a grade in Fortran programming class and since I was EE, I really did not listen too much to the instructor - come to think of it, the ol' man used to fall asleep in class half of the time anyway! And only graduate students could use the TSO.. we grunts had to use:

Ray

mrburnette:
But I respect DrDiettrich statement. Code 'reads' better when Magic Numbers are not utilized.

I agree that by providing a "magic number", it's hard for the OP to calculate for different resistor values or a different voltage, but at the same time I was trying to make it as understandable to him, and simple, as possible.

I see many cases of people using formulae that they don't understand at all, but just blindly use.

Anyway, I do basically agree with what Dr Dietrich said, and maybe I should really have provided both - the short form and the full formula.

When I said this, "And, of course, the result will be exactly what I said.", I was really just clarifying that the original formula was correct, and would work. I didn't want gc9n to think that there was an error.

With the 1.1V reference in use and a resistor divider of 91K and 11K across the battery with the 11k between AD pin and ground, you will have a relativly high value resistor divider, meaning low current, and you will read a 4V lipo to within 15mV accuracy, I use 1% resistors.

The AD reading returned is 10mV per step, so you dont need to use floating point to do the sums.

srnet:
With the 1.1V reference in use and a resistor divider of 91K and 11K across the battery with the 11k between AD pin and ground, you will have a relativly high value resistor divider, meaning low current, and you will read a 4V lipo to within 15mV accuracy, I use 1% resistors.

The AD reading returned is 10mV per step, so you dont need to use floating point to do the sums.

And this solution can be modeled by using the browser-only version of Circuit Simulator which is no longer Java but rather a web-friendly javascript. YES it can run on newer tablets!

Data can be exported or imported as a text file using the OS cut-paste buffer.

Ray