/* * main.c * * Copyright (c) 2025 Umber Networks & Robert McMahon * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #include #include #include // Added for gettimeofday #include // Added for time structs #include "sdkconfig.h" #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "esp_system.h" #include "esp_err.h" #include "esp_log.h" #include "esp_console.h" #include "esp_vfs_dev.h" #include "driver/uart.h" #include "nvs_flash.h" #include "nvs.h" #include "esp_netif.h" #include "esp_event.h" // Components #include "status_led.h" #include "board_config.h" #include "gps_sync.h" #include "wifi_controller.h" #include "wifi_cfg.h" #include "app_console.h" #include "iperf.h" #ifdef CONFIG_ESP_WIFI_CSI_ENABLED #include "csi_log.h" #include "csi_manager.h" #endif #define APP_VERSION "2.1.0-CONSOLE-DEBUG" static const char *TAG = "MAIN"; // --- Global Prompt Buffer (Mutable) --- static char s_cli_prompt[32] = "esp32> "; // --- Custom Log Formatter (Epoch Timestamp) --- // This ensures logs match tcpdump/wireshark format: [Seconds.Microseconds] static int custom_log_vprintf(const char *fmt, va_list args) { struct timeval tv; gettimeofday(&tv, NULL); // Print [1766437791.123456] prefix printf("[%ld.%06ld] ", (long)tv.tv_sec, (long)tv.tv_usec); return vprintf(fmt, args); } // --- Prompt Updater --- void app_console_update_prompt(void) { bool dirty = false; if (wifi_ctl_param_is_unsaved()) dirty = true; if (iperf_param_is_unsaved()) dirty = true; if (dirty) { snprintf(s_cli_prompt, sizeof(s_cli_prompt), "esp32*> "); } else { snprintf(s_cli_prompt, sizeof(s_cli_prompt), "esp32> "); } } // --- Helper: Check NVS for GPS Enable --- static bool is_gps_enabled(void) { nvs_handle_t h; uint8_t val = 1; // Default to Enabled (1) // Check 'storage' namespace first (where iperf/system settings live) if (nvs_open("storage", NVS_READONLY, &h) == ESP_OK) { if (nvs_get_u8(h, "gps_enabled", &val) != ESP_OK) { val = 1; // Key missing = Enabled } nvs_close(h); } return (val != 0); } // --- System Commands --- static int cmd_restart(int argc, char **argv) { ESP_LOGI(TAG, "Restarting..."); esp_restart(); return 0; } static int cmd_version(int argc, char **argv) { printf("APP_VERSION: %s\n", APP_VERSION); printf("IDF_VERSION: %s\n", esp_get_idf_version()); return 0; } static void register_system_common(void) { const esp_console_cmd_t restart_cmd = { .command = "reset", .help = "Software reset of the device", .func = &cmd_restart }; ESP_ERROR_CHECK(esp_console_cmd_register(&restart_cmd)); const esp_console_cmd_t version_cmd = { .command = "version", .help = "Get firmware version", .func = &cmd_version }; ESP_ERROR_CHECK(esp_console_cmd_register(&version_cmd)); } // --- Main Application --- void app_main(void) { // 1. Initialize NVS 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); // 2. Register Custom Log Formatter (Epoch Time) // Must be done before any logs are printed to ensure consistency esp_log_set_vprintf(custom_log_vprintf); // 3. Initialize Netif & Event Loop ESP_ERROR_CHECK(esp_netif_init()); ESP_ERROR_CHECK(esp_event_loop_create_default()); // ------------------------------------------------------------- // GPS Initialization (Conditional) // ------------------------------------------------------------- if (is_gps_enabled()) { const gps_sync_config_t gps_cfg = { .uart_port = UART_NUM_1, .tx_pin = GPS_TX_PIN, .rx_pin = GPS_RX_PIN, .pps_pin = GPS_PPS_PIN, }; gps_sync_init(&gps_cfg, true); } else { ESP_LOGW(TAG, "GPS initialization skipped (Disabled in NVS)"); } // Hardware Init status_led_init(RGB_LED_GPIO, HAS_RGB_LED); status_led_set_state(LED_STATE_FAILED); // Force Red Blink #ifdef CONFIG_ESP_WIFI_CSI_ENABLED ESP_ERROR_CHECK(csi_log_init()); csi_mgr_init(); #endif // 5. Initialize WiFi Controller & iPerf wifi_ctl_init(); iperf_param_init(); // 6. Initialize Console (REPL) ESP_LOGI(TAG, "Initializing console REPL..."); esp_console_repl_t *repl = NULL; esp_console_repl_config_t repl_config = ESP_CONSOLE_REPL_CONFIG_DEFAULT(); repl_config.prompt = s_cli_prompt; repl_config.max_cmdline_length = 1024; esp_console_dev_uart_config_t hw_config = ESP_CONSOLE_DEV_UART_CONFIG_DEFAULT(); esp_err_t repl_init_err = esp_console_new_repl_uart(&hw_config, &repl_config, &repl); if (repl_init_err != ESP_OK) { ESP_LOGE(TAG, "Failed to create console REPL: %s", esp_err_to_name(repl_init_err)); esp_restart(); } ESP_LOGI(TAG, "Console REPL object created successfully"); // 7. Register Commands ESP_LOGI(TAG, "Registering console commands..."); register_system_common(); app_console_register_commands(); ESP_LOGI(TAG, "Console commands registered"); // 8. Initial Prompt State Check app_console_update_prompt(); // 9. Start Shell ESP_LOGI(TAG, "Starting console REPL..."); printf("\n ==================================================\n"); printf(" | ESP32 iPerf Shell - Ready |\n"); printf(" | Type 'help' for commands |\n"); printf(" ==================================================\n"); fflush(stdout); esp_err_t repl_err = esp_console_start_repl(repl); if (repl_err != ESP_OK) { ESP_LOGE(TAG, "Failed to start console REPL: %s", esp_err_to_name(repl_err)); esp_restart(); } // Note: esp_console_start_repl() blocks and never returns on success // so code below would never execute }