2026-06-08 14:23:22 +03:00

1324 lines
44 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include <stdint.h>
#include <string.h>
#include <stdio.h>
#include <stdatomic.h>
#include <stdarg.h>
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
#include <freertos/event_groups.h>
#include <freertos/projdefs.h>
#include <esp_system.h>
#include <esp_wifi.h>
#include <driver/gpio.h>
#include <esp_event.h>
#include <esp_log.h>
#include <nvs_flash.h>
#include <lwip/err.h>
#include <lwip/sys.h>
#include <cJSON.h>
#include "esp_http_server.h"
#include "esp_adc/adc_oneshot.h"
#include "esp_adc/adc_cali.h"
#include "esp_adc/adc_cali_scheme.h"
#include "esp_task_wdt.h"
#include "mdns.h"
#include "esp_wifi.h"
#include "esp_event.h"
#include "esp_log.h"
#include "nvs_flash.h"
#include "freertos/event_groups.h"
#include "lwip/inet.h"
#include "sdkconfig.h"
#include "frontend.h"
#include "setup_frontend.h"
#include "favicon_svg.h"
// ============================================================================
// Configuration validation
// ============================================================================
#if !defined(CONFIG_PUMP_PIN)
#error "CONFIG_PUMP_PIN must be defined in menuconfig"
#endif
#if !defined(CONFIG_AP_WIFI_SSID)
#error "CONFIG_AP_WIFI_SSID must be defined in menuconfig"
#endif
#if !defined(CONFIG_AP_IP) || !defined(CONFIG_AP_GATEWAY) || !defined(CONFIG_AP_NETMASK)
#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 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 FILTER_SAMPLES 5
#define PRIORITY_HTTP 1
#define PRIORITY_CONTROL 2
#define PRIORITY_SENSOR 3
#define MDNS_DOMAIN "pumpctl"
#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_CONNECTED_BIT BIT0
#define WIFI_FAIL_BIT BIT1
#define DEFAULT_THRESHOLD_LOW 0
#define DEFAULT_THRESHOLD_UP 300
#define MAX_AP_SCAN_RESULTS 20
// ============================================================================
// 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 = 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 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 bool g_wifi_test_in_progress = false;
// Дополните существующий wifi_event_handler:
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 (g_wifi_test_in_progress) {
// Если идет тестирование, сразу сигнализируем об ошибке
xEventGroupSetBits(wifi_event_group, WIFI_FAIL_BIT);
} else 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);
}
}
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);
}
}
bool test_wifi_credentials(const char* ssid, const char* password, int timeout_ms) {
wifi_config_t old_config;
esp_wifi_get_config(WIFI_IF_STA, &old_config);
if (wifi_event_group == NULL) {
wifi_event_group = xEventGroupCreate();
if (wifi_event_group == NULL) {
ESP_LOGE(TAG, "Failed to create event group");
return false;
}
}
// Очищаем предыдущие биты
xEventGroupClearBits(wifi_event_group, WIFI_CONNECTED_BIT | WIFI_FAIL_BIT);
// Устанавливаем флаг тестирования
g_wifi_test_in_progress = true;
// Останавливаем текущее подключение если есть
esp_wifi_disconnect();
vTaskDelay(pdMS_TO_TICKS(500));
// Настраиваем STA интерфейс с новыми credentials
wifi_config_t wifi_config = {0};
strncpy((char*)wifi_config.sta.ssid, ssid, sizeof(wifi_config.sta.ssid) - 1);
strncpy((char*)wifi_config.sta.password, password, sizeof(wifi_config.sta.password) - 1);
wifi_config.sta.threshold.authmode = WIFI_AUTH_WPA2_PSK;
esp_err_t ret = esp_wifi_set_config(WIFI_IF_STA, &wifi_config);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Failed to set WiFi config: %s", esp_err_to_name(ret));
g_wifi_test_in_progress = false;
return false;
}
// Запускаем подключение
ret = esp_wifi_connect();
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Failed to start WiFi connection: %s", esp_err_to_name(ret));
g_wifi_test_in_progress = false;
return false;
}
ESP_LOGI(TAG, "Testing WiFi connection to SSID: %s", ssid);
// Ожидаем результат
EventBits_t bits = xEventGroupWaitBits(wifi_event_group,
WIFI_CONNECTED_BIT | WIFI_FAIL_BIT,
pdFALSE,
pdFALSE,
timeout_ms / portTICK_PERIOD_MS);
bool success = (bits & WIFI_CONNECTED_BIT) != 0;
if (success) {
ESP_LOGI(TAG, "✓ Successfully connected to %s", ssid);
esp_wifi_disconnect();
esp_wifi_set_config(WIFI_IF_STA, &old_config);
vTaskDelay(pdMS_TO_TICKS(500));
} else {
ESP_LOGE(TAG, "✗ Failed to connect to %s", ssid);
}
g_wifi_test_in_progress = false;
return success;
}
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);
esp_netif_t *sta_netif = esp_netif_create_default_wifi_sta();
CHECK_PTR(sta_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);
esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &wifi_event_handler, NULL);
esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &wifi_event_handler, NULL);
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_APSTA), APP_ERR_WIFI_INIT_FAIL);
CHECK_ERROR(esp_wifi_set_config(WIFI_IF_AP, &wifi_config), APP_ERR_WIFI_INIT_FAIL);
wifi_config_t sta_config = {
.sta = {
.ssid = "",
.password = "",
.scan_method = WIFI_FAST_SCAN,
.sort_method = WIFI_CONNECT_AP_BY_SIGNAL,
.threshold.rssi = -127,
.threshold.authmode = WIFI_AUTH_OPEN,
},
};
CHECK_ERROR(esp_wifi_set_config(WIFI_IF_STA, &sta_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 favicon_get_handler(httpd_req_t *req) {
httpd_resp_set_type(req, "image/svg+xml");
httpd_resp_send(req, (const char*)assets_favicon_svg, assets_favicon_svg_len);
return ESP_OK;
}
static esp_err_t send_json_response(httpd_req_t *req, const char *format, ...) {
esp_err_t ret = ESP_OK;
char *response = NULL;
va_list args;
va_list args_copy;
va_start(args, format);
va_copy(args_copy, args);
int len = vsnprintf(NULL, 0, format, args_copy);
va_end(args_copy);
va_end(args);
if (len < 0) {
httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Format error");
return ESP_FAIL;
}
response = malloc(len + 1);
if (response == NULL) {
httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Out of memory");
return ESP_FAIL;
}
va_start(args, format);
vsnprintf(response, len + 1, format, args);
va_end(args);
httpd_resp_set_type(req, "application/json");
ret = httpd_resp_send(req, response, len);
free(response);
return ret;
}
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;
}
cJSON *low_item = cJSON_GetObjectItem(json, "low");
cJSON *up_item = cJSON_GetObjectItem(json, "up");
bool valid = cJSON_IsNumber(low_item) && cJSON_IsNumber(up_item);
if (valid) {
*low_value = low_item->valueint;
*up_value = up_item->valueint;
}
cJSON_Delete(json);
return valid ? ESP_OK : ESP_FAIL;
}
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, assets_index_html_len);
return ESP_OK;
}
static esp_err_t current_pressure_handler(httpd_req_t *req) {
int sensor_value = atomic_load(&g_current_pressure);
return send_json_response(req, "{\"value\":%d}", sensor_value);
}
static esp_err_t pump_state_handler(httpd_req_t *req) {
return send_json_response(req, "{\"state\":%d}", g_pump_enabled ? 1 : 0);
}
static esp_err_t save_thresholds_handler(httpd_req_t *req) {
char *content = NULL;
if (receive_http_content(req, &content) != ESP_OK) {
return ESP_FAIL;
}
int low_value = 0, up_value = 0;
if (parse_thresholds_json(content, &low_value, &up_value) != ESP_OK) {
free(content);
httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Missing or invalid 'low' or 'up' parameters");
return ESP_FAIL;
}
free(content);
if (low_value >= up_value) {
httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Low value must be less than up value");
return ESP_FAIL;
}
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;
}
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);
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);
}
static esp_err_t set_thresholds_handler(httpd_req_t *req) {
char *content = NULL;
if (receive_http_content(req, &content) != ESP_OK) {
return ESP_FAIL;
}
int low_value = 0, up_value = 0;
if (parse_thresholds_json(content, &low_value, &up_value) != ESP_OK) {
free(content);
httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Missing or invalid 'low' or 'up' parameters");
return ESP_FAIL;
}
free(content);
if (low_value >= up_value) {
httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Low value must be less than up value");
return ESP_FAIL;
}
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);
return send_json_response(req, "{\"success\":true,\"low\":%d,\"up\":%d}", low_value, up_value);
}
// ============================================================================
// 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");
httpd_resp_send(req, (const char*)assets_setup_html, assets_setup_html_len);
return ESP_OK;
}
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;
}
cJSON *ssid_item = cJSON_GetObjectItem(json, "ssid");
cJSON *pass_item = cJSON_GetObjectItem(json, "password");
if (!ssid_item || !pass_item ||
ssid_item->type != cJSON_String ||
pass_item->type != cJSON_String) {
cJSON_Delete(json);
return ESP_FAIL;
}
if (strlen(ssid_item->valuestring) >= WIFI_SSID_MAX_LEN ||
strlen(pass_item->valuestring) >= WIFI_PASS_MAX_LEN) {
cJSON_Delete(json);
return ESP_FAIL;
}
strlcpy(ssid, ssid_item->valuestring, WIFI_SSID_MAX_LEN);
strlcpy(password, pass_item->valuestring, WIFI_PASS_MAX_LEN);
cJSON_Delete(json);
return ESP_OK;
}
static esp_err_t setup_set_settings_handler(httpd_req_t *req) {
char *content = NULL;
if (receive_http_content(req, &content) != ESP_OK) {
httpd_resp_set_type(req, "application/json");
httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "{\"status\":\"error\",\"message\":\"Failed to receive content\"}");
return ESP_FAIL;
}
char ssid[WIFI_SSID_MAX_LEN];
char password[WIFI_PASS_MAX_LEN];
if (parse_wifi_settings_json(content, ssid, password) != ESP_OK) {
free(content);
httpd_resp_set_type(req, "application/json");
httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "{\"status\":\"error\",\"message\":\"Missing or invalid 'ssid' or 'password' parameters\"}");
return ESP_FAIL;
}
free(content);
// Проверяем, что пароль не пустой для защищенных сетей
if (strlen(ssid) == 0) {
httpd_resp_set_type(req, "application/json");
httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "{\"status\":\"error\",\"message\":\"SSID cannot be empty\"}");
return ESP_FAIL;
}
ESP_LOGI(TAG, "Testing WiFi connection to SSID: %s", ssid);
// Сохраняем текущий режим WiFi перед тестированием
wifi_mode_t current_mode;
esp_wifi_get_mode(&current_mode);
// Убеждаемся, что STA режим активен
if (!(current_mode & WIFI_MODE_STA)) {
esp_wifi_set_mode(WIFI_MODE_APSTA);
vTaskDelay(pdMS_TO_TICKS(100));
}
bool wifi_ok = test_wifi_credentials(ssid, password, 15000); // 15 секунд таймаут
if (wifi_ok) {
save_wifi_config(ssid, password);
httpd_resp_set_type(req, "application/json");
httpd_resp_set_status(req, HTTPD_200);
const char *success_response = "{\"status\":\"success\",\"message\":\"WiFi connected successfully! Rebooting in 2 seconds...\"}";
httpd_resp_send(req, success_response, strlen(success_response));
ESP_LOGI(TAG, "WiFi test SUCCESSFUL, rebooting...");
vTaskDelay(pdMS_TO_TICKS(2000));
esp_restart();
} else {
httpd_resp_set_type(req, "application/json");
const char *error_response = "{\"status\":\"error\",\"message\":\"Failed to connect to WiFi. Please check SSID and password.\"}";
httpd_resp_send(req, error_response, strlen(error_response));
ESP_LOGW(TAG, "WiFi test FAILED for SSID: %s", ssid);
}
return wifi_ok ? ESP_OK : ESP_FAIL;
}
static esp_err_t setup_get_wifi_list_handler(httpd_req_t *req) {
esp_err_t ret;
uint16_t ap_count = 0;
wifi_ap_record_t ap_info[MAX_AP_SCAN_RESULTS];
memset(ap_info, 0, sizeof(ap_info));
// Получаем текущий режим Wi-Fi
wifi_mode_t current_mode;
ret = esp_wifi_get_mode(&current_mode);
if (ret != ESP_OK) {
send_json_response(req, "{\"success\":false,\"error\":\"Failed to get WiFi mode\"}");
return ESP_OK;
}
// Запускаем сканирование
wifi_scan_config_t scan_config = {
.ssid = NULL,
.bssid = NULL,
.channel = 0,
.show_hidden = true,
.scan_type = WIFI_SCAN_TYPE_ACTIVE,
.scan_time = {
.active = {
.min = 100,
.max = 300
}
}
};
ret = esp_wifi_scan_start(&scan_config, true);
if (ret != ESP_OK) {
send_json_response(req, "{\"success\":false,\"error\":\"Failed to start WiFi scan\"}");
return ESP_OK;
}
ret = esp_wifi_scan_get_ap_num(&ap_count);
if (ret != ESP_OK) {
send_json_response(req, "{\"success\":false,\"error\":\"Failed to get AP count\"}");
return ESP_OK;
}
if (ap_count > MAX_AP_SCAN_RESULTS) {
ap_count = MAX_AP_SCAN_RESULTS;
}
ret = esp_wifi_scan_get_ap_records(&ap_count, ap_info);
if (ret != ESP_OK) {
send_json_response(req, "{\"success\":false,\"error\":\"Failed to get AP records\"}");
return ESP_OK;
}
cJSON *root = cJSON_CreateObject();
if (root == NULL) {
send_json_response(req, "{\"success\":false,\"error\":\"JSON creation failed\"}");
return ESP_OK;
}
cJSON_AddBoolToObject(root, "success", true);
cJSON *networks_array = cJSON_CreateArray();
if (networks_array == NULL) {
cJSON_Delete(root);
send_json_response(req, "{\"success\":false,\"error\":\"JSON array creation failed\"}");
return ESP_OK;
}
for (int i = 0; i < ap_count; i++) {
cJSON *network = cJSON_CreateObject();
if (network == NULL) {
continue;
}
char ssid_str[33];
memcpy(ssid_str, ap_info[i].ssid, 32);
ssid_str[32] = '\0';
cJSON_AddStringToObject(network, "ssid", ssid_str);
// RSSI (сигнал)
cJSON_AddNumberToObject(network, "rssi", ap_info[i].rssi);
// Тип шифрования (в читаемом виде)
const char *auth_mode_str;
switch (ap_info[i].authmode) {
case WIFI_AUTH_OPEN:
auth_mode_str = "Open";
break;
case WIFI_AUTH_WEP:
auth_mode_str = "WEP";
break;
case WIFI_AUTH_WPA_PSK:
auth_mode_str = "WPA-PSK";
break;
case WIFI_AUTH_WPA2_PSK:
auth_mode_str = "WPA2-PSK";
break;
case WIFI_AUTH_WPA_WPA2_PSK:
auth_mode_str = "WPA/WPA2-PSK";
break;
case WIFI_AUTH_WPA3_PSK:
auth_mode_str = "WPA3-PSK";
break;
case WIFI_AUTH_WPA2_WPA3_PSK:
auth_mode_str = "WPA2/WPA3-PSK";
break;
default:
auth_mode_str = "Unknown";
break;
}
cJSON_AddStringToObject(network, "auth_mode", auth_mode_str);
cJSON_AddItemToArray(networks_array, network);
}
cJSON_AddItemToObject(root, "networks", networks_array);
cJSON_AddNumberToObject(root, "count", ap_count);
char *json_string = cJSON_PrintUnformatted(root);
if (json_string == NULL) {
cJSON_Delete(root);
send_json_response(req, "{\"success\":false,\"error\":\"JSON string conversion failed\"}");
return ESP_OK;
}
send_json_response(req, json_string);
free(json_string);
cJSON_Delete(root);
return ESP_OK;
}
// ============================================================================
// 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;
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 uris[] = {
{.uri = "/", .method = HTTP_GET, .handler = root_get_handler},
{.uri = "/favicon.svg", .method = HTTP_GET, .handler = favicon_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},
};
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 = 8192;
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 uris[] = {
{.uri = "/", .method = HTTP_GET, .handler = setup_get_handler},
{.uri = "/favicon.svg", .method = HTTP_GET, .handler = favicon_get_handler},
{.uri = "/settings", .method = HTTP_POST, .handler = setup_set_settings_handler},
{.uri = "/wifi_list", .method = HTTP_GET, .handler = setup_get_wifi_list_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;
}
// ============================================================================
// 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 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 ap_normal_mode_init() {
CHECK_ERROR(wifi_softap_init(), APP_ERR_WIFI_INIT_FAIL);
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);
CHECK_ERROR(http_server_start(), APP_ERR_HTTP_SERVER_START_FAIL);
CHECK_ERROR(tasks_create(), APP_ERR_TASK_CREATE_FAIL);
ESP_LOGI(TAG, "=========================================");
ESP_LOGI(TAG, "AP normal 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;
}
vTaskDelay(pdMS_TO_TICKS(50));
// Initialize GPIO
if (gpio_init() != APP_ERR_OK) {
handle_error(APP_ERR_GPIO_CONFIG_FAIL);
return;
}
vTaskDelay(pdMS_TO_TICKS(100));
#if CONFIG_WIFI_AP
if (ap_normal_mode_init() != APP_ERR_OK) {
handle_error(APP_ERR_WIFI_INIT_FAIL);
}
#else // CONFIG_WIFI_AP
// 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;
}
// Start appropriate mode
if (!is_wifi_config_exists()) {
if (setup_mode_init() != APP_ERR_OK) {
handle_error(APP_ERR_WIFI_INIT_FAIL);
}
} else {
if (normal_mode_init() != APP_ERR_OK) {
handle_error(APP_ERR_WIFI_CONNECT_FAIL);
}
}
#endif // CONFIG_WIFI_AP
vTaskDelete(NULL);
}