# 31.TimeSourceFallback - multi-source time with automatic fallback

**Layer:** L7 Application · **Build flags:** `DETWS_ENABLE_NTP`, `DETWS_ENABLE_TIME_SOURCE`

## What this example teaches

Real devices have more than one clock: NTP when the network is up, a battery RTC
when it is not, maybe a GPS pulse. `DETWS_ENABLE_TIME_SOURCE` lets you register
several time sources with priorities and have the library pick the best available
one automatically. This registers NTP (priority 0) and an RTC stand-in (priority
1); `GET /time` reports the epoch and which source supplied it. It composes the
SNTP client from [18.SNTP](../18.SNTP).

**A source is a function returning epoch seconds, or 0 if it has no time.**

```cpp
static uint32_t src_ntp() { return detws_ntp_synced() ? (uint32_t)detws_ntp_epoch() : 0; }
static uint32_t src_rtc() { return RTC_BASE + (uint32_t)(millis() / 1000); }
```

**Register by priority; lowest value wins.** When NTP returns 0 (not synced yet)
the library falls through to the RTC; once NTP syncs it transparently takes over:

```cpp
detws_ntp_begin();                        // start SNTP
detws_time_source_add("ntp", 0, src_ntp); // preferred
detws_time_source_add("rtc", 1, src_rtc); // fallback
```

`detws_time_now()` returns the chosen epoch and `detws_time_source_active()` names
the source that supplied it. Swap the RTC stand-in for a real DS3231/PCF8523 I2C
read, and add a GPS source the same way (returning 0 with no fix).

## Build and run

```sh
pio ci --board=esp32dev --project-option="framework=arduino" \
  --project-option="build_flags=-DDETWS_ENABLE_NTP=1 -DDETWS_ENABLE_TIME_SOURCE=1" \
  --lib="." examples/L7-Application/31.TimeSourceFallback/31.TimeSourceFallback.ino
```

```sh
curl http://<ip>/time   # {"epoch":...,"source":"rtc"} at boot, "ntp" after sync
```

## Annotated source

The complete sketch ([31.TimeSourceFallback.ino](31.TimeSourceFallback.ino)),
reproduced verbatim with added explanatory comments:

```cpp
// Copyright (C) 2026 Douglas Quigg (dstroy0) <dquigg123@gmail.com>
// SPDX-License-Identifier: AGPL-3.0-or-later

#define DETWS_ENABLE_NTP 1
#define DETWS_ENABLE_TIME_SOURCE 1

#include "dwserver.h"
#include "network_drivers/physical/physical.h"
#include "services/ntp_service.h"
#include "services/time_source/time_source.h"
#include <WiFi.h>

static const char *SSID = "YOUR_SSID";
static const char *PASSWORD = "YOUR_PASSWORD";

DetWebServer server;

// Priority 0: NTP - valid only once SNTP has synced (else 0 -> fall through).
static uint32_t src_ntp()
{
    return detws_ntp_synced() ? (uint32_t)detws_ntp_epoch() : 0;
}

// Priority 1: a coarse battery-RTC stand-in. A real device reads a DS3231/PCF8523
// over I2C here; this simulation is seeded at build time and counts via millis(),
// so the device always has a last-resort time.
static const uint32_t RTC_BASE = 1750000000u; // ~2025-06; replace with a real RTC read
static uint32_t src_rtc()
{
    return RTC_BASE + (uint32_t)(millis() / 1000);
}

void setup()
{
    Serial.begin(115200);
    init_wifi_physical(SSID, PASSWORD);
    Serial.print("Connecting to WiFi");
    while (!wifi_ready())
    {
        delay(250);
        Serial.print('.');
    }
    Serial.print("\nIP: ");
    Serial.println(WiFi.localIP());
    WiFi.setSleep(false);

    detws_ntp_begin();                        // start SNTP (GMT, pool.ntp.org)
    detws_time_source_add("ntp", 0, src_ntp); // preferred
    detws_time_source_add("rtc", 1, src_rtc); // fallback

    server.on("/time", HTTP_GET, [](uint8_t id, HttpReq *) {
        char body[96];
        uint32_t epoch = detws_time_now();
        const char *src = detws_time_source_active();
        snprintf(body, sizeof(body), "{\"epoch\":%u,\"source\":\"%s\"}", (unsigned)epoch, src ? src : "none");
        server.send(id, 200, "application/json", body);
    });
    server.begin(80);
}

void loop()
{
    server.handle();
}
```
