---
title: "ESP32 holdover, MCXO inspiration and the one-minute result — PToB Blog"
description: "W12 frozen-clock experiments reached 64 seconds below 1 µs at sampled P99. How we measure holdover, plan temperature capture and assess an external DS3231."
url: "https://ptob.dev/blog/esp32-sub-microsecond-holdover/"
language: "en"
robots: "index, follow, max-image-preview:large, max-snippet:-1, max-video-preview:-1"
representations: {"application/json":"https://ptob.dev/blog/esp32-sub-microsecond-holdover/?format=json","text/markdown":"https://ptob.dev/blog/esp32-sub-microsecond-holdover/?format=markdown"}
format_description: "https://ptob.dev/agent-formats.json"
---

# ESP32 holdover: a minute below one microsecond

Frozen software clocks on ESP32 reached a minute below 1 µs at sampled P99. Next, we want to learn how temperature history could improve their predictions.

[Jonathan Yantis](https://github.com/yantisj2/PToB/blob/main/CREDITS.md) 30 October 2026  4 min read

![Concept diagram showing BLE rate learning, a frozen ESP32 clock model and later BLE witnesses checking its prediction, with a separate temperature-history path for future experiments.](https://ptob.dev/blog/esp32-holdover-v1.png)

Concept illustration. Later BLE observations check the frozen clock without correcting it. Temperature learning remains a separate shadow experiment.

In this post

A precision clock gets interesting when its reference disappears. A low synchronization error tells us how well it follows; holdover tells us how long its own prediction remains useful. For inexpensive ESP32 hardware, crossing a minute below one microsecond would make short radio interruptions a much more manageable timing problem.

Our W12 experiments have reached **64 seconds below 1 µs at sampled P99**, using frozen software clocks checked against later BLE observations. The inspiration comes from the microcomputer-compensated crystal oscillator, or MCXO: learn enough about an oscillator to predict its behavior. Commercial [MCXOs](https://q-tech.com/wp-content/uploads/QPDS-0004-QT2010-MCXO.pdf) use specialized resonator thermometry and compensation. We are exploring what can be learned with the crystal and counters already on an ESP32-S3 board.

## A minute on a frozen clock

While BLE time is available, W12 learns its local clock’s rate. At hold entry, it freezes the selected rate and phase. Rate selection uses only observations already received; future packets cannot improve that prediction retrospectively.

For the experiment, offset copies of the clock keep running while the normal receiver continues synchronizing. Later BLE packets become witnesses: they check each frozen copy without correcting it. At each tested age, we retain that run’s worst sampled absolute error so far. P99 then describes those per-run maxima. A good final sample cannot erase an earlier crossing.

The [fully replayed 64-second window](https://ptob.dev/research/empirical-ble-holdover/) contains 128 prefixes, with **P99 760 ns and maximum 879 ns**. A separate fixed cohort of 128 mature runs passes through **75 seconds at P99 983 ns**, then fails at 76 seconds. That finer analysis uses its own population; it does not extend every rolling display result to 75 seconds. Both devices’ later rolling windows ended at 24 seconds.

These overlapping runs share clock history. They measure agreement with received BLE time during normal operation, with gaps and unknown outcomes retained. They establish neither continuous accuracy between witnesses nor independently calibrated UTC or electrical output. A deliberate reference outage and physical grandmaster transition remain separate experiments.

## Capture the thermal history first

Temperature is an obvious suspect when a learned rate stops predicting well. It is also easy to compensate the wrong thing. The [ESP32-S3 sensor](https://docs.espressif.com/projects/esp-idf/en/v5.5.5/esp32s3/api-reference/peripherals/temp_sensor.html) measures silicon temperature; the quartz can follow a different temperature and lag. An earlier thermal-correction trial reached 3.663 µs maximum error against 362 ns for its paired baseline. Production HOLD now uses BLE rate alone; temperature learning runs in shadow, making predictions without steering the delivered clock.

Our next step is a compact temperature-and-rate history for longer experiments. The proposal pairs one-second sensor observations with trained BLE frequency estimates, retaining variation, their relationship and missing-data counts. Half-hour summaries would keep storage small while preserving boot and source changes. Persistent collection is still planned, with flash-write timing and recovery checks required before enabling it.

We want warming, cooling and workload changes across several boards, followed by validation on runs the model has never seen. A useful thermal model must improve frozen-clock predictions and their tails, including recovery. A neat temperature curve alone will not earn a place in the clock.

## What an external DS3231 could add

An external [DS3231](https://www.analog.com/media/en/technical-documentation/data-sheets/ds3231.pdf) offers a temperature-compensated oscillator and battery-backed calendar. Its specified ±2 ppm from 0–40 °C corresponds to **120 µs of drift in a minute** at that frequency error. Sub-1 µs over 60 seconds needs roughly 17 parts per billion of residual rate error, even with perfect starting phase. The datasheet alone cannot promise that.

It could still provide useful longer-term continuity and another oscillator to characterize. We would capture its square-wave edges in hardware, learn phase and frequency against the live reference, then measure prediction through outages. Timestamping I²C calendar reads would introduce another timing path to characterize. Aging trim and calibration may improve an individual unit, but short-term noise, thermal changes and capture delay need measurement. We have no DS3231 holdover result yet.

## Holdover in a multi-master mesh

Holdover also belongs in our [future multi-master grandmaster strategy](https://ptob.dev/blog/grandmaster-failover-multi-master-ensemble/). A receiver tracking several streams could bridge a brief loss using its local prediction while evaluating the remaining sources. Each contributor’s measured stability, observation age and transported uncertainty would help determine its weight.

Shared references and recirculated timing cannot count as independent clocks. A good local prediction also cannot renew expired source authority. The aim is useful continuity with visible limits: learn while references are healthy, measure how predictions age, and let that evidence decide how long each clock deserves to contribute.

Every timestamp needs a reference.
**Which event did you capture?**

## Explore the details

[Empirical sub-microsecond holdover The paper, exact measurement populations, failures and reproducible evidence.](https://ptob.dev/research/empirical-ble-holdover/)[Towards a multi-master ensemble How several time sources could contribute to a receiver’s estimate.](https://ptob.dev/blog/grandmaster-failover-multi-master-ensemble/)[W12 timing architecture The ESP32-S3 receive clock, timing paths and source boundaries.](https://ptob.dev/hardware/esp32-s3/timing/)

## Sources and further reading

Primary references for the hardware and methods discussed.

1. [PToB · Empirical Sub-Microsecond Holdover](https://ptob.dev/papers/preview/?file=%2Fpapers%2Fptob-empirical-sub-microsecond-holdover-20261030.pdf)

   Frozen-clock prefix measurements, distinct rolling and fixed cohorts, and temperature-shadow history.
2. [Q-Tech · QT2010 MCXO](https://q-tech.com/wp-content/uploads/QPDS-0004-QT2010-MCXO.pdf)

   Commercial MCXO architecture and resonator self-thermometry; its specifications do not describe W12.
3. [Espressif · ESP32-S3 temperature sensor](https://docs.espressif.com/projects/esp-idf/en/v5.5.5/esp32s3/api-reference/peripherals/temp_sensor.html)

   The internal silicon temperature sensor and its measurement limits.
4. [Analog Devices · DS3231 datasheet](https://www.analog.com/media/en/technical-documentation/data-sheets/ds3231.pdf)

   RTC frequency accuracy, battery backup, square-wave outputs and aging trim.

## Cite this article

Jonathan Yantis. ESP32 holdover: a minute below one microsecond PToB Blog. Published 30 October 2026.

Article permalink: [https://ptob.dev/blog/esp32-sub-microsecond-holdover/](https://ptob.dev/blog/esp32-sub-microsecond-holdover/)

[Back to the blog](https://ptob.dev/blog/)[Inspect the project evidence](https://github.com/yantisj2/PToB/blob/main/evidence/README.md)

## Structured data

```json
{
  "@context": "https://schema.org",
  "@graph": [
    {
      "@type": "WebSite",
      "@id": "https://ptob.dev/#website",
      "url": "https://ptob.dev/",
      "name": "PToB",
      "alternateName": [
        "Precision Time over BLE",
        "Precision Time over Bluetooth"
      ],
      "description": "Bluetooth and USB software clocks: W12 60.3 ns relative-PPS RMS over 522/600 observations, separate 64 s sampled holdover, and CoreSE diagnostics.",
      "inLanguage": [
        "en",
        "de"
      ],
      "publisher": {
        "@id": "https://ptob.dev/#jonathan-yantis"
      }
    },
    {
      "@type": "Person",
      "@id": "https://ptob.dev/#jonathan-yantis",
      "name": "Jonathan Yantis",
      "url": "https://github.com/yantisj2/PToB/blob/main/CREDITS.md"
    },
    {
      "@type": "Project",
      "@id": "https://ptob.dev/#project",
      "name": "PToB",
      "alternateName": "Precision Time over BLE",
      "url": "https://ptob.dev/",
      "description": "An independent protocol project researching precision time transfer across Bluetooth-enabled microcontrollers and USB clients.",
      "sameAs": "https://github.com/yantisj2/PToB",
      "member": {
        "@id": "https://ptob.dev/#jonathan-yantis"
      },
      "knowsAbout": {
        "@id": "https://ptob.dev/#protocol"
      }
    },
    {
      "@type": "Thing",
      "@id": "https://ptob.dev/#protocol",
      "name": "Precision Time over BLE",
      "alternateName": "PToB",
      "description": "An experimental PTP-inspired protocol proposal for wireless clock synchronization over Bluetooth Low Energy."
    },
    {
      "@type": "WebPage",
      "@id": "https://ptob.dev/blog/esp32-sub-microsecond-holdover/#webpage",
      "url": "https://ptob.dev/blog/esp32-sub-microsecond-holdover/",
      "name": "ESP32 holdover, MCXO inspiration and the one-minute result — PToB Blog",
      "description": "W12 frozen-clock experiments reached 64 seconds below 1 µs at sampled P99. How we measure holdover, plan temperature capture and assess an external DS3231.",
      "inLanguage": "en",
      "keywords": [
        "ESP32-S3",
        "Timing holdover",
        "MCXO",
        "Temperature compensation",
        "DS3231",
        "Multi-master time mesh"
      ],
      "isPartOf": {
        "@id": "https://ptob.dev/#website"
      },
      "author": {
        "@id": "https://ptob.dev/#jonathan-yantis"
      },
      "about": {
        "@id": "https://ptob.dev/#protocol"
      },
      "dateModified": "2026-10-30T00:00:00.000Z",
      "image": "https://ptob.dev/blog/esp32-holdover-v1.png",
      "breadcrumb": {
        "@id": "https://ptob.dev/blog/esp32-sub-microsecond-holdover/#breadcrumbs"
      },
      "mainEntity": {
        "@id": "https://ptob.dev/blog/esp32-sub-microsecond-holdover/#article"
      }
    },
    {
      "@type": "BreadcrumbList",
      "@id": "https://ptob.dev/blog/esp32-sub-microsecond-holdover/#breadcrumbs",
      "itemListElement": [
        {
          "@type": "ListItem",
          "position": 1,
          "name": "PToB",
          "item": "https://ptob.dev/"
        },
        {
          "@type": "ListItem",
          "position": 2,
          "name": "Blog",
          "item": "https://ptob.dev/blog/"
        },
        {
          "@type": "ListItem",
          "position": 3,
          "name": "ESP32 holdover: a minute below one microsecond",
          "item": "https://ptob.dev/blog/esp32-sub-microsecond-holdover/"
        }
      ]
    },
    {
      "@type": "Blog",
      "@id": "https://ptob.dev/blog/#blog",
      "url": "https://ptob.dev/blog/",
      "name": "PToB Blog",
      "description": "Engineering notes on shared time, inexpensive hardware and the experiments behind PToB.",
      "inLanguage": "en",
      "publisher": {
        "@id": "https://ptob.dev/#jonathan-yantis"
      },
      "isPartOf": {
        "@id": "https://ptob.dev/#website"
      },
      "blogPost": [
        {
          "@id": "https://ptob.dev/blog/esp32-wireless-softpll/#article"
        },
        {
          "@id": "https://ptob.dev/blog/esp32-clock-fundamentals/#article"
        },
        {
          "@id": "https://ptob.dev/blog/firmware-stability-time-mesh/#article"
        },
        {
          "@id": "https://ptob.dev/blog/lab-sensor-timing-economics/#article"
        },
        {
          "@id": "https://ptob.dev/blog/starlink-local-stratum-1-ntp/#article"
        },
        {
          "@id": "https://ptob.dev/blog/raspberry-pi-ptp-ntp-ble/#article"
        },
        {
          "@id": "https://ptob.dev/blog/gnss-resilient-time-mesh/#article"
        },
        {
          "@id": "https://ptob.dev/blog/passive-lora-channel-hopping/#article"
        },
        {
          "@id": "https://ptob.dev/blog/esp32-sub-microsecond-holdover/#article"
        },
        {
          "@id": "https://ptob.dev/blog/local-time-utc-mesh/#article"
        },
        {
          "@id": "https://ptob.dev/blog/grandmaster-failover-multi-master-ensemble/#article"
        },
        {
          "@id": "https://ptob.dev/blog/ble-clocks-indoor-navigation/#article"
        },
        {
          "@id": "https://ptob.dev/blog/passive-ble-esp32/#article"
        },
        {
          "@id": "https://ptob.dev/blog/why-utc/#article"
        },
        {
          "@id": "https://ptob.dev/blog/why-ptob/#article"
        }
      ]
    },
    {
      "@type": "BlogPosting",
      "@id": "https://ptob.dev/blog/esp32-sub-microsecond-holdover/#article",
      "url": "https://ptob.dev/blog/esp32-sub-microsecond-holdover/",
      "headline": "ESP32 holdover: a minute below one microsecond",
      "description": "W12 frozen-clock experiments reached 64 seconds below 1 µs at sampled P99. How we measure holdover, plan temperature capture and assess an external DS3231.",
      "mainEntityOfPage": {
        "@id": "https://ptob.dev/blog/esp32-sub-microsecond-holdover/#webpage"
      },
      "isPartOf": {
        "@id": "https://ptob.dev/blog/#blog"
      },
      "author": {
        "@id": "https://ptob.dev/#jonathan-yantis"
      },
      "publisher": {
        "@id": "https://ptob.dev/#jonathan-yantis"
      },
      "inLanguage": "en",
      "dateCreated": "2026-10-30T00:00:00.000Z",
      "dateModified": "2026-10-30T00:00:00.000Z",
      "datePublished": "2026-10-30T00:00:00.000Z",
      "image": "https://ptob.dev/blog/esp32-holdover-v1.png",
      "articleSection": "Oscillator prediction and holdover",
      "keywords": [
        "ESP32-S3",
        "Timing holdover",
        "MCXO",
        "Temperature compensation",
        "DS3231",
        "Multi-master time mesh"
      ],
      "wordCount": 736,
      "citation": [
        {
          "@type": "CreativeWork",
          "name": "PToB · Empirical Sub-Microsecond Holdover",
          "url": "https://ptob.dev/papers/ptob-empirical-sub-microsecond-holdover-20261030.pdf"
        },
        {
          "@type": "CreativeWork",
          "name": "Q-Tech · QT2010 MCXO",
          "url": "https://q-tech.com/wp-content/uploads/QPDS-0004-QT2010-MCXO.pdf"
        },
        {
          "@type": "CreativeWork",
          "name": "Espressif · ESP32-S3 temperature sensor",
          "url": "https://docs.espressif.com/projects/esp-idf/en/v5.5.5/esp32s3/api-reference/peripherals/temp_sensor.html"
        },
        {
          "@type": "CreativeWork",
          "name": "Analog Devices · DS3231 datasheet",
          "url": "https://www.analog.com/media/en/technical-documentation/data-sheets/ds3231.pdf"
        }
      ],
      "about": {
        "@id": "https://ptob.dev/#protocol"
      }
    }
  ]
}
```
