esp_garden git · master
Distributed ESP sensor and actuator system (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.gzserver_hsnr/main/Server.cpp 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();
}