Nimbin[12]?Embedded / esp_garden / commits / 3aca48e

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 time after power loss

Chris esp · 2025-11-04 16:28 · 3aca48ea75e84a104823dabb129f5921a9a4e7a7

 client_sensor/main/ClientSensor.cpp | 116 ++++++++++++++++++++----------------
 client_sensor/main/ClientSensor.h   |   3 +-
 client_sensor/main/main.cpp         |  44 +++++++++-----
 globals.cpp                         |  10 ++--
 server/main/Server.cpp              |   2 +-
 5 files changed, 101 insertions(+), 74 deletions(-)

diff --git a/client_sensor/main/ClientSensor.cpp b/client_sensor/main/ClientSensor.cpp
index 8b16ef9..f342292 100644
--- a/client_sensor/main/ClientSensor.cpp
+++ b/client_sensor/main/ClientSensor.cpp
@@ -20,9 +20,6 @@ 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
@@ -241,6 +238,8 @@ void on_DataRecv(const esp_now_recv_info *info, const uint8_t *data, int len) {
         ESP_LOGI(TAG, "System time updated from server.");
 
         // Save epoch + uptime to NVS
+        int64_t stored_epoch_ref = 0;
+        int64_t stored_uptime_ref = 0;
         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
@@ -493,21 +492,7 @@ int64_t get_CurrentUnix() {
     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
+    return tv.tv_sec;
 }
 
 int64_t get_CurrentUnixMs() {
@@ -550,19 +535,17 @@ uint16_t get_CurrentMinutes() {
 // ----------------------------------------------------------------------------
 
 
-bool is_TimeToReadSensor(size_t config_id) {
+bool is_TimeToReadSensor(const SensorConfig_s& cfg) {
     static const char *TAG = "is_TimeToReadSensor";
 
-    SensorConfig_s& p = sensor_configs[config_id];
-
-    ESP_LOGI(TAG, "check %s", p.sensor_name);
+    ESP_LOGI(TAG, "check %s", cfg.sensor_name);
 
-    if (!p.is_active) {
-        ESP_LOGE(TAG, "Sensor is inactive!");
+    if (!cfg.is_active) {
+        ESP_LOGW(TAG, "Sensor is inactive!");
         return false;
     }
-    ESP_LOGI(TAG, "  %lld <= %lld ", p.next_sensor_read, get_CurrentUnix());
-    return (p.next_sensor_read <= get_CurrentUnix());
+    ESP_LOGI(TAG, "  %lld <= %lld ", cfg.next_sensor_read, get_CurrentUnix());
+    return (cfg.next_sensor_read <= get_CurrentUnix());
 }
 
 int64_t get_NextSensorReadFor(const char* sensor_name) {
@@ -570,28 +553,28 @@ int64_t get_NextSensorReadFor(const char* sensor_name) {
     uint16_t min_current = get_CurrentMinutes();
 
     for (size_t i = 0; i < SENSOR_CONFIG_COUNT; i++) {
-        SensorConfig_s &p = sensor_configs[i];
+        SensorConfig_s &cfg = sensor_configs[i];
 
-        if (!p.is_active) continue;
-        if (strcmp(p.sensor_name, sensor_name) != 0) continue;
+        if (!cfg.is_active) continue;
+        if (strcmp(cfg.sensor_name, sensor_name) != 0) continue;
 
-        int64_t last_read = p.last_sensor_read > 0 && p.last_sensor_read < unix_now
-                          ? p.last_sensor_read
+        int64_t last_read = cfg.last_sensor_read > 0 && cfg.last_sensor_read < unix_now
+                          ? cfg.last_sensor_read
                           : unix_now;
 
         int64_t next_time = INT64_MAX;
 
-        if (p.use_time && p.time_count > 0) {
+        if (cfg.use_time && cfg.time_count > 0) {
             uint32_t best_delta_min = 24 * 60;
-            for (size_t j = 0; j < p.time_count; j++) {
-                int16_t delta = (int16_t)p.times[j] - (int16_t)min_current;
+            for (size_t j = 0; j < cfg.time_count; j++) {
+                int16_t delta = (int16_t)cfg.times[j] - (int16_t)min_current;
                 if (delta <= 0) delta += 24 * 60;
                 if ((uint32_t)delta < best_delta_min)
                     best_delta_min = delta;
             }
             next_time = (unix_now - (unix_now % 60)) + (int64_t)best_delta_min * 60;
-        } else if (p.delay_ms > 0) {
-            next_time = last_read + (p.delay_ms / 1000);
+        } else if (cfg.delay_ms > 0) {
+            next_time = last_read + (cfg.delay_ms / 1000);
         }
 
         if (next_time != INT64_MAX) {
@@ -606,12 +589,12 @@ int64_t get_NextSensorReadsUnixTime(const uint8_t* mac) {
     int64_t unix_next = INT64_MAX;
 
     for (size_t i = 0; i < SENSOR_CONFIG_COUNT; i++) {
-        SensorConfig_s &p = sensor_configs[i];
-        if (!p.is_active) continue;
-        if (mac != nullptr && memcmp(mac, p.pump_mac, 6) != 0) continue;
+        SensorConfig_s &cfg = sensor_configs[i];
+        if (!cfg.is_active) continue;
+        if (mac != nullptr && memcmp(mac, cfg.pump_mac, 6) != 0) continue;
 
 
-        int64_t next_time = get_NextSensorReadFor(p.sensor_name);
+        int64_t next_time = get_NextSensorReadFor(cfg.sensor_name);
 
         if (next_time > 0 && next_time < unix_next)
             unix_next = next_time;
@@ -620,6 +603,17 @@ int64_t get_NextSensorReadsUnixTime(const uint8_t* mac) {
     return (unix_next == INT64_MAX) ? -1 : unix_next;
 }
 
+void set_AllNextSensorReads() {
+    for (size_t i = 0; i < SENSOR_CONFIG_COUNT; i++) {
+        SensorConfig_s &cfg = sensor_configs[i];
+        if (!cfg.is_active) continue;
+        cfg.next_sensor_read = get_NextSensorReadFor(cfg.sensor_name);
+
+        ESP_LOGI("set_AllNextSensorReads", "next read updated: %lld", cfg.next_sensor_read);
+    }
+}
+
+
 void sleep_UntilNextSend(int64_t next_read) {
     static const char *TAG = "sleep_UntilNextSend";
 
@@ -867,39 +861,55 @@ void get_AllSensorConfigs() {
 void send_DataToPumpAndServer(size_t config_id, uint32_t value, bool shall_pump) {
     static const char *TAG = "send_DataToPumpAndServer";
 
-    SensorConfig_s& p = sensor_configs[config_id];
-    ESP_LOGI(TAG, "%s", p.sensor_name);
+    SensorConfig_s& cfg = sensor_configs[config_id];
+    ESP_LOGI(TAG, "%s", cfg.sensor_name);
 
-    int pumping_duration = shall_pump ? p.pumping_duration : 0;
+    int pumping_duration = shall_pump ? cfg.pumping_duration : 0;
 
     char message[64];
 
-    ESP_LOGI(TAG, "Sending to Pump: "); printMac(TAG, p.pump_mac);
+    ESP_LOGI(TAG, "Sending to Pump: "); printMac(TAG, cfg.pump_mac);
     snprintf(message, sizeof(message), "p:%lld:%s:%d:%d:%lld",
             get_CurrentUnix(),
-            p.notification_name,
-            p.pumping_delay_sec,
+            cfg.notification_name,
+            cfg.pumping_delay_sec,
             pumping_duration,
-            get_NextSensorReadsUnixTime(p.pump_mac));
+            get_NextSensorReadsUnixTime(cfg.pump_mac));
 
+    // START MESSAGE PUMP
     bool reached_pump = false;
-    if (!send_AndWaitResponse(p.pump_mac, message)) {
-        ESP_LOGE(TAG, "Delivery failed or peer offline!");
+
+    // try three times..
+    for (int attempt = 1; attempt <= 3; attempt++) {
+        if (send_AndWaitResponse(cfg.pump_mac, message)) {
+            reached_pump = true;
+            break;
+        }
+
+        ESP_LOGW(TAG, "No response from Pump (attempt %d/3)", attempt);
+
+        if (attempt < 3)
+            vTaskDelay(pdMS_TO_TICKS(5000));
+    }
+
+    if (!reached_pump) {
+        ESP_LOGE(TAG, "Pump did not respond after 3 attempts!");
         send_response_process_complete = true;
-    } else reached_pump = true;
+    }
+    // END MESSAGE PUMP
 
     while (!send_response_process_complete)
             vTaskDelay(pdMS_TO_TICKS(100));
 
     ESP_LOGI(TAG, "Sending to Server: "); printMac(TAG, PEER_SERVER);
     snprintf(message, sizeof(message), "%s:%lu:%lld:%d",
-            p.sensor_name,
+            cfg.sensor_name,
             value,
-            p.last_sensor_read,
+            cfg.last_sensor_read,
             reached_pump);
     send_response_process_complete = false;
     if (!send_AndWaitResponse(PEER_SERVER, message)) {
-        ESP_LOGE(TAG, "Failed to get response from server!");
+        ESP_LOGE(TAG, "No response from server!");
         send_response_process_complete = true;
     }
     while (!send_response_process_complete)
diff --git a/client_sensor/main/ClientSensor.h b/client_sensor/main/ClientSensor.h
index 9c2ae78..3df5116 100644
--- a/client_sensor/main/ClientSensor.h
+++ b/client_sensor/main/ClientSensor.h
@@ -47,6 +47,7 @@ void setup_Timezone();
 int64_t get_NextSensorReadFor(const char* sensor_name);
 int64_t get_NextSensorReadsUnixTime(const uint8_t* mac = nullptr);
 void    sleep_UntilNextSend(int64_t next_read);
+void    set_AllNextSensorReads();
 
 // ----------------------------------------------------------------------------
 // Request sensor config from server via ESP-NOW
@@ -59,7 +60,7 @@ void process_PendingSensorConfigs();
 // Send Data to peers
 // ----------------------------------------------------------------------------
 
-bool is_TimeToReadSensor(size_t config_id);
+bool is_TimeToReadSensor(const SensorConfig_s& cfg);
 void send_DataToPumpAndServer(size_t config_id, uint32_t value, bool shall_pump);
 void send_NextSensorReadToPeer(const uint8_t* mac, int64_t unix_time);
 
diff --git a/client_sensor/main/main.cpp b/client_sensor/main/main.cpp
index ab8b530..208b98b 100644
--- a/client_sensor/main/main.cpp
+++ b/client_sensor/main/main.cpp
@@ -4,6 +4,8 @@
 #include "freertos/task.h"
 #include "esp_log.h"
 #include "esp_timer.h"
+#include <sys/time.h>
+
 
 extern "C" esp_err_t esp_base_mac_addr_set(const uint8_t mac[6]);
 
@@ -14,10 +16,6 @@ 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
 // ----------------------------------------------------------------------------
@@ -27,17 +25,17 @@ void sensor_loop(void*) {
     while (true) {
         ESP_LOGI(TAG, "## Unix time: %lld", get_CurrentUnix());
         for (size_t config_id = 0; config_id < SENSOR_CONFIG_COUNT; config_id++) {
-            SensorConfig_s& peer = sensor_configs[config_id];
-            ESP_LOGI(TAG, "LOOP checking config: %s", peer.sensor_name);
-            print_SensorConfig(peer);
+            SensorConfig_s& cfg = sensor_configs[config_id];
+            ESP_LOGI(TAG, "LOOP checking config: %s", cfg.sensor_name);
+            print_SensorConfig(cfg);
 
-            if (is_TimeToReadSensor(config_id)) {
+            if (is_TimeToReadSensor(cfg)) {
                 ESP_LOGI(TAG, "ITS TIME FOR THIS SENSOR!");
                 uint32_t value_to_send = fake_value_to_send;
 
                 // set last and next read times
-                peer.last_sensor_read = get_CurrentUnix();
-                peer.next_sensor_read = get_NextSensorReadFor(peer.sensor_name);
+                cfg.last_sensor_read = get_CurrentUnix();
+                cfg.next_sensor_read = get_NextSensorReadFor(cfg.sensor_name);
 
                 // decide if pump should run
                 bool shall_pump = fake_value_to_send % 2;
@@ -62,6 +60,26 @@ void sensor_loop(void*) {
     }
 }
 
+void restore_TimeBackup() {
+    // save time over power loss
+    int64_t stored_epoch_ref = 0;
+    int64_t stored_uptime_ref = 0;
+    // load persisted values from NVS
+    load_time_reference(&stored_epoch_ref, &stored_uptime_ref);
+    struct timeval tv;
+    gettimeofday(&tv, NULL);
+    int64_t now_unix = tv.tv_sec;
+    int64_t now_uptime = esp_timer_get_time() / 1000000LL;
+    if (now_unix < stored_epoch_ref && stored_epoch_ref > 100000 && stored_uptime_ref > 0) {
+        int64_t reconstructed = stored_epoch_ref + (now_uptime - stored_uptime_ref);
+        struct timeval new_tv = { .tv_sec = reconstructed, .tv_usec = 0 };
+        settimeofday(&new_tv, NULL);
+        ESP_LOGW("restore_TimeBackup", "System time restored from reference: %lld", get_CurrentUnix());
+        ESP_LOGW("restore_TimeBackup", "Resetting next sensor reads!");
+        set_AllNextSensorReads();
+    }
+}
+
 // ----------------------------------------------------------------------------
 // ESP-IDF entry point
 // ----------------------------------------------------------------------------
@@ -75,16 +93,14 @@ extern "C" void app_main(void) {
     init_WiFi();
     init_EspNow();
 
-    // load persisted values from NVS
-    load_time_reference(&stored_epoch_ref, &stored_uptime_ref);
+    restore_SensorConfigs();
 
+    restore_TimeBackup();
 
     ESP_LOGI(TAG, "Requesting time sync...");
     if (request_TimeSync()) ESP_LOGI(TAG, "done");
     else ESP_LOGE(TAG, "failed (timeout)");
 
-    restore_SensorConfigs();
-
     ESP_LOGI(TAG, "----------------------");
 
     if (!all_init_configs_received)
diff --git a/globals.cpp b/globals.cpp
index 59da482..7f44454 100644
--- a/globals.cpp
+++ b/globals.cpp
@@ -2,13 +2,13 @@
 
 uint32_t g_setup_duration = 0;
 uint32_t g_wakeup_earlier_server = 20000;
-uint32_t g_wakeup_earlier_sensor = 2000;
-uint32_t g_wakeup_earlier_pump = 2000;
+uint32_t g_wakeup_earlier_sensor =  2000;
+uint32_t g_wakeup_earlier_pump   =  8000;
 
-SensorEntry_s g_buffer[BUFFER_SIZE]; 
+SensorEntry_s g_buffer[BUFFER_SIZE];
 uint8_t g_buffer_count = 0;
-char g_pending_sensor_configs[4][32] = {};
-uint8_t g_pending_sensor_configs_count = 0;
+char    g_pending_sensor_configs[4][32] = {};
+uint8_t g_pending_sensor_configs_count  = 0;
 
 bool g_received_ok_from_server = false;
 
diff --git a/server/main/Server.cpp b/server/main/Server.cpp
index 7c7ca83..a9b5075 100644
--- a/server/main/Server.cpp
+++ b/server/main/Server.cpp
@@ -296,7 +296,7 @@ void sleep_UntilNextReceive() {
 
     if (next_connection_unix < now_sec) return;
 
-    ESP_LOGI(TAG, "Nex: %lld\nNow: %lld", next_connection_unix, now_sec);
+    ESP_LOGI(TAG, "Nex: %lld\tNow: %lld", next_connection_unix, now_sec);
 
     next_connection_unix = 0;