From 5d2ccf0a8e71ddc6dc94b49c9cc9a4ff787b82ea Mon Sep 17 00:00:00 2001 From: thek4n Date: Sat, 6 Jun 2026 14:44:49 +0300 Subject: [PATCH] refactor --- main/main.c | 1368 +++++++++++++++++++++++++++------------------------ 1 file changed, 736 insertions(+), 632 deletions(-) diff --git a/main/main.c b/main/main.c index 904a86a..90a9cf5 100644 --- a/main/main.c +++ b/main/main.c @@ -2,6 +2,7 @@ #include #include #include +#include #include #include #include @@ -32,6 +33,10 @@ #include "frontend.h" #include "setup_frontend.h" +// ============================================================================ +// Configuration validation +// ============================================================================ + #if !defined(CONFIG_PUMP_PIN) #error "CONFIG_PUMP_PIN must be defined in menuconfig" #endif @@ -44,54 +49,597 @@ #error "AP network configuration must be defined in menuconfig" #endif +// ============================================================================ +// Constants +// ============================================================================ + #define AP_MAX_CONN 4 #define AP_CHANNEL 6 -#define ADC_CHAN0 ADC_CHANNEL_4 -#define ADC_CHAN1 ADC_CHANNEL_5 -#define ADC_ATTEN_DB ADC_ATTEN_DB_12 +#define ADC_CHAN0 ADC_CHANNEL_4 +#define ADC_CHAN1 ADC_CHANNEL_5 +#define ADC_ATTEN_DB ADC_ATTEN_DB_12 -#define THRESHOLD_UP_NVS_NAME "threshold_up" -#define THRESHOLD_LOW_NVS_NAME "threshold_low" -#define NVS_PARTITION "nvs" +#define THRESHOLD_UP_NVS_NAME "threshold_up" +#define THRESHOLD_LOW_NVS_NAME "threshold_low" +#define NVS_PARTITION "nvs" -#define SENSOR_ADC_CHAN 0 -#define MAX_JSON_CONTENT 512 +#define SENSOR_ADC_CHAN 0 +#define MAX_JSON_CONTENT 512 -#define FILTER_SAMPLES 5 +#define FILTER_SAMPLES 5 -#define PRIORITY_HTTP 1 -#define PRIORITY_CONTROL 2 -#define PRIORITY_SENSOR 3 +#define PRIORITY_HTTP 1 +#define PRIORITY_CONTROL 2 +#define PRIORITY_SENSOR 3 -#define MDNS_DOMAIN "pumpctl" +#define MDNS_DOMAIN "pumpctl" -#define RESET_BTN_GPIO GPIO_NUM_15 -#define LED_GPIO GPIO_NUM_2 +#define RESET_BTN_GPIO GPIO_NUM_15 +#define LED_GPIO GPIO_NUM_2 -#define WIFI_SSID_MAX_LEN 32 -#define WIFI_PASS_MAX_LEN 64 +#define WIFI_SSID_MAX_LEN 32 +#define WIFI_PASS_MAX_LEN 64 -#define WIFI_CONNECTED_BIT BIT0 -#define WIFI_FAIL_BIT BIT1 +#define WIFI_CONNECTED_BIT BIT0 +#define WIFI_FAIL_BIT BIT1 -static EventGroupHandle_t wifi_event_group; +#define DEFAULT_THRESHOLD_LOW 100 +#define DEFAULT_THRESHOLD_UP 300 + +// ============================================================================ +// Error codes +// ============================================================================ + +typedef enum { + APP_ERR_OK = 0, + APP_ERR_NVS_INIT_FAIL, + APP_ERR_NVS_OPEN_FAIL, + APP_ERR_NVS_READ_FAIL, + APP_ERR_NVS_WRITE_FAIL, + APP_ERR_ADC_INIT_FAIL, + APP_ERR_ADC_CALIB_FAIL, + APP_ERR_PUMP_INIT_FAIL, + APP_ERR_WIFI_INIT_FAIL, + APP_ERR_WIFI_CONNECT_FAIL, + APP_ERR_MDNS_INIT_FAIL, + APP_ERR_HTTP_SERVER_START_FAIL, + APP_ERR_TASK_CREATE_FAIL, + APP_ERR_GPIO_CONFIG_FAIL, + APP_ERR_INVALID_CONFIG, + APP_ERR_MEMORY_ALLOC_FAIL, + APP_ERR_JSON_PARSE_FAIL +} app_error_t; + +// ============================================================================ +// Global error handler +// ============================================================================ + +static const char *TAG = "Pump Controller"; + +typedef struct { + app_error_t code; + const char *message; + bool fatal; +} error_info_t; + +static const error_info_t error_table[] = { + {APP_ERR_OK, "Success", false}, + {APP_ERR_NVS_INIT_FAIL, "NVS initialization failed", true}, + {APP_ERR_NVS_OPEN_FAIL, "NVS open failed", true}, + {APP_ERR_NVS_READ_FAIL, "NVS read failed", false}, + {APP_ERR_NVS_WRITE_FAIL, "NVS write failed", false}, + {APP_ERR_ADC_INIT_FAIL, "ADC initialization failed", true}, + {APP_ERR_ADC_CALIB_FAIL, "ADC calibration failed", false}, + {APP_ERR_PUMP_INIT_FAIL, "Pump GPIO initialization failed", true}, + {APP_ERR_WIFI_INIT_FAIL, "WiFi initialization failed", true}, + {APP_ERR_WIFI_CONNECT_FAIL, "WiFi connection failed", false}, + {APP_ERR_MDNS_INIT_FAIL, "mDNS service initialization failed", false}, + {APP_ERR_HTTP_SERVER_START_FAIL, "HTTP server start failed", false}, + {APP_ERR_TASK_CREATE_FAIL, "Task creation failed", true}, + {APP_ERR_GPIO_CONFIG_FAIL, "GPIO configuration failed", true}, + {APP_ERR_INVALID_CONFIG, "Invalid configuration", true}, + {APP_ERR_MEMORY_ALLOC_FAIL, "Memory allocation failed", false}, + {APP_ERR_JSON_PARSE_FAIL, "JSON parse failed", false} +}; + +static void handle_error(app_error_t error) { + const char *message = "Unknown error"; + bool fatal = true; + + for (int i = 0; i < sizeof(error_table) / sizeof(error_info_t); i++) { + if (error_table[i].code == error) { + message = error_table[i].message; + fatal = error_table[i].fatal; + break; + } + } + + ESP_LOGE(TAG, "ERROR [%d]: %s", error, message); + + if (fatal) { + ESP_LOGE(TAG, "Fatal error! Restarting in 5 seconds..."); + vTaskDelay(pdMS_TO_TICKS(5000)); + esp_restart(); + } +} + +#define CHECK_ERROR(expr, error_code) \ + do { \ + esp_err_t __err = (expr); \ + if (__err != ESP_OK) { \ + ESP_LOGE(TAG, "Error at %s:%d: %s", __FILE__, __LINE__, esp_err_to_name(__err)); \ + handle_error(error_code); \ + return error_code; \ + } \ + } while(0) + +#define CHECK_COND(cond, error_code) \ + do { \ + if (!(cond)) { \ + ESP_LOGE(TAG, "Condition failed at %s:%d", __FILE__, __LINE__); \ + handle_error(error_code); \ + return error_code; \ + } \ + } while(0) + +#define CHECK_PTR(ptr, error_code) \ + do { \ + if ((ptr) == NULL) { \ + ESP_LOGE(TAG, "Null pointer at %s:%d", __FILE__, __LINE__); \ + handle_error(error_code); \ + return error_code; \ + } \ + } while(0) + +// ============================================================================ +// Global state +// ============================================================================ + +static EventGroupHandle_t wifi_event_group = NULL; static int retry_count = 0; static const int MAX_RETRY_COUNT = 5; -static adc_oneshot_unit_handle_t adc_handle; -static adc_cali_handle_t cali_handle; +static adc_oneshot_unit_handle_t adc_handle = NULL; +static adc_cali_handle_t cali_handle = NULL; static bool is_calibrated = false; static atomic_int g_threshold_low = 0; static atomic_int g_threshold_up = 0; static atomic_int g_current_pressure = 0; -static const char TAG[] = "Pump Controller"; +static bool g_pump_enabled = false; +static bool g_setup_mode = false; + +// ============================================================================ +// Forward declarations +// ============================================================================ + +static app_error_t nvs_init(void); +static app_error_t gpio_init(void); +static app_error_t adc_init(void); +static app_error_t pump_init(void); +static app_error_t load_thresholds(void); +static app_error_t save_thresholds_to_nvs(int low, int up); +static app_error_t wifi_softap_init(void); +static app_error_t wifi_sta_init(const char *ssid, const char *password); +static app_error_t mdns_init_service(void); +static app_error_t http_server_start(void); +static app_error_t setup_http_server_start(void); +static app_error_t tasks_create(void); +static bool is_wifi_config_exists(void); +static void reset_settings(void); + +// ============================================================================ +// Utility functions +// ============================================================================ + +static int map(int x, int in_min, int in_max, int out_min, int out_max) { + return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min; +} + +static int convert_adc_to_pressure_atm(int adc_value) { + return map(adc_value, 330, 3145, 0, 1184); +} + +static void pump_disable(void) { + gpio_set_level(CONFIG_PUMP_PIN, false); + g_pump_enabled = false; +} + +static void pump_enable(void) { + gpio_set_level(CONFIG_PUMP_PIN, true); + g_pump_enabled = true; +} + +static void indicate_led(void) { + for (int i = 0; i < 3; i++) { + gpio_set_level(LED_GPIO, 1); + vTaskDelay(pdMS_TO_TICKS(200)); + gpio_set_level(LED_GPIO, 0); + vTaskDelay(pdMS_TO_TICKS(200)); + } +} + +// ============================================================================ +// ADC functions +// ============================================================================ + +static app_error_t adc_init(void) { + adc_oneshot_unit_init_cfg_t init_config = { + .unit_id = ADC_UNIT_1, + }; + CHECK_ERROR(adc_oneshot_new_unit(&init_config, &adc_handle), APP_ERR_ADC_INIT_FAIL); + + adc_oneshot_chan_cfg_t config = { + .atten = ADC_ATTEN_DB, + .bitwidth = ADC_BITWIDTH_DEFAULT, + }; + CHECK_ERROR(adc_oneshot_config_channel(adc_handle, ADC_CHAN0, &config), APP_ERR_ADC_INIT_FAIL); + CHECK_ERROR(adc_oneshot_config_channel(adc_handle, ADC_CHAN1, &config), APP_ERR_ADC_INIT_FAIL); + + adc_cali_line_fitting_config_t cali_config = { + .unit_id = ADC_UNIT_1, + .atten = ADC_ATTEN_DB, + .bitwidth = ADC_BITWIDTH_DEFAULT, + }; + + esp_err_t ret = adc_cali_create_scheme_line_fitting(&cali_config, &cali_handle); + if (ret == ESP_OK) { + is_calibrated = true; + ESP_LOGI(TAG, "ADC calibration successful"); + } else if (ret == ESP_ERR_NOT_SUPPORTED) { + ESP_LOGW(TAG, "ADC calibration not available"); + is_calibrated = false; + } else { + CHECK_ERROR(ret, APP_ERR_ADC_CALIB_FAIL); + } + + return ERR_OK; +} + +static int adc_read_raw(uint8_t channel) { + adc_channel_t adc_channel; + + if (channel == 0) { + adc_channel = ADC_CHAN0; + } else if (channel == 1) { + adc_channel = ADC_CHAN1; + } else { + ESP_LOGE(TAG, "Invalid ADC channel: %d", channel); + return -1; + } + + CHECK_PTR(adc_handle, APP_ERR_ADC_INIT_FAIL); + + int raw_value = 0; + CHECK_ERROR(adc_oneshot_read(adc_handle, adc_channel, &raw_value), APP_ERR_ADC_INIT_FAIL); + return raw_value; +} + +static int adc_read_voltage(uint8_t channel) { + int raw_value = adc_read_raw(channel); + if (raw_value < 0) return -1; + + if (is_calibrated) { + int voltage_mv = 0; + CHECK_ERROR(adc_cali_raw_to_voltage(cali_handle, raw_value, &voltage_mv), APP_ERR_ADC_INIT_FAIL); + return voltage_mv; + } else { + return (raw_value * 3300) / 4095; + } +} + +static int read_voltage_filtered(void) { + int sum = 0; + for (int i = 0; i < FILTER_SAMPLES; i++) { + int voltage = adc_read_voltage(SENSOR_ADC_CHAN); + if (voltage < 0) return -1; + sum += voltage; + vTaskDelay(pdMS_TO_TICKS(10)); + } + return sum / FILTER_SAMPLES; +} + +// ============================================================================ +// NVS functions +// ============================================================================ + +static app_error_t nvs_init(void) { + esp_err_t ret = nvs_flash_init(); + if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) { + CHECK_ERROR(nvs_flash_erase(), APP_ERR_NVS_INIT_FAIL); + CHECK_ERROR(nvs_flash_init(), APP_ERR_NVS_INIT_FAIL); + } else { + CHECK_ERROR(ret, APP_ERR_NVS_INIT_FAIL); + } + return ERR_OK; +} + +static bool is_wifi_config_exists(void) { + nvs_handle_t nvs_handle; + esp_err_t err = nvs_open(NVS_PARTITION, NVS_READONLY, &nvs_handle); + if (err != ESP_OK) { + return false; + } + + char ssid[WIFI_SSID_MAX_LEN]; + size_t ssid_len = sizeof(ssid); + err = nvs_get_str(nvs_handle, "wifi_ssid", ssid, &ssid_len); + nvs_close(nvs_handle); + + return (err == ESP_OK); +} + +static app_error_t load_thresholds(void) { + nvs_handle_t my_handle; + CHECK_ERROR(nvs_open(NVS_PARTITION, NVS_READWRITE, &my_handle), APP_ERR_NVS_OPEN_FAIL); + + int32_t threshold_low = DEFAULT_THRESHOLD_LOW; + esp_err_t err = nvs_get_i32(my_handle, THRESHOLD_LOW_NVS_NAME, &threshold_low); + if (err != ESP_OK && err != ESP_ERR_NVS_NOT_FOUND) { + nvs_close(my_handle); + CHECK_ERROR(err, APP_ERR_NVS_READ_FAIL); + } + atomic_store(&g_threshold_low, threshold_low); + + int32_t threshold_up = DEFAULT_THRESHOLD_UP; + err = nvs_get_i32(my_handle, THRESHOLD_UP_NVS_NAME, &threshold_up); + if (err != ESP_OK && err != ESP_ERR_NVS_NOT_FOUND) { + nvs_close(my_handle); + CHECK_ERROR(err, APP_ERR_NVS_READ_FAIL); + } + atomic_store(&g_threshold_up, threshold_up); + + nvs_close(my_handle); + + ESP_LOGI(TAG, "Thresholds loaded: low=%d, up=%d", threshold_low, threshold_up); + return ERR_OK; +} + +static app_error_t save_thresholds_to_nvs(int low, int up) { + nvs_handle_t my_handle; + CHECK_ERROR(nvs_open(NVS_PARTITION, NVS_READWRITE, &my_handle), APP_ERR_NVS_OPEN_FAIL); + + CHECK_ERROR(nvs_set_i32(my_handle, THRESHOLD_LOW_NVS_NAME, low), APP_ERR_NVS_WRITE_FAIL); + CHECK_ERROR(nvs_set_i32(my_handle, THRESHOLD_UP_NVS_NAME, up), APP_ERR_NVS_WRITE_FAIL); + CHECK_ERROR(nvs_commit(my_handle), APP_ERR_NVS_WRITE_FAIL); + + nvs_close(my_handle); + return ERR_OK; +} + +static void save_wifi_config(const char *ssid, const char *password) { + nvs_handle_t nvs_handle; + if (nvs_open(NVS_PARTITION, NVS_READWRITE, &nvs_handle) != ESP_OK) { + ESP_LOGE(TAG, "Failed to open NVS for WiFi config"); + return; + } + + nvs_set_str(nvs_handle, "wifi_ssid", ssid); + nvs_set_str(nvs_handle, "wifi_pass", password); + nvs_commit(nvs_handle); + nvs_close(nvs_handle); + + ESP_LOGI(TAG, "WiFi config saved: SSID=%s", ssid); +} + +static void reset_settings(void) { + ESP_LOGI(TAG, "Resetting settings..."); + nvs_flash_erase(); + nvs_flash_init(); + vTaskDelay(pdMS_TO_TICKS(500)); +} + +// ============================================================================ +// GPIO functions +// ============================================================================ + +static app_error_t gpio_init(void) { + gpio_config_t led_conf = { + .pin_bit_mask = (1ULL << LED_GPIO), + .mode = GPIO_MODE_OUTPUT, + .pull_up_en = GPIO_PULLUP_DISABLE, + .pull_down_en = GPIO_PULLUP_DISABLE, + .intr_type = GPIO_INTR_DISABLE, + }; + CHECK_ERROR(gpio_config(&led_conf), APP_ERR_GPIO_CONFIG_FAIL); + gpio_set_level(LED_GPIO, 0); + + gpio_config_t btn_conf = { + .pin_bit_mask = (1ULL << RESET_BTN_GPIO), + .mode = GPIO_MODE_INPUT, + .pull_up_en = GPIO_PULLUP_ENABLE, + .pull_down_en = GPIO_PULLDOWN_DISABLE, + .intr_type = GPIO_INTR_DISABLE, + }; + CHECK_ERROR(gpio_config(&btn_conf), APP_ERR_GPIO_CONFIG_FAIL); + + return ERR_OK; +} + +static app_error_t pump_init(void) { + gpio_config_t io_conf = { + .pin_bit_mask = (1ULL << CONFIG_PUMP_PIN), + .mode = GPIO_MODE_INPUT_OUTPUT, + .intr_type = GPIO_INTR_DISABLE, + .pull_down_en = 1, + .pull_up_en = 0, + }; + CHECK_ERROR(gpio_config(&io_conf), APP_ERR_PUMP_INIT_FAIL); + pump_disable(); + return ERR_OK; +} + +// ============================================================================ +// WiFi functions +// ============================================================================ + +static void wifi_event_handler(void* arg, esp_event_base_t event_base, + int32_t event_id, void* event_data) { + if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) { + esp_wifi_connect(); + ESP_LOGI(TAG, "Attempting to connect to WiFi..."); + } + else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) { + if (retry_count < MAX_RETRY_COUNT) { + esp_wifi_connect(); + retry_count++; + ESP_LOGI(TAG, "Retry connecting (%d/%d)...", retry_count, MAX_RETRY_COUNT); + } else { + xEventGroupSetBits(wifi_event_group, WIFI_FAIL_BIT); + ESP_LOGE(TAG, "Failed to connect after %d retries", MAX_RETRY_COUNT); + handle_error(APP_ERR_WIFI_CONNECT_FAIL); + } + } + else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) { + ip_event_got_ip_t* event = (ip_event_got_ip_t*) event_data; + ESP_LOGI(TAG, "Got IP: " IPSTR, IP2STR(&event->ip_info.ip)); + retry_count = 0; + xEventGroupSetBits(wifi_event_group, WIFI_CONNECTED_BIT); + } +} + +static app_error_t wifi_softap_init(void) { + CHECK_ERROR(esp_netif_init(), APP_ERR_WIFI_INIT_FAIL); + CHECK_ERROR(esp_event_loop_create_default(), APP_ERR_WIFI_INIT_FAIL); + + esp_netif_t *ap_netif = esp_netif_create_default_wifi_ap(); + CHECK_PTR(ap_netif, APP_ERR_WIFI_INIT_FAIL); + + wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); + CHECK_ERROR(esp_wifi_init(&cfg), APP_ERR_WIFI_INIT_FAIL); + + wifi_config_t wifi_config = { + .ap = { + .ssid = CONFIG_AP_WIFI_SSID, + .ssid_len = strlen(CONFIG_AP_WIFI_SSID), + .password = CONFIG_AP_WIFI_PASS, + .max_connection = AP_MAX_CONN, + .authmode = WIFI_AUTH_WPA_WPA2_PSK, + .channel = AP_CHANNEL, + }, + }; + + if (strlen(CONFIG_AP_WIFI_PASS) == 0) { + wifi_config.ap.authmode = WIFI_AUTH_OPEN; + } + + CHECK_ERROR(esp_wifi_set_mode(WIFI_MODE_AP), APP_ERR_WIFI_INIT_FAIL); + CHECK_ERROR(esp_wifi_set_config(WIFI_IF_AP, &wifi_config), APP_ERR_WIFI_INIT_FAIL); + CHECK_ERROR(esp_wifi_start(), APP_ERR_WIFI_INIT_FAIL); + + esp_netif_ip_info_t ip_info; + ip_info.ip.addr = inet_addr(CONFIG_AP_IP); + ip_info.gw.addr = inet_addr(CONFIG_AP_GATEWAY); + ip_info.netmask.addr = inet_addr(CONFIG_AP_NETMASK); + + CHECK_ERROR(esp_netif_dhcps_stop(ap_netif), APP_ERR_WIFI_INIT_FAIL); + CHECK_ERROR(esp_netif_set_ip_info(ap_netif, &ip_info), APP_ERR_WIFI_INIT_FAIL); + CHECK_ERROR(esp_netif_dhcps_start(ap_netif), APP_ERR_WIFI_INIT_FAIL); + + return ERR_OK; +} + +static app_error_t wifi_sta_init(const char* ssid, const char* password) { + wifi_event_group = xEventGroupCreate(); + CHECK_PTR(wifi_event_group, APP_ERR_WIFI_INIT_FAIL); + + CHECK_ERROR(esp_netif_init(), APP_ERR_WIFI_INIT_FAIL); + CHECK_ERROR(esp_event_loop_create_default(), APP_ERR_WIFI_INIT_FAIL); + esp_netif_create_default_wifi_sta(); + + wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); + CHECK_ERROR(esp_wifi_init(&cfg), APP_ERR_WIFI_INIT_FAIL); + + CHECK_ERROR(esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &wifi_event_handler, NULL), APP_ERR_WIFI_INIT_FAIL); + CHECK_ERROR(esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &wifi_event_handler, NULL), APP_ERR_WIFI_INIT_FAIL); + + wifi_config_t wifi_config = { + .sta = { + .threshold.authmode = WIFI_AUTH_WPA2_PSK, + .sae_pwe_h2e = WPA3_SAE_PWE_BOTH, + }, + }; + strcpy((char*)wifi_config.sta.ssid, ssid); + strcpy((char*)wifi_config.sta.password, password); + + CHECK_ERROR(esp_wifi_set_mode(WIFI_MODE_STA), APP_ERR_WIFI_INIT_FAIL); + CHECK_ERROR(esp_wifi_set_config(WIFI_IF_STA, &wifi_config), APP_ERR_WIFI_INIT_FAIL); + CHECK_ERROR(esp_wifi_start(), APP_ERR_WIFI_INIT_FAIL); + + ESP_LOGI(TAG, "WiFi STA started"); + + EventBits_t bits = xEventGroupWaitBits(wifi_event_group, + WIFI_CONNECTED_BIT | WIFI_FAIL_BIT, + pdFALSE, pdFALSE, portMAX_DELAY); + + if (bits & WIFI_CONNECTED_BIT) { + ESP_LOGI(TAG, "Successfully connected to WiFi!"); + return ERR_OK; + } else { + return APP_ERR_WIFI_CONNECT_FAIL; + } +} + +// ============================================================================ +// mDNS functions +// ============================================================================ + +static app_error_t mdns_init_service(void) { + CHECK_ERROR(mdns_init(), APP_ERR_MDNS_INIT_FAIL); + CHECK_ERROR(mdns_hostname_set(MDNS_DOMAIN), APP_ERR_MDNS_INIT_FAIL); + CHECK_ERROR(mdns_instance_name_set("PumpController"), APP_ERR_MDNS_INIT_FAIL); + CHECK_ERROR(mdns_service_add(NULL, "_http", "_tcp", 80, NULL, 0), APP_ERR_MDNS_INIT_FAIL); + return ERR_OK; +} + +// ============================================================================ +// HTTP handlers +// ============================================================================ + +static esp_err_t send_json_response(httpd_req_t *req, const char *format, ...) { + char response[256]; + va_list args; + va_start(args, format); + vsnprintf(response, sizeof(response), format, args); + va_end(args); + + httpd_resp_set_type(req, "application/json"); + httpd_resp_send(req, response, strlen(response)); + return ESP_OK; +} + +static esp_err_t receive_http_content(httpd_req_t *req, char **content) { + size_t content_len = req->content_len; + + if (content_len > MAX_JSON_CONTENT) { + httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Content too large"); + return ESP_FAIL; + } + + *content = malloc(content_len + 1); + if (!*content) { + httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Memory allocation failed"); + handle_error(APP_ERR_MEMORY_ALLOC_FAIL); + return ESP_FAIL; + } + + int received = 0; + while (received < content_len) { + int ret = httpd_req_recv(req, *content + received, content_len - received); + if (ret <= 0) break; + received += ret; + } + (*content)[content_len] = '\0'; + + return ESP_OK; +} static esp_err_t parse_thresholds_json(const char *content, int *low_value, int *up_value) { cJSON *json = cJSON_Parse(content); if (!json) { + handle_error(APP_ERR_JSON_PARSE_FAIL); return ESP_FAIL; } @@ -108,154 +656,6 @@ static esp_err_t parse_thresholds_json(const char *content, int *low_value, int return valid ? ESP_OK : ESP_FAIL; } -static esp_err_t receive_http_content(httpd_req_t *req, char **content) { - size_t content_len = req->content_len; - - if (content_len > MAX_JSON_CONTENT) { - httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Content too large"); - return ESP_FAIL; - } - - *content = malloc(content_len + 1); - if (!*content) { - httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Memory allocation failed"); - return ESP_FAIL; - } - - int ret; - int received = 0; - while (received < content_len) { - ret = httpd_req_recv(req, *content + received, content_len - received); - if (ret <= 0) break; - received += ret; - } - (*content)[content_len] = '\0'; - - return ESP_OK; -} - -static esp_err_t send_json_response(httpd_req_t *req, const char *format, ...) { - char response[100]; - va_list args; - va_start(args, format); - vsnprintf(response, sizeof(response), format, args); - va_end(args); - - httpd_resp_set_type(req, "application/json"); - httpd_resp_send(req, response, strlen(response)); - return ESP_OK; -} - -esp_err_t start_mdns_service() { - esp_err_t err = mdns_init(); - if (err) { - return err; - } - - mdns_hostname_set(MDNS_DOMAIN); - mdns_instance_name_set("Thek4n PumpController"); - - mdns_service_add(NULL, "_http", "_tcp", 80, NULL, 0); - - return ESP_OK; -} - -esp_err_t adc_init(void) { - adc_oneshot_unit_init_cfg_t init_config = { - .unit_id = ADC_UNIT_1, - }; - esp_err_t ret = adc_oneshot_new_unit(&init_config, &adc_handle); - if (ret != ESP_OK) return ret; - - adc_oneshot_chan_cfg_t config = { - .atten = ADC_ATTEN_DB, - .bitwidth = ADC_BITWIDTH_DEFAULT, - }; - ret = adc_oneshot_config_channel(adc_handle, ADC_CHAN0, &config); - if (ret != ESP_OK) return ret; - - ret = adc_oneshot_config_channel(adc_handle, ADC_CHAN1, &config); - if (ret != ESP_OK) return ret; - - adc_cali_line_fitting_config_t cali_config = { - .unit_id = ADC_UNIT_1, - .atten = ADC_ATTEN_DB, - .bitwidth = ADC_BITWIDTH_DEFAULT, - }; - - ret = adc_cali_create_scheme_line_fitting(&cali_config, &cali_handle); - if (ret == ESP_OK) { - is_calibrated = true; - ESP_LOGI(TAG, "ADC success calibration"); - } else if (ret == ESP_ERR_NOT_SUPPORTED) { - ESP_LOGW(TAG, "Calibrating not available (eFuse doesn't written)"); - } else { - ESP_LOGE(TAG, "Error calibrating"); - } - - return ESP_OK; -} - -static esp_err_t pump_init(void) { - gpio_config_t io_conf = { - .pin_bit_mask = (1ULL << CONFIG_PUMP_PIN), - .mode = GPIO_MODE_INPUT_OUTPUT, - .intr_type = GPIO_INTR_DISABLE, - .pull_down_en = 1, - .pull_up_en = 0, - }; - - esp_err_t ret = gpio_config(&io_conf); - if (ret != ESP_OK) return ret; - - gpio_set_level(CONFIG_PUMP_PIN, false); - - return ESP_OK; -} - -int adc_read_raw(uint8_t channel) { - int raw_value = 0; - adc_channel_t adc_channel; - - if (channel == 0) { - adc_channel = ADC_CHAN0; - } else if (channel == 1) { - adc_channel = ADC_CHAN1; - } else { - ESP_LOGE(TAG, "Wrong ADC channel: %d", channel); - return -1; - } - - if (adc_handle == NULL) { - ESP_LOGE(TAG, "ADC not initialized"); - return -1; - } - - esp_err_t ret = adc_oneshot_read(adc_handle, adc_channel, &raw_value); - if (ret != ESP_OK) { - ESP_LOGE(TAG, "Error reading ADC"); - return -1; - } - return raw_value; -} - -int adc_read_voltage(uint8_t channel) { - int raw_value = adc_read_raw(channel); - if (raw_value < 0) return -1; - - if (is_calibrated) { - int voltage_mv = 0; - esp_err_t ret = adc_cali_raw_to_voltage(cali_handle, raw_value, &voltage_mv); - if (ret != ESP_OK) { - ESP_LOGE(TAG, "Error convert to voltage"); - return -1; - } - return voltage_mv; - } else { - return (raw_value * 3300) / 4095; - } -} - static esp_err_t root_get_handler(httpd_req_t *req) { httpd_resp_set_type(req, "text/html; charset=utf-8"); httpd_resp_send(req, (const char*)assets_index_html, HTTPD_RESP_USE_STRLEN); @@ -268,9 +668,7 @@ static esp_err_t current_pressure_handler(httpd_req_t *req) { } static esp_err_t pump_state_handler(httpd_req_t *req) { - int pump_enabled = gpio_get_level(CONFIG_PUMP_PIN); - - return send_json_response(req, "{\"state\":%d}", pump_enabled); + return send_json_response(req, "{\"state\":%d}", g_pump_enabled ? 1 : 0); } static esp_err_t save_thresholds_handler(httpd_req_t *req) { @@ -292,46 +690,21 @@ static esp_err_t save_thresholds_handler(httpd_req_t *req) { return ESP_FAIL; } - nvs_handle_t my_handle; - esp_err_t err = nvs_open(NVS_PARTITION, NVS_READWRITE, &my_handle); - if (err != ESP_OK) { - ESP_LOGE(TAG, "Error opening NVS"); + if (save_thresholds_to_nvs(low_value, up_value) != APP_ERR_OK) { + httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Failed to save to NVS"); return ESP_FAIL; } - err = nvs_set_i32(my_handle, THRESHOLD_LOW_NVS_NAME, low_value); - if (err != ESP_OK) { - nvs_close(my_handle); - httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Failed to save"); - return ESP_FAIL; - } - - err = nvs_set_i32(my_handle, THRESHOLD_UP_NVS_NAME, up_value); - if (err != ESP_OK) { - nvs_close(my_handle); - httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Failed to save"); - return ESP_FAIL; - } - - err = nvs_commit(my_handle); - if (err != ESP_OK) { - ESP_LOGE(TAG, "Failed to commit NVS: %s", esp_err_to_name(err)); - } - - nvs_close(my_handle); - atomic_store(&g_threshold_low, low_value); atomic_store(&g_threshold_up, up_value); - ESP_LOGI(TAG, "Thresholds saved: low: %d, up: %d", low_value, up_value); - + ESP_LOGI(TAG, "Thresholds saved: low=%d, up=%d", low_value, up_value); return send_json_response(req, "{\"success\":true,\"low\":%d,\"up\":%d}", low_value, up_value); } static esp_err_t get_thresholds_handler(httpd_req_t *req) { int low_threshold = atomic_load(&g_threshold_low); int up_threshold = atomic_load(&g_threshold_up); - return send_json_response(req, "{\"low\":%d,\"up\":%d}", low_threshold, up_threshold); } @@ -356,194 +729,14 @@ static esp_err_t set_thresholds_handler(httpd_req_t *req) { atomic_store(&g_threshold_low, low_value); atomic_store(&g_threshold_up, up_value); - ESP_LOGI(TAG, "Thresholds updated: low: %d, up: %d", low_value, up_value); + ESP_LOGI(TAG, "Thresholds updated: low=%d, up=%d", low_value, up_value); return send_json_response(req, "{\"success\":true,\"low\":%d,\"up\":%d}", low_value, up_value); } -void wifi_init_softap(void) { - ESP_ERROR_CHECK(esp_netif_init()); - ESP_ERROR_CHECK(esp_event_loop_create_default()); - esp_netif_t *ap_netif = esp_netif_create_default_wifi_ap(); - - wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); - ESP_ERROR_CHECK(esp_wifi_init(&cfg)); - - wifi_config_t wifi_config = { - .ap = { - .ssid = CONFIG_AP_WIFI_SSID, - .ssid_len = strlen(CONFIG_AP_WIFI_SSID), - .password = CONFIG_AP_WIFI_PASS, - .max_connection = AP_MAX_CONN, - .authmode = WIFI_AUTH_WPA_WPA2_PSK, - .channel = AP_CHANNEL, - }, - }; - - if (strlen(CONFIG_AP_WIFI_PASS) == 0) { - wifi_config.ap.authmode = WIFI_AUTH_OPEN; - } - - ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_AP)); - ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_AP, &wifi_config)); - ESP_ERROR_CHECK(esp_wifi_start()); - - esp_netif_ip_info_t ip_info; - ip_info.ip.addr = inet_addr(CONFIG_AP_IP); - ip_info.gw.addr = inet_addr(CONFIG_AP_GATEWAY); - ip_info.netmask.addr = inet_addr(CONFIG_AP_NETMASK); - - ESP_ERROR_CHECK(esp_netif_dhcps_stop(ap_netif)); - ESP_ERROR_CHECK(esp_netif_set_ip_info(ap_netif, &ip_info)); - ESP_ERROR_CHECK(esp_netif_dhcps_start(ap_netif)); -} - -static void pump_disable(void) { - gpio_set_level(CONFIG_PUMP_PIN, false); -} - -static void pump_enable(void) { - gpio_set_level(CONFIG_PUMP_PIN, true); -} - -static void vPumpControlTask(void *pvParameters) { - esp_task_wdt_add(xTaskGetCurrentTaskHandle()); - bool pump_enabled = false; - - while (1) { - int current_pressure = atomic_load(&g_current_pressure); - int low_threshold = atomic_load(&g_threshold_low); - int up_threshold = atomic_load(&g_threshold_up); - - if ((current_pressure < low_threshold) && (!pump_enabled)) { - pump_enable(); - pump_enabled = true; - ESP_LOGI(TAG, "Pump enabled"); - } else if ((current_pressure >= up_threshold) && pump_enabled) { - pump_disable(); - pump_enabled = false; - ESP_LOGI(TAG, "Pump disabled"); - } - - esp_task_wdt_reset(); - - vTaskDelay(pdMS_TO_TICKS(1000)); - } -} - -static int read_voltage_filtered(void) { - int sum = 0; - for (int i = 0; i < FILTER_SAMPLES; i++) { - sum += adc_read_voltage(SENSOR_ADC_CHAN); - vTaskDelay(pdMS_TO_TICKS(10)); - } - return sum / FILTER_SAMPLES; -} - -int map(int x, int in_min, int in_max, int out_min, int out_max) { - return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min; -} - -static int convert_adc_to_pressure_atm(int adc_value) { - return map(adc_value, 330, 3145, 0, 1184); -} - -static void vReadSensorTask(void *pvParameters) { - esp_task_wdt_add(xTaskGetCurrentTaskHandle()); - - while (1) { - int voltage = read_voltage_filtered(); - int pressure = convert_adc_to_pressure_atm(voltage); - atomic_store(&g_current_pressure, pressure); - - esp_task_wdt_reset(); - vTaskDelay(pdMS_TO_TICKS(1000)); - } -} - -static void register_http_handlers(httpd_handle_t server) { - httpd_uri_t root = { - .uri = "/", - .method = HTTP_GET, - .handler = root_get_handler, - .user_ctx = NULL - }; - if (httpd_register_uri_handler(server, &root) != ESP_OK) { - ESP_LOGE(TAG, "Failed to register GET / handler"); - } - - httpd_uri_t pressure = { - .uri = "/pressure", - .method = HTTP_GET, - .handler = current_pressure_handler, - .user_ctx = NULL - }; - if (httpd_register_uri_handler(server, &pressure) != ESP_OK) { - ESP_LOGE(TAG, "Failed to register GET /pressure handler"); - } - - httpd_uri_t pump_state = { - .uri = "/state", - .method = HTTP_GET, - .handler = pump_state_handler, - .user_ctx = NULL - }; - if (httpd_register_uri_handler(server, &pump_state) != ESP_OK) { - ESP_LOGE(TAG, "Failed to register GET /state handler"); - } - - httpd_uri_t get_thresholds = { - .uri = "/thresholds", - .method = HTTP_GET, - .handler = get_thresholds_handler, - .user_ctx = NULL - }; - if (httpd_register_uri_handler(server, &get_thresholds) != ESP_OK) { - ESP_LOGE(TAG, "Failed to register GET /thresholds handler"); - } - - httpd_uri_t set_thresholds = { - .uri = "/thresholds", - .method = HTTP_POST, - .handler = set_thresholds_handler, - .user_ctx = NULL - }; - if (httpd_register_uri_handler(server, &set_thresholds) != ESP_OK) { - ESP_LOGE(TAG, "Failed to register POST /thresholds handler"); - } - - httpd_uri_t save_thresholds = { - .uri = "/persist_thresholds", - .method = HTTP_POST, - .handler = save_thresholds_handler, - .user_ctx = NULL - }; - if (httpd_register_uri_handler(server, &save_thresholds) != ESP_OK) { - ESP_LOGE(TAG, "Failed to register POST /persist_thresholds handler"); - } -} - -static void vHttpServerTask(void *pvParameters) { - esp_task_wdt_add(xTaskGetCurrentTaskHandle()); - - httpd_handle_t server = NULL; - httpd_config_t config = HTTPD_DEFAULT_CONFIG(); - config.server_port = CONFIG_WEBINTERFACE_PORT; - config.max_uri_handlers = 10; - config.stack_size = 4096; - - if (httpd_start(&server, &config) == ESP_OK) { - ESP_LOGI(TAG, "🚀 HTTP server run on port %d", config.server_port); - register_http_handlers(server); - } else { - ESP_LOGE(TAG, "❌Error HTTP server running"); - } - - while (1) { - esp_task_wdt_reset(); - vTaskDelay(pdMS_TO_TICKS(1000)); - } -} +// ============================================================================ +// Setup mode HTTP handlers +// ============================================================================ static esp_err_t setup_get_handler(httpd_req_t *req) { httpd_resp_set_type(req, "text/html; charset=utf-8"); @@ -551,26 +744,10 @@ static esp_err_t setup_get_handler(httpd_req_t *req) { return ESP_OK; } -void save_wifi_config(const char *ssid, const char *password) { - nvs_handle_t nvs_handle; - esp_err_t err = nvs_open(NVS_PARTITION, NVS_READWRITE, &nvs_handle); - if (err != ESP_OK) { - ESP_LOGE(TAG, "Error opening NVS"); - return; - } - - nvs_set_str(nvs_handle, "wifi_ssid", ssid); - nvs_set_str(nvs_handle, "wifi_pass", password); - nvs_commit(nvs_handle); - nvs_close(nvs_handle); - - ESP_LOGI(TAG, "WiFi config saved: SSID=%s", ssid); -} - - static esp_err_t parse_wifi_settings_json(const char *content, char *ssid, char *password) { cJSON *json = cJSON_Parse(content); if (!json) { + handle_error(APP_ERR_JSON_PARSE_FAIL); return ESP_FAIL; } @@ -612,277 +789,204 @@ static esp_err_t setup_set_settings_handler(httpd_req_t *req) { } free(content); - send_json_response(req, "{\"success\":true}"); - save_wifi_config(ssid, password); vTaskDelay(pdMS_TO_TICKS(1000)); esp_restart(); return ESP_OK; } -static void vSetupHttpServerTask(void *pvParameters) { - esp_task_wdt_add(xTaskGetCurrentTaskHandle()); +// ============================================================================ +// HTTP server tasks +// ============================================================================ +static app_error_t http_server_start(void) { httpd_handle_t server = NULL; httpd_config_t config = HTTPD_DEFAULT_CONFIG(); config.server_port = CONFIG_WEBINTERFACE_PORT; config.max_uri_handlers = 10; config.stack_size = 4096; - if (httpd_start(&server, &config) != ESP_OK) { - ESP_LOGE(TAG, "❌ Error HTTP server running"); - } else { - ESP_LOGI(TAG, "🚀 Setup HTTP server run on port %d", config.server_port); + CHECK_ERROR(httpd_start(&server, &config), APP_ERR_HTTP_SERVER_START_FAIL); + ESP_LOGI(TAG, "HTTP server started on port %d", config.server_port); - httpd_uri_t root = { - .uri = "/", - .method = HTTP_GET, - .handler = setup_get_handler, - .user_ctx = NULL - }; - if (httpd_register_uri_handler(server, &root) != ESP_OK) { - ESP_LOGE(TAG, "Failed to register GET / handler"); - } + httpd_uri_t uris[] = { + {.uri = "/", .method = HTTP_GET, .handler = root_get_handler}, + {.uri = "/pressure", .method = HTTP_GET, .handler = current_pressure_handler}, + {.uri = "/state", .method = HTTP_GET, .handler = pump_state_handler}, + {.uri = "/thresholds", .method = HTTP_GET, .handler = get_thresholds_handler}, + {.uri = "/thresholds", .method = HTTP_POST, .handler = set_thresholds_handler}, + {.uri = "/persist_thresholds", .method = HTTP_POST, .handler = save_thresholds_handler}, + }; - httpd_uri_t settings = { - .uri = "/settings", - .method = HTTP_POST, - .handler = setup_set_settings_handler, - .user_ctx = NULL - }; - if (httpd_register_uri_handler(server, &settings) != ESP_OK) { - ESP_LOGE(TAG, "Failed to register GET /settings handler"); - } + for (int i = 0; i < sizeof(uris) / sizeof(httpd_uri_t); i++) { + CHECK_ERROR(httpd_register_uri_handler(server, &uris[i]), APP_ERR_HTTP_SERVER_START_FAIL); } + return ERR_OK; +} + +static app_error_t setup_http_server_start(void) { + httpd_handle_t server = NULL; + httpd_config_t config = HTTPD_DEFAULT_CONFIG(); + config.server_port = CONFIG_WEBINTERFACE_PORT; + config.max_uri_handlers = 10; + config.stack_size = 4096; + + CHECK_ERROR(httpd_start(&server, &config), APP_ERR_HTTP_SERVER_START_FAIL); + ESP_LOGI(TAG, "Setup HTTP server started on port %d", config.server_port); + + httpd_uri_t root = { + .uri = "/", .method = HTTP_GET, .handler = setup_get_handler + }; + CHECK_ERROR(httpd_register_uri_handler(server, &root), APP_ERR_HTTP_SERVER_START_FAIL); + + httpd_uri_t settings = { + .uri = "/settings", .method = HTTP_POST, .handler = setup_set_settings_handler + }; + CHECK_ERROR(httpd_register_uri_handler(server, &settings), APP_ERR_HTTP_SERVER_START_FAIL); + + return ERR_OK; +} + +// ============================================================================ +// Application tasks +// ============================================================================ + +static void vReadSensorTask(void *pvParameters) { + esp_task_wdt_add(xTaskGetCurrentTaskHandle()); + while (1) { + int voltage = read_voltage_filtered(); + if (voltage >= 0) { + int pressure = convert_adc_to_pressure_atm(voltage); + atomic_store(&g_current_pressure, pressure); + } esp_task_wdt_reset(); vTaskDelay(pdMS_TO_TICKS(1000)); } } -static void load_thresholds_from_nvs(void) { - nvs_handle_t my_handle; - esp_err_t err = nvs_open(NVS_PARTITION, NVS_READWRITE, &my_handle); - if (err != ESP_OK) { - ESP_LOGE(TAG, "Error opening NVS"); +static void vPumpControlTask(void *pvParameters) { + esp_task_wdt_add(xTaskGetCurrentTaskHandle()); + + while (1) { + int current_pressure = atomic_load(&g_current_pressure); + int low_threshold = atomic_load(&g_threshold_low); + int up_threshold = atomic_load(&g_threshold_up); + + if ((current_pressure < low_threshold) && !g_pump_enabled) { + pump_enable(); + ESP_LOGI(TAG, "Pump enabled (pressure=%d < low=%d)", current_pressure, low_threshold); + } else if ((current_pressure >= up_threshold) && g_pump_enabled) { + pump_disable(); + ESP_LOGI(TAG, "Pump disabled (pressure=%d >= up=%d)", current_pressure, up_threshold); + } + + esp_task_wdt_reset(); + vTaskDelay(pdMS_TO_TICKS(1000)); + } +} + +// ============================================================================ +// Tasks creation +// ============================================================================ + +static app_error_t tasks_create(void) { + CHECK_ERROR(xTaskCreate(vReadSensorTask, "read_sensor", 2048, NULL, PRIORITY_SENSOR, NULL) == pdPASS ? ESP_OK : ESP_FAIL, APP_ERR_TASK_CREATE_FAIL); + CHECK_ERROR(xTaskCreate(vPumpControlTask, "pump_control", 2048, NULL, PRIORITY_CONTROL, NULL) == pdPASS ? ESP_OK : ESP_FAIL, APP_ERR_TASK_CREATE_FAIL); + return ERR_OK; +} + +// ============================================================================ +// Mode initialization +// ============================================================================ + +static app_error_t setup_mode_init(void) { + g_setup_mode = true; + + CHECK_ERROR(wifi_softap_init(), APP_ERR_WIFI_INIT_FAIL); + CHECK_ERROR(setup_http_server_start(), APP_ERR_HTTP_SERVER_START_FAIL); + + ESP_LOGI(TAG, "========================================="); + ESP_LOGI(TAG, "Setup mode active"); + ESP_LOGI(TAG, "📱 Connect to Wi-Fi: %s", CONFIG_AP_WIFI_SSID); + ESP_LOGI(TAG, "🔑 Password: %s", strlen(CONFIG_AP_WIFI_PASS) ? CONFIG_AP_WIFI_PASS : "Open network"); + ESP_LOGI(TAG, "🌐 Open browser: http://%s:%d", CONFIG_AP_IP, CONFIG_WEBINTERFACE_PORT); + ESP_LOGI(TAG, "========================================="); + + return ERR_OK; +} + +static app_error_t normal_mode_init(void) { + g_setup_mode = false; + + CHECK_ERROR(load_thresholds(), APP_ERR_NVS_READ_FAIL); + CHECK_ERROR(adc_init(), APP_ERR_ADC_INIT_FAIL); + CHECK_ERROR(pump_init(), APP_ERR_PUMP_INIT_FAIL); + + nvs_handle_t nvs_handle; + CHECK_ERROR(nvs_open(NVS_PARTITION, NVS_READONLY, &nvs_handle), APP_ERR_NVS_OPEN_FAIL); + + char ssid[WIFI_SSID_MAX_LEN]; + char password[WIFI_PASS_MAX_LEN]; + size_t ssid_len = sizeof(ssid); + size_t pass_len = sizeof(password); + + CHECK_ERROR(nvs_get_str(nvs_handle, "wifi_ssid", ssid, &ssid_len), APP_ERR_NVS_READ_FAIL); + CHECK_ERROR(nvs_get_str(nvs_handle, "wifi_pass", password, &pass_len), APP_ERR_NVS_READ_FAIL); + nvs_close(nvs_handle); + + ESP_LOGI(TAG, "Connecting to WiFi: %s", ssid); + CHECK_ERROR(wifi_sta_init(ssid, password), APP_ERR_WIFI_CONNECT_FAIL); + + mdns_init_service(); + CHECK_ERROR(http_server_start(), APP_ERR_HTTP_SERVER_START_FAIL); + CHECK_ERROR(tasks_create(), APP_ERR_TASK_CREATE_FAIL); + + return ERR_OK; +} + +// ============================================================================ +// Main application +// ============================================================================ + +void app_main(void) { + // Initialize NVS + if (nvs_init() != APP_ERR_OK) { + handle_error(APP_ERR_NVS_INIT_FAIL); return; } - int32_t threshold_low = 0; - err = nvs_get_i32(my_handle, THRESHOLD_LOW_NVS_NAME, &threshold_low); - if (err == ESP_OK) { - atomic_store(&g_threshold_low, threshold_low); - } else if (err == ESP_ERR_NVS_NOT_FOUND) { - atomic_store(&g_threshold_low, 100); - } else { - ESP_ERROR_CHECK(err); - } - - int32_t threshold_up = 0; - err = nvs_get_i32(my_handle, THRESHOLD_UP_NVS_NAME, &threshold_up); - if (err == ESP_OK) { - atomic_store(&g_threshold_up, threshold_up); - } else if (err == ESP_ERR_NVS_NOT_FOUND) { - atomic_store(&g_threshold_up, 300); - } else { - ESP_ERROR_CHECK(err); - } - - nvs_close(my_handle); -} - -bool is_wifi_config_exists() { - nvs_handle_t nvs_handle; - esp_err_t err = nvs_open(NVS_PARTITION, NVS_READONLY, &nvs_handle); - if (err != ESP_OK) { - return false; - } - - char ssid[WIFI_SSID_MAX_LEN]; - size_t ssid_len = sizeof(ssid); - err = nvs_get_str(nvs_handle, "wifi_ssid", ssid, &ssid_len); - - nvs_close(nvs_handle); - - return (err == ESP_OK); -} - -// Сброс настроек и очистка NVS -void reset_settings() { - ESP_LOGI(TAG, "Resetting settings..."); - - // Очищаем NVS - nvs_flash_erase(); - nvs_flash_init(); - - ESP_LOGI(TAG, "Settings reset, restarting..."); - vTaskDelay(pdMS_TO_TICKS(500)); -} - -void indicate_led() { - for (int i = 0; i < 3; i++) { - gpio_set_level(LED_GPIO, 1); - vTaskDelay(pdMS_TO_TICKS(200)); - gpio_set_level(LED_GPIO, 0); - vTaskDelay(pdMS_TO_TICKS(200)); - } - -} - -// Обработчик событий WiFi -static void event_handler(void* arg, esp_event_base_t event_base, - int32_t event_id, void* event_data) { - if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) { - esp_wifi_connect(); - ESP_LOGI(TAG, "Attempting to connect to WiFi..."); - } - else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) { - if (retry_count < MAX_RETRY_COUNT) { - esp_wifi_connect(); - retry_count++; - ESP_LOGI(TAG, "Retry connecting (%d/%d)...", retry_count, MAX_RETRY_COUNT); - } else { - xEventGroupSetBits(wifi_event_group, WIFI_FAIL_BIT); - ESP_LOGE(TAG, "Failed to connect after %d retries", MAX_RETRY_COUNT); - - esp_restart(); - } - } - else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) { - ip_event_got_ip_t* event = (ip_event_got_ip_t*) event_data; - ESP_LOGI(TAG, "Got IP: " IPSTR, IP2STR(&event->ip_info.ip)); - retry_count = 0; - xEventGroupSetBits(wifi_event_group, WIFI_CONNECTED_BIT); - } -} - -void wifi_connect_sta(const char* ssid, const char* password) { - wifi_event_group = xEventGroupCreate(); - - ESP_ERROR_CHECK(esp_netif_init()); - ESP_ERROR_CHECK(esp_event_loop_create_default()); - esp_netif_create_default_wifi_sta(); - - wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); - ESP_ERROR_CHECK(esp_wifi_init(&cfg)); - - esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &event_handler, NULL); - esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &event_handler, NULL); - - wifi_config_t wifi_config = { - .sta = { - .ssid = "", - .password = "", - .threshold.authmode = WIFI_AUTH_WPA2_PSK, - .sae_pwe_h2e = WPA3_SAE_PWE_BOTH, - }, - }; - - strcpy((char*)wifi_config.sta.ssid, ssid); - strcpy((char*)wifi_config.sta.password, password); - - ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA)); - ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config)); - ESP_ERROR_CHECK(esp_wifi_start()); - - ESP_LOGI(TAG, "WiFi STA started"); - - EventBits_t bits = xEventGroupWaitBits(wifi_event_group, - WIFI_CONNECTED_BIT | WIFI_FAIL_BIT, - pdFALSE, - pdFALSE, - portMAX_DELAY); - - if (bits & WIFI_CONNECTED_BIT) { - ESP_LOGI(TAG, "Successfully connected to WiFi!"); - } else if (bits & WIFI_FAIL_BIT) { - ESP_LOGE(TAG, "Failed to connect to WiFi!"); - } -} - -void app_main(void) { - 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); - vTaskDelay(pdMS_TO_TICKS(50)); - gpio_config_t io_conf = { - .pin_bit_mask = (1ULL << LED_GPIO), - .mode = GPIO_MODE_OUTPUT, - .pull_up_en = GPIO_PULLUP_DISABLE, - .pull_down_en = GPIO_PULLUP_DISABLE, - .intr_type = GPIO_INTR_DISABLE, - }; - gpio_config(&io_conf); - gpio_set_level(LED_GPIO, 0); - - gpio_config_t btn_conf = { - .pin_bit_mask = (1ULL << RESET_BTN_GPIO), - .mode = GPIO_MODE_INPUT, - .pull_up_en = GPIO_PULLUP_ENABLE, - .pull_down_en = GPIO_PULLDOWN_DISABLE, - .intr_type = GPIO_INTR_DISABLE, - }; - gpio_config(&btn_conf); + // Initialize GPIO + if (gpio_init() != APP_ERR_OK) { + handle_error(APP_ERR_GPIO_CONFIG_FAIL); + return; + } vTaskDelay(pdMS_TO_TICKS(100)); + // Check reset button if (gpio_get_level(RESET_BTN_GPIO) == 0) { ESP_LOGI(TAG, "RESET button pressed, resetting settings..."); reset_settings(); indicate_led(); - vTaskDelay(pdMS_TO_TICKS(5000)); esp_restart(); return; } - bool should_run_setup_mode = !is_wifi_config_exists(); - if (should_run_setup_mode) { - wifi_init_softap(); - ESP_LOGI(TAG, "========================================="); - ESP_LOGI(TAG, "No WiFi settings found, starting setup mode"); - ESP_LOGI(TAG, "📱 Connect to Wi-Fi: %s", CONFIG_AP_WIFI_SSID); - ESP_LOGI(TAG, "🔑 Wi-Fi Password: %s", strlen(CONFIG_AP_WIFI_PASS) ? CONFIG_AP_WIFI_PASS : "Open network"); - ESP_LOGI(TAG, "🌐 Open browser: http://%s:%d", CONFIG_AP_IP, CONFIG_WEBINTERFACE_PORT); - ESP_LOGI(TAG, "========================================="); - - xTaskCreate(vSetupHttpServerTask, "setup_http_server", 8192, NULL, PRIORITY_HTTP, NULL); + // Start appropriate mode + if (!is_wifi_config_exists()) { + if (setup_mode_init() != APP_ERR_OK) { + handle_error(APP_ERR_WIFI_INIT_FAIL); + } } else { - ESP_LOGI(TAG, "WiFi settings found, starting normal mode"); - - load_thresholds_from_nvs(); - ESP_ERROR_CHECK(adc_init()); - ESP_ERROR_CHECK(pump_init()); - - nvs_handle_t nvs_handle; - nvs_open(NVS_PARTITION, NVS_READONLY, &nvs_handle); - - char ssid[WIFI_SSID_MAX_LEN]; - char password[WIFI_PASS_MAX_LEN]; - size_t ssid_len = sizeof(ssid); - size_t pass_len = sizeof(password); - - nvs_get_str(nvs_handle, "wifi_ssid", ssid, &ssid_len); - nvs_get_str(nvs_handle, "wifi_pass", password, &pass_len); - nvs_close(nvs_handle); - - ESP_LOGI(TAG, "Using saved SSID: %s", ssid); - wifi_connect_sta(ssid, password); - - ESP_ERROR_CHECK(start_mdns_service()); - - vTaskDelay(pdMS_TO_TICKS(1000)); - - xTaskCreate(vReadSensorTask, "read_sensor", 2048, NULL, PRIORITY_SENSOR, NULL); - xTaskCreate(vPumpControlTask, "pump_control", 2048, NULL, PRIORITY_CONTROL, NULL); - xTaskCreate(vHttpServerTask, "http_server", 8192, NULL, PRIORITY_HTTP, NULL); + if (normal_mode_init() != APP_ERR_OK) { + handle_error(APP_ERR_WIFI_CONNECT_FAIL); + } } vTaskDelete(NULL);