Exercise: Calibrate a Sensor
This exercise builds a practical calibration workflow. You will collect two known points, compute scale and offset, convert raw ADC readings into engineering units, and verify the result. The example uses a simulated linear displacement sensor so the code can be built on a laptop before being moved to embedded firmware.
Learning Objectives
You will:
- perform two-point calibration for a linear sensor;
- implement fixed-point conversion without floating-point dependency;
- verify zero, span, midpoint, and monotonic behavior;
- identify offset, gain, noise, saturation, and reversed-wiring faults;
- document calibration constants with clear units.
Prerequisites
- Basic ADC concepts: code, resolution, reference voltage, and input range.
- Basic C programming with integer arithmetic.
- Ability to compile C code on a host computer or embedded target.
- A sensor, potentiometer, calibration jig, or simulated voltage source with two known reference points.
- Safe access to the mechanism with power removed or motion disabled during setup.
Task
A linear displacement sensor is read by an ADC. During calibration:
- at
0.00 mm, the measured raw code is820; - at
100.00 mm, the measured raw code is3270.
Write code that converts any raw code to displacement in 0.01 mm units. Clamp the result below zero and above span. Then verify that the midpoint code reports 50.00 mm.
Calibration Formula
For two-point linear calibration:
$$
y = y_0 + (x - x_0)\frac{y_1 - y_0}{x_1 - x_0}
$$
For this exercise, x is raw ADC code and y is position in centi-millimetres.
Buildable Implementation
Save this as sensor_calibration.c and build with a normal C compiler, for example gcc sensor_calibration.c -o sensor_calibration.
#include <stdint.h>
#include <stdio.h>
#define CODE_ZERO 820
#define CODE_SPAN 3270
#define POS_ZERO_CENTI_MM 0
#define POS_SPAN_CENTI_MM 10000
static int32_t code_to_centi_mm(int32_t code) {
const int32_t code_range = CODE_SPAN - CODE_ZERO;
const int32_t pos_range = POS_SPAN_CENTI_MM - POS_ZERO_CENTI_MM;
if (code_range <= 0) {
return POS_ZERO_CENTI_MM; /* invalid calibration constants */
}
if (code <= CODE_ZERO) {
return POS_ZERO_CENTI_MM;
}
if (code >= CODE_SPAN) {
return POS_SPAN_CENTI_MM;
}
return POS_ZERO_CENTI_MM + ((code - CODE_ZERO) * pos_range) / code_range;
}
static void print_position(int32_t code) {
int32_t pos = code_to_centi_mm(code);
printf("code=%ld position=%ld.%02ld mm\n",
(long)code,
(long)(pos / 100),
(long)(pos % 100));
}
int main(void) {
int32_t tests[] = {700, 820, 2045, 3270, 3400};
const unsigned count = sizeof(tests) / sizeof(tests[0]);
for (unsigned i = 0; i < count; ++i) {
print_position(tests[i]);
}
return 0;
}
Expected Behavior
| Raw code | Expected position | Reason |
|---|---|---|
700 |
0.00 mm |
clamped below calibrated zero |
820 |
0.00 mm |
zero reference |
2045 |
50.00 mm |
midpoint because it is halfway between 820 and 3270 |
3270 |
100.00 mm |
span reference |
3400 |
100.00 mm |
clamped above calibrated span |
Verification Steps
- With motion disabled, place the mechanism at the zero reference and record several raw readings.
- Place the mechanism at the span reference and record several raw readings.
- Confirm that
CODE_SPANis greater thanCODE_ZERO; otherwise the sensor direction or constants are wrong. - Build and run the program.
- Verify zero, midpoint, and span outputs.
- Move the mechanism slowly and confirm that position changes monotonically.
- Repeat readings at the same position to estimate noise and repeatability.
- Store calibration constants with date, units, sensor ID, and mechanical setup notes.
Common Failure Symptoms
| Symptom | Likely cause | First check |
|---|---|---|
| zero reads nonzero | wrong zero point, preload, offset drift | log raw code at zero |
| span is wrong but zero is correct | wrong span point or gain calculation | recompute code range |
| output decreases with travel | sensor direction reversed | compare zero and span codes |
| value jumps randomly | loose connector, noisy supply, ADC reference noise | inspect raw codes before filtering |
| value saturates early | sensor range or mechanical travel mismatch | measure voltage at both endpoints |
| works on bench but not in machine | mounting changed calibration | recalibrate after installation |
Debugging Guidance
- Print raw ADC code before printing calibrated position.
- Confirm ADC reference voltage, sensor supply voltage, and input range.
- Move the mechanism slowly while watching raw code; it should be smooth and monotonic.
- Check cable shield termination and strain relief before adding software filtering.
- Average several samples only after wiring and range are correct.
- Use fixed-width integer types and check multiplication range before increasing span units.
- Reject calibration constants that produce zero or negative span.
Extension Challenge
Add a third verification point at 75.00 mm. The expected raw code is:
$$
x_{75} = 820 + 0.75(3270 - 820) = 2657.5
$$
Use either 2657 or 2658 as the nearest integer test code. If the converted value is outside +/-0.50 mm, report a calibration warning. Then add EEPROM or flash storage for calibration constants with a checksum or version field.
Explained Solution
Two-point calibration fits a straight line between a known zero point and a known span point. The zero code defines offset. The difference between span and zero codes defines gain. The implementation subtracts the zero code, multiplies by the engineering-unit span, divides by the code span, and clamps outside the calibrated travel. Clamping avoids unsafe extrapolated positions, but it should not hide a wiring or travel-range fault during commissioning.
Summary
Calibration turns raw sensor values into trustworthy engineering units. The workflow is measure known points, compute scale and offset, verify intermediate points, check repeatability, preserve constants with clear units, and debug raw measurements before trusting filtered values.
Further Reading
- NIST Engineering Statistics Handbook, calibration concepts.
- Texas Instruments application notes on sensor calibration and linearization.
- IEC 60770 transmitter performance methods for industrial-process measurement.