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.gzUpdate syncTime and receive config
post_server/main/WiFiManager.cpp | 171 ++++++++++++++++++++++++++++++++++++++- post_server/main/WiFiManager.h | 11 ++- post_server/main/main.cpp | 11 ++- 3 files changed, 187 insertions(+), 6 deletions(-) diff --git a/post_server/main/WiFiManager.cpp b/post_server/main/WiFiManager.cpp index 0c20769..0aead7e 100644 --- a/post_server/main/WiFiManager.cpp +++ b/post_server/main/WiFiManager.cpp @@ -7,7 +7,9 @@ #include <cstdio> #include <cstdlib> #include <cinttypes> +#include <ctime> +#include "esp_sntp.h" #include "esp_log.h" #include "esp_wifi.h" #include "esp_http_client.h" @@ -43,19 +45,57 @@ esp_err_t WiFi_connect() { return ESP_OK; } -esp_err_t WiFi_disconnect(void) { +esp_err_t WiFi_disconnect() { esp_err_t err = esp_wifi_disconnect(); ESP_ERROR_CHECK(esp_wifi_stop()); ESP_ERROR_CHECK(esp_wifi_deinit()); return err; } +void WIFI_SyncTime() { + static const char *TAG = "WIFI_SyncTime"; + ESP_LOGI(TAG, "Syncing time..."); + + // Configure SNTP + sntp_setoperatingmode(SNTP_OPMODE_POLL); + sntp_setservername(0, NTP_SERVER); + sntp_init(); + + // Wait until time is set (or timeout) + const int64_t min_valid_time = 1600000000; + const int max_wait_ms = 10000; + const int interval_ms = 500; + int waited_ms = 0; + time_t now = 0; + + while (waited_ms < max_wait_ms) { + time(&now); + if (now > min_valid_time) break; + vTaskDelay(pdMS_TO_TICKS(interval_ms)); + waited_ms += interval_ms; + } + if (now < min_valid_time) { + ESP_LOGW(TAG, "NTP sync failed (no valid time within %d ms)", max_wait_ms); + } else { + // Set timezone + setenv("TZ", "CET-1CEST,M3.5.0,M10.5.0/3", 1); + tzset(); + char buf[64]; + struct tm timeinfo; + localtime_r(&now, &timeinfo); + strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", &timeinfo); + ESP_LOGI(TAG, "Time synced: %s", buf); + } + + // Should i stop it? +} ///////////////////////////////////////////////// ///////////////////////////////////////////////// +/// Post data to server static std::string http_response; @@ -131,7 +171,7 @@ void post_ToServer( const char* path, char* json, size_t len) { ESP_LOGE(TAG, "esp_http_client_perform failed: %d", err); } - // cleanup client (this will not free http_response) + // cleanup client esp_err_t cleanup_err = esp_http_client_cleanup(client); if (cleanup_err != ESP_OK) { ESP_LOGW(TAG, "esp_http_client_cleanup returned %d", cleanup_err); @@ -277,3 +317,130 @@ void WiFi_PostDataToServer(size_t buffer_len, SensorEntry_s buffer[BUFFER_SIZE]) free(json); } + + + + + +////////////////////////////////////// +/// Config stuff + +static int findKey(const std::string &src, const char *key) { + std::string pattern = std::string("\"") + key + "\":"; + auto pos = src.find(pattern); + return (pos == std::string::npos) ? -1 : (int)pos + pattern.size(); +} + + +bool WIFI_GetSensorConfigServer(const char* sensor_name, SensorConfigServer_s &cfg) { + static const char *TAG = "WIFI_GetSensorConfigServer"; + + // construct URL + char name_enc[64]; + snprintf(name_enc, sizeof(name_enc), "%s", sensor_name); + char url[256]; + snprintf(url, sizeof(url), "https://%s:%d/get_sensor_config.php?name=%s", server_host, server_port, name_enc); + + ESP_LOGI(TAG, "Requesting config for sensor: %s", sensor_name); + http_response.clear(); + + // configure HTTP client + esp_http_client_config_t config = { + .url = url, + .event_handler = http_event_handler, + }; + + esp_http_client_handle_t client = esp_http_client_init(&config); + if (!client) { + ESP_LOGE(TAG, "esp_http_client_init failed"); + return false; + } + + esp_http_client_set_method(client, HTTP_METHOD_GET); + esp_http_client_set_header(client, "X-API-Token", api_token); + + esp_err_t err = esp_http_client_perform(client); + if (err != ESP_OK) { + ESP_LOGE(TAG, "HTTP GET failed: %s", esp_err_to_name(err)); + esp_http_client_cleanup(client); + return false; + } + + int status = esp_http_client_get_status_code(client); + esp_http_client_cleanup(client); + + if (status != 200) { + ESP_LOGW(TAG, "Server returned HTTP %d", status); + return false; + } + + ESP_LOGI(TAG, "Received %zu bytes", http_response.size()); + ESP_LOGD(TAG, "Body: %s", http_response.c_str()); + + // Parse json response + const std::string &body = http_response; + memset(&cfg, 0, sizeof(cfg)); + + auto extractInt = [&](const char *key) -> int { + int pos = findKey(body, key); + if (pos < 0) return 0; + return std::atoi(body.c_str() + pos); + }; + + auto extractBool = [&](const char *key) -> bool { + int pos = findKey(body, key); + if (pos < 0) return false; + const char *p = body.c_str() + pos; + return (strncmp(p, "true", 4) == 0 || *p == '1'); + }; + + auto extractString = [&](const char *key, char *out, size_t out_size) { + std::string pattern = std::string("\"") + key + "\":\""; + auto start = body.find(pattern); + if (start == std::string::npos) { out[0] = '\0'; return; } + start += pattern.size(); + auto end = body.find('"', start); + if (end == std::string::npos) { out[0] = '\0'; return; } + size_t len = end - start; + if (len >= out_size) len = out_size - 1; + memcpy(out, body.c_str() + start, len); + out[len] = '\0'; + }; + + cfg.success = extractBool("success"); + extractString("sensor_name", cfg.sensor_name, sizeof(cfg.sensor_name)); + cfg.pin = extractInt("pin"); + cfg.use_time = extractBool("use_time"); + cfg.delay_ms = extractInt("delay_ms"); + cfg.min_moisture = extractInt("min_moisture"); + cfg.is_active = extractBool("is_active"); + extractString("notification_name", cfg.notification_name, sizeof(cfg.notification_name)); + cfg.pumping_delay_sec = extractInt("pumping_delay_sec"); + cfg.pumping_duration = extractInt("pumping_duration"); + + // Parse times array + cfg.time_count = 0; + size_t arr_start = body.find("\"times\":["); + if (arr_start != std::string::npos) { + arr_start += 9; + size_t arr_end = body.find(']', arr_start); + if (arr_end != std::string::npos) { + std::string arr = body.substr(arr_start, arr_end - arr_start); + size_t pos = 0; + while (cfg.time_count < 8 && pos < arr.size()) { + size_t comma = arr.find(',', pos); + std::string token = arr.substr(pos, (comma == std::string::npos ? arr.size() : comma) - pos); + pos = (comma == std::string::npos) ? arr.size() : comma + 1; + int val = std::atoi(token.c_str()); + if (val > 0) cfg.times[cfg.time_count++] = (uint16_t)val; + } + } + } + + ESP_LOGI(TAG, "Parsed config: success=%d pin=%d min=%d delay=%d use_time=%d time_count=%u", + cfg.success, cfg.pin, cfg.min_moisture, cfg.delay_ms, cfg.use_time, cfg.time_count); + ESP_LOGI(TAG, "sensor_name='%s' notification='%s' pumping_delay_sec=%d pumping_duration=%d", + cfg.sensor_name, cfg.notification_name, cfg.pumping_delay_sec, cfg.pumping_duration); + + return cfg.success; +} diff --git a/post_server/main/WiFiManager.h b/post_server/main/WiFiManager.h index 140f98d..48e2518 100644 --- a/post_server/main/WiFiManager.h +++ b/post_server/main/WiFiManager.h @@ -1,5 +1,5 @@ -// tutorial.h -#pragma once +#ifndef WIFIMANAGER_H +#define WIFIMANAGER_H #include "globals.h" @@ -14,7 +14,12 @@ #include "freertos/FreeRTOS.h" esp_err_t WiFi_connect(); -esp_err_t WiFi_disconnect(void); +esp_err_t WiFi_disconnect(); +void WIFI_SyncTime(); void WiFi_PostDataToServer(size_t buffer_len, SensorEntry_s buffer[BUFFER_SIZE]); +bool WIFI_GetSensorConfigServer(const char* sensor_name, SensorConfigServer_s &cfg); + + +#endif diff --git a/post_server/main/main.cpp b/post_server/main/main.cpp index cde3010..a17c410 100644 --- a/post_server/main/main.cpp +++ b/post_server/main/main.cpp @@ -24,7 +24,11 @@ extern "C" void app_main() { vTaskDelay(10000 / portTICK_PERIOD_MS); + // we should do that more than once! + WIFI_SyncTime(); + SensorConfigServer_s cfg; + WIFI_GetSensorConfigServer("moisture_1", cfg); strcpy(g_buffer[0].table, "moisture_1"); strcpy(g_buffer[0].value, "123"); g_buffer[0].time = 0; @@ -37,10 +41,15 @@ extern "C" void app_main() { while (true) { WiFi_PostDataToServer(g_buffer_count, g_buffer); - vTaskDelay(pdMS_TO_TICKS(5000)); + // Check stack size + UBaseType_t free_stack = uxTaskGetStackHighWaterMark(NULL); + ESP_LOGI("wifi_task", "Stack remaining: %u bytes", (unsigned)free_stack); + vTaskDelay(pdMS_TO_TICKS(5000)); } vTaskDelay(10000 / portTICK_PERIOD_MS); + + WiFi_disconnect(); }, "wifi_task", 8192, NULL, 1, NULL); }