64-bit monotonic time helpers for Arduino, using esp_timer_get_time() on ESP32 and rollover-tracked micros() elsewhere.
- 64-bit time accessors:
micros64(),millis64(),seconds64()- no 70-minute rollover - Elapsed helpers:
microsSince(),millisSince(),secondsSince()for interval checks - Elapsed timer classes:
ElapsedMicros64,ElapsedMillis64,ElapsedSeconds64for non-blocking intervals - Stopwatch: Start/stop/resume/reset with microsecond precision
- Human-readable formatting: allocation-free
formatTimeTo()/formatNowTo()plus String wrappers - ESP32 optimized: Uses
esp_timer_get_time()for true 64-bit monotonic time - Arduino compatible: Falls back to wrap-tracked
micros()on other platforms
# Clone
git clone https://github.com/janhavelka/SystemChrono.git
cd SystemChrono
# Build for ESP32-S3
pio run -e cli_esp32s3
# Upload and monitor
pio run -e cli_esp32s3 -t upload && pio device monitor -e cli_esp32s3| Board | Environment | Notes |
|---|---|---|
| ESP32-S3-DevKitC-1 | cli_esp32s3 |
PSRAM enabled |
| ESP32-S2-Saola-1 | cli_esp32s2 |
USB CDC |
#include "SystemChrono/SystemChrono.h"
using namespace SystemChrono;
void loop() {
// 64-bit timestamps - no 70-minute rollover
int64_t us = micros64();
int64_t ms = millis64();
int64_t s = seconds64();
Serial.printf("micros=%lld millis=%lld seconds=%lld\n",
(long long)us, (long long)ms, (long long)s);
}#include "SystemChrono/SystemChrono.h"
using namespace SystemChrono;
ElapsedMillis64 heartbeat;
void loop() {
// Non-blocking 1-second interval
if (heartbeat >= 1000) {
heartbeat = 0; // Reset timer
Serial.println("Tick!");
}
}#include "SystemChrono/SystemChrono.h"
using namespace SystemChrono;
Stopwatch sw;
void measureSomething() {
sw.start();
// ... do work ...
sw.stop();
Serial.printf("Elapsed: %lld ms\n", (long long)sw.elapsedMillis());
}#include "SystemChrono/SystemChrono.h"
using namespace SystemChrono;
void printUptime() {
char timeBuf[TIME_FORMAT_BUFFER_SIZE];
// Deterministic, allocation-free formatting (recommended in production)
if (formatNowTo(timeBuf, sizeof(timeBuf)).ok()) {
Serial.println(timeBuf);
}
// String wrappers remain available for convenience
Serial.println(formatNow());
}| Function | Description |
|---|---|
int64_t micros64() |
Monotonic microseconds since boot |
int64_t millis64() |
Monotonic milliseconds since boot |
int64_t seconds64() |
Monotonic seconds since boot |
int64_t microsSince(int64_t) |
Elapsed microseconds since timestamp |
int64_t millisSince(int64_t) |
Elapsed milliseconds since timestamp |
int64_t secondsSince(int64_t) |
Elapsed seconds since timestamp |
Status formatTimeTo(int64_t, char*, size_t) |
Allocation-free format into caller buffer |
Status formatNowTo(char*, size_t) |
Allocation-free format into caller buffer |
String formatTime(int64_t) |
Format microseconds as HH:MM:SS.mmm |
String formatNow() |
Format current time as HH:MM:SS.mmm |
| Method | Description |
|---|---|
void start() |
Reset and start |
void stop() |
Stop and accumulate elapsed time |
void resume() |
Resume without clearing accumulated time |
void reset() |
Clear accumulated time |
int64_t elapsedMicros() |
Get accumulated microseconds |
int64_t elapsedMillis() |
Get accumulated milliseconds |
int64_t elapsedSeconds() |
Get accumulated seconds |
bool isRunning() |
Check if currently running |
All three classes (ElapsedMicros64, ElapsedMillis64, ElapsedSeconds64) support:
- Implicit conversion to
int64_t(returns elapsed time) - Assignment
= 0to reset - Arithmetic operators
+=,-=,+,-
The library version is defined in library.json. A pre-build script automatically generates include/SystemChrono/Version.h.
#include "SystemChrono/Version.h"
Serial.println(SystemChrono::VERSION); // "1.1.0"
Serial.println(SystemChrono::VERSION_FULL); // "1.1.0 (a1b2c3d, 2026-02-10 15:30:00)"
Serial.println(SystemChrono::BUILD_TIMESTAMP); // "2026-02-10 15:30:00"
Serial.println(SystemChrono::GIT_COMMIT); // "a1b2c3d"| Example | Description |
|---|---|
01_basic_bringup_cli |
Interactive CLI demonstrating all features |
# CLI example (S3)
pio run -e cli_esp32s3 -t upload
pio device monitor -e cli_esp32s3
# CLI example (S2)
pio run -e cli_esp32s2 -t upload
pio device monitor -e cli_esp32s2- Single-threaded: All functions safe to call from main loop
- Non-blocking: No delays or waits
- ISR safety (ESP32):
micros64()usesesp_timer_get_time()which is ISR-safe - ISR safety (other): Uses
noInterrupts()/interrupts()briefly for wrap tracking
- No pins, buses, tasks, or peripherals are owned by this library
- Time source is platform-provided (
esp_timer_get_time()ormicros()) - No hidden storage or NVS side effects
- No heap allocations in
micros64(), elapsed helpers,Stopwatch, or elapsed timer classes formatTimeTo()andformatNowTo()are allocation-freeformatTime()andformatNow()returnStringand may allocate heap memory
- Allocation-free formatting APIs return
Statusand never fail silently - Invalid formatting buffer configuration returns
Err::INVALID_CONFIG
Uses esp_timer_get_time() for true 64-bit monotonic microseconds since boot. Thread-safe.
Extends 32-bit micros() to 64-bit via wrap tracking. Requires periodic calls (at least once per ~70 minutes) to detect rollovers. Uses interrupt-disable briefly when reading.
├── include/SystemChrono/ # Public headers (library API)
│ ├── Config.h # Configuration struct (reserved)
│ ├── Status.h # Error types
│ ├── SystemChrono.h # Main API header
│ └── Version.h # Auto-generated version info
├── src/ # Implementation
│ └── SystemChrono.cpp
├── examples/
│ ├── 01_basic_bringup_cli/ # CLI demo
│ └── common/ # Shared example utilities
├── library.json # PlatformIO library metadata
└── platformio.ini # Build environments
This project follows Semantic Versioning 2.0.0:
- MAJOR: Breaking API changes
- MINOR: New features, backward compatible
- PATCH: Bug fixes, backward compatible
See CONTRIBUTING.md for guidelines.
This project is licensed under the MIT License - see LICENSE for details.
- CHANGELOG.md - Version history
- SECURITY.md - Security policy
- AGENTS.md - AI agent guidelines