Language: EN

analogicas-mas-precisas

More accurate analog inputs by measuring Vcc on Arduino

In this post we are going to see a little trick that will allow us to obtain measurements of our analog inputs more accurate.

It is little known that some models of Arduino can measure their own voltage to which they are powered. This is valid for Arduino models based on AVR 168 and 328 processors.

For this, the 1.1V reference is used, which is built into most Arduino models. We can use the Arduino ADC to measure this reference and, by interpolation, measure the actual voltage at which Arduino is being supplied.

long readVcc() {
  ADMUX = _BV(REFS0) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);
  delay(2);
  ADCSRA |= _BV(ADSC);
  while (bit_is_set(ADCSRA, ADSC));
  
  long 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);
}

Where the value 1126400L corresponds to 1.1 x 1024 x 1000. To have adequate precision we must measure the 1.1V reference of our Arduino with a multimeter to calibrate this value. This value is independent of Vcc, so it only needs to be calibrated once.

What is this for? Mainly to rectify the ADC measurements and thus obtain more accurate analog inputs than those we would obtain without this rectification.

The ADC takes the Vcc of Arduino as reference. Therefore, any variation in the supply voltage results in a proportional error in the analog measurement.

By measuring the actual supply voltage, we can correct possible deviations due to variations in Vcc.

long readVcc()
{
  ADMUX = _BV(REFS0) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);
  delay(2);
  ADCSRA |= _BV(ADSC);
  while (bit_is_set(ADCSRA, ADSC));
  
  long result = ADCL;
  result |= ADCH << 8;
  result = 1126400L / result; // Back-calculate AVcc in mV
  return result;
}

void setup()
{
  Serial.begin(9600);
}

void loop()
{
  float vcc = readVcc() / 1000.0;
  float voltage = (analogRead(A0) / 1024.0) * Vcc;

  Serial.println(voltage);
  delay(1000);
}

A little trick that can be useful when we need to make more accurate analog readings on Arduino, when the need for precision justifies the increase in code.

Download the code

The entire code for this post is available for download on Github. github-full