switching analog reference in a sketch.

Want to make an adaptive voltmeter with the Arduino based upon the following idea;

I start with measuring with an analogReference of 5.0 volt and if the voltage is less than approx 1 volt (raw 200) , I switch the analog reference to INTERNAL and make a new measurement which should be ~5x more precise, => to get that 3rd digit . In short the analogRead would adapt its range automatically to the voltage supplied.

Written a sketch and it sort of works. The problem I encounter is that it takes quite a while (many reads) before the new analog reference "stabilizes". In the sketch below I use 64 reads to be sure it stabilizes. I have not tried yet to find the optimum nr of reads yet but 8 reads was too few. IN my next try I will make that adaptive too.

Q: is there a method known to get the analogRead() stable much quicker after a switch of the analogReference()?

//
//    FILE: volts.ino
//  AUTHOR: Rob Tillaart
// VERSION: 0.1.00
// PURPOSE: 
//    DATE: 
//     URL:
//
// Released to the public domain
//

void setup() 
{
  Serial.begin(115200);
  Serial.println("Start ");
  Serial.println(voltage(A0), 4);
}

void loop() 
{
  Serial.print(millis());
  Serial.print("\t");
  Serial.println(volts(A0), 4);
  delay(500);
}

float voltage(uint8_t pin)
{
  int raw;
  analogReference(DEFAULT);  // 5.0
  for (int i = 0 ; i < 64; i++) raw = analogRead(pin);
  Serial.print(raw);
  Serial.print("\t");
  Serial.print(raw * 5.0 / 1.1);  // expected raw for 1.1V
  Serial.print("\t");

  if (raw < 200)
  {
    analogReference(INTERNAL);  // 1.1
    for (int i = 0 ; i < 64; i++) raw = analogRead(pin);
    Serial.print(raw);
    Serial.print("\t");
    return raw * 1.1 / 1024;
  }
  Serial.print(raw);
  Serial.print("\t");
  return raw * 5.0 / 1024;
}

I am also thinking about connecting the externalReference pin to a PWM output of the Arduino with a low pass filter so I can set the analogReference(EXTERNAL) to 0 ... 5.0 Volts in 255 steps. Probably just use 5 or 10 levels in practice. But that is another experiment.
But if the above does not work well, this PWM experiment makes even less sense :slight_smile:

Switch the reference, do a analogRead and throw it away, wait 20ms. After that it should be good.
I did not check the library, but as far as I know you must read a value before waiting.
With "good" I mean use the average of at least 5 samples for the most accuracy.

A pwm output for the reference ? That would introduce so much inaccuracy that the default of 5V is just as good.
Averaging a few samples will get so much more accuracy.

I would rather use the internal reference and switch something outside the Arduino.
If you can use two analog inputs, with two ranges and safety for overload, you can read without delay.

Thanks,
your remakes confirm my experiments so far. However I do not like a hard coded delay().
The remark about averaging is a good one,
==> I could calc a running average and see when it stabilizes. that makes it adaptive again.

The PWM path is indeed too noisy, maybe using a voltage divider with at digital pot is a better way:)

Came up with this alternative analogRead() averaging.

It sums samples as long as the sum fits in a 16 bit int so the number of samples depends on the value of the analogRead() instead of using a fixed number of samples. The version below takes between 64 and 254 samples - quite a lot but these numbers can be adjusted of course -
Drawback is that the function takes between 8 and 40 millisec to return.

int AARead(int pin)
{
  uint16_t sum = 0;
  uint8_t samples = 0;
  while ((sum < (65535-1023)) && (samples < 255))  // prevents overflow of sum AND samples before it occurs
  {
    sum += analogRead(pin);
    samples++;
  }
  return (sum + samples/2)/samples;  // rounding
}

robtillaart:
The problem I encounter is that it takes quite a while (many reads) before the new analog reference "stabilizes".

That is consistent with the Atmega datasheet.

...R