Skip to content

Instantly share code, notes, and snippets.

@Jeff-Russ
Last active October 31, 2018 16:11
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Jeff-Russ/0aaf423cbdc81aca81e68e4fb7aa4fa8 to your computer and use it in GitHub Desktop.
Save Jeff-Russ/0aaf423cbdc81aca81e68e4fb7aa4fa8 to your computer and use it in GitHub Desktop.
Accurate Arduino analogRead() with a Simple VCC Detection

Accurate Arduino analogRead() with a Simple VCC Detection

This is a function to determine your Arduino's supply voltage automatically in order to convert the results of analogRead into a much more accurate voltage. As you may know, analogRead() returns a value that's relative to the supply voltage to the Arduino, which can deviate. If you feed a known voltage to an analog pin, this function can read and use it to calculate what Arduino's supply voltage is:

double getArduinoVcc(int analog_pin, double expected_volt)
{
    double received_frac = (double)analogRead(analog_pin) / 1023.0;    
    double expected_frac = (expected_volt / 5.0);  
    return (expected_frac / received_frac) * 5.0; // == vcc
}

Usage

You must have a known, accurate and stable voltage at analog_pin, be it from a precision reference IC or anything else you know is stable and accurate. The function will read the voltage at that pin and compare what it should see if the supply was 5.0V vs what it actually sees. The ratio between them * 5.0 is the supply voltage, which is returned. Normally you would do something like this:

int sensorValue = analogRead(A0);
float voltage = sensorValue * (5.0 / 1023.0);

But now you can do this:

// make sure A3 has 4.0 volts at this point in time...
float vcc = getArduinoVcc(A3, 4.0); // only called once. Use the vcc value everywhere

int sensorValue = analogRead(A0);
float voltage = sensorValue * (vcc / 1023.0);

Or to be more efficient, do vcc / 1023.0 just once and use everywhere:

double vcc_div1023 = getArduinoVcc(A3, 4.0) / 1023.0; // use everywhere

float voltage = analogRead(A0) * vcc_div1023;

Testing & Messing Around

In case you're curious how it work and/or want to test it without having the known voltage applied + simulating different supply voltages... put this atop your sketch:

// TESTING for getArduinoVcc()...
double fakeAnalogRead(double expected_volt, double simulated_vcc)
{
    return 1023.0 * (expected_volt/simulated_vcc);
}
double testGetArduinoVcc(int analog_pin, double expected_volt, double simulated_vcc)
{
    // received_frac * VCC == expected_volt... but what is VCC?
    double received_frac = (double)fakeAnalogRead(expected_volt, simulated_vcc) / 1023.0;
    Serial.println( "Got: " + String(received_frac) + " * VCC");

    // expected_frac is received_frac * VCC if VCC was exactly 5.0V
    double expected_frac = (expected_volt / 5.0);
    Serial.println( "Expected: " + String(expected_frac) + " * VCC");

    double error_frac = expected_frac / received_frac;
    Serial.print("VCC is "); Serial.println(error_frac * 5.0);
    return error_frac * 5.0; // == vcc
}

and call it like this:

void setup()
{
    Serial.begin(9600);
    double vcc = testGetArduinoVcc(A0, 4.1, 4.9);
    Serial.print(vcc);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment