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.gzclient_pump/main/ClientPump.cpp 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();
}
}