Nimbin[12]?Embedded / esp_garden / commits / 9d5bd86

esp_garden git · master

Distributed ESP sensor and actuator system (garden)

esp32 esp-idf iot sensors c++ sql · first commit 2025-10-15 · last commit 2026-01-20 (8 months ago) · synced 3 days ago · upstream: git.ide3.de/hsnr/mic/esp_garden

C++ 70.7% Markdown 18% C 10.7%
git clone https://git.christianimmanuel.de/embedded/esp_garden.gitwget https://git.christianimmanuel.de/embedded/esp_garden/archive/esp_garden.tar.gz

Fix broken time after power loss

Chris esp · 2025-11-04 14:48 · 9d5bd86ad508b3b8170dc943863f86c2fc39bc90

 client_sensor/main/ClientSensor.cpp | 61 ++++++++++++++++++++++++++++++++++---
 client_sensor/main/ClientSensor.h   |  3 +-
 client_sensor/main/main.cpp         | 10 ++++++
 3 files changed, 68 insertions(+), 6 deletions(-)

diff --git a/client_sensor/main/ClientSensor.cpp b/client_sensor/main/ClientSensor.cpp
index 77d656a..8b16ef9 100644
--- a/client_sensor/main/ClientSensor.cpp
+++ b/client_sensor/main/ClientSensor.cpp
@@ -14,12 +14,16 @@
 #include <sys/time.h>
 
 volatile bool rtc_synced_with_server = false;
-volatile bool response_received = false;
-volatile bool send_process_complete = false;
+volatile bool response_received      = false;
+volatile bool send_process_complete  = false;
 volatile bool send_response_process_complete = false;
 
 uint8_t config_request_attempts = 0;
 
+// save time over power loss
+extern int64_t stored_epoch_ref;
+extern int64_t stored_uptime_ref;
+
 // ----------------------------------------------------------------------------
 // Helper
 // ----------------------------------------------------------------------------
@@ -235,8 +239,14 @@ void on_DataRecv(const esp_now_recv_info *info, const uint8_t *data, int len) {
         settimeofday(&tv, nullptr);
         rtc_synced_with_server = true;
         ESP_LOGI(TAG, "System time updated from server.");
+
+        // Save epoch + uptime to NVS
+        stored_epoch_ref  = server_unix;
+        stored_uptime_ref = esp_timer_get_time() / 1000000LL;
+        save_time_reference(stored_epoch_ref, stored_uptime_ref); // persist to flash
     }
 
+
     ESP_LOGI(TAG, "Setting send_process_complete to TRUE");
     send_process_complete = true;
 }
@@ -455,8 +465,49 @@ bool send_AndWaitResponse(const uint8_t* peer, const char* message, uint32_t tim
 // Time handling
 // ----------------------------------------------------------------------------
 
+#define TIME_NAMESPACE "time_ref"
+
+void save_time_reference(int64_t epoch, int64_t uptime) {
+    nvs_handle_t nvs;
+    if (nvs_open(TIME_NAMESPACE, NVS_READWRITE, &nvs) == ESP_OK) {
+        nvs_set_i64(nvs, "epoch", epoch);
+        nvs_set_i64(nvs, "uptime", uptime);
+        nvs_commit(nvs);
+        nvs_close(nvs);
+    }
+}
+
+void load_time_reference(int64_t *epoch, int64_t *uptime) {
+    nvs_handle_t nvs;
+    int64_t e = 0, u = 0;
+    if (nvs_open(TIME_NAMESPACE, NVS_READONLY, &nvs) == ESP_OK) {
+        nvs_get_i64(nvs, "epoch", &e);
+        nvs_get_i64(nvs, "uptime", &u);
+        nvs_close(nvs);
+    }
+    if (epoch) *epoch = e;
+    if (uptime) *uptime = u;
+}
+
 int64_t get_CurrentUnix() {
-    return (int64_t)time(nullptr);
+    struct timeval tv;
+    gettimeofday(&tv, NULL);
+
+    if (tv.tv_sec > 100000)
+        return tv.tv_sec;
+
+    // time not set
+    // Try to reconstruct approximate epoch from stored reference
+    extern int64_t stored_epoch_ref;     // last known good epoch
+    extern int64_t stored_uptime_ref;    // esp_timer_get_time() at that epoch
+
+    int64_t now_uptime = esp_timer_get_time() / 1000000LL;
+
+    if (stored_epoch_ref > 100000 && stored_uptime_ref > 0) {
+        return stored_epoch_ref + (now_uptime - stored_uptime_ref);
+    }
+
+    return now_uptime; // absolute fallback
 }
 
 int64_t get_CurrentUnixMs() {
@@ -504,13 +555,13 @@ bool is_TimeToReadSensor(size_t config_id) {
 
     SensorConfig_s& p = sensor_configs[config_id];
 
-    ESP_LOGI(TAG, "check is_TimeToReadSensor: %s", p.sensor_name);
+    ESP_LOGI(TAG, "check %s", p.sensor_name);
 
     if (!p.is_active) {
         ESP_LOGE(TAG, "Sensor is inactive!");
         return false;
     }
-
+    ESP_LOGI(TAG, "  %lld <= %lld ", p.next_sensor_read, get_CurrentUnix());
     return (p.next_sensor_read <= get_CurrentUnix());
 }
 
diff --git a/client_sensor/main/ClientSensor.h b/client_sensor/main/ClientSensor.h
index 4a03fa1..9c2ae78 100644
--- a/client_sensor/main/ClientSensor.h
+++ b/client_sensor/main/ClientSensor.h
@@ -34,7 +34,8 @@ void init_EspNow();
 // ----------------------------------------------------------------------------
 // Time handling
 // ----------------------------------------------------------------------------
-
+void save_time_reference(int64_t epoch, int64_t uptime);
+void load_time_reference(int64_t *epoch, int64_t *uptime);
 int64_t get_CurrentUnix();
 int64_t get_CurrentUnixMs();
 bool request_TimeSync(uint32_t timeout_ms = 3000);
diff --git a/client_sensor/main/main.cpp b/client_sensor/main/main.cpp
index 937362a..ab8b530 100644
--- a/client_sensor/main/main.cpp
+++ b/client_sensor/main/main.cpp
@@ -14,6 +14,10 @@ extern "C" esp_err_t esp_base_mac_addr_set(const uint8_t mac[6]);
 RTC_DATA_ATTR uint32_t fake_value_to_send = 0;
 
 
+// save time over power loss
+int64_t stored_epoch_ref  = 0;
+int64_t stored_uptime_ref = 0;
+
 // ----------------------------------------------------------------------------
 // Sensor task loop
 // ----------------------------------------------------------------------------
@@ -53,6 +57,8 @@ void sensor_loop(void*) {
 
         // allow other tasks to run
         vTaskDelay(pdMS_TO_TICKS(100));
+
+        ESP_LOGI(TAG, "\n\n");
     }
 }
 
@@ -69,6 +75,10 @@ extern "C" void app_main(void) {
     init_WiFi();
     init_EspNow();
 
+    // load persisted values from NVS
+    load_time_reference(&stored_epoch_ref, &stored_uptime_ref);
+
+
     ESP_LOGI(TAG, "Requesting time sync...");
     if (request_TimeSync()) ESP_LOGI(TAG, "done");
     else ESP_LOGE(TAG, "failed (timeout)");