Nimbin[12]?Embedded / esp_garden / commits / f1c6daf

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

Update timing stuff

Christian Immanuel · 2025-10-24 10:05 · f1c6dafc6e130a3775d55a7f823c2dc84230abb2

 client_sensor/ClientSensor.cpp  | 129 ++++++++++++++++++++++++----------------
 client_sensor/ClientSensor.h    |   7 ++-
 client_sensor/client_sensor.ino |   9 ++-
 globals.cpp                     |   4 +-
 globals.h                       |   1 +
 5 files changed, 91 insertions(+), 59 deletions(-)

diff --git a/client_sensor/ClientSensor.cpp b/client_sensor/ClientSensor.cpp
index 4e1d088..22870c8 100644
--- a/client_sensor/ClientSensor.cpp
+++ b/client_sensor/ClientSensor.cpp
@@ -47,18 +47,19 @@ void init_WiFi() {
 
 uint8_t config_request_attempts = 0;
 
-void printSensorConfig(const SensorConfig_s& cfg) {
+void print_SensorConfig(const SensorConfig_s& cfg) {
     DBG_PRINTF("SensorConfig for '%s'\n", cfg.sensor_name);
     DBG_PRINTF("  is_active: %s\n", cfg.is_active ? "true" : "false");
     DBG_PRINTF("  pin: %d\n", cfg.pin);
     DBG_PRINTF("  min_moisture: %d\n", cfg.min_moisture);
     DBG_PRINTF("  notification_delay_ms: %d\n", cfg.notification_delay_ms);
     DBG_PRINTF("  notification_name: %s\n", cfg.notification_name);
-    DBG_PRINTF("  pump_mac: %s\n", (char*)cfg.pump_mac);
+    DBG_PRINTF("  pump_mac: "); printMac(cfg.pump_mac);
     DBG_PRINTF("  delay_ms: %d\n", cfg.delay_ms);
     DBG_PRINTF("  use_time: %s\n", cfg.use_time ? "true" : "false");
     DBG_PRINTF("  time_count: %u\n", cfg.time_count);
-    DBG_PRINTF("  last_sensor_read: %llu\n", cfg.last_sensor_read);
+    DBG_PRINTF("  last_sensor_read: %lld\n", cfg.last_sensor_read);
+    DBG_PRINTF("  next_sensor_read: %lld\n", cfg.next_sensor_read);
 
     if (cfg.time_count > 0) {
         DBG_PRINT("  times: ");
@@ -323,12 +324,13 @@ bool send_AndWaitAck(const uint8_t* peer, const char* message, uint32_t timeout_
 // ----------------------------------------------------------------------------
 // Time handling
 // ----------------------------------------------------------------------------
-uint64_t get_CurrentUnixMs() {
-    time_t now_sec;
-    time(&now_sec);
-    struct timeval tv;
-    gettimeofday(&tv, nullptr);
-    return (uint64_t)tv.tv_sec * 1000ULL + tv.tv_usec / 1000ULL;
+
+int64_t get_CurrentUnix() {
+    return (int64_t)time(nullptr);
+}
+
+int64_t get_CurrentUnixMs() {
+    return get_CurrentUnix() * 1000;
 }
 
 bool request_TimeSync(uint32_t timeout_ms) {
@@ -348,7 +350,8 @@ void setup_Timezone() {
     tzset();
 }
 
-void get_LocalTime(time_t unix_time, int *hour, int *minute) {
+void get_LocalTime(int *hour, int *minute) {
+    time_t unix_time = get_CurrentUnix();
     struct tm tm_time;
     localtime_r(&unix_time, &tm_time);
     *hour = tm_time.tm_hour;
@@ -356,46 +359,81 @@ void get_LocalTime(time_t unix_time, int *hour, int *minute) {
 }
 
 uint16_t get_CurrentMinutes() {
-    uint64_t t = get_CurrentUnixMs();
-    if (t == 0) return 0;
-    time_t sec = t / 1000;
     int hour, minute;
-    get_LocalTime(sec, &hour, &minute);
+    get_LocalTime(&hour, &minute);
     return hour * 60 + minute;
 }
 
 // ----------------------------------------------------------------------------
 // Deep sleep scheduler
 // ----------------------------------------------------------------------------
-void sleep_UntilNextSend() {
-    uint64_t ms_now = get_CurrentUnixMs();
-    uint32_t min_delay_ms = UINT32_MAX;
+int64_t getAndSet_NextSensorReadFor(const char* sensor_name) {
+    int64_t unix_now = get_CurrentUnix();
+    uint16_t min_current = get_CurrentMinutes();
 
     for (size_t i = 0; i < SENSOR_CONFIG_COUNT; i++) {
-        SensorConfig_s & p = sensor_configs[i];
+        SensorConfig_s &p = sensor_configs[i];
         if (!p.is_active) continue;
+        if (strcmp(p.sensor_name, sensor_name) != 0) continue;
+        if (p.next_sensor_read > unix_now)
+            return p.next_sensor_read;
+
+        int64_t next_time = INT64_MAX;
 
-        if (p.use_time) {
-            uint16_t min_current = get_CurrentMinutes();
+        if (p.use_time && p.time_count > 0) {
+            uint32_t best_delta_min = 24 * 60;
             for (size_t j = 0; j < p.time_count; j++) {
-                int16_t min_delta = (int16_t)p.times[j] - (int16_t)min_current;
-                if (min_delta <= 0) min_delta += 24 * 60; // wrap to next day
-                uint32_t delta_ms = min_delta * 60 * 1000;
-                if (delta_ms < min_delay_ms) min_delay_ms = delta_ms;
+                int16_t delta = (int16_t)p.times[j] - (int16_t)min_current;
+                if (delta <= 0) delta += 24 * 60;
+                if ((uint32_t)delta < best_delta_min)
+                    best_delta_min = delta;
             }
-        } else {
-            uint64_t last_read = p.last_sensor_read * 1000ULL;
-            uint32_t delta_ms = (p.delay_ms > (ms_now - last_read))
-                ? (p.delay_ms - (ms_now - last_read))
-                : 0;
-            if (delta_ms < min_delay_ms) min_delay_ms = delta_ms;
+            next_time = unix_now + (int64_t)best_delta_min * 60;
+        } else if (p.delay_ms > 0) {
+            next_time = p.last_sensor_read + (p.delay_ms / 1000);
+        }
+
+        if (next_time != INT64_MAX) {
+            p.next_sensor_read = next_time;
+            return next_time;
+        }
+    }
+
+    return -1;
+}
+
+int64_t getAndSet_NextSensorReadsUnixTime() {
+    int64_t unix_now = get_CurrentUnix();
+    int64_t unix_next = INT64_MAX;
+    unsigned long start = millis();
+
+    for (size_t i = 0; i < SENSOR_CONFIG_COUNT; i++) {
+        SensorConfig_s &p = sensor_configs[i];
+        if (!p.is_active) continue;
+
+        int64_t next_time = getAndSet_NextSensorReadFor(p.sensor_name);
+        if (next_time > 0) {
+            // optional: compensate for function execution time
+            next_time -= (int64_t)((millis() - start) / 1000);
+
+            if (next_time < unix_next)
+                unix_next = next_time;
         }
     }
 
-    if (min_delay_ms == UINT32_MAX) return;
+    return (unix_next == INT64_MAX) ? -1 : unix_next;
+}
+
+void sleep_UntilNextSend(int64_t next_read) {
+    int64_t now_sec = get_CurrentUnix();
+
+    int64_t delta_sec = next_read - now_sec;
+    if (delta_sec <= 0) delta_sec = 0;
 
+    uint32_t min_delay_ms = delta_sec > UINT32_MAX / 1000
+                          ? UINT32_MAX
+                          : (uint32_t)(delta_sec * 1000);
 
-    // i just need the next sensor read from here, the sleep part should get handled somewhere else as other esp need the next sensor read, sleep is just for that one.
 
     // Prevent sleeping if next send is within 1 minute
     if (min_delay_ms < 60 * 1000) {
@@ -410,7 +448,7 @@ void sleep_UntilNextSend() {
                            ? (min_delay_ms - prep_time_ms)
                            : 0;
 
-    DBG_PRINTF("Sleeping for %u ms (prep margin: %u ms)\n",
+    DBG_PRINTF("Sleeping for %u ms (setup margin: %u ms)\n",
                sleep_time_ms, prep_time_ms);
     DBG_PRINTF("######################\n\n");
 
@@ -418,6 +456,7 @@ void sleep_UntilNextSend() {
     esp_deep_sleep_start();
 }
 
+
 // ----------------------------------------------------------------------------
 // Request sensor config from server via ESP-NOW
 // ----------------------------------------------------------------------------
@@ -551,6 +590,8 @@ bool request_SensorConfig(const char* sensor_name) {
          }
     }
 
+    cfg->next_sensor_read = 0;
+    getAndSet_NextSensorReadFor(cfg->sensor_name);
     config_request_attempts = 0;
     return true;
 }
@@ -620,35 +661,19 @@ void get_AllSensorConfigs() {
 // ----------------------------------------------------------------------------
 
 bool is_TimeToReadSensor(size_t config_id) {
-    uint64_t ms_now = get_CurrentUnixMs();
-
     SensorConfig_s& p = sensor_configs[config_id];
 
     DBG_PRINTF("check is_TimeToReadSensor: %s\n", p.sensor_name);
-    printSensorConfig(p);
 
-    if (!p.is_active) { 
+    if (!p.is_active) {
         DBG_PRINTLN("Sensor is inactive!");
         return false;
     }
 
-    if (p.use_time) {
-        uint16_t min_current = get_CurrentMinutes();
-        for (size_t j = 0; j < p.time_count; j++) {
-            if (min_current == p.times[j] &&
-                    ms_now - (p.last_sensor_read * 1000ULL) > 60000) {
-                return true;
-                break;
-            }
-        }
-    } else {
-        //if (ms_now - last_sent_array[config_id] >= p.delay_ms) is_time_to_read = true;
-        if (ms_now - (p.last_sensor_read * 1000ULL) >= p.delay_ms)
-            return true;
-    }
-    return false;
+    return (p.next_sensor_read <= get_CurrentUnix());
 }
 
+
 void send_DataToPeer(size_t config_id, uint32_t value) {
     char message[64];
 
diff --git a/client_sensor/ClientSensor.h b/client_sensor/ClientSensor.h
index 5d8aa45..b37918f 100644
--- a/client_sensor/ClientSensor.h
+++ b/client_sensor/ClientSensor.h
@@ -22,6 +22,7 @@ void init_WiFi();
 // ----------------------------------------------------------------------------
 // Peers
 // ----------------------------------------------------------------------------
+void print_SensorConfig(const SensorConfig_s& cfg);
 
 // ----------------------------------------------------------------------------
 // Config to flash
@@ -41,14 +42,16 @@ void init_EspNow();
 // Time handling
 // ----------------------------------------------------------------------------
 
-uint64_t get_CurrentUnixMs();
+int64_t get_CurrentUnix();
+int64_t get_CurrentUnixMs();
 bool request_TimeSync(uint32_t timeout_ms = 3000);
 void setup_Timezone();
 
 // ----------------------------------------------------------------------------
 // Deep sleep scheduler
 // ----------------------------------------------------------------------------
-void sleep_UntilNextSend();
+int64_t getAndSet_NextSensorReadsUnixTime();
+void    sleep_UntilNextSend(int64_t next_read);
 
 // ----------------------------------------------------------------------------
 // Request sensor config from server via ESP-NOW
diff --git a/client_sensor/client_sensor.ino b/client_sensor/client_sensor.ino
index 6e5194d..e126ab2 100644
--- a/client_sensor/client_sensor.ino
+++ b/client_sensor/client_sensor.ino
@@ -41,8 +41,9 @@ void setup() {
 // ----------------------------------------------------------------------------
 
 void loop() {
-    DBG_PRINTF("## Unix time: %lld\n", (long long)get_CurrentUnixMs() / 1000);
+    DBG_PRINTF("## Unix time: %lld\n", get_CurrentUnix());
     for (size_t config_id = 0; config_id < SENSOR_CONFIG_COUNT; config_id++) {
+        print_SensorConfig(sensor_configs[config_id]);
         if ( is_TimeToReadSensor(config_id) ) {
             /*
              * read sensor value
@@ -52,7 +53,7 @@ void loop() {
             uint32_t value_to_send = fake_value_to_send;
 
             // set last read time
-            sensor_configs[config_id].last_sensor_read = (long long)get_CurrentUnixMs() / 1000;
+            sensor_configs[config_id].last_sensor_read = get_CurrentUnix();
 
             // send to server and configured peer (e.g. pump)
             send_DataToPeer(config_id, value_to_send);
@@ -70,5 +71,7 @@ void loop() {
      */
 
 
-    sleep_UntilNextSend();
+    int64_t next_read = getAndSet_NextSensorReadsUnixTime();
+
+    sleep_UntilNextSend(next_read);
 }
diff --git a/globals.cpp b/globals.cpp
index 11c7af4..e658ac8 100644
--- a/globals.cpp
+++ b/globals.cpp
@@ -22,8 +22,8 @@ constexpr uint8_t PEER_SERVER[] = {0x58,0xcf,0x79,0x1e,0xed,0x18};
 constexpr uint8_t PEER_PUMP[]   = {0x51,0xc1,0x79,0x1e,0xed,0x18};
 
 RTC_DATA_ATTR SensorConfig_s sensor_configs[] = {
-    {"moisture_1", true, 11, 10, 0, "pump_1", {0},  90000, true,  {465, 466}, 2, 0},
-    {"moisture_2", true, 11, 10, 0, "pump_2", {0}, 120000, false, {0}, 0, 0}
+    {"moisture_1", true, 11, 10, 0, "pump_1", {0},  90000, true,  {465, 466}, 2, 0, 0},
+    {"moisture_2", true, 11, 10, 0, "pump_2", {0}, 120000, false, {0}, 0, 0, 0}
 };
 
 uint8_t config_request_attempts_max = 3;
diff --git a/globals.h b/globals.h
index d0b4985..c3f787d 100644
--- a/globals.h
+++ b/globals.h
@@ -66,6 +66,7 @@ struct SensorConfig_s {
     uint16_t times[8];
     uint8_t  time_count;
     int64_t last_sensor_read; // Unix time
+    int64_t next_sensor_read; // Unix time
 };
 
 struct SensorEntry {