Nimbin[12]?Embedded / esp_garden / commits / 4b50834

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

Add pump

Christian Immanuel · 2025-10-26 17:24 · 4b50834f31695d262bd1389794178de603b30594

 client_pump/client_pump.ino     | 244 ++++++++++++++++++++++++++++++++++++++++
 client_sensor/client_sensor.ino |  13 ++-
 globals.cpp                     |   2 +
 globals.h                       |   2 +
 4 files changed, 258 insertions(+), 3 deletions(-)

diff --git a/client_pump/client_pump.ino b/client_pump/client_pump.ino
new file mode 100644
index 0000000..c175419
--- /dev/null
+++ b/client_pump/client_pump.ino
@@ -0,0 +1,244 @@
+#include "globals.h"
+#include "secrets.h"
+
+#include <Arduino.h>
+#include <WiFi.h>
+#include <esp_wifi.h>
+#include <esp_now.h>
+#include <esp_sleep.h>
+#include <sys/time.h>
+
+
+#include "esp_system.h"
+extern "C" esp_err_t esp_base_mac_addr_set(const uint8_t mac[6]);
+
+long long next_message_unix = 0;
+bool go_to_sleep = false;
+
+
+enum PumpSatatus_e {
+    INACTIVE,
+    START,
+    RUNNING,
+    FINISHED
+};
+
+struct PumpConfig_s {
+    char pump_name[32];
+    int pumping_delay_sec;
+    int pumping_duration;
+    long long next_message_unix;
+    long long pumping_started_at_unix;
+    PumpSatatus_e pumping_status;
+};
+
+#define PUMP_COUNT 2
+PumpConfig_s pump_configs[PUMP_COUNT] = {
+    {"pump_1", 0, 0, 0, 0, INACTIVE},
+    {"pump_2", 0, 0, 0, 0, INACTIVE},
+};
+
+// ----------------------------------------------------------------------------
+// Time handling
+// ----------------------------------------------------------------------------
+
+int64_t get_CurrentUnix() {
+    return (int64_t)time(nullptr);
+}
+
+void setup_Timezone() {
+    setenv("TZ", "CET-1CEST,M3.5.0,M10.5.0/3", 1);
+    tzset();
+}
+
+void set_LocalTime(long long unix_now) {
+    struct timeval tv;
+    tv.tv_sec = unix_now;   // seconds since epoch
+    tv.tv_usec = 0;         // microseconds
+
+    settimeofday(&tv, nullptr);  // set system time
+}
+
+// ----------------------------------------------------------------------------
+// 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);
+    }
+}
+
+
+// ----------------------------------------------------------------------------
+// ESP-NOW handling
+// ----------------------------------------------------------------------------
+
+void on_DataRecv(const esp_now_recv_info *info, const uint8_t *data, int len) {
+    if (!info || !data || len <= 0) return;
+    if (memcmp(info->src_addr, PEER_SENSOR, 6) != 0) return;
+
+    PumpConfig_s pump_config;
+    char msg[64];
+    long long unix_now = 0;
+    memcpy(msg, data, len);
+    msg[len] = '\0';
+
+    DBG_PRINTF("Received from server: %s\n", msg);
+
+    if (strncmp(msg, "p:", 2) == 0) {
+
+        int n = sscanf(msg, "p:%lld:%31[^:]:%d:%d:%lld",
+                &unix_now,
+                pump_config.pump_name,
+                &pump_config.pumping_delay_sec,
+                &pump_config.pumping_duration,
+                &pump_config.next_message_unix);
+
+        if (n == 5) {
+            for (size_t i = 0; i < PUMP_COUNT; i++) {
+                if (strcmp(pump_configs[i].pump_name, pump_config.pump_name) != 0)
+                    continue;
+
+                memcpy(&pump_configs[i], &pump_config, sizeof(PumpConfig_s));
+                next_message_unix = pump_config.next_message_unix;
+
+                DBG_PRINTF("unix_now: %lld\n", unix_now);
+                DBG_PRINTF("pump_name: %s\n", pump_configs[i].pump_name);
+                DBG_PRINTF("pumping_delay_sec: %d\n", pump_configs[i].pumping_delay_sec);
+                DBG_PRINTF("pumping_duration:  %d\n", pump_configs[i].pumping_duration);
+                DBG_PRINTF("next_message_unix: %lld\n", pump_configs[i].next_message_unix);
+
+                set_LocalTime(unix_now);
+
+                if (pump_configs[i].pumping_duration > 0) {
+                    pump_configs[i].pumping_status = START;
+                } else {
+                    go_to_sleep = true;
+                }
+                break;
+            }
+        } else {
+            DBG_PRINTLN("Failed to parse message");
+        }
+    }
+}
+
+
+void init_EspNow() {
+    if (esp_now_init() != ESP_OK) {
+        DBG_PRINTLN("ESP NOW init failed");
+        while (true);
+    }
+    esp_now_register_recv_cb(on_DataRecv);
+}
+
+
+// ----------------------------------------------------------------------------
+// Deep sleep scheduler
+// ----------------------------------------------------------------------------
+
+void sleep_UntilNextSend(int64_t next_read) {
+    go_to_sleep = false;
+
+    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);
+
+
+    // 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);
+        return;
+    }
+
+    uint32_t prep_time_ms = g_setup_duration + g_wakeup_earlier_pump;
+    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();
+}
+
+
+
+
+void start_Pump(PumpConfig_s& pump_data) {
+    pump_data.pumping_started_at_unix = get_CurrentUnix();
+    pump_data.pumping_status = RUNNING;
+    DBG_PRINTF("Starting Pump %s\n", pump_data.pump_name);
+    // whatever we have to do to make the pump running
+}
+
+void stop_Pump(PumpConfig_s& pump_data) {
+    pump_data.pumping_status = INACTIVE;
+    DBG_PRINTF("Stopped Pump %s\n", pump_data.pump_name);
+    // whatever we have to do to stop the pump
+}
+
+void setup() {
+    uint32_t setup_start = millis();
+    go_to_sleep = false;
+
+    DBG_BEGIN();
+
+    esp_base_mac_addr_set(PEER_PUMP);
+
+    setup_Timezone();
+    init_WiFi();
+    init_EspNow();
+
+    g_setup_duration = millis() - setup_start;
+    DBG_PRINTF("Setup duration: %u ms\n", g_setup_duration);
+}
+
+
+void loop() {
+    bool pumping = false;
+    for (size_t i = 0; i < PUMP_COUNT; i++) {
+        PumpConfig_s& cfg = pump_configs[i];
+
+        if (START == cfg.pumping_status) {
+
+            start_Pump(cfg);
+            pumping = true;
+
+        } else
+        if (RUNNING == cfg.pumping_status) {
+
+            long long stop_time = cfg.pumping_started_at_unix + cfg.pumping_duration;
+            long long now = get_CurrentUnix();
+            if (stop_time <= now)
+                stop_Pump(cfg);
+            else pumping = true;
+
+        }
+    }
+    if (go_to_sleep && !pumping)
+        sleep_UntilNextSend(next_message_unix);
+}
diff --git a/client_sensor/client_sensor.ino b/client_sensor/client_sensor.ino
index d2269d6..a7d0c1a 100644
--- a/client_sensor/client_sensor.ino
+++ b/client_sensor/client_sensor.ino
@@ -1,5 +1,8 @@
 #include "ClientSensor.h"
 
+#include "esp_system.h"
+extern "C" esp_err_t esp_base_mac_addr_set(const uint8_t mac[6]);
+
 
 // this is going to be a sensor read value!
 // RTC_DATA_ATTR is not required than!
@@ -12,6 +15,9 @@ void setup() {
     uint32_t setup_start = millis();
 
     DBG_BEGIN();
+
+    esp_base_mac_addr_set(PEER_SENSOR);
+
     setup_Timezone();
     init_WiFi();
     init_EspNow();
@@ -43,7 +49,7 @@ void setup() {
 void loop() {
     DBG_PRINTF("## Unix time: %lld\n", get_CurrentUnix());
     for (size_t config_id = 0; config_id < SENSOR_CONFIG_COUNT; config_id++) {
-        SensorConfig_s &peer = sensor_configs[config_id];
+        SensorConfig_s& peer = sensor_configs[config_id];
         DBG_PRINTF("LOOP checking config: %s\n", peer.sensor_name);
         print_SensorConfig(peer);
         if ( is_TimeToReadSensor(config_id) ) {
@@ -60,8 +66,9 @@ void loop() {
             peer.next_sensor_read = 
                     get_NextSensorReadFor(sensor_configs[config_id].sensor_name);
 
-            // send to server and configured peer (e.g. pump)
-            send_DataToPeer(config_id, value_to_send);
+            // set to true if value says we should pump
+            bool shall_pump = fake_value_to_send % 2;
+            send_DataToPumpAndServer(config_id, value_to_send, shall_pump);
             fake_value_to_send++;
         }
         print_SensorConfig(peer);
diff --git a/globals.cpp b/globals.cpp
index b97506e..e3abcde 100644
--- a/globals.cpp
+++ b/globals.cpp
@@ -3,6 +3,7 @@
 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;
 
 SensorEntry g_buffer[BUFFER_SIZE]; 
 uint8_t g_buffer_count = 0;
@@ -20,6 +21,7 @@ const uint32_t g_min_sleep_time = 60000;
 RTC_DATA_ATTR bool all_init_configs_received = false;
 
 constexpr uint8_t PEER_SERVER[6] = {0xc2,0xde,0x67,0x2a,0x1e,0x29};
+constexpr uint8_t PEER_SENSOR[6] = {0xc2,0xc2,0x80,0x2f,0xfe,0x28};
 constexpr uint8_t PEER_PUMP[6]   = {0xc2,0xc1,0x79,0x1e,0xed,0x18};
 
 SensorConfig_s sensor_configs[] = {
diff --git a/globals.h b/globals.h
index 5816a53..de85a5c 100644
--- a/globals.h
+++ b/globals.h
@@ -38,6 +38,7 @@
 extern uint32_t g_setup_duration; // getting set automaticall in setup so we know how long it takes
 extern uint32_t g_wakeup_earlier_server; // some safety space if setup takes longere (maybe 20sec safety?)
 extern uint32_t g_wakeup_earlier_sensor; // some safety space if setup takes longere (maybe 20sec safety?)
+extern uint32_t g_wakeup_earlier_pump; // some safety space if setup takes longere (maybe 20sec safety?)
 
 struct SensorConfigServer_s {
     char sensor_name[32];
@@ -99,6 +100,7 @@ extern bool    g_received_ok_from_server;
 extern const uint32_t g_min_sleep_time;
 extern RTC_DATA_ATTR bool all_init_configs_received;
 extern const uint8_t PEER_SERVER[];
+extern const uint8_t PEER_SENSOR[];
 extern const uint8_t PEER_PUMP[];
 extern RTC_DATA_ATTR SensorConfig_s sensor_configs[];
 extern uint8_t config_request_attempts_max;