Nimbin[12]?Embedded / esp_garden / server_hsnr/main/Server.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
server_hsnr/main/Server.cpp 11 KB · 322 lines raw
#include "Server.h"
#include "secrets.h"
#include "WiFiManager.h"

#include <esp_sleep.h>
#include "esp_wifi_types.h"
#include "esp_now.h"
#include "esp_log.h"
#include <sys/time.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>


uint8_t received_from_mac_addr[6];


// ----------------------------------------------------------------------------
// Helper
// ----------------------------------------------------------------------------

int64_t get_CurrentUnix() {
    return (int64_t)time(nullptr);
}

void printMac(const uint8_t* mac) {
    if (!mac) return;
    ESP_LOGI("APP", "%02X:%02X:%02X:%02X:%02X:%02X",
             mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
}

void print_SensorConfigServer(const SensorConfigServer_s& cfg) {
    static const char *TAG = "print_SensorConfig";
    ESP_LOGI(TAG, "----------------------------");
    ESP_LOGI(TAG, "Got cfg for moisture_1:");
    ESP_LOGI(TAG, "Pin: %d", cfg.pin);
    ESP_LOGI(TAG, "Use Time: %d", cfg.use_time);
    ESP_LOGI(TAG, "Delay: %d", cfg.delay_ms);
    ESP_LOGI(TAG, "Notification Name: %s", cfg.notification_name);
    ESP_LOGI(TAG, "Min Moisture: %d", cfg.min_moisture);
    ESP_LOGI(TAG, "Active: %d", cfg.is_active);
    ESP_LOGI(TAG, "Pumping Delay: %d", cfg.pumping_delay_sec);
    ESP_LOGI(TAG, "Pumping Duration: %d", 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;
        ESP_LOGI(TAG, "Time[%d]: %02u:%02u (%u min)", i, hh, mm, minutes);
    }
    ESP_LOGI(TAG, "----------------------------");
}


// ----------------------------------------------------------------------------
// ESP-NOW helpers
// ----------------------------------------------------------------------------

void on_DataSent(const wifi_tx_info_t *info, esp_now_send_status_t status) {
    static const char *TAG = "on_DataSent";

    if (!info) return;

    const uint8_t *mac_addr = info->des_addr; // destination MAC

    ESP_LOGI(TAG, "Delivery to %02X:%02X:%02X:%02X:%02X:%02X %s",
             mac_addr[0], mac_addr[1], mac_addr[2],
             mac_addr[3], mac_addr[4], mac_addr[5],
             status == ESP_NOW_SEND_SUCCESS ? "OK" : "FAIL");
}

void add_Peer(const uint8_t *addr)
{
    if (!addr) return;

    if (esp_now_is_peer_exist(addr)) return;

    esp_now_peer_info_t peer = {0};
    memcpy(peer.peer_addr, addr, 6);
    peer.ifidx   = WIFI_IF_STA;
    peer.channel = 0;          // auto-select current Wi-Fi channel
    peer.encrypt = false;

    esp_now_add_peer(&peer);
}


// ----------------------------------------------------------------------------
// ESP-NOW receive callback
// ----------------------------------------------------------------------------

void add_Reading(const char* table, const char* value, const int64_t time, const bool pump_reached) {
    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[g_buffer_count].pump_reached = pump_reached;
    g_buffer_count++;
}

void on_DataRecv(const esp_now_recv_info_t *info, const uint8_t *data, int len) {
    static const char *TAG = "on_DataRecv";

    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';

    ESP_LOGI(TAG, "Received from %02X:%02X:%02X:%02X:%02X:%02X: %s",
             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", get_CurrentUnix());
        esp_now_send(received_from_mac_addr, (uint8_t *)time_msg, strlen(time_msg));
        return;
    }

    if (strncmp(msg, "next_read:", 10) == 0) {
        const char *value_str = msg + 10;
        next_connection_unix = atoll(value_str);
        go_to_sleep = true;
        ESP_LOGI(TAG, "Received next_read: %lld", next_connection_unix);
        return;
    }

    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';
        ESP_LOGI(TAG, "Queued config request for %s", pending_config.sensor_name);
        return;
    }

    ESP_LOGI(TAG, "Message: %s", msg);
    g_received_ok_from_server = false;

    char *first = strchr(msg, ':');
    char *second = first ? strchr(first + 1, ':') : NULL;
    char *third = second ? strchr(second + 1, ':') : NULL;

    if (first && second && third) {
        *first = '\0';
        *second = '\0';
        *third = '\0';

        const char *sensor_name = msg;
        const char *value_str = first + 1;
        int64_t time = atoll(second + 1);
        bool pump_reached = (*(third + 1) == '1');

        add_Reading(sensor_name, value_str, time, pump_reached);
    } else {
        ESP_LOGW(TAG, "Invalid message format (expected <name>:<value>:<time>:<reached_pump>)");
    }

    // Response deferred to main loop
}

// ----------------------------------------------------------------------------
// Init ESP-NOW
// ----------------------------------------------------------------------------

void init_EspNow() {
    static const char *TAG = "init_EspNow";

    if (esp_now_init() != ESP_OK) {
        ESP_LOGI(TAG, "ESP NOW init failed");
        while (true);
    }
    esp_now_register_send_cb(on_DataSent);
    esp_now_register_recv_cb(on_DataRecv);
}


// ----------------------------------------------------------------------------
// Config handling
// ----------------------------------------------------------------------------

void get_PendingConfigFromServer(void) {
    static const char *TAG = "get_PendingConfigFromServer";

    pending_config.active = false;
    SensorConfigServer_s cfg_response = {0};

    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,%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.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_err_t res = esp_now_send(pending_config.mac, (uint8_t *)response, strlen(response));
        if (res == ESP_OK)
            ESP_LOGI(TAG, "Sent config for %s: %s", cfg_response.sensor_name, response);
        else
            ESP_LOGW(TAG, "Failed to send config for %s (err=%d)", cfg_response.sensor_name, res);

    } else {
        const char *err = "cfg_error:fetch_failed";
        esp_err_t res = esp_now_send(pending_config.mac, (uint8_t *)err, strlen(err));
        ESP_LOGW(TAG, "Config fetch failed (send result=%d)", res);
    }
}

void send_ConfigToEsp(void) {
    static const char *TAG = "send_ConfigToEsp";

    ESP_LOGI(TAG, "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);

    // append configs if available
    if (g_pending_sensor_configs_count > 0) {
        ESP_LOGI(TAG, "Appending pending configs...");
        for (size_t i = 0; i < g_pending_sensor_configs_count; i++) {
            ESP_LOGI(TAG, "  adding config: %s", 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;
    }

    ESP_LOGI(TAG, "Sending combined message: %s", msg);

    esp_err_t res = esp_now_send(received_from_mac_addr,
                                 (uint8_t *)msg,
                                 strlen(msg));

    if (res == ESP_OK)
        ESP_LOGI(TAG, "Send queued successfully");
    else
        ESP_LOGW(TAG, "Send failed, err=%d", res);

    g_received_ok_from_server = false;
}


// ----------------------------------------------------------------------------
// Sleep
// ----------------------------------------------------------------------------

void sleep_UntilNextReceive() {
    static const char *TAG = "sleep_UntilNextSend";

    go_to_sleep = false;
    int64_t now_sec = get_CurrentUnix();

    int64_t delta_sec = next_connection_unix - 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);

    if (next_connection_unix < now_sec) return;

    ESP_LOGI(TAG, "Nex: %lld || Now: %lld", next_connection_unix, now_sec);

    next_connection_unix = 0;

    // 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);
        ESP_LOGI(TAG, "######################");
        vTaskDelay(pdMS_TO_TICKS(min_delay_ms));
        return;
    }

    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;

    ESP_LOGI(TAG, "Sleeping for %u ms (setup margin: %u ms)",
               sleep_time_ms, prep_time_ms);
    ESP_LOGI(TAG, "######################");

    esp_sleep_enable_timer_wakeup((uint64_t)sleep_time_ms * 1000ULL);
    esp_deep_sleep_start();
}