Time Sync

Source: src/time_sync.c · 140 lines · 9 functions.

Module entry points

These functions are exposed to the rest of the project. Open one to follow its decisions and the helpers it calls.

How to read these diagrams

Each function has its own expandable diagram using the actual expressions and statements from this file. Decisions split into True and False; loop bodies are indented. Returns end the function immediately; break and continue follow their C meanings. Arrows between statements show their order when execution reaches them.

These are structural source diagrams before preprocessing, not runtime traces. Conditional compilation and macros remain source expressions. Ternary choices remain inside their statement. Calls are linked separately; callback execution and task scheduling are not inferred.

Functions in this file

time_sync_callback() · source line 26
static void time_sync_callback(struct timeval *time_value)
  1. Execute(void)time_value;
  2. Executestatus = TIME_SYNC_SYNCHRONIZED;
  3. Executesave_pending = true;
  4. ExecuteESP_LOGI(TAG, "Time synchronized");
Read original function source
static void time_sync_callback(struct timeval *time_value)
{
    (void)time_value;
    status = TIME_SYNC_SYNCHRONIZED;
    save_pending = true;
    ESP_LOGI(TAG, "Time synchronized");
}
timeSyncInitialize() · source line 34
bool timeSyncInitialize(void)

Calls into this documentation:

  1. Executebool found = false;
  2. Executetime_sync_record_t saved = {0};
  3. Decision!game_storage_read(TIME_SYNC_STORAGE_NAMESPACE, TIME_SYNC_STORAGE_KEY, &saved, sizeof(saved), &found)
    True
    1. Exit this functionreturn false;
    False

    Continue after this decision.

  4. Decisionfound && saved.version == TIME_SYNC_RECORD_VERSION && saved.epoch_seconds >= 1704067200LL
    True
      1. Executeconst struct timeval time_value = {.tv_sec = (time_t)saved.epoch_seconds, .tv_usec = 0};
      2. Decisionsettimeofday(&time_value, NULL) == 0
        True
        1. ExecuteESP_LOGI(TAG, "Restored saved clock time");
        False

        Continue after this decision.

    False

    Continue after this decision.

  5. Exit this functionreturn true;
Read original function source
bool timeSyncInitialize(void)
{
    bool found = false;
    time_sync_record_t saved = {0};
    if (!game_storage_read(TIME_SYNC_STORAGE_NAMESPACE, TIME_SYNC_STORAGE_KEY,
                           &saved, sizeof(saved), &found)) return false;
    if (found && saved.version == TIME_SYNC_RECORD_VERSION && saved.epoch_seconds >= 1704067200LL) {
        const struct timeval time_value = {.tv_sec = (time_t)saved.epoch_seconds, .tv_usec = 0};
        if (settimeofday(&time_value, NULL) == 0) ESP_LOGI(TAG, "Restored saved clock time");
    }
    return true;
}
saveSynchronizedTime() · source line 47
static void saveSynchronizedTime(void)

Calls into this documentation:

  1. Decision!save_pending
    True
    1. Exit this functionreturn;
    False

    Continue after this decision.

  2. Executeconst time_t now = time(NULL);
  3. Decisionnow >= 1704067200
    True
      1. Executeconst time_sync_record_t saved = {TIME_SYNC_RECORD_VERSION, (int64_t)now};
      2. Decision!game_storage_write(TIME_SYNC_STORAGE_NAMESPACE, TIME_SYNC_STORAGE_KEY, &saved, sizeof(saved))
        True
        1. ExecuteESP_LOGW(TAG, "Could not save synchronized time");
        False

        Continue after this decision.

    False

    Continue after this decision.

  4. Executesave_pending = false;
Read original function source
static void saveSynchronizedTime(void)
{
    if (!save_pending) return;
    const time_t now = time(NULL);
    if (now >= 1704067200) {
        const time_sync_record_t saved = {TIME_SYNC_RECORD_VERSION, (int64_t)now};
        if (!game_storage_write(TIME_SYNC_STORAGE_NAMESPACE, TIME_SYNC_STORAGE_KEY,
                                &saved, sizeof(saved))) ESP_LOGW(TAG, "Could not save synchronized time");
    }
    save_pending = false;
}
timeSyncDue() · source line 59
bool timeSyncDue(uint32_t now_ms)
  1. Exit this functionreturn !attempt_recorded || now_ms - sync_started_at >= TIME_SYNC_INTERVAL_MS;
Read original function source
bool timeSyncDue(uint32_t now_ms)
{
    return !attempt_recorded || now_ms - sync_started_at >= TIME_SYNC_INTERVAL_MS;
}
timeSyncUpdate() · source line 64
void timeSyncUpdate(bool wifi_connected, uint32_t now_ms)

Calls into this documentation:

  1. ExecutesaveSynchronizedTime();
  2. Decisionsntp_initialized && status == TIME_SYNC_SYNCING && now_ms - sync_started_at >= TIME_SYNC_TIMEOUT_MS
    True
      1. Executestatus = TIME_SYNC_FAILED;
      2. ExecuteESP_LOGW(TAG, "Time sync timed out");
    False

    Continue after this decision.

  3. Decisionsntp_initialized && !timeSyncDue(now_ms)
    True
    1. Exit this functionreturn;
    False

    Continue after this decision.

  4. Decisionsntp_initialized
    True
      1. Executeesp_netif_sntp_deinit();
      2. Executesntp_initialized = false;
      3. Executestatus = TIME_SYNC_OFF;
    False

    Continue after this decision.

  5. Decision!timeSyncDue(now_ms) && status != TIME_SYNC_WAITING_FOR_WIFI
    True
    1. Exit this functionreturn;
    False

    Continue after this decision.

  6. Decision!wifi_connected
    True
      1. Decision!sntp_initialized && timeSyncDue(now_ms)
        True
          1. Executesync_started_at = now_ms;
          2. Executeattempt_recorded = true;
          3. Executestatus = TIME_SYNC_WAITING_FOR_WIFI;
        False

        Continue after this decision.

      2. Exit this functionreturn;
    False

    Continue after this decision.

  7. Decisionstatus == TIME_SYNC_SYNCHRONIZED
    True
    1. Exit this functionreturn;
    False

    Continue after this decision.

  8. Executeconst esp_sntp_config_t configuration = { .smooth_sync = false, .server_from_dhcp = false, .wait_for_sync = false, .start = true, .sync_cb = time_sync_callback, .renew_servers_after_new_IP = false, .ip_event_to_renew = IP_EVENT_STA_GOT_IP, .index_of_first_server = 0, .num_of_servers = 1, .servers = {"se.pool.ntp.org"}, };
  9. Executeconst esp_err_t result = esp_netif_sntp_init(&configuration);
  10. Decisionresult != ESP_OK
    True
      1. Executestatus = TIME_SYNC_FAILED;
      2. ExecuteESP_LOGW(TAG, "Time sync start failed: %s", esp_err_to_name(result));
      3. Exit this functionreturn;
    False

    Continue after this decision.

  11. Executesntp_initialized = true;
  12. Executesync_started_at = now_ms;
  13. Executeattempt_recorded = true;
  14. Executestatus = TIME_SYNC_SYNCING;
  15. ExecuteESP_LOGI(TAG, "Time sync started");
Read original function source
void timeSyncUpdate(bool wifi_connected, uint32_t now_ms)
{
    saveSynchronizedTime();
    if (sntp_initialized && status == TIME_SYNC_SYNCING &&
        now_ms - sync_started_at >= TIME_SYNC_TIMEOUT_MS) {
        status = TIME_SYNC_FAILED;
        ESP_LOGW(TAG, "Time sync timed out");
    }
    if (sntp_initialized && !timeSyncDue(now_ms)) return;
    if (sntp_initialized) {
        esp_netif_sntp_deinit();
        sntp_initialized = false;
        status = TIME_SYNC_OFF;
    }
    if (!timeSyncDue(now_ms) && status != TIME_SYNC_WAITING_FOR_WIFI) return;
    if (!wifi_connected) {
        if (!sntp_initialized && timeSyncDue(now_ms)) {
            sync_started_at = now_ms;
            attempt_recorded = true;
            status = TIME_SYNC_WAITING_FOR_WIFI;
        }
        return;
    }
    if (status == TIME_SYNC_SYNCHRONIZED) return;

    const esp_sntp_config_t configuration = {
        .smooth_sync = false,
        .server_from_dhcp = false,
        .wait_for_sync = false,
        .start = true,
        .sync_cb = time_sync_callback,
        .renew_servers_after_new_IP = false,
        .ip_event_to_renew = IP_EVENT_STA_GOT_IP,
        .index_of_first_server = 0,
        .num_of_servers = 1,
        .servers = {"se.pool.ntp.org"},
    };
    const esp_err_t result = esp_netif_sntp_init(&configuration);
    if (result != ESP_OK) {
        status = TIME_SYNC_FAILED;
        ESP_LOGW(TAG, "Time sync start failed: %s", esp_err_to_name(result));
        return;
    }
    sntp_initialized = true;
    sync_started_at = now_ms;
    attempt_recorded = true;
    status = TIME_SYNC_SYNCING;
    ESP_LOGI(TAG, "Time sync started");
}
timeSyncStop() · source line 114
void timeSyncStop(void)
  1. Decisionsntp_initialized
    True
    1. Executeesp_netif_sntp_deinit();
    False

    Continue after this decision.

  2. Executesntp_initialized = false;
  3. Executestatus = TIME_SYNC_OFF;
Read original function source
void timeSyncStop(void)
{
    if (sntp_initialized) esp_netif_sntp_deinit();
    sntp_initialized = false;
    status = TIME_SYNC_OFF;
}
timeSyncAbandonAttempt() · source line 121
void timeSyncAbandonAttempt(void)
  1. Decisionstatus == TIME_SYNC_WAITING_FOR_WIFI || status == TIME_SYNC_SYNCING
    True
    1. Executestatus = TIME_SYNC_FAILED;
    False

    Continue after this decision.

Read original function source
void timeSyncAbandonAttempt(void)
{
    if (status == TIME_SYNC_WAITING_FOR_WIFI || status == TIME_SYNC_SYNCING) status = TIME_SYNC_FAILED;
}
timeSyncStatus() · source line 126
time_sync_status_t timeSyncStatus(void)
  1. Exit this functionreturn status;
Read original function source
time_sync_status_t timeSyncStatus(void)
{
    return status;
}
timeSyncStatusText() · source line 131
const char *timeSyncStatusText(void)
  1. Select a casestatus

    Enter matching case; follow source fall-through until a break, return or end.

      1. Case / labelcase TIME_SYNC_WAITING_FOR_WIFI:
      2. Exit this functionreturn "TIME WAITING FOR WIFI";
      3. Case / labelcase TIME_SYNC_SYNCING:
      4. Exit this functionreturn "TIME SYNCING...";
      5. Case / labelcase TIME_SYNC_SYNCHRONIZED:
      6. Exit this functionreturn "TIME SYNCHRONIZED";
      7. Case / labelcase TIME_SYNC_FAILED:
      8. Exit this functionreturn "TIME SYNC FAILED";
      9. Case / labeldefault:
      10. Exit this functionreturn "TIME SYNC OFF";
Read original function source
const char *timeSyncStatusText(void)
{
    switch (status) {
    case TIME_SYNC_WAITING_FOR_WIFI: return "TIME WAITING FOR WIFI";
    case TIME_SYNC_SYNCING: return "TIME SYNCING...";
    case TIME_SYNC_SYNCHRONIZED: return "TIME SYNCHRONIZED";
    case TIME_SYNC_FAILED: return "TIME SYNC FAILED";
    default: return "TIME SYNC OFF";
    }
}
Complete original file, including constants and data
/* Coded By JD(2026) */
#error "Reference listing only — not provided as build-ready source."
#include "time_sync.h"

#include "esp_err.h"
#include "esp_log.h"
#include "esp_netif_sntp.h"
#include "game_storage.h"

#include <sys/time.h>
#include <time.h>

#define TIME_SYNC_TIMEOUT_MS 20000U
#define TIME_SYNC_INTERVAL_MS (6U * 60U * 60U * 1000U)
#define TIME_SYNC_STORAGE_NAMESPACE "arcade_clock"
#define TIME_SYNC_STORAGE_KEY "last_good_time"
#define TIME_SYNC_RECORD_VERSION 1U

typedef struct { uint32_t version; int64_t epoch_seconds; } time_sync_record_t;

static const char *TAG = "time_sync";
static bool sntp_initialized;
static time_sync_status_t status = TIME_SYNC_OFF;
static uint32_t sync_started_at;
static bool attempt_recorded;
static bool save_pending;

static void time_sync_callback(struct timeval *time_value)
{
    (void)time_value;
    status = TIME_SYNC_SYNCHRONIZED;
    save_pending = true;
    ESP_LOGI(TAG, "Time synchronized");
}

bool timeSyncInitialize(void)
{
    bool found = false;
    time_sync_record_t saved = {0};
    if (!game_storage_read(TIME_SYNC_STORAGE_NAMESPACE, TIME_SYNC_STORAGE_KEY,
                           &saved, sizeof(saved), &found)) return false;
    if (found && saved.version == TIME_SYNC_RECORD_VERSION && saved.epoch_seconds >= 1704067200LL) {
        const struct timeval time_value = {.tv_sec = (time_t)saved.epoch_seconds, .tv_usec = 0};
        if (settimeofday(&time_value, NULL) == 0) ESP_LOGI(TAG, "Restored saved clock time");
    }
    return true;
}

static void saveSynchronizedTime(void)
{
    if (!save_pending) return;
    const time_t now = time(NULL);
    if (now >= 1704067200) {
        const time_sync_record_t saved = {TIME_SYNC_RECORD_VERSION, (int64_t)now};
        if (!game_storage_write(TIME_SYNC_STORAGE_NAMESPACE, TIME_SYNC_STORAGE_KEY,
                                &saved, sizeof(saved))) ESP_LOGW(TAG, "Could not save synchronized time");
    }
    save_pending = false;
}

bool timeSyncDue(uint32_t now_ms)
{
    return !attempt_recorded || now_ms - sync_started_at >= TIME_SYNC_INTERVAL_MS;
}

void timeSyncUpdate(bool wifi_connected, uint32_t now_ms)
{
    saveSynchronizedTime();
    if (sntp_initialized && status == TIME_SYNC_SYNCING &&
        now_ms - sync_started_at >= TIME_SYNC_TIMEOUT_MS) {
        status = TIME_SYNC_FAILED;
        ESP_LOGW(TAG, "Time sync timed out");
    }
    if (sntp_initialized && !timeSyncDue(now_ms)) return;
    if (sntp_initialized) {
        esp_netif_sntp_deinit();
        sntp_initialized = false;
        status = TIME_SYNC_OFF;
    }
    if (!timeSyncDue(now_ms) && status != TIME_SYNC_WAITING_FOR_WIFI) return;
    if (!wifi_connected) {
        if (!sntp_initialized && timeSyncDue(now_ms)) {
            sync_started_at = now_ms;
            attempt_recorded = true;
            status = TIME_SYNC_WAITING_FOR_WIFI;
        }
        return;
    }
    if (status == TIME_SYNC_SYNCHRONIZED) return;

    const esp_sntp_config_t configuration = {
        .smooth_sync = false,
        .server_from_dhcp = false,
        .wait_for_sync = false,
        .start = true,
        .sync_cb = time_sync_callback,
        .renew_servers_after_new_IP = false,
        .ip_event_to_renew = IP_EVENT_STA_GOT_IP,
        .index_of_first_server = 0,
        .num_of_servers = 1,
        .servers = {"se.pool.ntp.org"},
    };
    const esp_err_t result = esp_netif_sntp_init(&configuration);
    if (result != ESP_OK) {
        status = TIME_SYNC_FAILED;
        ESP_LOGW(TAG, "Time sync start failed: %s", esp_err_to_name(result));
        return;
    }
    sntp_initialized = true;
    sync_started_at = now_ms;
    attempt_recorded = true;
    status = TIME_SYNC_SYNCING;
    ESP_LOGI(TAG, "Time sync started");
}

void timeSyncStop(void)
{
    if (sntp_initialized) esp_netif_sntp_deinit();
    sntp_initialized = false;
    status = TIME_SYNC_OFF;
}

void timeSyncAbandonAttempt(void)
{
    if (status == TIME_SYNC_WAITING_FOR_WIFI || status == TIME_SYNC_SYNCING) status = TIME_SYNC_FAILED;
}

time_sync_status_t timeSyncStatus(void)
{
    return status;
}

const char *timeSyncStatusText(void)
{
    switch (status) {
    case TIME_SYNC_WAITING_FOR_WIFI: return "TIME WAITING FOR WIFI";
    case TIME_SYNC_SYNCING: return "TIME SYNCING...";
    case TIME_SYNC_SYNCHRONIZED: return "TIME SYNCHRONIZED";
    case TIME_SYNC_FAILED: return "TIME SYNC FAILED";
    default: return "TIME SYNC OFF";
    }
}