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

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 main folder to ASM_client_pump

DSLap · 2026-01-16 17:52 · 4fe63ac0ffbf0a1bf1dbb8374a8132620df78a4b

 ASM_client_pump/main/CMakeLists.txt      |   6 +
 ASM_client_pump/main/ClientPump.cpp      | 234 +++++++++++++++++++++++++++++++
 ASM_client_pump/main/ClientPump.h        |  59 ++++++++
 ASM_client_pump/main/OLD.client_pump.ino |  76 ++++++++++
 ASM_client_pump/main/asm.S               |  23 +++
 ASM_client_pump/main/globals.cpp         |   1 +
 ASM_client_pump/main/globals.h           |  94 +++++++++++++
 ASM_client_pump/main/main.cpp            |  84 +++++++++++
 8 files changed, 577 insertions(+)

diff --git a/ASM_client_pump/main/CMakeLists.txt b/ASM_client_pump/main/CMakeLists.txt
new file mode 100644
index 0000000..a5c71d4
--- /dev/null
+++ b/ASM_client_pump/main/CMakeLists.txt
@@ -0,0 +1,6 @@
+idf_component_register(
+    SRCS "main.cpp" "ClientPump.cpp" "globals.cpp" "asm.S"
+    INCLUDE_DIRS "."
+    REQUIRES esp_timer freertos esp_system esp_wifi esp_event nvs_flash
+    REQUIRES vfs esp_driver_uart
+    )
diff --git a/ASM_client_pump/main/ClientPump.cpp b/ASM_client_pump/main/ClientPump.cpp
new file mode 100644
index 0000000..b1fa672
--- /dev/null
+++ b/ASM_client_pump/main/ClientPump.cpp
@@ -0,0 +1,234 @@
+#include "ClientPump.h"
+
+#include "nvs_flash.h"
+#include "esp_wifi.h"
+#include "esp_event.h"
+#include "esp_netif.h"
+#include "esp_log.h"
+
+#include <esp_now.h>
+#include <esp_sleep.h>
+#include <sys/time.h>
+
+
+// ----------------------------------------------------------------------------
+// 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;
+    tv.tv_usec = 0;
+
+    settimeofday(&tv, nullptr);
+}
+
+// ----------------------------------------------------------------------------
+// WiFi handling
+// ----------------------------------------------------------------------------
+int32_t get_WiFiChannel(const char* target_ssid) {
+    wifi_scan_config_t scan_config = {};
+    ESP_ERROR_CHECK(esp_wifi_scan_start(&scan_config, true)); // blocking scan
+
+    uint16_t ap_count = 0;
+    ESP_ERROR_CHECK(esp_wifi_scan_get_ap_num(&ap_count));
+    if (ap_count == 0) return 0;
+
+    wifi_ap_record_t *ap_list = (wifi_ap_record_t*)malloc(sizeof(wifi_ap_record_t) * ap_count);
+    if (!ap_list) return 0;
+
+    ESP_ERROR_CHECK(esp_wifi_scan_get_ap_records(&ap_count, ap_list));
+
+    int32_t channel = 0;
+    for (int i = 0; i < ap_count; i++) {
+        if (strcmp((char*)ap_list[i].ssid, target_ssid) == 0) {
+            channel = ap_list[i].primary;
+            break;
+        }
+    }
+
+    free(ap_list);
+    return channel;
+}
+
+void init_WiFi() {
+    // Initialize NVS
+    esp_err_t ret = nvs_flash_init();
+    if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
+        ESP_ERROR_CHECK(nvs_flash_erase());
+        ret = nvs_flash_init();
+    }
+    ESP_ERROR_CHECK(ret);
+
+    // Initialize TCP/IP stack and default event loop
+    ESP_ERROR_CHECK(esp_netif_init());
+    ESP_ERROR_CHECK(esp_event_loop_create_default());
+
+    // Initialize Wi-Fi driver
+    wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
+    ESP_ERROR_CHECK(esp_wifi_init(&cfg));
+    ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
+    ESP_ERROR_CHECK(esp_wifi_start());
+
+    // Scan and set channel
+    int32_t channel = get_WiFiChannel(WIFI_SSID);
+    if (channel > 0) {
+        esp_wifi_set_promiscuous(true);
+        ESP_ERROR_CHECK(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) {
+    static const char *TAG = "on_DataRecv";
+
+    if (!info || !data || len <= 0) return;
+    if (memcmp(info->src_addr, PEER_SENSOR, 6) != 0) return;
+
+    received_starting_message = true;
+
+    PumpConfig_s pump_config;
+    char msg[64];
+    long long unix_now = 0;
+    memcpy(msg, data, len);
+    msg[len] = '\0';
+
+    ESP_LOGI(TAG, "Received from server: %s", 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;
+
+                // --- Response to sender ---
+                // register peer
+                uint8_t primary;
+                wifi_second_chan_t second;
+                esp_wifi_get_channel(&primary, &second);
+
+                esp_now_peer_info_t peer = {};
+                memcpy(peer.peer_addr, info->src_addr, 6);
+                peer.channel = primary;
+                peer.encrypt = false;
+
+                if (!esp_now_is_peer_exist(info->src_addr)) {
+                    esp_err_t add_status = esp_now_add_peer(&peer);
+                    if (add_status != ESP_OK) {
+                        ESP_LOGE(TAG, "Failed to add peer before sending ACK, err=%d", add_status);
+                        return;
+                    }
+                }
+                // send acknowledgment
+                const char ack_msg[] = "thanks";
+                esp_err_t result = esp_now_send(info->src_addr, (const uint8_t *)ack_msg, sizeof(ack_msg));
+                if (result == ESP_OK)
+                    ESP_LOGI(TAG, "Sent acknowledgment: thanks");
+                else
+                    ESP_LOGE(TAG, "Failed to send acknowledgment, err=%d", result);
+
+                // --- Copy data from message ---
+                // make sure we keep old data before memcpy
+                pump_config.pumping_started_at_unix = pump_configs[i].pumping_started_at_unix;
+                pump_config.pumping_status = pump_configs[i].pumping_status;
+
+                
+                memcpy(&pump_configs[i], &pump_config, sizeof(PumpConfig_s));
+                next_message_unix = pump_config.next_message_unix;
+
+                ESP_LOGI(TAG, "unix_now: %lld", unix_now);
+                ESP_LOGI(TAG, "pump_name: %s", pump_configs[i].pump_name);
+                ESP_LOGI(TAG, "pumping_delay_sec: %d", pump_configs[i].pumping_delay_sec);
+                ESP_LOGI(TAG, "pumping_duration:  %d", pump_configs[i].pumping_duration);
+                ESP_LOGI(TAG, "next_message_unix: %lld", pump_configs[i].next_message_unix);
+
+                if (pump_configs[i].pumping_duration > 0)
+                    pump_configs[i].pumping_status = START;
+                else
+                    received_starting_message = false;
+
+                // --- Update local time ---
+                set_LocalTime(unix_now);
+
+                return;
+            }
+        } else {
+            ESP_LOGE(TAG, "Failed to parse message");
+        }
+    }
+    received_starting_message = false;
+}
+
+
+void init_EspNow() {
+    if (esp_now_init() != ESP_OK) {
+        ESP_LOGE("init_EspNow", "ESP NOW init failed");
+        while (true);
+    }
+    esp_now_register_recv_cb(on_DataRecv);
+}
+
+
+// ----------------------------------------------------------------------------
+// Deep sleep scheduler
+// ----------------------------------------------------------------------------
+
+void sleep_UntilNextSend(int64_t next_read) {
+    static const char *TAG = "sleep_UntilNextSend";
+
+    ESP_LOGI(TAG, "Next read: %lld", 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);
+
+
+    // Prevent sleeping if next send is within g_min_sleep_time
+    if (min_delay_ms < g_min_sleep_time) {
+        ESP_LOGI(TAG, "Delaying for %u ms (no deep sleep)", min_delay_ms);
+        if (!received_starting_message)
+            vTaskDelay(pdMS_TO_TICKS(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;
+
+
+    esp_sleep_enable_timer_wakeup((uint64_t)sleep_time_ms * 1000ULL);
+    if (!received_starting_message) {
+        ESP_LOGI(TAG, "Sleeping for %u ms (setup margin: %u ms)",
+                   sleep_time_ms, prep_time_ms);
+        esp_deep_sleep_start();
+    }
+}
+
diff --git a/ASM_client_pump/main/ClientPump.h b/ASM_client_pump/main/ClientPump.h
new file mode 100644
index 0000000..2362b6d
--- /dev/null
+++ b/ASM_client_pump/main/ClientPump.h
@@ -0,0 +1,59 @@
+#ifndef CLIENTPUMP_H
+#define CLIENTPUMP_H
+
+
+#include "globals.h"
+#include "secrets.h"
+
+
+extern long long next_message_unix;
+extern bool received_starting_message;
+
+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
+extern PumpConfig_s pump_configs[PUMP_COUNT];
+
+// ----------------------------------------------------------------------------
+// Time handling
+// ----------------------------------------------------------------------------
+
+int64_t get_CurrentUnix();
+void    setup_Timezone();
+
+// ----------------------------------------------------------------------------
+// WiFi handling
+// ----------------------------------------------------------------------------
+
+void init_WiFi();
+
+
+// ----------------------------------------------------------------------------
+// ESP-NOW handling
+// ----------------------------------------------------------------------------
+
+void init_EspNow();
+
+
+// ----------------------------------------------------------------------------
+// Deep sleep scheduler
+// ----------------------------------------------------------------------------
+
+void sleep_UntilNextSend(int64_t next_read);
+
+
+#endif
diff --git a/ASM_client_pump/main/OLD.client_pump.ino b/ASM_client_pump/main/OLD.client_pump.ino
new file mode 100644
index 0000000..2b8f923
--- /dev/null
+++ b/ASM_client_pump/main/OLD.client_pump.ino
@@ -0,0 +1,76 @@
+#include "ClientPump.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 received_starting_message = false;
+
+PumpConfig_s pump_configs[PUMP_COUNT] = {
+    {"pump_1", 0, 0, 0, 0, INACTIVE},
+    {"pump_2", 0, 0, 0, 0, INACTIVE},
+};
+
+
+
+void start_Pump(PumpConfig_s& pump_data) {
+    DBG_PRINTF("Starting Pump %s\n", pump_data.pump_name);
+    pump_data.pumping_started_at_unix = get_CurrentUnix();
+    pump_data.pumping_status = RUNNING;
+    // whatever we have to do to make the pump running
+}
+
+void stop_Pump(PumpConfig_s& pump_data) {
+    DBG_PRINTF("Stopped Pump %s\n", pump_data.pump_name);
+    pump_data.pumping_status = INACTIVE;
+    // whatever we have to do to stop the pump
+}
+
+void setup() {
+    uint32_t setup_start = millis();
+
+    DBG_BEGIN();
+
+    esp_base_mac_addr_set(PEER_PUMP);
+
+    setup_Timezone();
+    init_WiFi();
+    init_EspNow();
+
+    next_message_unix = 0;
+    received_starting_message = false;
+
+    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) {
+
+            pumping = true;
+            received_starting_message = false;
+            start_Pump(cfg);
+
+        } else
+        if (RUNNING == cfg.pumping_status) {
+
+            long long stop_time = cfg.pumping_started_at_unix + cfg.pumping_duration;
+            long long now = get_CurrentUnix();
+            // maybe we should add a safety here so we dont pump longer than n seconds,...
+            if (stop_time <= now)
+                stop_Pump(cfg);
+            else pumping = true;
+
+        }
+    }
+    if (!pumping && next_message_unix != 0 && !received_starting_message)
+        sleep_UntilNextSend(next_message_unix);
+}
diff --git a/ASM_client_pump/main/asm.S b/ASM_client_pump/main/asm.S
new file mode 100644
index 0000000..f87847b
--- /dev/null
+++ b/ASM_client_pump/main/asm.S
@@ -0,0 +1,23 @@
+.section .text
+.global set_gpio_output_level
+.equ REG_GPIO_BASE, 0x60004000
+
+set_gpio_output_level:
+    li t2,REG_GPIO_BASE         //Basisadresse laden
+    li t1, 1
+    sll t1,t1, a0               //Bitamske für GPIO a0
+
+    //GPIO als Output aktivieren(GPIO_ENABLE_W1TS_REG,Offset 0x20)
+
+    sw t1,0x20(t2)
+
+    //Pegel Setzen
+    beq a1,x0,gpio_low          //Wenn a1==0 -> LOW
+
+gpio_high:
+    sw t1,0x8(t2)               //GPIO HIGH
+    ret
+
+gpio_low:
+    sw t1,0xC(t2)               //GPIO LOW
+    ret
\ No newline at end of file
diff --git a/ASM_client_pump/main/globals.cpp b/ASM_client_pump/main/globals.cpp
new file mode 100644
index 0000000..e61d53a
--- /dev/null
+++ b/ASM_client_pump/main/globals.cpp
@@ -0,0 +1 @@
+../../globals.cpp
\ No newline at end of file
diff --git a/ASM_client_pump/main/globals.h b/ASM_client_pump/main/globals.h
new file mode 100644
index 0000000..894d085
--- /dev/null
+++ b/ASM_client_pump/main/globals.h
@@ -0,0 +1,94 @@
+#ifndef GLOBALS_H
+#define GLOBALS_H
+
+#include "esp_log.h"
+#include "esp_timer.h"
+#include "esp_attr.h"
+#include <stdint.h>
+#include <stdbool.h>
+
+
+// Network Time
+#define NTP_SERVER "pool.ntp.org"
+
+// Size for buffering data
+#define BUFFER_SIZE 50
+#define TABLE_SIZE  12
+#define VALUE_SIZE  16
+#define JSON_PER_ENTRY (29 + (TABLE_SIZE - 1) + (VALUE_SIZE - 1) + 10)
+#define JSON_BUFFER_SIZE (BUFFER_SIZE * JSON_PER_ENTRY + 2)
+
+#define SENSOR_CONFIG_COUNT 2
+
+#define millis() (esp_timer_get_time() / 1000ULL)
+
+// Global variables
+extern uint32_t g_setup_duration;
+extern uint32_t g_wakeup_earlier_server;
+extern uint32_t g_wakeup_earlier_sensor;
+extern uint32_t g_wakeup_earlier_pump;
+
+// Structs
+struct SensorConfigServer_s {
+    char sensor_name[32];
+    bool success;
+    bool is_active;
+    int pin;
+    int min_moisture;
+    char notification_name[32];
+    int pumping_delay_sec;
+    int pumping_duration;
+    int delay_ms;
+    bool use_time;
+    uint16_t times[8];
+    uint8_t time_count;
+};
+
+struct SensorConfig_s {
+    char sensor_name[32];
+    bool is_active;
+    int pin;
+    int min_moisture;
+    char notification_name[32];
+    int pumping_delay_sec;
+    int pumping_duration;
+    uint8_t pump_mac[6];
+    int delay_ms;
+    bool use_time;
+    uint16_t times[8];
+    uint8_t time_count;
+    int64_t last_sensor_read;
+    int64_t next_sensor_read;
+};
+
+struct PumpInformation_s {
+    char notifiaction_name[32];
+    int pumping_delay_sec;
+    int pumping_duration;
+    int64_t next_message;
+};
+
+struct SensorEntry_s {
+    char table[TABLE_SIZE];
+    char value[VALUE_SIZE];
+    int64_t time;
+    bool pump_reached;
+};
+
+// Extern variables
+extern SensorEntry_s g_buffer[BUFFER_SIZE];
+extern uint8_t g_buffer_count;
+#define MAX_PENDING_SENSOR_CONFIGS 4
+extern char g_pending_sensor_configs[MAX_PENDING_SENSOR_CONFIGS][32];
+extern uint8_t g_pending_sensor_configs_count;
+extern bool g_received_ok_from_server;
+
+extern const uint32_t g_min_sleep_time;
+extern 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;
+
+#endif
diff --git a/ASM_client_pump/main/main.cpp b/ASM_client_pump/main/main.cpp
new file mode 100644
index 0000000..9bd3a44
--- /dev/null
+++ b/ASM_client_pump/main/main.cpp
@@ -0,0 +1,84 @@
+#include "ClientPump.h"
+#include "esp_system.h"
+#include "freertos/FreeRTOS.h"
+#include "freertos/task.h"
+#include "esp_log.h"
+
+extern void set_gpio_output_levl(int gpio_num,int level);
+extern "C" esp_err_t esp_base_mac_addr_set(const uint8_t mac[6]);
+
+long long next_message_unix = 0;
+bool received_starting_message = false;
+
+PumpConfig_s pump_configs[PUMP_COUNT] = {
+    {"pump_1", 0, 0, 0, 0, INACTIVE},
+    {"pump_2", 0, 0, 0, 0, INACTIVE},
+};
+
+void start_Pump(PumpConfig_s& pump_data) {
+    ESP_LOGI("start_Pump", "Starting Pump %s", pump_data.pump_name);
+    pump_data.pumping_started_at_unix = get_CurrentUnix();
+    pump_data.pumping_status = RUNNING;
+    set_gpio_output_level(8,1);
+    set_gpio_output_level(10,1);
+}
+
+void stop_Pump(PumpConfig_s& pump_data) {
+    ESP_LOGI("stop_Pump", "Stopped Pump %s", pump_data.pump_name);
+    pump_data.pumping_status = INACTIVE;
+    set_gpio_output_level(10,0);
+    set_gpio_output_level(8,0);
+}
+
+void pump_loop(void*) {
+    while (true) {
+        bool pumping = false;
+
+        for (size_t i = 0; i < PUMP_COUNT; i++) {
+            PumpConfig_s& cfg = pump_configs[i];
+
+            if (START == cfg.pumping_status) {
+                pumping = true;
+                received_starting_message = false;
+                start_Pump(cfg);
+            } 
+            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 (!pumping && next_message_unix != 0 && !received_starting_message)
+            sleep_UntilNextSend(next_message_unix);
+
+        // give other tasks CPU time
+        vTaskDelay(pdMS_TO_TICKS(100));
+    }
+}
+
+extern "C" void app_main(void) {
+    static const char *TAG = "app_main";
+
+    uint32_t setup_start = esp_timer_get_time() / 1000ULL;
+
+    ESP_LOGI(TAG, "Initializing ClientPump...");
+
+    esp_base_mac_addr_set(PEER_PUMP);
+
+    setup_Timezone();
+    init_WiFi();
+    init_EspNow();
+
+    next_message_unix = 0;
+    received_starting_message = false;
+
+    uint32_t setup_duration = (esp_timer_get_time() / 1000ULL) - setup_start;
+    ESP_LOGI(TAG, "Setup duration: %u ms", setup_duration);
+
+    xTaskCreate(pump_loop, "pump_loop", 4096, nullptr, 5, nullptr);
+}