Nimbin[12]?Embedded / esp_garden / client_pump/main/ClientPump.cpp

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
client_pump/main/ClientPump.cpp 7.5 KB · 235 lines raw
#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();
    }
}