Nimbin[12]?Embedded / esp_garden / commits / 6276e6a

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

Cleanup New Server header file

Christian Immanuel · 2025-10-25 19:08 · 6276e6aabe6f8c3e6c5c40d39514b6d7807f1a28

 server/Server.cpp | 316 ++++++++++++++++++++++++++++++++++++++++++++++++++
 server/Server.h   |  57 +++++++++
 server/server.ino | 340 +++---------------------------------------------------
 3 files changed, 389 insertions(+), 324 deletions(-)

diff --git a/server/Server.cpp b/server/Server.cpp
new file mode 100644
index 0000000..fcdf6f3
--- /dev/null
+++ b/server/Server.cpp
@@ -0,0 +1,316 @@
+#include "Server.h"
+#include "secrets.h"
+#include "WiFiManager.h"
+
+#include <esp_now.h>
+
+
+uint8_t received_from_mac_addr[6];
+
+
+// ----------------------------------------------------------------------------
+// Helper
+// ----------------------------------------------------------------------------
+
+void print_SensorConfigServer(const SensorConfigServer_s& cfg) {
+    DBG_PRINTLN("----------------------------");
+    DBG_PRINTF("Got cfg for moisture_1:\n");
+    DBG_PRINTF("Pin: %d\n", cfg.pin);
+    DBG_PRINTF("Use Time: %d\n", cfg.use_time);
+    DBG_PRINTF("Delay: %d\n", cfg.delay_ms);
+    DBG_PRINTF("Notification MAC: %s\n", cfg.pump_mac);
+    DBG_PRINTF("Notification Name: %s\n", cfg.notification_name);
+    DBG_PRINTF("Min Moisture: %d\n", cfg.min_moisture);
+    DBG_PRINTF("Active: %d\n", cfg.is_active);
+    DBG_PRINTF("Pumping Delay: %d\n", cfg.pumping_delay_sec);
+    DBG_PRINTF("Pumping Duration: %d\n", cfg.pumping_duration);
+
+    for (uint8_t i = 0; i < cfg.time_count; i++) {
+        uint16_t minutes = cfg.times[i];
+        uint8_t hh = minutes / 60;
+        uint8_t mm = minutes % 60;
+        DBG_PRINTF("Time[%d]: %02u:%02u (%u min)\n", i, hh, mm, minutes);
+    }
+    DBG_PRINTLN("----------------------------");
+}
+
+// ----------------------------------------------------------------------------
+// Server handling (g_buffer)
+ // ----------------------------------------------------------------------------
+
+void add_ReadingCallback(const char* table, const char* value, const int64_t time) {
+    if (g_buffer_count >= BUFFER_SIZE) {
+        for (int i = 1; i < g_buffer_count; i++) g_buffer[i-1] = g_buffer[i];
+        g_buffer_count--;
+    }
+    strncpy(g_buffer[g_buffer_count].table, table, TABLE_SIZE-1);
+    g_buffer[g_buffer_count].table[TABLE_SIZE-1] = '\0';
+    strncpy(g_buffer[g_buffer_count].value, value, VALUE_SIZE-1);
+    g_buffer[g_buffer_count].time = time;
+    g_buffer_count++;
+}
+
+void init_Server() {
+    WIFI_OnAddReading = add_ReadingCallback;
+}
+
+// ----------------------------------------------------------------------------
+// WiFi handling
+// ----------------------------------------------------------------------------
+
+void init_WiFi() {
+    WiFi.mode(WIFI_MODE_APSTA);
+    WiFi.begin(ssid, password);
+
+    DBG_PRINTF("Connecting to %s .", ssid);
+    while (WiFi.status() != WL_CONNECTED) { DBG_PRINT("."); delay(200); }
+    DBG_PRINTLN(" ok");
+
+    IPAddress ip = WiFi.localIP();
+
+    DBG_PRINTF("SSID: %s\n", ssid);
+    DBG_PRINTF("Channel: %u\n", WiFi.channel());
+    DBG_PRINTF("IP: %u.%u.%u.%u\n", ip & 0xff, (ip >> 8) & 0xff, (ip >> 16) & 0xff, ip >> 24);
+}
+
+// ----------------------------------------------------------------------------
+// ESP-NOW helpers (add peer + send callback)
+// ----------------------------------------------------------------------------
+
+void on_DataSent(const wifi_tx_info_t *info, esp_now_send_status_t status) {
+    if (!info) return;
+
+    DBG_PRINT("Delivery to ");
+    for (int i = 0; i < 6; i++) {
+        DBG_PRINTF("%02X", info->des_addr[i]);
+        if (i < 5) DBG_PRINT(":");
+    }
+    DBG_PRINT(" -> ");
+    DBG_PRINTLN(status == ESP_NOW_SEND_SUCCESS ? "OK" : "FAIL");
+}
+
+void add_Peer(const uint8_t* addr) {
+    if (esp_now_is_peer_exist(addr)) return;
+
+    esp_now_peer_info_t peer{};
+    memcpy(peer.peer_addr, addr, 6);
+    peer.ifidx = WIFI_IF_STA; // server is in APSTA mode but acts as STA on same SSID
+    peer.channel = (uint8_t)WiFi.channel(); // ensure same channel
+    peer.encrypt = false;
+
+    esp_err_t res = esp_now_add_peer(&peer);
+    if (res != ESP_OK) {
+        DBG_PRINT("Failed to add peer: ");
+        for (int i=0; i<6; i++) {
+            DBG_PRINTF("%02X", addr[i]);
+            if (i<5) DBG_PRINT(":");
+        }
+        DBG_PRINTF(" -> error code %d\n", res);
+    } else {
+        DBG_PRINT("Added peer: ");
+        for (int i=0; i<6; i++) {
+            DBG_PRINTF("%02X", addr[i]);
+            if (i<5) DBG_PRINT(":");
+        }
+        DBG_PRINTLN("");
+    }
+}
+
+// ----------------------------------------------------------------------------
+// ESP-NOW receive callback
+// ----------------------------------------------------------------------------
+
+void on_DataRecv(const esp_now_recv_info *info, const uint8_t *data, int len) {
+    if (!info || !data || len <= 0) return;
+    memcpy(received_from_mac_addr, info->src_addr, 6);
+    add_Peer(received_from_mac_addr);
+
+    char msg[128];
+    int copylen = len < (int)sizeof(msg)-1 ? len : (int)sizeof(msg)-1;
+    memcpy(msg, data, copylen);
+    msg[copylen] = '\0';
+
+    DBG_PRINTF("Received from %02X:%02X:%02X:%02X:%02X:%02X: %s\n",
+               received_from_mac_addr[0], received_from_mac_addr[1], received_from_mac_addr[2],
+               received_from_mac_addr[3], received_from_mac_addr[4], received_from_mac_addr[5], msg);
+
+    if (strcmp(msg, "time?") == 0) {
+        char time_msg[32];
+        snprintf(time_msg, sizeof(time_msg), "time:%lld", now());
+        esp_now_send(received_from_mac_addr, (uint8_t*)time_msg, strlen(time_msg));
+        return;
+    }
+
+    // Queue config request instead of calling HTTP directly
+    if (strncmp(msg, "next_read:", 10) == 0) {
+
+        const char* value_str = msg + 10;
+        next_connection_unix = atoll(value_str);
+        go_to_sleep = true;
+        DBG_PRINTF("Received next_read: %lld\n", next_connection_unix);
+        return;
+
+    } else if (strncmp(msg, "get_config:", 11) == 0 && !pending_config.active) {
+
+        pending_config.active = true;
+        memcpy(pending_config.mac, received_from_mac_addr, 6);
+        strncpy(pending_config.sensor_name, msg + 11, sizeof(pending_config.sensor_name) - 1);
+        pending_config.sensor_name[sizeof(pending_config.sensor_name) - 1] = '\0';
+        DBG_PRINTF("Queued config request for %s\n", pending_config.sensor_name);
+        return;
+    }
+
+    // --- Normal table:value:time handling ---
+    DBG_PRINTLN("Message:");
+    DBG_PRINTF("  %s\n", msg);
+    g_received_ok_from_server = false;
+
+    char *first = strchr(msg, ':');
+    char *second = first ? strchr(first + 1, ':') : NULL;
+
+    if (first && second) {
+        *first = '\0';
+        *second = '\0';
+
+        const char *sensor_name = msg;
+        const char *value_str   = first + 1;
+        const int64_t time = atoll(second + 1);
+
+        add_ReadingCallback(sensor_name, value_str, time);
+    } else {
+        DBG_PRINTLN("Invalid message format (expected <name>:<value>:<time>)");
+    }
+
+    /*
+     * Replying to Sensor happens in main loop
+     */
+}
+
+
+// ----------------------------------------------------------------------------
+// Init ESP-NOW
+// ----------------------------------------------------------------------------
+
+void init_EspNow() {
+    if (esp_now_init() != ESP_OK) {
+        DBG_PRINTLN("ESP-NOW failed to initialize");
+        while (true);
+    }
+
+    esp_now_register_recv_cb(on_DataRecv);
+    esp_now_register_send_cb(on_DataSent);
+}
+
+// ----------------------------------------------------------------------------
+// Config handling
+// ----------------------------------------------------------------------------
+
+void get_PendingConfigFromServer() {
+    pending_config.active = false;
+    SensorConfigServer_s cfg_response;
+
+    if (WIFI_GetSensorConfigServer(pending_config.sensor_name, cfg_response)) {
+        char response[250];
+        int len_out = snprintf(response, sizeof(response),
+                "cfg:%s,%d,%d,%d,%s,%d,%d,%s,%d,%d",
+                cfg_response.sensor_name,
+                cfg_response.is_active,
+                cfg_response.pin,
+                cfg_response.min_moisture,
+                cfg_response.notification_name,
+                cfg_response.pumping_delay_sec,
+                cfg_response.pumping_duration,
+                cfg_response.pump_mac,
+                cfg_response.delay_ms,
+                cfg_response.use_time);
+
+        if (cfg_response.time_count > 0) {
+            strncat(response, ",times=", sizeof(response)-strlen(response)-1);
+            for (uint8_t i = 0; i < cfg_response.time_count; i++) {
+                char buf[8];
+                snprintf(buf, sizeof(buf), "%u", cfg_response.times[i]);
+                strncat(response, buf, sizeof(response)-strlen(response)-1);
+                if (i < cfg_response.time_count - 1)
+                    strncat(response, "|", sizeof(response)-strlen(response)-1);
+            }
+        }
+
+        esp_now_send(pending_config.mac, (uint8_t*)response, strlen(response));
+        DBG_PRINTF("Sent config for %s\n", cfg_response.sensor_name);
+        DBG_PRINTF("Sending: %s\n", response);
+    } else {
+        const char* err = "cfg_error:fetch_failed";
+        esp_now_send(pending_config.mac, (uint8_t*)err, strlen(err));
+        DBG_PRINTLN("Config fetch failed");
+    }
+}
+
+void send_ConfigToEsp() {
+    DBG_PRINTLN("Preparing time + configs message");
+    char msg[256];
+    size_t offset = 0;
+
+    time_t now_t;
+    time(&now_t);
+    offset += snprintf(msg + offset, sizeof(msg) - offset, "time:%lu", (unsigned long)now_t);
+
+    // add configs if available
+    if (g_pending_sensor_configs_count > 0) {
+        DBG_PRINTLN("Appending pending configs...");
+        for (size_t i = 0; i < g_pending_sensor_configs_count; i++) {
+            DBG_PRINTF("  adding config: %s\n", g_pending_sensor_configs[i]);
+            offset += snprintf(msg + offset, sizeof(msg) - offset, ",cfg:%s", g_pending_sensor_configs[i]);
+            if (offset >= sizeof(msg)) break;
+        }
+        g_pending_sensor_configs_count = 0;
+    }
+
+    DBG_PRINTLN("Sending combined message:");
+    DBG_PRINTLN(msg);
+
+    esp_err_t result = esp_now_send(received_from_mac_addr, (uint8_t*)msg, strlen(msg));
+    if (result == ESP_OK)
+        DBG_PRINTLN("Send queued successfully.");
+    else
+        DBG_PRINTF("Send failed: %d\n", result);
+
+    g_received_ok_from_server = false;
+}
+
+
+// ----------------------------------------------------------------------------
+// Sleep
+// ----------------------------------------------------------------------------
+
+void sleep_UntilNextReceive() {
+    go_to_sleep = false;
+    uint32_t min_delay_ms = (next_connection_unix - now())*1000;
+
+    int64_t unix_now = now();
+    if (next_connection_unix < unix_now) return;
+
+    DBG_PRINTF("Nex: %lld\nNow: %lld\n", next_connection_unix, unix_now);
+
+    next_connection_unix = 0;
+
+    // Prevent sleeping if next send is within g_min_sleep_time
+    if (min_delay_ms < g_min_sleep_time) {
+        DBG_PRINTF("Delaying for %u ms (no deep sleep)\n", min_delay_ms);
+        DBG_PRINTF("######################\n\n");
+        delay(min_delay_ms);
+    } else {
+        uint32_t prep_time_ms = g_setup_duration + g_wakeup_earlier_server;
+        uint32_t sleep_time_ms = (min_delay_ms > prep_time_ms)
+            ? (min_delay_ms - prep_time_ms)
+            : 0;
+
+
+        DBG_PRINTF("Sleeping for %u ms (setup margin: %u ms)\n",
+                sleep_time_ms, prep_time_ms);
+        DBG_PRINTF("######################\n\n");
+
+        esp_sleep_enable_timer_wakeup((uint64_t)sleep_time_ms * 1000ULL);
+        esp_deep_sleep_start();
+    }
+
+}
diff --git a/server/Server.h b/server/Server.h
new file mode 100644
index 0000000..a17c1be
--- /dev/null
+++ b/server/Server.h
@@ -0,0 +1,57 @@
+#ifndef SERVER_H
+#define SERVER_H
+
+#include "globals.h"
+
+#include <Arduino.h>
+
+
+extern int64_t next_connection_unix;
+extern bool go_to_sleep;
+
+struct PendingConfigRequest_s {
+    bool active = false;
+    uint8_t mac[6];
+    char sensor_name[32];
+};
+
+extern PendingConfigRequest_s pending_config;
+
+// ----------------------------------------------------------------------------
+// Helper
+// ----------------------------------------------------------------------------
+
+void print_SensorConfigServer(const SensorConfigServer_s& cfg);
+
+// ----------------------------------------------------------------------------
+// Server handling (g_buffer)
+// ----------------------------------------------------------------------------
+
+void init_Server();
+
+// ----------------------------------------------------------------------------
+// WiFi handling
+// ----------------------------------------------------------------------------
+
+void init_WiFi();
+
+// ----------------------------------------------------------------------------
+// Init ESP-NOW
+// ----------------------------------------------------------------------------
+
+void init_EspNow();
+
+// ----------------------------------------------------------------------------
+// Config handling
+// ----------------------------------------------------------------------------
+
+void get_PendingConfigFromServer();
+void send_ConfigToEsp();
+
+// ----------------------------------------------------------------------------
+// Sleep
+// ----------------------------------------------------------------------------
+
+void sleep_UntilNextReceive();
+
+#endif
diff --git a/server/server.ino b/server/server.ino
index e7206d0..a342986 100644
--- a/server/server.ino
+++ b/server/server.ino
@@ -1,196 +1,11 @@
-#include "secrets.h"
+#include "Server.h"
 #include "WiFiManager.h"
-#include "globals.h"
 
-#include <Arduino.h>
-#include <WiFi.h>
-#include <esp_now.h>
-
-
-uint8_t received_from_mac_addr[6];
-
-int64_t next_connection_unix;
-bool go_to_sleep;
-
-struct PendingConfigRequest_s {
-    bool active = false;
-    uint8_t mac[6];
-    char sensor_name[32];
-};
+int64_t next_connection_unix = 0;
+bool go_to_sleep = false;
 
 PendingConfigRequest_s pending_config;
-SensorConfigServer_s cfg_response;
-
-// ----------------------------------------------------------------------------
-// Server handling (g_buffer)
- // ----------------------------------------------------------------------------
-
-void add_ReadingCallback(const char* table, const char* value, const int64_t time) {
-    if (g_buffer_count >= BUFFER_SIZE) {
-        for (int i = 1; i < g_buffer_count; i++) g_buffer[i-1] = g_buffer[i];
-        g_buffer_count--;
-    }
-    strncpy(g_buffer[g_buffer_count].table, table, TABLE_SIZE-1);
-    g_buffer[g_buffer_count].table[TABLE_SIZE-1] = '\0';
-    strncpy(g_buffer[g_buffer_count].value, value, VALUE_SIZE-1);
-    g_buffer[g_buffer_count].time = time;
-    g_buffer_count++;
-}
-
-void init_Server() {
-    WIFI_OnAddReading = add_ReadingCallback;
-}
-
-// ----------------------------------------------------------------------------
-// WiFi handling
-// ----------------------------------------------------------------------------
-
-void init_WiFi() {
-    WiFi.mode(WIFI_MODE_APSTA);
-    WiFi.begin(ssid, password);
-
-    DBG_PRINTF("Connecting to %s .", ssid);
-    while (WiFi.status() != WL_CONNECTED) { DBG_PRINT("."); delay(200); }
-    DBG_PRINTLN(" ok");
-
-    IPAddress ip = WiFi.localIP();
-
-    DBG_PRINTF("SSID: %s\n", ssid);
-    DBG_PRINTF("Channel: %u\n", WiFi.channel());
-    DBG_PRINTF("IP: %u.%u.%u.%u\n", ip & 0xff, (ip >> 8) & 0xff, (ip >> 16) & 0xff, ip >> 24);
-}
-
-// ----------------------------------------------------------------------------
-// ESP-NOW helpers (add peer + send callback)
-// ----------------------------------------------------------------------------
-
-void on_DataSent(const wifi_tx_info_t *info, esp_now_send_status_t status) {
-    if (!info) return;
-
-    DBG_PRINT("Delivery to ");
-    for (int i = 0; i < 6; i++) {
-        DBG_PRINTF("%02X", info->des_addr[i]);
-        if (i < 5) DBG_PRINT(":");
-    }
-    DBG_PRINT(" -> ");
-    DBG_PRINTLN(status == ESP_NOW_SEND_SUCCESS ? "OK" : "FAIL");
-}
-
-void add_Peer(const uint8_t* addr) {
-    if (esp_now_is_peer_exist(addr)) return;
-
-    esp_now_peer_info_t peer{};
-    memcpy(peer.peer_addr, addr, 6);
-    peer.ifidx = WIFI_IF_STA; // server is in APSTA mode but acts as STA on same SSID
-    peer.channel = (uint8_t)WiFi.channel(); // ensure same channel
-    peer.encrypt = false;
-
-    esp_err_t res = esp_now_add_peer(&peer);
-    if (res != ESP_OK) {
-        DBG_PRINT("Failed to add peer: ");
-        for (int i=0; i<6; i++) {
-            DBG_PRINTF("%02X", addr[i]);
-            if (i<5) DBG_PRINT(":");
-        }
-        DBG_PRINTF(" -> error code %d\n", res);
-    } else {
-        DBG_PRINT("Added peer: ");
-        for (int i=0; i<6; i++) {
-            DBG_PRINTF("%02X", addr[i]);
-            if (i<5) DBG_PRINT(":");
-        }
-        DBG_PRINTLN("");
-    }
-}
-
-// ----------------------------------------------------------------------------
-// ESP-NOW receive callback
-// ----------------------------------------------------------------------------
-
-void on_DataRecv(const esp_now_recv_info *info, const uint8_t *data, int len) {
-    if (!info || !data || len <= 0) return;
-    memcpy(received_from_mac_addr, info->src_addr, 6);
-    add_Peer(received_from_mac_addr);
-
-    char msg[128];
-    int copylen = len < (int)sizeof(msg)-1 ? len : (int)sizeof(msg)-1;
-    memcpy(msg, data, copylen);
-    msg[copylen] = '\0';
-
-    DBG_PRINTF("Received from %02X:%02X:%02X:%02X:%02X:%02X: %s\n",
-               received_from_mac_addr[0], received_from_mac_addr[1], received_from_mac_addr[2],
-               received_from_mac_addr[3], received_from_mac_addr[4], received_from_mac_addr[5], msg);
-
-    if (strcmp(msg, "time?") == 0) {
-        char time_msg[32];
-        snprintf(time_msg, sizeof(time_msg), "time:%lld", now());
-        esp_now_send(received_from_mac_addr, (uint8_t*)time_msg, strlen(time_msg));
-        return;
-    }
-
-    // Queue config request instead of calling HTTP directly
-    if (strncmp(msg, "next_read:", 10) == 0) {
-
-        const char* value_str = msg + 10;
-        next_connection_unix = atoll(value_str);
-        go_to_sleep = true;
-        DBG_PRINTF("Received next_read: %lld\n", next_connection_unix);
-        return;
-
-    } else if (strncmp(msg, "get_config:", 11) == 0 && !pending_config.active) {
-
-        pending_config.active = true;
-        memcpy(pending_config.mac, received_from_mac_addr, 6);
-        strncpy(pending_config.sensor_name, msg + 11, sizeof(pending_config.sensor_name) - 1);
-        pending_config.sensor_name[sizeof(pending_config.sensor_name) - 1] = '\0';
-        DBG_PRINTF("Queued config request for %s\n", pending_config.sensor_name);
-        return;
-    }
-
-    // --- Normal table:value:time handling ---
-    DBG_PRINTLN("Message:");
-    DBG_PRINTF("  %s\n", msg);
-    g_received_ok_from_server = false;
-
-    char *first = strchr(msg, ':');
-    char *second = first ? strchr(first + 1, ':') : NULL;
 
-    if (first && second) {
-        *first = '\0';
-        *second = '\0';
-
-        const char *sensor_name = msg;
-        const char *value_str   = first + 1;
-        const int64_t time = atoll(second + 1);
-
-        add_ReadingCallback(sensor_name, value_str, time);
-    } else {
-        DBG_PRINTLN("Invalid message format (expected <name>:<value>:<time>)");
-    }
-
-    /*
-     * Replying to Sensor happens in main loop
-     */
-}
-
-
-// ----------------------------------------------------------------------------
-// Init ESP-NOW
-// ----------------------------------------------------------------------------
-
-void init_EspNow() {
-    if (esp_now_init() != ESP_OK) {
-        DBG_PRINTLN("ESP-NOW failed to initialize");
-        while (true);
-    }
-
-    esp_now_register_recv_cb(on_DataRecv);
-    esp_now_register_send_cb(on_DataSent);
-}
-
-// ----------------------------------------------------------------------------
-// Initialization
-// ----------------------------------------------------------------------------
 
 void setup() {
     uint32_t setup_start = millis();
@@ -202,31 +17,12 @@ void setup() {
     init_Server();
     init_EspNow();
 
-    SensorConfigServer_s cfg;
-
     /*
-if (WIFI_GetSensorConfigServer("moisture_1", cfg)) {
-    DBG_PRINTF("Got cfg for moisture_1:\n");
-    DBG_PRINTF("Pin: %d\n", cfg.pin);
-    DBG_PRINTF("Use Time: %d\n", cfg.use_time);
-    DBG_PRINTF("Delay: %d\n", cfg.delay_ms);
-    DBG_PRINTF("Notification MAC: %s\n", cfg.pump_mac);
-    DBG_PRINTF("Notification Name: %s\n", cfg.notification_name);
-    DBG_PRINTF("Min Moisture: %d\n", cfg.min_moisture);
-    DBG_PRINTF("Active: %d\n", cfg.is_active);
-    DBG_PRINTF("Pumping Delay: %d\n", cfg.pumping_delay_sec);
-    DBG_PRINTF("Pumping Duration: %d\n", cfg.pumping_duration);
-
-    for (uint8_t i = 0; i < cfg.time_count; i++) {
-        uint16_t minutes = cfg.times[i];
-        uint8_t hh = minutes / 60;
-        uint8_t mm = minutes % 60;
-        DBG_PRINTF("Time[%d]: %02u:%02u (%u min)\n", i, hh, mm, minutes);
-    }
-} else {
-    DBG_PRINTLN("Failed to get cfg!");
-}
-*/
+SensorConfigServer_s cfg;
+if (WIFI_GetSensorConfigServer("moisture_1", cfg))
+    print_SensorConfigServer(cfg);
+else DBG_PRINTLN("Failed to get cfg!");
+    */
 
     go_to_sleep = false;
     next_connection_unix = 0;
@@ -234,122 +30,18 @@ if (WIFI_GetSensorConfigServer("moisture_1", cfg)) {
     DBG_PRINTF("Setup duration: %u ms\n", g_setup_duration);
 }
 
-// ----------------------------------------------------------------------------
-// Main control loop
-// ----------------------------------------------------------------------------
 
 void loop() {
-    if (pending_config.active) {
-        pending_config.active = false;
-        if (WIFI_GetSensorConfigServer(pending_config.sensor_name, cfg_response)) {
-            char response[250];
-            int len_out = snprintf(response, sizeof(response),
-                                   "cfg:%s,%d,%d,%d,%s,%d,%d,%s,%d,%d",
-                                   cfg_response.sensor_name,
-                                   cfg_response.is_active,
-                                   cfg_response.pin,
-                                   cfg_response.min_moisture,
-                                   cfg_response.notification_name,
-                                   cfg_response.pumping_delay_sec,
-                                   cfg_response.pumping_duration,
-                                   cfg_response.pump_mac,
-                                   cfg_response.delay_ms,
-                                   cfg_response.use_time);
-
-            if (cfg_response.time_count > 0) {
-                strncat(response, ",times=", sizeof(response)-strlen(response)-1);
-                for (uint8_t i = 0; i < cfg_response.time_count; i++) {
-                    char buf[8];
-                    snprintf(buf, sizeof(buf), "%u", cfg_response.times[i]);
-                    strncat(response, buf, sizeof(response)-strlen(response)-1);
-                    if (i < cfg_response.time_count - 1)
-                        strncat(response, "|", sizeof(response)-strlen(response)-1);
-                }
-            }
-
-            esp_now_send(pending_config.mac, (uint8_t*)response, strlen(response));
-            DBG_PRINTF("Sent config for %s\n", cfg_response.sensor_name);
-            DBG_PRINTF("Sending: %s\n", response);
-        } else {
-            const char* err = "cfg_error:fetch_failed";
-            esp_now_send(pending_config.mac, (uint8_t*)err, strlen(err));
-            DBG_PRINTLN("Config fetch failed");
-        }
-    }
-
-    if (g_buffer_count > 0) {
-      DBG_PRINTLN("Posting g_buffer...");
-      bool ok = WIFI_PostBufferSync();
-      if (!ok) DBG_PRINTLN("Post failed, will retry next loop");
-    }
-
-
-
-
-    if (g_received_ok_from_server) {
-      DBG_PRINTLN("Preparing time + configs message");
-
-      char msg[256];
-      size_t offset = 0;
-
-      time_t now_t;
-      time(&now_t);
-      offset += snprintf(msg + offset, sizeof(msg) - offset, "time:%lu", (unsigned long)now_t);
-
-      // add configs if available
-      if (g_pending_sensor_configs_count > 0) {
-        DBG_PRINTLN("Appending pending configs...");
-        for (size_t i = 0; i < g_pending_sensor_configs_count; i++) {
-          DBG_PRINTF("  adding config: %s\n", g_pending_sensor_configs[i]);
-          offset += snprintf(msg + offset, sizeof(msg) - offset, ",cfg:%s", g_pending_sensor_configs[i]);
-          if (offset >= sizeof(msg)) break;
-        }
-        g_pending_sensor_configs_count = 0;
-      }
-
-      DBG_PRINTLN("Sending combined message:");
-      DBG_PRINTLN(msg);
-
-      esp_err_t result = esp_now_send(received_from_mac_addr, (uint8_t*)msg, strlen(msg));
-      if (result == ESP_OK)
-        DBG_PRINTLN("Send queued successfully.");
-      else
-        DBG_PRINTF("Send failed: %d\n", result);
-
-      g_received_ok_from_server = false;
-    }
-
-    if (go_to_sleep) {
-        go_to_sleep = false;
-        uint32_t min_delay_ms = (next_connection_unix - now())*1000;
-
-        int64_t unix_now = now();
-        if (next_connection_unix < unix_now) return;
-
-        DBG_PRINTF("Nex: %lld\nNow: %lld\n", next_connection_unix, unix_now);
-
-        next_connection_unix = 0;
-
-        // Prevent sleeping if next send is within g_min_sleep_time
-        if (min_delay_ms < g_min_sleep_time) {
-            DBG_PRINTF("Delaying for %u ms (no deep sleep)\n", min_delay_ms);
-            DBG_PRINTF("######################\n\n");
-            delay(min_delay_ms);
-        } else {
-            uint32_t prep_time_ms = g_setup_duration + g_wakeup_earlier_server;
-            uint32_t sleep_time_ms = (min_delay_ms > prep_time_ms)
-                ? (min_delay_ms - prep_time_ms)
-                : 0;
-
+    if (pending_config.active)
+        get_PendingConfigFromServer();
 
-            DBG_PRINTF("Sleeping for %u ms (setup margin: %u ms)\n",
-                    sleep_time_ms, prep_time_ms);
-            DBG_PRINTF("######################\n\n");
+    if (g_buffer_count > 0)
+        WIFI_PostBufferSync();
 
-            esp_sleep_enable_timer_wakeup((uint64_t)sleep_time_ms * 1000ULL);
-            esp_deep_sleep_start();
-        }
+    if (g_received_ok_from_server)
+        send_ConfigToEsp();
 
-    }
+    if (go_to_sleep)
+        sleep_UntilNextReceive();
 }