← Home Few Bits
Cover image for DriftTrack — RTC Drift Calibration for Tiny MCUs

DriftTrack — RTC Drift Calibration for Tiny MCUs

Alex Solis Alex Solis ·

Why calibrate the RTC?

Tiny microcontrollers often run their clocks from a cheap internal RC oscillator or a low‑power crystal that still drifts with temperature and voltage. For devices that log events, timestamp telemetry, or wake at scheduled times, that drift quickly becomes annoying. External RTC chips solve this, but they cost parts, pins and power. DriftTrack is a low‑complexity approach: measure the drift, compute a small correction factor, and apply it in software so your timestamps stay useful without adding hardware.

The idea in one sentence

Measure the real time elapsed versus the MCU's tick count, compute a correction multiplier, store that multiplier in nonvolatile memory, and apply it when translating ticks to wall time — only updating the stored calibration when a meaningful change is detected.

What you need

  • Any tiny MCU (AVR/ATTiny, ESP8266/ESP32, Cortex‑M0+) with a timer or systick.
  • A reference clock: smartphone, PC, NTP, or a 1 Hz pulse from a known good source.
  • Nonvolatile storage: one flash page, EEPROM, or emulated flash area.

Measurement procedure (hands on)

  1. Start with a known baseline: set the MCU's software clock to a clear known wall time.
  2. Let it run for a reasonably long interval. Short tests lie — aim for at least 1 hour. Better: 6–24 hours. Longer gives cleaner numbers.
  3. At the end, read the MCU's elapsed ticks and compare to the real elapsed seconds from your reference.
  4. Compute a correction multiplier M = real_seconds / measured_seconds.

Example: if your MCU counted 3590 seconds but the real elapsed time was 3600 seconds, M = 3600/3590 ≈ 1.002785. You'll use M to scale future measured seconds into corrected seconds.

Applying the correction

Keep your internal timebase as ticks (e.g., timer overflows or systicks). When you need a wall timestamp, convert ticks→seconds then multiply by M, or maintain the current wall time and advance it by scaled deltas on each tick. Using scaled deltas avoids accumulating large rounding error when M is applied repeatedly.

Simple pseudocode (fixed‑point friendly):

// Fixed point Q16 multiplier uint32_t M_q16 = (uint32_t)(M * 65536.0 + 0.5); // When delta_ticks gives delta_seconds (float) use integer arithmetic: uint32_t corrected_seconds = (delta_seconds * M_q16) >> 16;

On tiny MCUs where floating point is expensive, store M as a Q16 or Q24 fixed point and do integer multiplies. If your tick source is in microseconds, convert to seconds using integer division first, then apply the multiplier.

Storing the calibration safely

Flash/EEPROM writes are limited. Keep writes rare and atomic:

  • Only write M when the new multiplier differs from the stored one by more than a threshold (for example, 20 parts per million).
  • Use two slots (A/B) and a small CRC or version counter to avoid corrupt states. Write the new slot, then update a flag or version to point to the new slot.
  • If writes are still critical, compress the multiplier to a compact fixed‑point format and only update once a day.

Simple AVR example

// after measurement: uint32_t measured = measured_seconds; // integer seconds from timer uint32_t real = real_seconds; // reference seconds // store as Q16 uint32_t M_q16 = (uint32_t)((((uint64_t)real) << 16) / measured); // write M_q16 to EEPROM if it changed significantly

When updating system time: multiply your delta_seconds by M_q16 using 64‑bit intermediate to avoid overflow, then shift right 16.

Practical tips and gotchas

  • Temperature matters: if your device sees large temperature swings, one calibration may not be enough. Either calibrate at the expected operating temperature or implement a two‑point correction using a cheap silicon temperature sensor (or even the MCU’s internal temp reading) and interpolate.
  • Voltage affects RC oscillators. If your device runs on a battery that droops significantly, calibrate at low and high voltages or avoid RC oscillators for critical timing.
  • Jitter and resolution: short measurement windows amplify timer quantization. Use the longest practical interval you can.
  • Watch for nonlinearity: some cheap RC oscillators don't behave linearly over long periods. If residual error remains after calibration, consider adding a tiny external 32.768 kHz crystal or an external RTC chip.
  • Don't rewrite on every boot. Rewriting the same calibration value shortens flash life — only write when the improvement exceeds your threshold.

Verification and continuous tuning

After applying M, run another validation window (shorter acceptable, e.g., 30–60 minutes). Compute residual error. If residual error is within your tolerance, keep the value. If not, extend the measurement window and recompute. For long‑lived devices, occasional background recalibration (e.g., weekly) is a low‑risk way to track slow drift.

When to reach for an external RTC

If you need sub‑second accuracy over months, or your environment has wide temperature swings and battery voltage variation, an external RTC or temperature‑compensated clock is the pragmatic choice. DriftTrack is for when you want fewer parts and good‑enough timestamps with minimal complexity.

Keep it minimal, keep it honest: measure for longer, store less, and correct in software. Often that gets you 90% of the benefit for 10% of the cost.

Wrap up

DriftTrack lets you squeeze useful timekeeping out of tiny MCUs without extra hardware. The recipe is simple: measure long, compute a fixed‑point multiplier, store it carefully, and apply it when converting ticks to wall time. You'll save parts and power — and learn a surprising amount about how your MCU behaves in the real world.