18  Sensor Calibration Techniques

In 60 Seconds

Sensor calibration corrects manufacturing variations so readings match reality. One-point calibration fixes constant offset errors (sensor always reads 2.3°C high). Two-point calibration fixes both offset and scale errors using two reference values (ice water at 0°C, boiling water at 100°C). Store calibration coefficients in non-volatile memory and recalibrate periodically since sensors drift over time.

Key Concepts
  • Calibration: the process of comparing a sensor’s output against a known reference standard and applying correction factors to make readings accurate
  • Offset Error: a constant additive error where the sensor always reads too high or too low by the same amount regardless of the measured value; corrected by one-point calibration
  • Gain (Scale) Error: a proportional error where the sensor’s reading diverges from truth at a rate proportional to the measured value; requires at least two reference points to correct
  • One-Point Calibration: a calibration technique using a single reference value to correct offset error; appropriate when the sensor’s gain is known to be accurate
  • Two-Point Calibration: a calibration technique using two reference values to simultaneously correct offset and gain errors across a defined measurement range
  • Reference Standard: a known, traceable measurement source (ice-water bath at 0°C, boiling water at 100°C, certified weights) used as ground truth during calibration
  • Drift: the gradual change in a sensor’s calibration over time due to ageing, mechanical stress, or environmental exposure; requires periodic recalibration to maintain accuracy
  • Non-Volatile Memory (NVM): storage (EEPROM, flash) that retains calibration coefficients after power-off so the sensor does not need to be recalibrated every time it starts up

Learning Objectives

After completing this chapter, you will be able to:

  • Justify why sensor calibration is necessary for production IoT systems and identify which error type each technique corrects
  • Execute one-point and two-point calibration procedures using reference standards
  • Implement calibration routines in MicroPython on ESP32 and store coefficients in non-volatile memory
  • Design a calibration maintenance schedule for deployed sensors based on sensor type and environment

Calibration is like setting your bathroom scale to zero before weighing yourself. Every sensor has small manufacturing differences, so two identical temperature sensors might give slightly different readings in the same room. Calibration corrects these differences by comparing the sensor’s reading against a known reference value and applying a simple correction formula to get accurate results.

18.1 Prerequisites

18.2 Every Sensor Needs Calibration

~25 min | Intermediate | P06.C08.U06

Even the best sensors have manufacturing variations. Two DHT22 sensors from the same batch might read 0.5°C apart when measuring the same temperature. Calibration corrects these individual differences.

Types of Sensor Errors:

Error Type Description Fixable with Calibration?
Offset (Bias) Constant error at all values Yes - one-point calibration
Gain (Scale) Error proportional to reading Yes - two-point calibration
Nonlinearity Curved error across range Partially - multi-point calibration
Noise Random variation No - requires filtering
Drift Error changes over time Requires periodic recalibration

18.3 One-Point Calibration (Offset Correction)

When to use: Sensor reads consistently high or low by a fixed amount.

Method:

  1. Measure a known reference value
  2. Calculate the offset: offset = sensor_reading - true_value
  3. Subtract offset from all future readings
# One-Point Calibration Example

# Step 1: Measure reference (ice water = 0C)
reference_temp = 0.0
sensor_reading = 2.3  # Sensor reads 2.3C in ice water

# Step 2: Calculate offset
offset = sensor_reading - reference_temp  # offset = 2.3

# Step 3: Apply correction
def read_calibrated_temp():
    raw = sensor.read()
    return raw - offset  # Subtract 2.3 from all readings

18.3.1 Interactive: One-Point Offset Calculator

18.4 Two-Point Calibration (Offset + Gain Correction)

When to use: Sensor has both offset error AND scale error.

Method:

  1. Measure two known reference values (low and high)
  2. Calculate scale and offset
  3. Apply linear correction: corrected = raw * scale + offset
# Two-Point Calibration Example

# Step 1: Reference measurements
ref_low = 0.0    # Ice water
ref_high = 100.0  # Boiling water

raw_at_low = 1.8   # Sensor reads 1.8 in ice water
raw_at_high = 98.5  # Sensor reads 98.5 in boiling water

# Step 2: Calculate calibration coefficients
scale = (ref_high - ref_low) / (raw_at_high - raw_at_low)
# scale = (100 - 0) / (98.5 - 1.8) = 1.034

offset = ref_low - (raw_at_low * scale)
# offset = 0 - (1.8 * 1.034) = -1.86

# Step 3: Apply correction
def read_calibrated_temp():
    raw = sensor.read()
    return raw * scale + offset

Two-point calibration corrects both gain and offset errors. Let \(R\) = reference (true) value and \(M\) = measured (raw sensor) value. Given ice water (0°C) reads 1.8°C and boiling water (100°C) reads 98.5°C:

\[\text{scale} = \frac{R_{high} - R_{low}}{M_{high} - M_{low}} = \frac{100 - 0}{98.5 - 1.8} = \frac{100}{96.7} = 1.034\]

\[\text{offset} = R_{low} - M_{low} \times \text{scale} = 0 - 1.8 \times 1.034 = -1.86\]

Corrected reading: \(T_{corrected} = T_{raw} \times 1.034 - 1.86\). At room temperature (sensor reads 24.5°C): \(T_{corrected} = 24.5 \times 1.034 - 1.86 = 23.5°C\). Without calibration, the 1.0°C error would violate many HVAC control requirements.

18.4.1 Interactive: Two-Point Calibration Calculator

Enter your sensor’s raw readings at two known reference points to compute the calibration coefficients and test a correction.

18.5 Practical Calibration References

Measurement Reference Accuracy Notes
Temperature Ice water (0°C) +/-0.1°C Use crushed ice, stir
Temperature Boiling water (100°C*) +/-0.5°C *Altitude dependent
Temperature Room thermometer +/-0.5°C Use NIST-traceable reference
Humidity Saturated salt (75.3% RH) +/-0.5% Sodium chloride (NaCl) solution
Humidity Saturated salt (32.8% RH at 25°C) +/-0.5% Magnesium chloride (MgCl₂)
Pressure Weather station data +/-1 hPa Compare with local airport
Distance Tape measure +/-1mm Fixed distance reference

18.6 Multi-Point Calibration

When to use: Sensor has nonlinear response that two-point calibration cannot correct (as demonstrated in the worked example below).

The following example uses NumPy for polynomial fitting. On resource-constrained microcontrollers, compute the coefficients on a PC and hard-code them, or use a simple lookup table with linear interpolation between points.

# Multi-Point Calibration with Polynomial Fitting (run on PC)

import numpy as np

# Calibration points: (true_reference, raw_sensor_reading)
calibration_points = [
    (0.0, 1.8),
    (25.0, 26.5),
    (50.0, 52.1),
    (75.0, 77.8),
    (100.0, 98.5)
]

# Separate reference and raw values
refs = [p[0] for p in calibration_points]
raws = [p[1] for p in calibration_points]

# Fit quadratic polynomial: true = a*raw^2 + b*raw + c
coefficients = np.polyfit(raws, refs, deg=2)
print(f"Coefficients: {coefficients}")
# Use these coefficients in your microcontroller code

def calibrate(raw):
    """Apply polynomial correction."""
    return np.polyval(coefficients, raw)

# Verify at calibration points
for ref, raw in calibration_points:
    corrected = calibrate(raw)
    print(f"Raw: {raw:.1f} -> Corrected: {corrected:.1f} (Expected: {ref:.1f})")
Try It: Multi-Point Polynomial Calibration Explorer

Enter 3–5 calibration points (reference vs raw) and see how a quadratic polynomial fits the data. Adjust individual points to observe how nonlinearity affects the correction curve.

18.7 Storing Calibration Data

Best Practice: Store calibration coefficients in non-volatile memory (EEPROM/flash):

import json

# Save calibration to file
calibration = {
    "sensor_id": "DHT22_001",
    "date": "2026-01-19",
    "scale": 1.034,
    "offset": -1.86,
    "reference_instrument": "NIST_thermometer_123"
}

with open("calibration.json", "w") as f:
    json.dump(calibration, f)

# Load calibration at startup
with open("calibration.json", "r") as f:
    cal = json.load(f)
    scale = cal["scale"]
    offset = cal["offset"]

18.8 Best Practices for Sensor Calibration

Calibration Checklist
  1. Document everything: Date, reference instrument, environmental conditions, coefficients
  2. Use stable conditions: Allow sensor to warm up (30 min for gas sensors), avoid drafts
  3. Average multiple readings: Take 10–20 readings at each calibration point to reduce noise
  4. Use accurate references: Your reference must be more accurate than your sensor
  5. Verify after calibration: Validate against a third reference NOT used in calibration
  6. Recalibrate periodically: Follow the schedule guidelines below for your sensor type
  7. Store coefficients safely: Non-volatile memory (EEPROM/flash) with backup
  8. Avoid overfitting: Too many polynomial terms can amplify noise between calibration points

18.9 Calibration Schedule Guidelines

Sensor Type Recommended Interval Trigger Events
Temperature Every 6 months After shipping, extreme temps
Humidity Every 3 months After high humidity exposure
Pressure Every 12 months After altitude changes
Gas sensors Every 3-6 months After contamination
pH sensors Before each use After storage
Load cells Every 12 months After overload events
Try It: Calibration Drift Estimator

Sensors drift over time due to aging, contamination, and environmental stress. Estimate when your sensor will exceed its accuracy tolerance and needs recalibration.

18.10 Worked Example: Complete Two-Point Calibration on ESP32

This example walks through a real calibration procedure for a DHT22 temperature sensor deployed in a greenhouse monitoring system. We cover the physical setup, the math, the code, and how to validate the result.

Scenario: You are deploying 20 DHT22 sensors in a commercial greenhouse. Each sensor will trigger HVAC adjustments, so accuracy matters – a 2°C error could stress plants or waste energy. Your reference instrument is a NIST-traceable digital thermometer accurate to +/-0.1°C.

Step 1: Prepare Reference Points

Reference Setup Expected Reading Notes
Low point Crushed ice + water slurry in insulated container 0.0°C +/- 0.1°C Stir continuously, wait 5 min for equilibrium
High point Warm water bath at greenhouse max temp 45.0°C (from reference thermometer) Use 45°C, not 100°C – closer to operating range gives better results

Why 45°C instead of 100°C? The DHT22 operates between 0–50°C in a greenhouse. Calibrating at 100°C (outside operating range) introduces extrapolation error. Always calibrate within or near the expected operating range.

Step 2: Collect Raw Readings

Place the DHT22 and reference thermometer side by side in each bath. Wait 3 minutes for the sensor to stabilize, then take 20 readings over 2 minutes:

# MicroPython on ESP32 -- collect calibration data
import dht
import machine
import time

sensor = dht.DHT22(machine.Pin(4))

def collect_readings(n=20, interval_ms=6000):
    """Collect n readings with interval between each.
    DHT22 minimum sampling period is 2 seconds;
    we use 6 seconds for better stability."""
    readings = []
    for i in range(n):
        sensor.measure()
        temp = sensor.temperature()
        readings.append(temp)
        print(f"Reading {i+1}: {temp:.1f}C")
        time.sleep_ms(interval_ms)
    return readings

# Collect at low reference (ice bath, reference = 0.0C)
print("Place sensor in ice bath, wait 3 min, then press Enter")
low_readings = collect_readings()
# Result: [2.1, 2.3, 2.2, 2.3, 2.1, 2.2, 2.3, 2.2, 2.1, 2.3,
#          2.2, 2.2, 2.3, 2.1, 2.2, 2.3, 2.2, 2.1, 2.2, 2.1]

# Collect at high reference (warm bath, reference = 45.0C)
print("Place sensor in warm bath, wait 3 min, then press Enter")
high_readings = collect_readings()
# Result: [43.8, 43.9, 43.7, 43.8, 43.9, 43.8, 43.7, 43.8, 43.9, 43.8,
#          43.7, 43.8, 43.9, 43.8, 43.7, 43.9, 43.8, 43.8, 43.7, 43.8]

Step 3: Calculate Calibration Coefficients

# Average the readings to reduce noise
raw_low = sum(low_readings) / len(low_readings)   # 2.20C
raw_high = sum(high_readings) / len(high_readings) # 43.80C

ref_low = 0.0    # Ice bath reference
ref_high = 45.0  # Reference thermometer reading

# Two-point linear calibration: corrected = raw * scale + offset
scale = (ref_high - ref_low) / (raw_high - raw_low)
# scale = (45.0 - 0.0) / (43.80 - 2.20) = 45.0 / 41.60 = 1.0817

offset = ref_low - (raw_low * scale)
# offset = 0.0 - (2.20 * 1.0817) = -2.38

print(f"Scale:  {scale:.4f}")   # 1.0817
print(f"Offset: {offset:.2f}")  # -2.38

Step 4: Apply and Validate

def read_calibrated():
    """Read temperature with two-point calibration applied."""
    sensor.measure()
    raw = sensor.temperature()
    corrected = raw * scale + offset
    return round(corrected, 1)

# Validation: measure a third reference point NOT used in calibration
# Room temperature, reference thermometer reads 22.5C
validation_readings = collect_readings(n=10)
# Raw readings: [21.4, 21.5, 21.4, 21.5, 21.4, 21.5, 21.4, 21.5, 21.4, 21.5]
# Raw average: 21.45C
# Calibrated: 21.45 * 1.0817 + (-2.38) = 23.20 - 2.38 = 20.82C...

# Wait -- that's off by 1.7C! Let's check the math:
# calibrated = 21.45 * 1.0817 - 2.38 = 23.20 - 2.38 = 20.82C
# Expected: 22.5C, Got: 20.82C -- error of 1.68C

# This reveals NONLINEARITY in the sensor. The DHT22 error
# is not purely linear across its range. For better accuracy
# at room temperature, add a third calibration point.

The Validation Catch: This is why Step 4 matters. Two-point calibration assumes the sensor error is linear. When validation at a third point reveals significant residual error (>0.5°C for DHT22), you need multi-point calibration instead.

Try It: Calibration Validation Checker

After calibrating a sensor, test it against a third reference point to verify accuracy. Enter your calibration coefficients and a validation measurement to determine if your calibration is adequate or if you need a different approach.

Step 5: Store Coefficients in Flash

import json

cal_data = {
    "sensor_id": "DHT22_GH_014",
    "date": "2026-02-07",
    "scale": round(scale, 4),
    "offset": round(offset, 2),
    "ref_instrument": "Fluke_1524_SN4892",
    "ref_low": ref_low,
    "ref_high": ref_high,
    "raw_low_avg": round(raw_low, 2),
    "raw_high_avg": round(raw_high, 2),
    "validation_error": 1.68,
    "next_cal_date": "2026-08-07"
}

# Save to ESP32 flash filesystem
with open("cal.json", "w") as f:
    json.dump(cal_data, f)

# Load at boot
with open("cal.json", "r") as f:
    cal = json.load(f)
    scale = cal["scale"]
    offset = cal["offset"]
Lesson from This Example

The validation step (Step 4) caught a problem that two-point calibration alone would have missed. In production, always validate against a reference point that was NOT used for calibration. If the validation error exceeds your application’s tolerance (here, 0.5°C for greenhouse HVAC), switch to multi-point calibration or select a more linear sensor (e.g., the SHT31 has better linearity than the DHT22).

Common Calibration Pitfall

Temperature and humidity affect many sensor types beyond the one you are calibrating. A pressure sensor calibrated in a heated lab may behave differently at outdoor deployment temperatures. Always note the ambient conditions during calibration and recalibrate if the deployment environment differs significantly from the calibration environment.

Sammy the Sensor was feeling embarrassed. “I keep saying it is 2.3 degrees when it should be zero!” he told the Squad while sitting in a bowl of ice water.

“Do not worry, Sammy!” said Max the Microcontroller. “That is totally normal. Every sensor is a little bit different from the factory. We just need to calibrate you!”

“Cali-what?” asked Lila the LED.

Max explained: “We dip Sammy in ice water – we KNOW that is 0 degrees. If Sammy says 2.3, we learn his offset. Then we dip him in boiling water – we KNOW that is 100 degrees. If Sammy says 98.5, we learn his scale. Now I can do math to fix every future reading!”

Bella the Battery pulled out a notebook. “We write down today’s date, what references we used, and the calibration numbers. In six months, we do it again because sensors can drift over time – like a clock that slowly gets behind.”

“And ALWAYS use a reference that is MORE accurate than the sensor you are calibrating,” Max added. “Otherwise, it is like asking someone who is MORE lost for directions!”

Try It: Calibration Method Advisor

Describe your sensor scenario and get a recommended calibration approach.

18.11 Summary

Key calibration takeaways:

  1. All sensors benefit from calibration - Even factory-calibrated ones
  2. One-point for offset - Simple constant correction
  3. Two-point for offset + gain - Linear correction
  4. Multi-point for nonlinearity - Polynomial or lookup table
  5. Recalibrate regularly - Sensors drift over time

Beginner: You have a DHT22 that reads 23.8°C while your reference thermometer reads 22.0°C. Calculate the offset and write one line of code to apply it. Then check: if the DHT22 reads 30.5°C, what is the corrected temperature?

Intermediate: Using the MicroPython code from the worked example above, calibrate a sensor with ice water (0°C) and a warm bath (40°C). Your raw readings average 1.5°C at the low point and 38.9°C at the high point. Calculate the scale and offset, then predict the corrected reading if the sensor outputs 25.0°C.

Advanced: An MQ-135 gas sensor gives these readings at known CO₂ concentrations: (400 ppm, raw=120), (800 ppm, raw=280), (1200 ppm, raw=390), (1600 ppm, raw=460), (2000 ppm, raw=505). Plot these points – is the relationship linear? Fit a quadratic polynomial and calculate the predicted concentration when the sensor outputs raw=350.

18.12 Concept Relationships

Core Concept Related Concepts Why It Matters
Offset Error Bias, Zero Point Constant shift at all values
Gain Error Scale Factor, Span Proportional error increasing with reading
Nonlinearity Polynomial Fit, Lookup Tables Cannot be fixed with two-point calibration
Drift Aging, Temperature, Recalibration Schedule Calibration degrades over time

Common Pitfalls

Calibrating a sensor against a reference that has not been verified against a primary standard propagates the reference’s error into the calibrated sensor. Always trace reference standards to national measurement standards (NIST/NPL) or use certified reference materials.

A one-point offset calibration at 25 C assumes the offset is constant across the sensor’s range. Many sensors have temperature-dependent offset. Perform calibration at the actual expected operating temperature, or characterize and compensate for the temperature coefficient in firmware.

Many sensors require a warm-up period (2-30 minutes) for internal electronics to stabilize. Calibrating during warm-up captures an unstable reference reading and produces incorrect coefficients. Always wait for the manufacturer’s recommended stabilization time before taking calibration reference readings.

Verifying calibration only at the two reference points used to derive the coefficients provides false confidence. Linearity errors are not visible at calibration endpoints. Always verify the calibrated output at 3-5 intermediate values spanning the measurement range.

18.13 What’s Next

If you want to… Read this
Practice calibration hands-on with an ESP32 simulator Sensor Calibration Lab (Wokwi)
Learn signal filtering to complement calibration Sensor Data Processing
Understand sensor specifications that calibration corrects Sensor Specifications
Apply calibration in a complete IoT sensor deployment Sensor Labs: Implementation and Review