Date
Ref
PC-260902-Q9M
Revised
Length
4 min read
Tags

Fourteen days with three budget real-time clocks: measured drift, temperature, and what the datasheets leave out

Three inexpensive RTC modules were logged against a GPS-disciplined reference for two weeks. The temperature-compensated part met its specification; the two uncompensated parts drifted five to eight times more than their headline figures suggest.

Line chart of accumulated clock error in seconds over fourteen days for three RTC modules; two curves climb steadily while one stays close to zero.

This is a sample post. Part names and numbers are invented so the layout can be reviewed with realistic material.

Why this measurement

Datasheets for low-cost real-time clock (RTC) chips quote accuracy as a parts-per-million figure at 25 °C. That number is honest as far as it goes, but a device on a shelf, in a cabinet or outdoors does not live at 25 °C, and the crystal’s frequency error is a parabola in temperature.1 The question here was simple: how far do three common modules actually drift over two weeks in an ordinary indoor environment, and does the cheap temperature-compensated part earn its price?

Setup

Three modules were tested, all bought from the usual online marketplaces for under ten dollars each:

ModuleOscillatorClaimed accuracyInterfacePrice paid
RTC-A32.768 kHz crystal, uncompensated±20 ppm at 25 °CI²C$1.90
RTC-B32.768 kHz crystal, uncompensated±2 min/month (≈ ±46 ppm)I²C$2.40
RTC-CIntegrated TCXO±2 ppm, 0–40 °CI²C$7.80

A single microcontroller polled all three once a minute, alongside a GPS receiver’s pulse-per-second output used as the reference. Board temperature was logged from a separate sensor within a few centimetres of the modules. The whole rig sat on a desk near a north-facing window in an unheated room; daily temperature swing was about 9 °C.

The logging loop is short enough to show in full. Each line records the reference second, each module’s reported second, and the temperature:

void loop_once(void)
{
    wait_for_pps();                     // rising edge from the GPS module
    uint32_t ref = gps_epoch_seconds(); // from the last NMEA sentence

    uint32_t a = rtc_read_epoch(RTC_A_ADDR);
    uint32_t b = rtc_read_epoch(RTC_B_ADDR);
    uint32_t c = rtc_read_epoch(RTC_C_ADDR);
    int16_t  t = temp_read_centi();     // hundredths of a degree

    printf("%lu,%ld,%ld,%ld,%d.%02d\n",
           (unsigned long)ref,
           (long)(a - ref), (long)(b - ref), (long)(c - ref),
           t / 100, abs(t % 100));
    sleep_seconds(59);
}

Reading the RTC and the reference within the same second means the resolution of a single sample is one second. Over fourteen days that is more than adequate for the drift rates involved; it is not adequate for the sub-second behaviour of RTC-C, which is addressed separately in §4.2.

Data handling

The serial log was captured with a plain terminal program and reduced with a few lines of Python. Nothing more sophisticated is needed:

import csv
from statistics import mean

rows = list(csv.reader(open("rtc-log.csv")))
days = 14
per_day = 24 * 60

for col, name in ((1, "RTC-A"), (2, "RTC-B"), (3, "RTC-C")):
    err = [int(r[col]) for r in rows]
    total = err[-1] - err[0]
    ppm = total / (days * 86400) * 1e6
    print(f"{name}: {total:+d} s over {days} d  ->  {ppm:+.1f} ppm")
$ python3 reduce.py
RTC-A: +131 s over 14 d  ->  +108.3 ppm
RTC-B: +214 s over 14 d  ->  +176.9 ppm
RTC-C: +2 s over 14 d    ->  +1.7 ppm

Results

Accumulated error for the three modules over fourteen days
Accumulated clock error against the GPS reference. The daily ripple on the two uncompensated modules follows room temperature.

The headline numbers are in the table below, with the datasheet figure for comparison. The uncompensated crystals are cut for 25 °C and the room averaged around 17 °C, so the parabola predicts they should run slightly slow; both in fact ran fast, which points at initial frequency tolerance rather than temperature.2

ModuleDatasheetMeasured (14 d)RatioDaily rippleMean temp.
RTC-A±20 ppm+108.3 ppm5.4×±0.6 s17.2 °C
RTC-B±46 ppm+176.9 ppm3.8×±0.9 s17.2 °C
RTC-C±2 ppm+1.7 ppm0.85×< 1 s17.2 °C

Uncompensated modules

Both uncompensated parts drifted several times faster than their headline figure. This is not a defect: the datasheet number is a room-temperature figure and the crystal’s tempco does the rest. Working from the standard tuning-fork coefficient, an 8 °C departure from turnover accounts for only about −2 ppm, so essentially all of the measured +108 and +177 ppm is initial frequency error — either a poorly matched load capacitance on the module or simply a wide-tolerance crystal. The daily ripple, not the mean, is the temperature signature.

Anyone specifying one of these modules should treat the datasheet ppm as the best case at one temperature and budget an order of magnitude more, or plan to trim the clock against a reference periodically.

The TCXO module

RTC-C accumulated two seconds in fourteen days, comfortably inside its ±2 ppm band, and showed no visible daily ripple at one-second resolution. To see its behaviour at all, a second run captured its square-wave output against the GPS PPS with a counter over 48 hours; the frequency error stayed between −0.4 and +1.1 ppm and tracked the compensation steps the chip takes every 64 seconds. That detail is not in the datasheet either, but in the other direction: the part is better than its specification in this environment.

The configuration used is worth recording, since the default aging offset on two of the three units received was non-zero:

{
  "module": "RTC-C",
  "aging_offset": 0,
  "compensation_interval_s": 64,
  "sqw_output": "1Hz",
  "battery_backed_sqw": false
}

Practical conclusions

  1. For anything that will not be re-synchronised regularly, the extra few dollars for a temperature-compensated RTC buys roughly a 50–100× improvement in real-world drift.
  2. Where an uncompensated part is unavoidable, measure its actual offset over a few days and apply a software correction; the offset is stable enough that a single linear term removes most of it.
  3. Log the temperature alongside the time error. Without it, the daily ripple looks like noise instead of the signal it is.

Revision note

Revised 10 September 2026: corrected the price paid for RTC-B and added the 48-hour counter measurement for RTC-C.

Footnotes

  1. Tuning-fork crystals used in RTCs have a turnover temperature near 25 °C and a coefficient of about −0.035 ppm/°C². Ten degrees away from turnover is therefore roughly −3.5 ppm from that effect alone, in addition to the initial tolerance. ↩

  2. The sign convention here is that a positive error means the module is ahead of the reference. Both uncompensated modules were ahead, which is consistent with a crystal running slightly fast at its initial tolerance rather than a temperature effect, which would make them run slow. ↩