|
SecretVoltmeter
Accessing the secret voltmeter on the Arduino 168 or 328
Featured IntroductionIt turns out the Arduino 168 and 328 can measure their own voltage rail. CodeCopy, paste into Arduino and see what it returns. This works on an Arduino 168 or 328. long readVcc() {
long result;
// Read 1.1V reference against AVcc
ADMUX = _BV(REFS0) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);
delay(2); // Wait for Vref to settle
ADCSRA |= _BV(ADSC); // Convert
while (bit_is_set(ADCSRA,ADSC));
result = ADCL;
result |= ADCH<<8;
result = 1126400L / result; // Back-calculate AVcc in mV
return result;
}
void setup() {
Serial.begin(9600);
}
void loop() {
Serial.println( readVcc(), DEC );
delay(1000);
}The voltage is returned in millivolts. So 5000 is 5V, 3300 is 3.3V. Note the following:
How it worksThe Arduino 328 and 168 have a built in precision voltage reference of 1.1V. This is used sometimes for precision measurement, although for Arduino it usually makes more sense to measure against Vcc, the positive power rail. The chip has an internal switch that selects which pin the analogue to digital converter reads. That switch has a few leftover connections, so the chip designers wired them up to useful signals. One of those signals is that 1.1V reference. So if you measure how large the known 1.1V reference is in comparison to Vcc, you can back-calculate what Vcc is with a little algebra. That is how this works. See alsoThe SecretThermometer in the Arduino 328 |
Isn't this comparing the 1.1V analog reference voltage to what's connected to AVcc? So you're only measuring Vcc if it's tied to AVcc, correct?
@blalor Yes. AVcc is connected to Vcc on Arduinos. The processor spec sheet says AVcc must be within 300mV of Vcc at all times, so I can't see any circuit scenario where it wouldn't be near identical to Vcc.
Man, I'm really on a roll, eh? :-( I got ARef and AVcc confused. I don't understand why you'd need a separate voltage source for the ADC, but I do see that note and get the difference, now. Sorry for the noise…
@blalor No problem. AVcc is separate from Vcc so you can add power supply filters to keep the digital noise away, or even use separate voltage regulators. But the interface between analog and digital sections breaks down unless you keep the voltages close.
Not having much progress with an Arduino Mega. It's spitting out ~1101 (1.101 volts), while displaying negative results every now and then.
@didanix "This works on Arduinos with a 328 or 168 only."
Thanks. Just found this through a link on the arduino forum: http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1263440087
Very useful. I can confirm that it works on a ATMega328P-PU (arduino duemilanove).
:)
Looking through the datasheet, it should be possible to measure ARef by setting
(ie. leaving REFS1 and REFS0 at 0). Is that correct? Instead of selecting AVcc I'd be selecting ARef. Both against Vbg. Or is there a danger there with switching the voltage reference with something attached to ARef?
@blalor Sounds fine to me. Aref could then potentially be used as an extra analogue input by that method. (It can also be used as a digital output - but not a very good one). Try it out!
Hmmm.... great work! Can't you use this to obviate the need for any external reference voltage whatsoever?
If you know the precise Vcc then you can just use it as the ARef, even if you don't have a regulated power supply (aka just running it off AAs).
This is great for my little thermometer project, thanks!
robokaren
Can confirm that this also works on the ATtiny84 (using the arduino-tiny core) if you change the registers to:
ADMUX = BV(MUX5) | BV(MUX0);
Nathan.
Is it possible to adapt the code for an ATmega32U4 based board? Did someone make it? What are the necessary changes? Thank you in advance for any advice on this isuue. Dimitar
Using trial and error approach, I found (I think so...) that it works on ATmega32U4 too and answered my own question. I used the following line:
ADMUX = BV(REFS0) | BV(MUX2);
And it seems that if you use:
ADMUX = BV(REFS0) | BV(MUX1);
you can measure the 1.1V line inside ATmega32U4. Is that correct? I experimented with my Olimexino-32U4, which is an Arduino Leonardo compatible board and posted the adapted code on my blog page: http://open-dc-ups-monitor.blogspot.com/ Best wishes, Dimitar