voltmeter/Reading a voltage/10. Calibrating and averaging
Reading a voltage · 10 of 12

Calibrating and averaging

Two fixes for two different errors. A multimeter reading divided by the sketch's reading gives one factor, CAL, that removes the steady offset from the resistors and the reference. Averaging sixteen readings removes a quarter of the wander. Neither does the other's job.

Two kinds of wrong

A reading can be wrong in two ways, and they need different fixes.

It can be off: 9.14 V every time for a battery the multimeter calls 9.00 V. That is the resistors' tolerance, the reference voltage your board measures against, and the ADC's own gain, all added up. It is the same from one reading to the next.

It can wander: 9.02, 8.98, 9.05, 8.99. That is noise in the ADC and on the wires, and it is different every time.

One factor, and an average
This block reads off by+1.5 %
Readings averaged
Sketch says
9.13 V
CAL
0.9852
Spread
full
The meter says 9.00 V and the sketch says 9.13 V, so CAL is 9.00 / 9.13 = 0.9852. Multiply every reading by it and the error is gone, whatever caused it. One reading at a time wanders; the dots are twenty of them. Averaging takes the wander out, calibration takes the offset out, and they do not replace each other.

Calibrating: one number

Run the first sketch with CAL at 1.000 and a battery on the terminal. Measure the same battery with a multimeter, probes on the two screws. Then:

CAL = what the meter says / what the sketch says

A meter at 9.00 V and a sketch at 9.14 V gives CAL = 0.9847. Put that in the sketch and every reading is multiplied by it. The offset is gone whatever caused it, because every cause was a multiplication too.

The block alone should need a factor within about 2 % of 1. Going by their part numbers the resistors are ±1 % parts, which puts the ratio within about 1.6 % of a fifth; your board's reference adds its own share. A factor much further from 1 is a wiring or settings mistake, not a calibration.

Averaging: sixteen readings

sigVolts() now reads SAMPLES times and returns the mean. Random wander shrinks by the square root of the number of readings, so sixteen leaves a quarter of it. The cost is time, and sixteen readings take a few milliseconds.

Averaging does nothing to the offset. Sixteen readings that are all 1.5 % high average to 1.5 % high. Calibrate, and average, and the reading is both steady and right.

The code

The first sketch with two changes: sigVolts() averages SAMPLES readings, and every reading is multiplied by CAL. Leave CAL at 1.000, measure the battery with a multimeter, then set CAL to the meter's reading divided by the sketch's.

voltmeter_calibrated.ino
/*
  Voltmeter - calibrated and averaged                    TK09 / /p/tk09

  Wiring. Count from the square pad on the TinkerBlock board, terminal
  at the top, header at the bottom:

    GND    -> GND
    NC     -> nothing   (both NC pins are unconnected on the board)
    SIG    -> A0 on an Uno, GPIO 34 on an ESP32, GPIO 4 on an
              ESP32-S3, GP26 on a Raspberry Pi Pico

  Terminal, same way up: left screw GND, right screw +. DC only, at
  most 25 V on an Uno, about 15.5 V on an ESP32 or ESP32-S3, 16.5 V on
  a Pico. Never mains.

  Arduino IDE
    Tools > Board                 your board, e.g. ESP32S3 Dev Module
    Tools > Port                  the one that appears when you plug in
    Tools > USB CDC On Boot       Enabled   (ESP32-S3 only)
    No library needed.
*/

// The pin SIG is wired to. Uno: A0. ESP32: 34. ESP32-S3: 4. Pico: 26.
const int SIG_PIN = A0;

// Uno: 5.0 and 1023. Pico: 3.3 and 1023. The ESP32s do not use these.
const float VREF = 5.0;
const float ADC_MAX = 1023.0;

const float SCALE = 5.0;   // 30 k over 7.5 k: the terminal is 5 x SIG
const float CAL = 1.000;   // multimeter / sketch, once you have both
const int SAMPLES = 16;    // averaging 16 leaves a quarter of the wander

float sigVolts() {
  float sum = 0;
  for (int i = 0; i < SAMPLES; i++) {
#if defined(ARDUINO_ARCH_ESP32)
    sum += analogReadMilliVolts(SIG_PIN) / 1000.0;
#else
    sum += analogRead(SIG_PIN) * VREF / ADC_MAX;
#endif
  }
  return sum / SAMPLES;
}

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

void loop() {
  float input = sigVolts() * SCALE * CAL;
  Serial.print("input ");
  Serial.print(input, 2);
  Serial.println(" V");
  delay(500);
}

CAL belongs to one block on one board with one supply. Move the block to another board, or the Uno to another USB port, and measure it again. Everything else is as in the first sketch: SIG_PIN, and VREF and ADC_MAX for the Uno and the Pico.

When it does not work

How accurate is my multimeter for this?

An ordinary hand-held meter is usually good to well under a percent on its DC volts range, which is better than the block uncalibrated. Its own accuracy is printed in its manual. Calibrating makes the sketch agree with the meter, so it can never be more accurate than the meter.

Do I need to calibrate at the voltage I will measure?

It helps. The divider's error is the same at every voltage, but an ESP32's ADC error is not, so calibrate near the middle of the range you care about: at 9 V for a 9 V battery, at 12 V for a 12 V one.

Why sixteen readings?

Averaging n readings shrinks random wander by the square root of n, so sixteen is a quarter of it. Sixty-four would be an eighth, and takes four times as long. Sixteen analogRead calls take a few milliseconds at most on any of these boards, which is nothing next to a half-second loop.

CAL came out as 1.2 or 0.8. Is that normal?

No. The block itself is good to within about 2 %. A factor that far from 1 means something else is wrong: VREF or ADC_MAX set for a different board, a source with resistance of its own, or the meter's probes on different points from the terminal's screws. Find it before calibrating it away.

Where this goes next

A 9 V battery, a TK01, and a warning that does not flicker.

A low-battery warning

Edit this page — content/books/voltmeter/calibrating-and-averaging.mdx

Community

Questions about this product

See what other owners have asked, and read their solutions.

Ask a question ↗

Voltmeter

Loading discussions…

Discuss this article

Ask about this page. The answer stays here, on the page it belongs to, for whoever hits the same wall next.

Browse Modules and blocks on the forum