File espos_time.h¶
File List > espos_time > include > espos_time.h
Go to the documentation of this file
/*
* SPDX-FileCopyrightText: 2026 Dirk Wahrheit
* SPDX-License-Identifier: Apache-2.0
*
* espos_time — the device's wall clock, and the reason a delta can carry a
* timestamp at all.
*
* An ESP32 boots with no idea what time it is. Until something tells it, the
* only clock it has is a monotonic millisecond counter that starts at zero,
* which is enough to schedule work and useless for saying when a measurement
* was taken. That gap is not academic: values a device buffers while the
* server is unreachable are replayed minutes or hours later, and a delta
* without a timestamp is stamped by the server with the time it arrived — so
* an hour of wind data recorded during an outage lands in the log as one
* burst at reconnect, in the wrong place and the wrong order.
*
* This component closes it. It keeps one wall clock, learned from whichever
* source got there first and is ranked highest (SNTP over a manual set over
* the SignalK stream over an RTC value carried through a deep sleep), and
* hands it out as unix milliseconds or as an ISO 8601 string. Everything
* else — the delta engine's timestamps, the log's wall-clock prefix, the
* `at` field of a reset record — reads it from here.
*
* The clock may be wrong or absent, and callers must cope: espos_time_now_ms()
* returns 0 while unsynced rather than a plausible-looking 1970, so a consumer
* that forgets to check produces an obviously missing value instead of a
* quietly wrong one.
*
* Timezone stance: SignalK is UTC and so is everything this component
* publishes. A local time is a display concern — a panel showing the crew what
* o'clock it is — so espos_time_set_tz() and espos_time_parts() exist for a UI
* to use, and nothing in the data path ever looks at them.
*
* Threading: every getter is a lock-protected snapshot copy and never waits on
* a network. The SNTP sync callback runs on IDF's SNTP task, espos_time_set()
* on whatever task the source lives on, and subscriber callbacks run on that
* same task with no espos_time lock held: copy what you need and return, never
* block there.
*/
#pragma once
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include "esp_err.h"
#ifdef __cplusplus
extern "C" {
#endif
/* Longest string espos_time_iso8601() writes, NUL included:
* "2026-09-07T10:12:13.456Z" is 24 characters. Part of the ABI. */
#define ESPOS_TIME_ISO_MAX 25
/* Longest POSIX TZ string espos_time_set_tz() accepts, NUL included.
* "CET-1CEST,M3.5.0,M10.5.0/3" and friends fit comfortably. */
#define ESPOS_TIME_TZ_MAX 40
typedef enum {
ESPOS_TIME_SRC_NONE = 0, /* no clock: espos_time_now_ms() returns 0 */
ESPOS_TIME_SRC_RTC = 1, /* carried through a deep sleep in RTC memory */
ESPOS_TIME_SRC_SK = 2, /* navigation.datetime from the SignalK stream */
ESPOS_TIME_SRC_MANUAL = 3, /* PUT /api/v1/time, or an application call */
ESPOS_TIME_SRC_SNTP = 4, /* an NTP server */
ESPOS_TIME_SRC_MAX = 5,
} espos_time_src_t;
esp_err_t espos_time_start(void);
bool espos_time_is_synced(void);
int64_t espos_time_now_ms(void);
espos_time_src_t espos_time_source(void);
const char *espos_time_src_str(espos_time_src_t src);
size_t espos_time_iso8601(char *buf, size_t n);
size_t espos_time_iso8601_of(int64_t unix_ms, char *buf, size_t n);
esp_err_t espos_time_set(int64_t unix_ms, espos_time_src_t src);
esp_err_t espos_time_set_tz(const char *posix_tz);
const char *espos_time_tz(void);
typedef struct {
int32_t year; /* full year, e.g. 2026 */
uint8_t month; /* 1-12 */
uint8_t day; /* 1-31 */
uint8_t hour; /* 0-23 */
uint8_t minute; /* 0-59 */
uint8_t second; /* 0-60, a leap second included */
uint16_t millisecond; /* 0-999 */
uint8_t wday; /* 0 = Sunday */
uint16_t yday; /* 0-365 */
int32_t utc_offset_s; /* seconds east of UTC at this instant */
} espos_time_parts_t;
esp_err_t espos_time_parts(espos_time_parts_t *out);
typedef void (*espos_time_cb_t)(espos_time_src_t src, void *arg);
esp_err_t espos_time_subscribe(espos_time_cb_t cb, void *arg);
esp_err_t espos_time_unsubscribe(espos_time_cb_t cb, void *arg);
esp_err_t espos_time_status_json(char **out_json);
int64_t espos_time_now_ms_or_zero(void *arg);
#ifdef __cplusplus
}
#endif