{"format":"ptob-page","version":1,"canonical":"https://ptob.dev/blog/esp32-clock-fundamentals/","title":"ESP32 clock fundamentals and clock discipline — PToB Blog","description":"Learn how a software clock turns ESP32 counter ticks into time, corrects drift and catches up without jumping. Try the first ESP32-REF example on your computer.","language":"en","robots":"index, follow, max-image-preview:large, max-snippet:-1, max-video-preview:-1","dates":{"published":"2026-11-07T00:00:00.000Z","modified":"2026-11-07T00:00:00.000Z"},"authors":["Jonathan Yantis"],"mainEntity":"https://ptob.dev/blog/esp32-clock-fundamentals/#article","representations":{"application/json":"https://ptob.dev/blog/esp32-clock-fundamentals/?format=json","text/markdown":"https://ptob.dev/blog/esp32-clock-fundamentals/?format=markdown"},"documentation":"https://ptob.dev/agent-formats.json","social":{"openGraph":{"title":"ESP32 clock fundamentals and clock discipline — PToB Blog","description":"Learn how a software clock turns ESP32 counter ticks into time, corrects drift and catches up without jumping. Try the first ESP32-REF example on your computer.","url":"https://ptob.dev/blog/esp32-clock-fundamentals/","type":"article","siteName":"PToB","locale":"en_US","image":{"url":"https://ptob.dev/blog/esp32-clock-fundamentals-v1.jpg","secureUrl":"https://ptob.dev/blog/esp32-clock-fundamentals-v1.jpg","type":"image/jpeg","width":"1586","height":"992","alt":"An engineering illustration follows counter pulses through a software clock model to a dial showing the time estimate."}},"twitter":{"card":"summary_large_image","title":"ESP32 clock fundamentals and clock discipline — PToB Blog","description":"Learn how a software clock turns ESP32 counter ticks into time, corrects drift and catches up without jumping. Try the first ESP32-REF example on your computer.","image":{"url":"https://ptob.dev/blog/esp32-clock-fundamentals-v1.jpg","alt":"An engineering illustration follows counter pulses through a software clock model to a dial showing the time estimate."}}},"citations":[],"provenance":{"sourceUrl":"https://ptob.dev/blog/esp32-clock-fundamentals/","sourceMediaType":"text/html","sourceSha256":"81e7c8bdbe087e3ee9c3673bb57117f2d119e7094f4a5c3568332d254084348d","rightsUrl":"https://github.com/yantisj2/PToB/blob/main/LICENSING.md"},"contentUsage":{"aiTrain":true,"search":true,"aiInput":true,"reason":"summary-or-introduction"},"structuredData":[{"@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-clock-fundamentals/#webpage","url":"https://ptob.dev/blog/esp32-clock-fundamentals/","name":"ESP32 clock fundamentals and clock discipline — PToB Blog","description":"Learn how a software clock turns ESP32 counter ticks into time, corrects drift and catches up without jumping. Try the first ESP32-REF example on your computer.","inLanguage":"en","keywords":["ESP32-S3","Clock discipline","Firmware examples"],"isPartOf":{"@id":"https://ptob.dev/#website"},"author":{"@id":"https://ptob.dev/#jonathan-yantis"},"about":{"@id":"https://ptob.dev/#protocol"},"dateModified":"2026-11-07T00:00:00.000Z","image":"https://ptob.dev/blog/esp32-clock-fundamentals-v1.jpg","breadcrumb":{"@id":"https://ptob.dev/blog/esp32-clock-fundamentals/#breadcrumbs"},"mainEntity":{"@id":"https://ptob.dev/blog/esp32-clock-fundamentals/#article"}},{"@type":"BreadcrumbList","@id":"https://ptob.dev/blog/esp32-clock-fundamentals/#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":"Building a basic ESP32 clock","item":"https://ptob.dev/blog/esp32-clock-fundamentals/"}]},{"@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-clock-fundamentals/#article","url":"https://ptob.dev/blog/esp32-clock-fundamentals/","headline":"Building a basic ESP32 clock","description":"Learn how a software clock turns ESP32 counter ticks into time, corrects drift and catches up without jumping. Try the first ESP32-REF example on your computer.","mainEntityOfPage":{"@id":"https://ptob.dev/blog/esp32-clock-fundamentals/#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-11-07T00:00:00.000Z","dateModified":"2026-11-07T00:00:00.000Z","datePublished":"2026-11-07T00:00:00.000Z","image":"https://ptob.dev/blog/esp32-clock-fundamentals-v1.jpg","articleSection":"ESP32 firmware examples · Part 1","keywords":["ESP32-S3","Clock discipline","Firmware examples"],"wordCount":1167,"citation":[{"@type":"CreativeWork","name":"PToB · ESP32-REF 00 — Clock fundamentals","url":"https://github.com/yantisj2/PToB/tree/c8686bb38de0802f21e9b93b803c538dc7b2306f/firmware/examples/esp32-ref/00_clock_fundamentals"},{"@type":"CreativeWork","name":"PToB · ESP32-REF timing model","url":"https://github.com/yantisj2/PToB/blob/c8686bb38de0802f21e9b93b803c538dc7b2306f/firmware/examples/esp32-ref/docs/timing-model.md"},{"@type":"CreativeWork","name":"PToB · ESP32-REF 01 — Wireless BLE SoftPLL","url":"https://github.com/yantisj2/PToB/tree/c8686bb38de0802f21e9b93b803c538dc7b2306f/firmware/examples/esp32-ref/01_wireless_softpll"}],"about":{"@id":"https://ptob.dev/#protocol"}}]}],"content":{"mediaType":"text/markdown","headings":[{"level":1,"text":"Building a basic ESP32 clock","url":"https://ptob.dev/blog/esp32-clock-fundamentals/#main"},{"level":2,"text":"Turn counter ticks into time","url":"https://ptob.dev/blog/esp32-clock-fundamentals/#turn-counter-ticks-into-time"},{"level":2,"text":"Correct the pace","url":"https://ptob.dev/blog/esp32-clock-fundamentals/#correct-the-pace"},{"level":2,"text":"Catch up without a jump","url":"https://ptob.dev/blog/esp32-clock-fundamentals/#catch-up-without-a-jump"},{"level":2,"text":"Keep the reading you had at the time","url":"https://ptob.dev/blog/esp32-clock-fundamentals/#keep-the-reading-you-had-at-the-time"},{"level":2,"text":"Take the same questions to the board","url":"https://ptob.dev/blog/esp32-clock-fundamentals/#take-the-same-questions-to-the-board"},{"level":2,"text":"Explore the details","url":"https://ptob.dev/blog/esp32-clock-fundamentals/#related-title"},{"level":3,"text":"ESP32-S3 timing schematic","url":"https://ptob.dev/blog/esp32-clock-fundamentals/#main"},{"level":3,"text":"How passive BLE time works on ESP32","url":"https://ptob.dev/blog/esp32-clock-fundamentals/#main"},{"level":2,"text":"Sources and further reading","url":"https://ptob.dev/blog/esp32-clock-fundamentals/#sources-title"},{"level":2,"text":"Cite this article","url":"https://ptob.dev/blog/esp32-clock-fundamentals/#cite-title"}],"links":[{"text":"Jonathan Yantis","url":"https://github.com/yantisj2/PToB/blob/main/CREDITS.md"},{"text":"Example 00: Clock fundamentals","url":"https://ptob.dev/blog/esp32-clock-fundamentals/#source-clock-fundamentals"},{"text":"ESP32-S3 timing schematic","url":"https://ptob.dev/hardware/esp32-s3/timing/"},{"text":"run Example 00 on your computer","url":"https://ptob.dev/blog/esp32-clock-fundamentals/#source-clock-fundamentals"},{"text":"Sync ESP32 sensors over BLE","url":"https://ptob.dev/blog/esp32-wireless-softpll/"},{"text":"ESP32-S3 timing schematic Trace the W12 timing path and see what to check at each stage.","url":"https://ptob.dev/hardware/esp32-s3/timing/"},{"text":"How passive BLE time works on ESP32 Follow the path from a radio event to a receiver’s clock estimate.","url":"https://ptob.dev/blog/passive-ble-esp32/"},{"text":"PToB · ESP32-REF 00 — Clock fundamentals","url":"https://github.com/yantisj2/PToB/tree/c8686bb38de0802f21e9b93b803c538dc7b2306f/firmware/examples/esp32-ref/00_clock_fundamentals"},{"text":"PToB · ESP32-REF timing model","url":"https://github.com/yantisj2/PToB/blob/c8686bb38de0802f21e9b93b803c538dc7b2306f/firmware/examples/esp32-ref/docs/timing-model.md"},{"text":"PToB · ESP32-REF 01 — Wireless BLE SoftPLL","url":"https://github.com/yantisj2/PToB/tree/c8686bb38de0802f21e9b93b803c538dc7b2306f/firmware/examples/esp32-ref/01_wireless_softpll"},{"text":"https://ptob.dev/blog/esp32-clock-fundamentals/","url":"https://ptob.dev/blog/esp32-clock-fundamentals/"},{"text":"Back to the blog","url":"https://ptob.dev/blog/"},{"text":"Inspect the project evidence","url":"https://github.com/yantisj2/PToB/blob/main/evidence/README.md"}],"text":"# Building a basic ESP32 clock\n\nOur first ESP32 firmware example shows how to turn ticks into time, correct a clock that runs fast and catch up without making its reading jump.\n\n[Jonathan Yantis](https://github.com/yantisj2/PToB/blob/main/CREDITS.md) 7 November 2026  3 min read\n\n![An engineering illustration follows counter pulses through a software clock model to a dial showing the time estimate.](https://ptob.dev/blog/esp32-clock-fundamentals-v1.webp)\n\nThe hardware counts ticks. The software clock converts them into an estimate of time.\n\nIn this post\n\nSet two clocks to the same time and come back later. If one has pulled ahead, setting it again will bring them together for a moment. To keep them together, you also have to change how fast that clock advances.\n\nThat is the idea behind **clock discipline**: use a reference to bring a clock’s reading and its pace into agreement. We start our ESP32 firmware series with [Example 00: Clock fundamentals](https://ptob.dev/blog/esp32-clock-fundamentals/#source-clock-fundamentals). It runs on your computer with simulated inputs, so you can see what each correction does before adding an ESP32 board, a radio or a UTC reference.\n\n## Turn counter ticks into time\n\nA counter tells you how many ticks have passed. To turn that count into time, software needs a starting point where a particular tick has a known time, and a rate for converting later ticks into elapsed time. The example uses a simulated 80 MHz counter and starts from a relative time of zero.\n\nThe counter can wrap to zero while time keeps moving. The lesson follows a count across that boundary and keeps the ticks on a continuous timeline. It also shows why keeping raw ticks matters: at the nominal rate one tick is 12.5 nanoseconds, so rounding each tick separately loses information. A timestamp can be written in nanoseconds even when the clock’s accuracy is much coarser.\n\n## Correct the pace\n\nIn the lesson, the simulated counter runs 20 parts per million fast. That is 20 extra microseconds of clock error every second if software keeps using the nominal conversion. After ten seconds, the clock is 200 microseconds ahead.\n\nThe example supplies a known correction. Software assigns slightly less time to each tick, so the estimate advances at nearly the reference rate. The oscillator keeps running as before. A later lesson will estimate the correction from observations.\n\nSimulated example. The tiny error left after rate correction is too small to see at this scale.\n\n## Catch up without a jump\n\nTwo clocks can run at the same pace and still disagree. The difference between their readings is called **phase error**. If the software clock is behind, it can advance a little faster until it catches up, then return to its usual pace. This gradual adjustment is called a **slew**. If the clock is ahead, it can slow down while still moving forward.\n\nIn the lesson, a clock starts two microseconds behind. It reads the same value just before and just after the update. Over the next two seconds it makes up the difference. That continuity matters when another part of your program uses the clock to order events or measure intervals.\n\nSimulated example. The clock gains one microsecond per second until it catches up. The blue line shows the error shrinking while the clock itself keeps advancing.\n\n## Keep the reading you had at the time\n\nSometimes the reference time for an event arrives after the event itself. Save the clock’s prediction when the event happens. When the reference value arrives, compare it with that saved prediction before applying the correction. That tells you how well the clock was doing when your program needed it.\n\nA delayed observation can improve future readings. The error recorded for the earlier event stays the same.\n\nA clock also needs to say when it has no usable reading. In Example 00, a read at the deadline is refused, even if you recently adjusted the clock. A timing correction does not extend that deadline. A counter can also become unreliable if you wait so long between reads that you can no longer tell how far it advanced.\n\n## Take the same questions to the board\n\nWhen you move from simulation to a board, open the [ESP32-S3 timing schematic](https://ptob.dev/hardware/esp32-s3/timing/). It follows the W12 implementation from the local timebase and event capture through to the software clock. Select a block to see what to check there. If the error grows steadily, start with the raw tick intervals and their conversion rate. If timestamps vary when the system gets busy, check whether you recorded when the event happened or when software got around to handling it. If time disappears after a restart or a gap, check counter continuity and whether the clock still has a usable reference.\n\nTo get a feel for the difference between rate and phase, [run Example 00 on your computer](https://ptob.dev/blog/esp32-clock-fundamentals/#source-clock-fundamentals) and reverse the sign of the rate correction. Predict which way the error will grow before you run it again. In Part 2, the clock learns the correction from Bluetooth observations. [Sync ESP32 sensors over BLE](https://ptob.dev/blog/esp32-wireless-softpll/)\n\nEvery timestamp needs a reference.\n**Which event did you capture?**\n\n## Explore the details\n\n[ESP32-S3 timing schematic Trace the W12 timing path and see what to check at each stage.](https://ptob.dev/hardware/esp32-s3/timing/)[How passive BLE time works on ESP32 Follow the path from a radio event to a receiver’s clock estimate.](https://ptob.dev/blog/passive-ble-esp32/)\n\n## Sources and further reading\n\nPrimary references for the hardware and methods discussed.\n\n1. [PToB · ESP32-REF 00 — Clock fundamentals](https://github.com/yantisj2/PToB/tree/c8686bb38de0802f21e9b93b803c538dc7b2306f/firmware/examples/esp32-ref/00_clock_fundamentals)\n\n   Run the exercises and inspect the source for counter rollover, rate correction, phase slew and expiry.\n2. [PToB · ESP32-REF timing model](https://github.com/yantisj2/PToB/blob/c8686bb38de0802f21e9b93b803c538dc7b2306f/firmware/examples/esp32-ref/docs/timing-model.md)\n\n   The exact rules for converting ticks, applying corrections and deciding when a reading is usable.\n3. [PToB · ESP32-REF 01 — Wireless BLE SoftPLL](https://github.com/yantisj2/PToB/tree/c8686bb38de0802f21e9b93b803c538dc7b2306f/firmware/examples/esp32-ref/01_wireless_softpll)\n\n   The next example estimates clock rate from Bluetooth Low Energy observations.\n\n## Cite this article\n\nJonathan Yantis. Building a basic ESP32 clock PToB Blog. Published 7 November 2026.\n\nArticle permalink: [https://ptob.dev/blog/esp32-clock-fundamentals/](https://ptob.dev/blog/esp32-clock-fundamentals/)\n\n[Back to the blog](https://ptob.dev/blog/)[Inspect the project evidence](https://github.com/yantisj2/PToB/blob/main/evidence/README.md)"}}
