#include "esp_event.h" #include "esp_log.h" #include "esp_netif.h" #include "esp_sntp.h" #include "esp_system.h" #include "esp_timer.h" #include "nvs_flash.h" #include "mqtt.h" #include "wifi.h" #include #include static const char *TAG = "app"; static EventGroupHandle_t wifi_event_group; #define WIFI_CONNECTED_BIT BIT0 /** * @brief Initialize NVS (Non-Volatile Storage) flash storage, handling full or * incompatible pages. * * This function checks the NVS flash storage to ensure that there is enough * space and that the version of NVS is compatible. If the partition is full or * incompatible, it will erase the NVS partition and attempt initialization * again. * * @return esp_err_t * - ESP_OK: Initialization successful. * - Other error codes if initialization or erasure fails. */ static esp_err_t nvs_flash_init_check(void) { esp_err_t ret = nvs_flash_init(); if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) { ESP_LOGW(TAG, "NVS partition is full or incompatible, erasing..."); ESP_ERROR_CHECK(nvs_flash_erase()); ret = nvs_flash_init(); } if (ret != ESP_OK) ESP_LOGE(TAG, "Failed to initialize NVS (%s)", esp_err_to_name(ret)); return ret; } /** * @brief General helper function to initialize a given component and log * errors. * * This function takes an initialization function and a component name, attempts * to initialize the component, and logs an error if initialization fails. * * @param init_func A function pointer to the initialization function of the * component. * @param component_name A string describing the name of the component being * initialized. * * @return esp_err_t * - ESP_OK: If initialization is successful. * - Other error codes if initialization fails. */ 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(); } else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) { ESP_LOGW(TAG, "Wi-Fi disconnected, reconnecting..."); esp_wifi_connect(); xEventGroupClearBits(wifi_event_group, WIFI_CONNECTED_BIT); } else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) { ESP_LOGI(TAG, "Wi-Fi connected and got IP!"); xEventGroupSetBits(wifi_event_group, WIFI_CONNECTED_BIT); } } /** * @TODO missing doc */ static void obtain_time(void) { ESP_LOGI(TAG, "Starting SNTP time sync..."); sntp_setoperatingmode(SNTP_OPMODE_POLL); sntp_setservername(0, "pool.ntp.org"); sntp_init(); time_t now = 0; struct tm timeinfo = {0}; int retry = 0; const int retry_count = 10; while (timeinfo.tm_year < (2022 - 1900) && ++retry < retry_count) { ESP_LOGI(TAG, "Waiting for system time to be set... (%d/%d)", retry, retry_count); vTaskDelay(pdMS_TO_TICKS(1000)); time(&now); localtime_r(&now, &timeinfo); } if (timeinfo.tm_year >= (2022 - 1900)) { char buf[64]; strftime(buf, sizeof(buf), "%c", &timeinfo); ESP_LOGI(TAG, "System time synchronized: %s", buf); } else { ESP_LOGW(TAG, "Failed to synchronize time."); } } /** * @brief Main entry point of the application. * * This function is responsible for initializing the core components of the * application, including the NVS storage, network interface, and event loop. If * any initialization step fails, the application will log the error and stop. * * After initialization, this function will configure the WiFi station and start * the MQTT client. */ void app_main(void) { ESP_ERROR_CHECK(nvs_flash_init_check()); ESP_ERROR_CHECK(esp_netif_init()); ESP_ERROR_CHECK(esp_event_loop_create_default()); wifi_event_group = xEventGroupCreate(); ESP_LOGI(TAG, "Initializing WiFi (station mode)"); wifi_init_sta(); ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &wifi_event_handler, NULL)); ESP_ERROR_CHECK(esp_event_handler_register( IP_EVENT, IP_EVENT_STA_GOT_IP, &wifi_event_handler, NULL)); ESP_LOGI(TAG, "Waiting for Wi-Fi connection..."); xEventGroupWaitBits(wifi_event_group, WIFI_CONNECTED_BIT, pdFALSE, pdFALSE, portMAX_DELAY); obtain_time(); ESP_LOGI(TAG, "Starting MQTT client..."); mqtt_app_start(); }