Nimbin[12]?Embedded / esp_garden / commits / 865166c

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

Christian Immanuel · 2025-10-23 08:30 · 865166cca43515d11c45f9916817a571449ea79e

 client_sensor/ClientSensor.cpp  | 609 ++++++++++++++++++++++++++++++++++++++
 client_sensor/ClientSensor.h    |  65 +++++
 client_sensor/client_sensor.ino | 628 +---------------------------------------
 globals.cpp                     |  18 ++
 globals.h                       |  14 +
 5 files changed, 709 insertions(+), 625 deletions(-)

diff --git a/client_sensor/ClientSensor.cpp b/client_sensor/ClientSensor.cpp
new file mode 100644
index 0000000..99e59ac
--- /dev/null
+++ b/client_sensor/ClientSensor.cpp
@@ -0,0 +1,609 @@
+#include "ClientSensor.h"
+#include "secrets.h"
+
+volatile bool rtc_synced_with_server = false;
+volatile bool ack_received = false;
+volatile bool send_process_complete = false;
+volatile bool send_ack_process_complete = false;
+
+
+// ----------------------------------------------------------------------------
+// Helper
+// ----------------------------------------------------------------------------
+void printMac(const uint8_t* mac) {
+    for (size_t i = 0; i < 6; i++) {
+        if (i > 0) DBG_PRINT(":");
+        DBG_PRINTF("%02X", mac[i]);
+    }
+    DBG_PRINTLN();
+}
+
+// ----------------------------------------------------------------------------
+// WiFi handling
+// ----------------------------------------------------------------------------
+int32_t get_WiFiChannel() {
+    DBG_PRINTLN(ssid);
+    int32_t n = WiFi.scanNetworks();
+    if (n <= 0) return 0;
+    for (uint8_t i = 0; i < n; i++) {
+        if (!strcmp(ssid, WiFi.SSID(i).c_str())) return WiFi.channel(i);
+    }
+    return 0;
+}
+
+void init_WiFi() {
+    WiFi.mode(WIFI_MODE_STA);
+    int32_t channel = get_WiFiChannel();
+    if (channel > 0) {
+        esp_wifi_set_promiscuous(true);
+        esp_wifi_set_channel(channel, WIFI_SECOND_CHAN_NONE);
+        esp_wifi_set_promiscuous(false);
+    }
+}
+
+
+// ----------------------------------------------------------------------------
+// Peers
+// ----------------------------------------------------------------------------
+
+uint8_t config_request_attempts = 0;
+
+void printSensorConfig(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("  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);
+
+    if (cfg.time_count > 0) {
+        DBG_PRINT("  times: ");
+        for (uint8_t i = 0; i < cfg.time_count; i++) {
+            DBG_PRINTF("%u", cfg.times[i]);
+            if (i < cfg.time_count - 1)
+                DBG_PRINT("|");
+        }
+        DBG_PRINTLN("");
+    }
+}
+
+// ----------------------------------------------------------------------------
+// Config to flash
+// ----------------------------------------------------------------------------
+
+#include <Preferences.h>
+
+Preferences prefs;
+
+bool load_SensorConfigs() {
+    DBG_PRINTLN("Loading sensor configs from NVS...");
+
+    prefs.begin("sensors", true);
+    size_t stored_size = prefs.getBytesLength("configs");
+    DBG_PRINTF("Stored size: %u bytes\n", (unsigned)stored_size);
+    bool ok = false;
+    size_t sc_size = SENSOR_CONFIG_COUNT * sizeof(SensorConfig_s);
+    if (stored_size == sc_size) {
+        prefs.getBytes("configs", sensor_configs, sc_size);
+        ok = true;
+        DBG_PRINTLN("Config read successfully.");
+    } else DBG_PRINTLN("No valid stored config found.");
+    prefs.end();
+
+    return ok;
+}
+
+void save_SensorConfigs() {
+    DBG_PRINTLN("Saving sensor configs to NVS...");
+
+    prefs.begin("sensors", false);
+    prefs.putBytes("configs", sensor_configs, SENSOR_CONFIG_COUNT * sizeof(SensorConfig_s));
+    prefs.end();
+
+    DBG_PRINTLN("Save complete.");
+}
+
+void restore_SensorConfigs() {
+    DBG_PRINTLN("Trying to load old config!");
+
+    bool restored = load_SensorConfigs();
+    if (restored) {
+        DBG_PRINTLN("Sensor configs restored from flash.");
+    } else {
+        DBG_PRINTLN("No stored configs found, using defaults.");
+        for (size_t i = 0; i < SENSOR_CONFIG_COUNT; i++)
+            memcpy(sensor_configs[i].pump_mac, PEER_PUMP, MAC_ADDR_LEN);
+    }
+}
+
+
+// ----------------------------------------------------------------------------
+// RTC variables
+// ----------------------------------------------------------------------------
+RTC_DATA_ATTR uint64_t last_sent_array[SENSOR_CONFIG_COUNT] = {0};
+
+// ----------------------------------------------------------------------------
+// ESP-NOW handling
+// ----------------------------------------------------------------------------
+
+void on_DataSent(const wifi_tx_info_t* info, esp_now_send_status_t status) {
+    DBG_PRINT("Packet to ");
+    for (int i = 0; i < 6; i++) {
+        DBG_PRINTF("%02X", info->des_addr[i]);
+        if (i < 5) DBG_PRINT(":");
+    }
+
+    if (status == ESP_NOW_SEND_SUCCESS)
+        DBG_PRINTLN(" queued successfully (waiting for ACK)");
+    else
+        DBG_PRINTLN(" failed to queue!");
+}
+
+void on_DataRecv(const esp_now_recv_info *info, const uint8_t *data, int len) {
+    if (!info || !data || len <= 0) return;
+
+    char msg[256];
+    memcpy(msg, data, len);
+    msg[len] = '\0';
+
+    DBG_PRINTF("Received from server: %s\n", msg);
+
+    if (strncmp(msg, "time:", 5) == 0) {
+        time_t server_unix = strtoul(msg + 5, nullptr, 10);
+        struct timeval tv = { .tv_sec = server_unix, .tv_usec = 0 };
+        settimeofday(&tv, nullptr);
+        rtc_synced_with_server = true;
+        DBG_PRINTLN("System time updated from server.");
+    }
+
+    DBG_PRINTLN("Setting send_process_complete to TRUE");
+    send_process_complete = true;
+}
+
+
+void on_DataRecvAck(const esp_now_recv_info *info, const uint8_t *data, int len) {
+    if (!info || !data || len <= 0) return;
+
+    char msg[256];
+    int copylen = len < (int)sizeof(msg) - 1 ? len : (int)sizeof(msg) - 1;
+    memcpy(msg, data, copylen);
+    msg[copylen] = '\0';
+
+    DBG_PRINTF("ACK received: %s\n", msg);
+
+    const char *time_ptr = strstr(msg, "time:");
+    if (time_ptr) {
+        time_t server_unix = strtoul(time_ptr + 5, nullptr, 10);
+        struct timeval tv = { .tv_sec = server_unix, .tv_usec = 0 };
+        settimeofday(&tv, nullptr);
+        rtc_synced_with_server = true;
+        ack_received = true;
+        DBG_PRINTLN("System time updated from server (ACK).");
+    }
+
+    const char *cfg_ptr = msg;
+    while ((cfg_ptr = strstr(cfg_ptr, "cfg:")) != nullptr) {
+        cfg_ptr += 4;
+        const char *end = strchr(cfg_ptr, ',');
+        size_t len_cfg = end ? (size_t)(end - cfg_ptr) : strlen(cfg_ptr);
+        if (len_cfg >= sizeof(g_pending_sensor_configs[0])) len_cfg = sizeof(g_pending_sensor_configs[0]) - 1;
+
+        char new_cfg[32];
+        memcpy(new_cfg, cfg_ptr, len_cfg);
+        new_cfg[len_cfg] = '\0';
+
+        bool exists = false;
+        for (uint8_t i = 0; i < g_pending_sensor_configs_count; i++) {
+            if (strcmp(g_pending_sensor_configs[i], new_cfg) == 0) {
+                exists = true;
+                break;
+            }
+        }
+
+        if (!exists && g_pending_sensor_configs_count < 4) {
+            strncpy(g_pending_sensor_configs[g_pending_sensor_configs_count], new_cfg, sizeof(g_pending_sensor_configs[0]) - 1);
+            g_pending_sensor_configs[g_pending_sensor_configs_count][sizeof(g_pending_sensor_configs[0]) - 1] = '\0';
+            g_pending_sensor_configs_count++;
+            DBG_PRINTF("Added new config: %s\n", new_cfg);
+        }
+
+        if (!end) break;
+        cfg_ptr = end;
+    }
+
+    DBG_PRINTLN("Setting send_ack_process_complete to TRUE");
+    send_ack_process_complete = true;
+}
+
+void add_Peer(const uint8_t* addr) {
+    esp_now_peer_info_t peer{};
+    memcpy(peer.peer_addr, addr, 6);
+    peer.ifidx = WIFI_IF_STA;
+    peer.channel = 0;
+    peer.encrypt = false;
+    if (!esp_now_is_peer_exist(addr)) esp_now_add_peer(&peer);
+}
+
+void ensure_Peers() {
+    for (size_t i=0; i<SENSOR_CONFIG_COUNT; i++) add_Peer(sensor_configs[i].pump_mac);
+    add_Peer(PEER_SERVER);
+}
+
+void init_EspNow() {
+    if (esp_now_init() != ESP_OK) {
+        DBG_PRINTLN("ESP NOW init failed");
+        while (true);
+    }
+    esp_now_register_send_cb(on_DataSent);
+    ensure_Peers();
+}
+
+bool send_Message(const uint8_t* peer, const char* message) {
+    if (!esp_now_is_peer_exist(peer)) {
+        DBG_PRINTLN("ERROR: peer doese not exist!");
+        return false;
+    }
+    esp_err_t result = esp_now_send(peer, (uint8_t*)message, strlen(message));
+    return result == ESP_OK;
+}
+
+bool send_And_wait_Ack(const uint8_t* peer, const char* message, uint32_t timeout_ms = 5000) {
+    send_ack_process_complete = false;
+    ack_received = false;
+
+    esp_now_register_recv_cb(on_DataRecvAck);
+
+    if (!send_Message(peer, message)) {
+        DBG_PRINTLN("Failed to queue message for sending!");
+        return false;
+    }
+
+    unsigned long start = millis();
+    while (!ack_received && millis() - start < timeout_ms) {
+        delay(10);
+    }
+
+    if (!ack_received) {
+        DBG_PRINT("No ACK received from ");
+        printMac(peer);
+        return false;
+    }
+
+    return true;
+}
+
+// ----------------------------------------------------------------------------
+// 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;
+}
+
+bool request_TimeSync(uint32_t timeout_ms) {
+    const uint8_t* server_mac = PEER_SERVER;
+    unsigned long start = millis();
+    esp_now_register_recv_cb(on_DataRecv);
+    send_Message(server_mac, "time?");
+    while (millis() - start < timeout_ms) {
+        if (rtc_synced_with_server) return true;
+        delay(50);
+    }
+    return false;
+}
+
+void setup_Timezone() {
+    setenv("TZ", "CET-1CEST,M3.5.0,M10.5.0/3", 1);
+    tzset();
+}
+
+void get_LocalTime(time_t unix_time, int *hour, int *minute) {
+    struct tm tm_time;
+    localtime_r(&unix_time, &tm_time);
+    *hour = tm_time.tm_hour;
+    *minute = tm_time.tm_min;
+}
+
+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);
+    return hour * 60 + minute;
+}
+
+// ----------------------------------------------------------------------------
+// Deep sleep scheduler
+// ----------------------------------------------------------------------------
+void sleep_UntilNextSend() {
+    uint64_t ms_now = get_CurrentUnixMs();
+    uint32_t min_delay_ms = UINT32_MAX;
+
+    for (size_t i = 0; i < SENSOR_CONFIG_COUNT; i++) {
+        SensorConfig_s & p = sensor_configs[i];
+        if (!p.is_active) continue;
+
+        if (p.use_time) {
+            uint16_t min_current = get_CurrentMinutes();
+            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;
+            }
+        } else {
+            uint32_t delta_ms = (p.delay_ms > (ms_now - last_sent_array[i]))
+                ? (p.delay_ms - (ms_now - last_sent_array[i]))
+                : 0;
+            if (delta_ms < min_delay_ms) min_delay_ms = delta_ms;
+        }
+    }
+
+    if (min_delay_ms == UINT32_MAX) return;
+
+    // Prevent sleeping if next send is within 1 minute
+    if (min_delay_ms < min_sleep_time) {
+        DBG_PRINTF("Delaying for %u ms\n", min_delay_ms);
+        DBG_PRINTF("######################\n\n");
+        delay(min_delay_ms);
+        return;
+    }
+
+    DBG_PRINTF("Sleeping for %u ms\n", min_delay_ms);
+    esp_sleep_enable_timer_wakeup((uint64_t)min_delay_ms * 1000ULL);
+    // set server_time for the case we dont get a new connection.
+    // idk why its 1000 to big,..
+    DBG_PRINTF("######################\n\n");
+    esp_deep_sleep_start();
+}
+
+// ----------------------------------------------------------------------------
+// Request sensor config from server via ESP-NOW
+// ----------------------------------------------------------------------------
+volatile
+bool sensor_config_received     = false;
+char sensor_config_message[256] = { 0 };
+
+void on_DataRecvSensorConfig(const esp_now_recv_info* info, const uint8_t* data, int len) {
+    if (!info || !data || len <= 0) return;
+    if (memcmp(info->src_addr, PEER_SERVER, 6) != 0) return;
+
+    int copylen = len < (int)sizeof(sensor_config_message) - 1 ? len : (int)sizeof(sensor_config_message) - 1;
+    memcpy(sensor_config_message, data, copylen);
+    sensor_config_message[copylen] = '\0';
+
+    if (strncmp(sensor_config_message, "cfg:", 4) != 0) return;
+
+    sensor_config_received = true;
+}
+
+bool request_SensorConfig(const char* sensor_name) {
+    const uint8_t* server_mac = PEER_SERVER;
+    if (!esp_now_is_peer_exist(server_mac)) add_Peer(server_mac);
+
+    sensor_config_received = false;
+    memset(sensor_config_message, 0, sizeof(sensor_config_message));
+
+    esp_now_register_recv_cb(on_DataRecvSensorConfig);
+
+    char msg[64];
+    snprintf(msg, sizeof(msg), "get_config:%s", sensor_name);
+
+    if (!send_Message(server_mac, msg)) {
+        DBG_PRINTLN("Failed to send get_config request");
+        return false;
+    }
+
+    unsigned long start = millis();
+    const uint32_t max_wait = 10000;
+
+    while (!sensor_config_received && millis() - start < max_wait) {
+        delay(50);
+        yield();
+    }
+
+    if (!sensor_config_received) {
+        DBG_PRINTLN("No config reply received (timeout)");
+        return false;
+    }
+
+    if (strncmp(sensor_config_message, "cfg:", 4) != 0) {
+        DBG_PRINTF("Invalid config response: %s\n", sensor_config_message);
+        return false;
+    }
+
+    char* ptr = sensor_config_message + 4;
+    char* tokens[20];
+    uint8_t count_token = 0;
+    char* tok = strtok(ptr, ",");
+    while (tok && count_token < 20) {
+        tokens[count_token++] = tok;
+        tok = strtok(nullptr, ",");
+    }
+
+    if (count_token == 0) return false;
+
+    DBG_PRINTF("Config received for %s:\n", tokens[0]);
+    for (uint8_t i = 1; i < count_token; i++) {
+        DBG_PRINTF("  %u: %s\n", i, tokens[i]);
+    }
+
+
+    if (count_token < 9) {
+        DBG_PRINTLN("Incomplete config data");
+        return false;
+    }
+
+    SensorConfig_s *cfg = NULL;
+    size_t config_id = 0;
+    for (size_t i = 0; i < SENSOR_CONFIG_COUNT; i++) {
+        if (strcmp(sensor_configs[i].sensor_name, tokens[0]) == 0) {
+            config_id = i;
+            cfg = &sensor_configs[i];
+            break;
+        }
+    }
+
+    if (!cfg) {
+        DBG_PRINTF("Unknown sensor name in config: %s\n", tokens[0]);
+        return false;
+    }
+
+    // basic fields
+    strncpy(cfg->sensor_name, tokens[0], sizeof(cfg->sensor_name)-1);
+    cfg->sensor_name[sizeof(cfg->sensor_name)-1] = '\0';
+    cfg->is_active = atoi(tokens[1]) != 0;
+    cfg->pin = atoi(tokens[2]);
+    cfg->min_moisture = atoi(tokens[3]);
+    cfg->notification_delay_ms = atoi(tokens[4]);
+    strncpy(cfg->notification_name, tokens[5], sizeof(cfg->notification_name)-1);
+    cfg->notification_name[sizeof(cfg->notification_name)-1] = '\0';
+    strncpy((char*)cfg->pump_mac, tokens[6], sizeof(cfg->pump_mac)-1);
+    cfg->pump_mac[sizeof(cfg->pump_mac)-1] = '\0';
+    cfg->delay_ms = atoi(tokens[7]);
+    cfg->use_time = atoi(tokens[8]) != 0;
+
+    // times
+    cfg->time_count = 0;
+    if (cfg->use_time && count_token > 9 && strncmp(tokens[9], "times=", 6) == 0) {
+        char *time_str = tokens[9] + 6;
+        char *time_tok = strtok(time_str, "|");
+        while (time_tok && cfg->time_count < 8) {
+            cfg->times[cfg->time_count++] = (uint16_t)atoi(time_tok);
+            time_tok = strtok(NULL, "|");
+        }
+    }
+
+    config_request_attempts = 0;
+    return true;
+}
+
+void process_PendingSensorConfigs() {
+    config_request_attempts = 0;
+    DBG_PRINTF("NEW CONFIGS: %d\n", g_pending_sensor_configs_count);
+
+    size_t config_id = 0;
+    bool save_new_configs = false;
+    while (config_id < g_pending_sensor_configs_count) {
+        DBG_PRINTF("SENSOR: %s\n", g_pending_sensor_configs[config_id]);
+
+        bool success = false;
+        for (config_request_attempts = 0;
+                config_request_attempts < config_request_attempts_max;
+                config_request_attempts++) {
+
+            DBG_PRINTF("Requesting config attempt %u...\n", config_request_attempts + 1);
+            if (request_SensorConfig(g_pending_sensor_configs[config_id])) {
+                success = true;
+                break;
+            }
+            delay(2000);
+        }
+
+        if (success) {
+            DBG_PRINTF("Config %s processed successfully, removing.\n",
+                    g_pending_sensor_configs[config_id]);
+            if (config_id >= g_pending_sensor_configs_count) return;
+            for (size_t i = config_id; i < g_pending_sensor_configs_count - 1; i++)
+                strcpy(g_pending_sensor_configs[i], g_pending_sensor_configs[i + 1]);
+            g_pending_sensor_configs_count--;
+            save_new_configs = true;
+        } else {
+            DBG_PRINTLN("Config request failed repeatedly, aborting loop.");
+            break;
+        }
+    }
+
+    if (save_new_configs) save_SensorConfigs();
+}
+
+void get_AllSensorConfigs() {
+    g_pending_sensor_configs_count = 0;
+
+    for (size_t i = 0; i < SENSOR_CONFIG_COUNT; ++i) {
+        strncpy(
+            g_pending_sensor_configs[g_pending_sensor_configs_count],
+            sensor_configs[i].sensor_name,
+            sizeof(g_pending_sensor_configs[0]) - 1
+        );
+
+        g_pending_sensor_configs[g_pending_sensor_configs_count][sizeof(g_pending_sensor_configs[0]) - 1] = '\0';
+
+        ++g_pending_sensor_configs_count;
+    }
+
+
+    process_PendingSensorConfigs();
+    if (g_pending_sensor_configs_count == 0)
+        all_init_configs_received = true;
+}
+
+// ----------------------------------------------------------------------------
+// Send Data to peers
+// ----------------------------------------------------------------------------
+
+void send_DataToPeer(size_t config_id, uint32_t value) {
+    uint64_t ms_now = get_CurrentUnixMs();
+
+    SensorConfig_s& p = sensor_configs[config_id];
+
+    DBG_PRINTF("SENSOR: %s\n", p.sensor_name);
+    printSensorConfig(p);
+    if (!p.is_active) { 
+        DBG_PRINTLN("Sensor is inactive!");
+        return;
+    }
+
+    bool should_send = 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 - last_sent_array[config_id] > 60000) {
+                should_send = true;
+                break;
+            }
+        }
+    } else {
+        if (ms_now - last_sent_array[config_id] >= p.delay_ms) should_send = true;
+    }
+
+    if (should_send) {
+        char message[32];
+        snprintf(message, sizeof(message), "%s:%u", p.sensor_name, value);
+
+        DBG_PRINT("Sending to peer: ");
+        printMac(p.pump_mac);
+        send_ack_process_complete = false;
+        if (!send_And_wait_Ack(p.pump_mac, message)) {
+            DBG_PRINTLN("Delivery failed or peer offline!");
+            send_ack_process_complete = true;
+        } else {
+            last_sent_array[config_id] = get_CurrentUnixMs();
+            value++;
+        }
+
+        while (!send_ack_process_complete) delay(100);
+
+        DBG_PRINT("Sending to peer: ");
+        printMac(PEER_SERVER);
+        send_ack_process_complete = false;
+        if (!send_And_wait_Ack(PEER_SERVER, message)) {
+            DBG_PRINTLN("Failed to get ack from server!");
+            send_ack_process_complete = true;
+        }
+        while (!send_ack_process_complete) delay(100);
+
+        delay(5000);
+    }
+}
+
diff --git a/client_sensor/ClientSensor.h b/client_sensor/ClientSensor.h
new file mode 100644
index 0000000..7130a98
--- /dev/null
+++ b/client_sensor/ClientSensor.h
@@ -0,0 +1,65 @@
+#ifndef CLIENTSENSOR_H
+#define CLIENTSENSOR_H
+
+#include "globals.h"
+
+#include <Arduino.h>
+#include <WiFi.h>
+#include <esp_wifi.h>
+#include <esp_now.h>
+#include <esp_sleep.h>
+#include <sys/time.h>
+
+
+// ----------------------------------------------------------------------------
+// WiFi handling
+// ----------------------------------------------------------------------------
+void init_WiFi();
+
+
+// ----------------------------------------------------------------------------
+// Peers
+// ----------------------------------------------------------------------------
+
+// ----------------------------------------------------------------------------
+// Config to flash
+// ----------------------------------------------------------------------------
+
+void restore_SensorConfigs();
+
+
+// ----------------------------------------------------------------------------
+// ESP-NOW handling
+// ----------------------------------------------------------------------------
+
+void ensure_Peers();
+void init_EspNow();
+
+// ----------------------------------------------------------------------------
+// Time handling
+// ----------------------------------------------------------------------------
+
+uint64_t get_CurrentUnixMs();
+bool request_TimeSync(uint32_t timeout_ms = 3000);
+void setup_Timezone();
+
+// ----------------------------------------------------------------------------
+// Deep sleep scheduler
+// ----------------------------------------------------------------------------
+void sleep_UntilNextSend();
+
+// ----------------------------------------------------------------------------
+// Request sensor config from server via ESP-NOW
+// ----------------------------------------------------------------------------
+
+void get_AllSensorConfigs();
+void process_PendingSensorConfigs();
+
+// ----------------------------------------------------------------------------
+// Send Data to peers
+// ----------------------------------------------------------------------------
+
+void send_DataToPeer(size_t config_id, uint32_t value);
+
+
+#endif
diff --git a/client_sensor/client_sensor.ino b/client_sensor/client_sensor.ino
index 37709f7..4155e53 100644
--- a/client_sensor/client_sensor.ino
+++ b/client_sensor/client_sensor.ino
@@ -1,632 +1,10 @@
-#include "secrets.h"
-#include "globals.h"
+#include "ClientSensor.h"
 
-#include <Arduino.h>
-#include <WiFi.h>
-#include <esp_wifi.h>
-#include <esp_now.h>
-#include <esp_sleep.h>
-#include <sys/time.h>
-
-volatile bool rtc_synced_with_server = false;
-volatile bool ack_received = false;
-volatile bool send_process_complete = false;
-volatile bool send_ack_process_complete = false;
-
-const uint32_t min_sleep_time = 60000;
-
-
-// ----------------------------------------------------------------------------
-// Helper
-// ----------------------------------------------------------------------------
-void print_Mac(const uint8_t* mac) {
-    for (size_t i = 0; i < 6; i++) {
-        if (i > 0) DBG_PRINT(":");
-        DBG_PRINTF("%02X", mac[i]);
-    }
-    DBG_PRINTLN();
-}
-
-// ----------------------------------------------------------------------------
-// WiFi handling
-// ----------------------------------------------------------------------------
-int32_t get_WiFiChannel() {
-    DBG_PRINTLN(ssid);
-    int32_t n = WiFi.scanNetworks();
-    if (n <= 0) return 0;
-    for (uint8_t i = 0; i < n; i++) {
-        if (!strcmp(ssid, WiFi.SSID(i).c_str())) return WiFi.channel(i);
-    }
-    return 0;
-}
-
-void init_WiFi() {
-    WiFi.mode(WIFI_MODE_STA);
-    int32_t channel = get_WiFiChannel();
-    if (channel > 0) {
-        esp_wifi_set_promiscuous(true);
-        esp_wifi_set_channel(channel, WIFI_SECOND_CHAN_NONE);
-        esp_wifi_set_promiscuous(false);
-    }
-}
-
-
-// ----------------------------------------------------------------------------
-// Peers
-// ----------------------------------------------------------------------------
-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},
-    {"moisture_2", true, 11, 10, 0, "pump_2", {0}, 120000, false, {0}, 0}
-};
-
-constexpr size_t num_sensor_configs = sizeof(sensor_configs)/sizeof(SensorConfig_s);
-
-uint8_t config_request_attempts = 0;
-uint8_t config_request_attempts_max = 3;
-
-void printSensorConfig(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("  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);
-
-    if (cfg.time_count > 0) {
-        DBG_PRINT("  times: ");
-        for (uint8_t i = 0; i < cfg.time_count; i++) {
-            DBG_PRINTF("%u", cfg.times[i]);
-            if (i < cfg.time_count - 1)
-                DBG_PRINT("|");
-        }
-        DBG_PRINTLN("");
-    }
-}
-
-// ----------------------------------------------------------------------------
-// Config to flash
-// ----------------------------------------------------------------------------
-
-#include <Preferences.h>
-
-Preferences prefs;
-
-bool load_SensorConfigs() {
-    DBG_PRINTLN("Loading sensor configs from NVS...");
-
-    prefs.begin("sensors", true);
-    size_t stored_size = prefs.getBytesLength("configs");
-    DBG_PRINTF("Stored size: %u bytes\n", (unsigned)stored_size);
-    bool ok = false;
-    if (stored_size == sizeof(sensor_configs)) {
-        prefs.getBytes("configs", sensor_configs, sizeof(sensor_configs));
-        ok = true;
-        DBG_PRINTLN("Config read successfully.");
-    } else DBG_PRINTLN("No valid stored config found.");
-    prefs.end();
-
-    return ok;
-}
-
-void save_SensorConfigs() {
-    DBG_PRINTLN("Saving sensor configs to NVS...");
-
-    prefs.begin("sensors", false);
-    prefs.putBytes("configs", sensor_configs, sizeof(sensor_configs));
-    prefs.end();
-
-    DBG_PRINTLN("Save complete.");
-}
-
-void restore_SensorConfigs() {
-    DBG_PRINTLN("Trying to load old config!");
-
-    bool restored = load_SensorConfigs();
-    if (restored) {
-        DBG_PRINTLN("Sensor configs restored from flash.");
-    } else {
-        DBG_PRINTLN("No stored configs found, using defaults.");
-        for (size_t i = 0; i < num_sensor_configs; i++)
-            memcpy(sensor_configs[i].pump_mac, PEER_PUMP, sizeof(PEER_PUMP));
-    }
-}
-
-
-// ----------------------------------------------------------------------------
-// RTC variables
-// ----------------------------------------------------------------------------
-RTC_DATA_ATTR uint64_t last_sent_array[num_sensor_configs] = {0};
 
 // this is going to be a sensor read value!
+// RTC_DATA_ATTR is not required than!
 RTC_DATA_ATTR uint32_t value_to_send = 0;
 
-// ----------------------------------------------------------------------------
-// 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;
-}
-
-bool request_TimeSync(uint32_t timeout_ms = 3000) {
-    const uint8_t* server_mac = PEER_SERVER;
-    unsigned long start = millis();
-    esp_now_register_recv_cb(on_DataRecv);
-    send_Message(server_mac, "time?");
-    while (millis() - start < timeout_ms) {
-        if (rtc_synced_with_server) return true;
-        delay(50);
-    }
-    return false;
-}
-
-void setup_Timezone() {
-    setenv("TZ", "CET-1CEST,M3.5.0,M10.5.0/3", 1);
-    tzset();
-}
-
-void get_LocalTime(time_t unix_time, int *hour, int *minute) {
-    struct tm tm_time;
-    localtime_r(&unix_time, &tm_time);
-    *hour = tm_time.tm_hour;
-    *minute = tm_time.tm_min;
-}
-
-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);
-    return hour * 60 + minute;
-}
-
-// ----------------------------------------------------------------------------
-// ESP-NOW handling
-// ----------------------------------------------------------------------------
-void on_DataSent(const wifi_tx_info_t* info, esp_now_send_status_t status) {
-    DBG_PRINT("Packet to ");
-    for (int i = 0; i < 6; i++) {
-        DBG_PRINTF("%02X", info->des_addr[i]);
-        if (i < 5) DBG_PRINT(":");
-    }
-
-    if (status == ESP_NOW_SEND_SUCCESS)
-        DBG_PRINTLN(" queued successfully (waiting for ACK)");
-    else
-        DBG_PRINTLN(" failed to queue!");
-}
-
-void on_DataRecv(const esp_now_recv_info *info, const uint8_t *data, int len) {
-    if (!info || !data || len <= 0) return;
-
-    char msg[256];
-    memcpy(msg, data, len);
-    msg[len] = '\0';
-
-    DBG_PRINTF("Received from server: %s\n", msg);
-
-    if (strncmp(msg, "time:", 5) == 0) {
-        time_t server_unix = strtoul(msg + 5, nullptr, 10);
-        struct timeval tv = { .tv_sec = server_unix, .tv_usec = 0 };
-        settimeofday(&tv, nullptr);
-        rtc_synced_with_server = true;
-        DBG_PRINTLN("System time updated from server.");
-    }
-
-    DBG_PRINTLN("Setting send_process_complete to TRUE");
-    send_process_complete = true;
-}
-
-
-void on_DataRecvAck(const esp_now_recv_info *info, const uint8_t *data, int len) {
-    if (!info || !data || len <= 0) return;
-
-    char msg[256];
-    int copylen = len < (int)sizeof(msg) - 1 ? len : (int)sizeof(msg) - 1;
-    memcpy(msg, data, copylen);
-    msg[copylen] = '\0';
-
-    DBG_PRINTF("ACK received: %s\n", msg);
-
-    const char *time_ptr = strstr(msg, "time:");
-    if (time_ptr) {
-        time_t server_unix = strtoul(time_ptr + 5, nullptr, 10);
-        struct timeval tv = { .tv_sec = server_unix, .tv_usec = 0 };
-        settimeofday(&tv, nullptr);
-        rtc_synced_with_server = true;
-        ack_received = true;
-        DBG_PRINTLN("System time updated from server (ACK).");
-    }
-
-    const char *cfg_ptr = msg;
-    while ((cfg_ptr = strstr(cfg_ptr, "cfg:")) != nullptr) {
-        cfg_ptr += 4;
-        const char *end = strchr(cfg_ptr, ',');
-        size_t len_cfg = end ? (size_t)(end - cfg_ptr) : strlen(cfg_ptr);
-        if (len_cfg >= sizeof(g_pending_sensor_configs[0])) len_cfg = sizeof(g_pending_sensor_configs[0]) - 1;
-
-        char new_cfg[32];
-        memcpy(new_cfg, cfg_ptr, len_cfg);
-        new_cfg[len_cfg] = '\0';
-
-        bool exists = false;
-        for (uint8_t i = 0; i < g_pending_sensor_configs_count; i++) {
-            if (strcmp(g_pending_sensor_configs[i], new_cfg) == 0) {
-                exists = true;
-                break;
-            }
-        }
-
-        if (!exists && g_pending_sensor_configs_count < 4) {
-            strncpy(g_pending_sensor_configs[g_pending_sensor_configs_count], new_cfg, sizeof(g_pending_sensor_configs[0]) - 1);
-            g_pending_sensor_configs[g_pending_sensor_configs_count][sizeof(g_pending_sensor_configs[0]) - 1] = '\0';
-            g_pending_sensor_configs_count++;
-            DBG_PRINTF("Added new config: %s\n", new_cfg);
-        }
-
-        if (!end) break;
-        cfg_ptr = end;
-    }
-
-    DBG_PRINTLN("Setting send_ack_process_complete to TRUE");
-    send_ack_process_complete = true;
-}
-
-void add_Peer(const uint8_t* addr) {
-    esp_now_peer_info_t peer{};
-    memcpy(peer.peer_addr, addr, 6);
-    peer.ifidx = WIFI_IF_STA;
-    peer.channel = 0;
-    peer.encrypt = false;
-    if (!esp_now_is_peer_exist(addr)) esp_now_add_peer(&peer);
-}
-
-void ensure_Peers() {
-    for (size_t i=0; i<num_sensor_configs; i++) add_Peer(sensor_configs[i].pump_mac);
-    add_Peer(PEER_SERVER);
-}
-
-void init_EspNow() {
-    if (esp_now_init() != ESP_OK) {
-        DBG_PRINTLN("ESP NOW init failed");
-        while (true);
-    }
-    esp_now_register_send_cb(on_DataSent);
-    ensure_Peers();
-}
-
-bool send_Message(const uint8_t* peer, const char* message) {
-    if (!esp_now_is_peer_exist(peer)) {
-        DBG_PRINTLN("ERROR: peer doese not exist!");
-        return false;
-    }
-    esp_err_t result = esp_now_send(peer, (uint8_t*)message, strlen(message));
-    return result == ESP_OK;
-}
-
-bool send_And_wait_Ack(const uint8_t* peer, const char* message, uint32_t timeout_ms = 5000) {
-    send_ack_process_complete = false;
-    ack_received = false;
-
-    esp_now_register_recv_cb(on_DataRecvAck);
-
-    if (!send_Message(peer, message)) {
-        DBG_PRINTLN("Failed to queue message for sending!");
-        return false;
-    }
-
-    unsigned long start = millis();
-    while (!ack_received && millis() - start < timeout_ms) {
-        delay(10);
-    }
-
-    if (!ack_received) {
-        DBG_PRINT("No ACK received from ");
-        print_Mac(peer);
-        return false;
-    }
-
-    return true;
-}
-
-// ----------------------------------------------------------------------------
-// Deep sleep scheduler
-// ----------------------------------------------------------------------------
-void sleep_UntilNextSend() {
-    uint64_t ms_now = get_CurrentUnixMs();
-    uint32_t min_delay_ms = UINT32_MAX;
-
-    for (size_t i = 0; i < num_sensor_configs; i++) {
-        SensorConfig_s & p = sensor_configs[i];
-        if (!p.is_active) continue;
-
-        if (p.use_time) {
-            uint16_t min_current = get_CurrentMinutes();
-            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;
-            }
-        } else {
-            uint32_t delta_ms = (p.delay_ms > (ms_now - last_sent_array[i]))
-                ? (p.delay_ms - (ms_now - last_sent_array[i]))
-                : 0;
-            if (delta_ms < min_delay_ms) min_delay_ms = delta_ms;
-        }
-    }
-
-    if (min_delay_ms == UINT32_MAX) return;
-
-    // Prevent sleeping if next send is within 1 minute
-    if (min_delay_ms < min_sleep_time) {
-        DBG_PRINTF("Delaying for %u ms\n", min_delay_ms);
-        DBG_PRINTF("######################\n\n");
-        delay(min_delay_ms);
-        return;
-    }
-
-    DBG_PRINTF("Sleeping for %u ms\n", min_delay_ms);
-    esp_sleep_enable_timer_wakeup((uint64_t)min_delay_ms * 1000ULL);
-    // set server_time for the case we dont get a new connection.
-    // idk why its 1000 to big,..
-    DBG_PRINTF("######################\n\n");
-    esp_deep_sleep_start();
-}
-
-// ----------------------------------------------------------------------------
-// Request sensor config from server via ESP-NOW
-// ----------------------------------------------------------------------------
-RTC_DATA_ATTR bool all_init_configs_received = false;
-volatile bool sensor_config_received = false;
-char sensor_config_message[256] = {0};
-
-void on_DataRecvSensorConfig(const esp_now_recv_info* info, const uint8_t* data, int len) {
-    if (!info || !data || len <= 0) return;
-    if (memcmp(info->src_addr, PEER_SERVER, 6) != 0) return;
-
-    int copylen = len < (int)sizeof(sensor_config_message) - 1 ? len : (int)sizeof(sensor_config_message) - 1;
-    memcpy(sensor_config_message, data, copylen);
-    sensor_config_message[copylen] = '\0';
-
-    if (strncmp(sensor_config_message, "cfg:", 4) != 0) return;
-
-    sensor_config_received = true;
-}
-
-bool request_SensorConfig(const char* sensor_name) {
-    const uint8_t* server_mac = PEER_SERVER;
-    if (!esp_now_is_peer_exist(server_mac)) add_Peer(server_mac);
-
-    sensor_config_received = false;
-    memset(sensor_config_message, 0, sizeof(sensor_config_message));
-
-    esp_now_register_recv_cb(on_DataRecvSensorConfig);
-
-    char msg[64];
-    snprintf(msg, sizeof(msg), "get_config:%s", sensor_name);
-
-    if (!send_Message(server_mac, msg)) {
-        DBG_PRINTLN("Failed to send get_config request");
-        return false;
-    }
-
-    unsigned long start = millis();
-    const uint32_t max_wait = 10000;
-
-    while (!sensor_config_received && millis() - start < max_wait) {
-        delay(50);
-        yield();
-    }
-
-    if (!sensor_config_received) {
-        DBG_PRINTLN("No config reply received (timeout)");
-        return false;
-    }
-
-    if (strncmp(sensor_config_message, "cfg:", 4) != 0) {
-        DBG_PRINTF("Invalid config response: %s\n", sensor_config_message);
-        return false;
-    }
-
-    char* ptr = sensor_config_message + 4;
-    char* tokens[20];
-    uint8_t count_token = 0;
-    char* tok = strtok(ptr, ",");
-    while (tok && count_token < 20) {
-        tokens[count_token++] = tok;
-        tok = strtok(nullptr, ",");
-    }
-
-    if (count_token == 0) return false;
-
-    DBG_PRINTF("Config received for %s:\n", tokens[0]);
-    for (uint8_t i = 1; i < count_token; i++) {
-        DBG_PRINTF("  %u: %s\n", i, tokens[i]);
-    }
-
-
-    if (count_token < 9) {
-        DBG_PRINTLN("Incomplete config data");
-        return false;
-    }
-
-    SensorConfig_s *cfg = NULL;
-    size_t config_id = 0;
-    for (size_t i = 0; i < sizeof(sensor_configs) / sizeof(sensor_configs[0]); i++) {
-        if (strcmp(sensor_configs[i].sensor_name, tokens[0]) == 0) {
-            config_id = i;
-            cfg = &sensor_configs[i];
-            break;
-        }
-    }
-
-    if (!cfg) {
-        DBG_PRINTF("Unknown sensor name in config: %s\n", tokens[0]);
-        return false;
-    }
-
-    // basic fields
-    strncpy(cfg->sensor_name, tokens[0], sizeof(cfg->sensor_name)-1);
-    cfg->sensor_name[sizeof(cfg->sensor_name)-1] = '\0';
-    cfg->is_active = atoi(tokens[1]) != 0;
-    cfg->pin = atoi(tokens[2]);
-    cfg->min_moisture = atoi(tokens[3]);
-    cfg->notification_delay_ms = atoi(tokens[4]);
-    strncpy(cfg->notification_name, tokens[5], sizeof(cfg->notification_name)-1);
-    cfg->notification_name[sizeof(cfg->notification_name)-1] = '\0';
-    strncpy((char*)cfg->pump_mac, tokens[6], sizeof(cfg->pump_mac)-1);
-    cfg->pump_mac[sizeof(cfg->pump_mac)-1] = '\0';
-    cfg->delay_ms = atoi(tokens[7]);
-    cfg->use_time = atoi(tokens[8]) != 0;
-
-    // times
-    cfg->time_count = 0;
-    if (cfg->use_time && count_token > 9 && strncmp(tokens[9], "times=", 6) == 0) {
-        char *time_str = tokens[9] + 6;
-        char *time_tok = strtok(time_str, "|");
-        while (time_tok && cfg->time_count < 8) {
-            cfg->times[cfg->time_count++] = (uint16_t)atoi(time_tok);
-            time_tok = strtok(NULL, "|");
-        }
-    }
-
-    config_request_attempts = 0;
-    return true;
-}
-
-void process_PendingSensorConfigs() {
-    config_request_attempts = 0;
-    DBG_PRINTF("NEW CONFIGS: %d\n", g_pending_sensor_configs_count);
-
-    size_t config_id = 0;
-    bool save_new_configs = false;
-    while (config_id < g_pending_sensor_configs_count) {
-        DBG_PRINTF("SENSOR: %s\n", g_pending_sensor_configs[config_id]);
-
-        bool success = false;
-        for (config_request_attempts = 0;
-                config_request_attempts < config_request_attempts_max;
-                config_request_attempts++) {
-
-            DBG_PRINTF("Requesting config attempt %u...\n", config_request_attempts + 1);
-            if (request_SensorConfig(g_pending_sensor_configs[config_id])) {
-                success = true;
-                break;
-            }
-            delay(2000);
-        }
-
-        if (success) {
-            DBG_PRINTF("Config %s processed successfully, removing.\n",
-                    g_pending_sensor_configs[config_id]);
-            if (config_id >= g_pending_sensor_configs_count) return;
-            for (size_t i = config_id; i < g_pending_sensor_configs_count - 1; i++)
-                strcpy(g_pending_sensor_configs[i], g_pending_sensor_configs[i + 1]);
-            g_pending_sensor_configs_count--;
-            save_new_configs = true;
-        } else {
-            DBG_PRINTLN("Config request failed repeatedly, aborting loop.");
-            break;
-        }
-    }
-
-    if (save_new_configs) save_SensorConfigs();
-}
-
-void get_AllSensorConfigs() {
-    g_pending_sensor_configs_count = 0;
-
-    for (size_t i = 0; i < num_sensor_configs; ++i) {
-        strncpy(
-            g_pending_sensor_configs[g_pending_sensor_configs_count],
-            sensor_configs[i].sensor_name,
-            sizeof(g_pending_sensor_configs[0]) - 1
-        );
-
-        g_pending_sensor_configs[g_pending_sensor_configs_count][sizeof(g_pending_sensor_configs[0]) - 1] = '\0';
-
-        ++g_pending_sensor_configs_count;
-    }
-
-
-    process_PendingSensorConfigs();
-    if (g_pending_sensor_configs_count == 0)
-        all_init_configs_received = true;
-}
-
-// ----------------------------------------------------------------------------
-// Send Data to peers
-// ----------------------------------------------------------------------------
-
-void send_DataToPeer(size_t config_id, uint32_t value) {
-    uint64_t ms_now = get_CurrentUnixMs();
-
-    SensorConfig_s& p = sensor_configs[config_id];
-
-    DBG_PRINTF("SENSOR: %s\n", p.sensor_name);
-    printSensorConfig(p);
-    if (!p.is_active) { 
-        DBG_PRINTLN("Sensor is inactive!");
-        return;
-    }
-
-    bool should_send = 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 - last_sent_array[config_id] > 60000) {
-                should_send = true;
-                break;
-            }
-        }
-    } else {
-        if (ms_now - last_sent_array[config_id] >= p.delay_ms) should_send = true;
-    }
-
-    if (should_send) {
-        char message[32];
-        snprintf(message, sizeof(message), "%s:%u", p.sensor_name, value);
-
-        DBG_PRINT("Sending to peer: ");
-        print_Mac(p.pump_mac);
-        send_ack_process_complete = false;
-        if (!send_And_wait_Ack(p.pump_mac, message)) {
-            DBG_PRINTLN("Delivery failed or peer offline!");
-            send_ack_process_complete = true;
-        } else {
-            last_sent_array[config_id] = get_CurrentUnixMs();
-            value++;
-        }
-
-        while (!send_ack_process_complete) delay(100);
-
-        DBG_PRINT("Sending to peer: ");
-        print_Mac(PEER_SERVER);
-        send_ack_process_complete = false;
-        if (!send_And_wait_Ack(PEER_SERVER, message)) {
-            DBG_PRINTLN("Failed to get ack from server!");
-            send_ack_process_complete = true;
-        }
-        while (!send_ack_process_complete) delay(100);
-
-        delay(5000);
-    }
-}
-
 // ----------------------------------------------------------------------------
 // Setup
 // ----------------------------------------------------------------------------
@@ -660,7 +38,7 @@ void loop() {
     ensure_Peers();
 
     DBG_PRINTF("## Unix time: %llu\n", (unsigned long long)get_CurrentUnixMs() / 1000);
-    for (size_t config_id = 0; config_id < num_sensor_configs; config_id++) {
+    for (size_t config_id = 0; config_id < SENSOR_CONFIG_COUNT; config_id++) {
         send_DataToPeer(config_id, value_to_send);
     }
 
diff --git a/globals.cpp b/globals.cpp
index a91d74e..034bf82 100644
--- a/globals.cpp
+++ b/globals.cpp
@@ -8,3 +8,21 @@ char g_pending_sensor_configs[4][32] = {};
 uint8_t g_pending_sensor_configs_count = 0;
 
 bool g_received_ok_from_server = false;
+
+
+
+
+
+// Client sensor stuff
+const uint32_t min_sleep_time = 60000;
+RTC_DATA_ATTR bool all_init_configs_received = false;
+
+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},
+    {"moisture_2", true, 11, 10, 0, "pump_2", {0}, 120000, false, {0}, 0}
+};
+
+uint8_t config_request_attempts_max = 3;
diff --git a/globals.h b/globals.h
index 2f1be4c..6752685 100644
--- a/globals.h
+++ b/globals.h
@@ -31,6 +31,8 @@
 #define JSON_PER_ENTRY (29 + (TABLE_SIZE-1) + (VALUE_SIZE-1) + 10)
 #define JSON_BUFFER_SIZE (BUFFER_SIZE * JSON_PER_ENTRY + 2)
 
+#define MAC_ADDR_LEN 6
+
 
 struct SensorConfigServer_s {
     char sensor_name[32];
@@ -81,3 +83,15 @@ extern uint32_t g_time_now;
 extern char g_pending_sensor_configs[4][32];
 extern uint8_t g_pending_sensor_configs_count;
 extern bool g_received_ok_from_server;
+
+
+
+
+
+extern const uint32_t min_sleep_time;
+extern RTC_DATA_ATTR bool all_init_configs_received;
+extern const uint8_t PEER_SERVER[];
+extern const uint8_t PEER_PUMP[];
+#define SENSOR_CONFIG_COUNT 2
+extern RTC_DATA_ATTR SensorConfig_s sensor_configs[];
+extern uint8_t config_request_attempts_max;