// STD Lib #include // SDK #include "esp_log.h" #include "esp_ota_ops.h" #include "esp_psram.h" #include "esp_system.h" #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "nvs.h" #include "nvs_flash.h" #include "soc/gpio_num.h" // Project headers #include "appstate.hpp" #include "types.hpp" // Project cpp (Unity Build entry) // clang-format off #include "led_status.cpp" #include "connect.cpp" #include "http_server.cpp" // clang-format on internal constexpr bool kBlockUntilEthernetEstablished = false; extern "C" void app_main() { printf("Hello, Calendink OTA! [V1.1]\n"); printf("PSRAM size: %d bytes\n", esp_psram_get_size()); httpd_handle_t web_server = NULL; esp_err_t err = nvs_flash_init(); if (err == ESP_ERR_NVS_NO_FREE_PAGES || err == ESP_ERR_NVS_NEW_VERSION_FOUND) { ESP_ERROR_CHECK(nvs_flash_erase()); err = nvs_flash_init(); } ESP_ERROR_CHECK(err); nvs_handle_t my_handle; if (nvs_open("storage", NVS_READWRITE, &my_handle) == ESP_OK) { // Read active www partition from NVS err = nvs_get_u8(my_handle, "www_part", &g_Active_WWW_Partition); if (err == ESP_ERR_NVS_NOT_FOUND) { // First boot (no NVS key yet): default to www_0 // This ensures that after a fresh USB flash (which only writes www_0), // we start from the correct partition. printf("No www_part in NVS, defaulting to 0.\n"); g_Active_WWW_Partition = 0; nvs_set_u8(my_handle, "www_part", 0); nvs_commit(my_handle); } else if (err != ESP_OK) { printf("Error reading www_part from NVS: %s\n", esp_err_to_name(err)); g_Active_WWW_Partition = 0; } if (g_Active_WWW_Partition > 1) { g_Active_WWW_Partition = 0; } nvs_close(my_handle); } else { printf("Error opening NVS handle!\n"); } ESP_ERROR_CHECK(esp_event_loop_create_default()); setup_led(); set_led_status(led_status::ConnectingEthernet); g_Ethernet_Initialized = true; esp_err_t result = connect_ethernet(kBlockUntilEthernetEstablished); if (result != ESP_OK) { set_led_status(led_status::Failed); vTaskDelay(pdMS_TO_TICKS(1000)); goto shutdown; } // Check for ethernet connection until its made if (!kBlockUntilEthernetEstablished) { uint8 retries = 1; do { set_led_status(led_status::ConnectingEthernet); result = check_ethernet_connection(retries); if (result == ESP_ERR_INVALID_STATE) { printf("Ethernet cable not plugged in, skipping retries.\n"); break; } if (result != ESP_OK) { set_led_status(led_status::Failed); vTaskDelay(pdMS_TO_TICKS(1000)); } retries++; } while (result == ESP_ERR_TIMEOUT && retries <= CONFIG_CALENDINK_ETH_RETRIES); } if (result != ESP_OK) { printf("Ethernet failed, trying wifi\n"); disconnect_ethernet(); g_Ethernet_Initialized = false; set_led_status(led_status::ConnectingWifi); g_Wifi_Initialized = true; result = connect_wifi(CONFIG_CALENDINK_WIFI_SSID, CONFIG_CALENDINK_WIFI_PASSWORD, kBlockUntilEthernetEstablished); if (result != ESP_OK) { set_led_status(led_status::Failed); vTaskDelay(pdMS_TO_TICKS(1000)); goto shutdown; } if (!kBlockUntilEthernetEstablished) { uint8 retries = 1; do { set_led_status(led_status::ConnectingWifi); result = check_wifi_connection(retries); if (result != ESP_OK) { set_led_status(led_status::Failed); vTaskDelay(pdMS_TO_TICKS(1000)); } retries++; } while (result == ESP_ERR_TIMEOUT && retries <= CONFIG_CALENDINK_WIFI_RETRIES); } if (result != ESP_OK) { printf("Wifi failed.\n"); goto shutdown; } set_led_status(led_status::ReadyWifi); printf("Will use Wifi!\n"); } else { set_led_status(led_status::ReadyEthernet); printf("Will use Ethernet!\n"); } printf("Connected!\n"); // Mark the current app as valid to cancel rollback esp_ota_mark_app_valid_cancel_rollback(); // Start the webserver web_server = start_webserver(); // Keep the main task alive indefinitely while (true) { vTaskDelay(pdMS_TO_TICKS(1000)); } shutdown: printf("Shutting down.\n"); if (web_server) { stop_webserver(web_server); web_server = NULL; } if (g_Ethernet_Initialized) { disconnect_ethernet(); g_Ethernet_Initialized = false; } if (g_Wifi_Initialized) { disconnect_wifi(); g_Wifi_Initialized = false; } destroy_led(); ESP_ERROR_CHECK(esp_event_loop_delete_default()); ESP_ERROR_CHECK(nvs_flash_deinit()); }