#include "ClientPump.h" #include "nvs_flash.h" #include "esp_wifi.h" #include "esp_event.h" #include "esp_netif.h" #include "esp_log.h" #include #include #include // ---------------------------------------------------------------------------- // 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(); } }