#include "driver/uart.h" #include "console_completion.h" #include "esp_console.h" #include "esp_err.h" #include "esp_log.h" #include "esp_psram.h" #include "network_console.h" #include "rs232_hw_test.h" #include "rs232_port_owner.h" #include "secure_random.h" #include "serial_config.h" #include "serial_console.h" #include "serial_service.h" #include "session_broker.h" #include "session_console.h" #include "ssh_console.h" #include "ssh_security.h" #include "ssh_transport.h" #include "status_led.h" #include "system_console.h" #include "usb_cdc_transport.h" #include "usb_console.h" #include "web_console.h" #include "web_security.h" #include "web_server.h" #include "wifi_config.h" #include "wifi_console.h" #include "wifi_manager.h" #define CONSOLE_BAUD_RATE 115200 #define CONSOLE_TX_GPIO 43 #define CONSOLE_RX_GPIO 44 static const char *TAG = "firmware"; void app_main(void) { ESP_LOGI(TAG, "ESP32-S3 Serial Swiss Army Knife SSH transport phase started"); if (esp_psram_is_initialized()) { ESP_LOGI(TAG, "PSRAM initialized: %u bytes", (unsigned int)esp_psram_get_size()); } else { ESP_LOGW(TAG, "PSRAM is not initialized"); } /* Seed the sole device DRBG before any future RF, Bluetooth, or ADC use. */ esp_err_t random_error = secure_random_init(); if (random_error != ESP_OK) { ESP_LOGE(TAG, "Secure random initialization failed: %s", esp_err_to_name(random_error)); } /* Blue means the firmware is initialized and waiting for a console command. */ ESP_ERROR_CHECK(status_led_init()); ESP_ERROR_CHECK(rs232_port_owner_init()); ESP_ERROR_CHECK(rs232_hw_test_init()); serial_config_t serial_config; bool used_stored_config = false; esp_err_t config_error = serial_config_load(&serial_config, &used_stored_config); if (config_error != ESP_OK) { serial_config_defaults(&serial_config); ESP_LOGW( TAG, "NVS serial configuration unavailable (%s); using RAM defaults without erasing storage", esp_err_to_name(config_error)); } ESP_ERROR_CHECK(serial_service_init(&serial_config)); ESP_ERROR_CHECK(session_broker_init()); /* Native USB owns GPIO19/20; UART0 logging stays on the USB-to-UART bridge. */ ESP_ERROR_CHECK(usb_cdc_transport_init()); /* Provision HTTPS identity before Wi-Fi starts; failures leave UART/USB recovery intact. */ web_security_load_result_t web_security_source = WEB_SECURITY_LOAD_STORED; esp_err_t web_security_error = random_error; if (web_security_error == ESP_OK) { web_security_error = web_security_init(&web_security_source); } if (web_security_error != ESP_OK) { ESP_LOGE(TAG, "HTTPS security material unavailable (%s); use UART0 'web reset --force' to replace it", esp_err_to_name(web_security_error)); } else { ESP_LOGI(TAG, "Using %s HTTPS identity and administrative credential", web_security_source == WEB_SECURITY_LOAD_STORED ? "stored" : "newly generated"); } esp_err_t web_runtime_error = web_server_init(); if (web_runtime_error != ESP_OK) { ESP_LOGE(TAG, "HTTPS runtime initialization failed: %s", esp_err_to_name(web_runtime_error)); } ssh_security_load_result_t ssh_security_source = SSH_SECURITY_LOAD_STORED; esp_err_t ssh_security_error = random_error; if (ssh_security_error == ESP_OK) { ssh_security_error = ssh_security_init(&ssh_security_source); } if (ssh_security_error != ESP_OK) { ESP_LOGE(TAG, "SSH host key unavailable (%s); use UART0 'ssh reset --force' to replace it", esp_err_to_name(ssh_security_error)); } else { ESP_LOGI(TAG, "Using %s SSH host key", ssh_security_source == SSH_SECURITY_LOAD_STORED ? "stored" : "newly generated"); } esp_err_t ssh_runtime_error = ssh_transport_init(); if (ssh_runtime_error != ESP_OK) { ESP_LOGE(TAG, "SSH runtime initialization failed: %s", esp_err_to_name(ssh_runtime_error)); } wifi_app_config_t wifi_config; wifi_config_load_source_t wifi_config_source; esp_err_t wifi_config_error = random_error; if (wifi_config_error == ESP_OK) { wifi_config_error = wifi_config_load(&wifi_config, &wifi_config_source); } if (wifi_config_error != ESP_OK) { ESP_LOGW(TAG, "NVS Wi-Fi configuration unavailable (%s); trying generated RAM defaults", esp_err_to_name(wifi_config_error)); wifi_config_error = wifi_config_defaults(&wifi_config); } else if (wifi_config_source == WIFI_CONFIG_LOAD_GENERATED_MISSING) { /* Persist the random per-device fallback-AP credential on true first boot. */ esp_err_t save_error = wifi_config_save(&wifi_config); if (save_error != ESP_OK) { ESP_LOGW(TAG, "Could not persist initial Wi-Fi defaults: %s", esp_err_to_name(save_error)); } } else if (wifi_config_source == WIFI_CONFIG_LOAD_GENERATED_INVALID) { ESP_LOGW(TAG, "Stored Wi-Fi configuration is incompatible; using RAM defaults without overwriting it"); } esp_err_t wifi_error = wifi_config_error; if (wifi_config_error == ESP_OK) { wifi_error = wifi_manager_init(&wifi_config); if (wifi_error == ESP_OK && wifi_config.enabled_at_boot != 0U) { wifi_error = wifi_manager_start(); } if (wifi_error != ESP_OK) { /* UART0 and native USB remain recovery paths if networking is unavailable. */ ESP_LOGE(TAG, "Wi-Fi manager unavailable: %s", esp_err_to_name(wifi_error)); } else { ESP_LOGI(TAG, "Using %s Wi-Fi configuration; AP policy=%s", wifi_config_source == WIFI_CONFIG_LOAD_STORED ? "stored" : "generated default", wifi_config_ap_policy_to_string(wifi_config.ap_policy)); } } else { ESP_LOGE(TAG, "Could not create a valid Wi-Fi configuration: %s", esp_err_to_name(wifi_config_error)); } wifi_config_secure_wipe(&wifi_config, sizeof(wifi_config)); if (wifi_error == ESP_OK && web_security_error == ESP_OK && web_runtime_error == ESP_OK) { esp_err_t start_error = web_server_start(); if (start_error != ESP_OK) { ESP_LOGE(TAG, "HTTPS startup failed: %s; UART0 recovery remains available", esp_err_to_name(start_error)); } else { ESP_LOGI(TAG, "Authenticated HTTPS listening on TCP port 443"); } } if (wifi_error == ESP_OK && web_security_error == ESP_OK && ssh_security_error == ESP_OK && ssh_runtime_error == ESP_OK) { esp_err_t start_error = ssh_transport_start(); if (start_error != ESP_OK) { ESP_LOGE(TAG, "SSH startup failed: %s; UART0 recovery remains available", esp_err_to_name(start_error)); } else { ESP_LOGI(TAG, "Authenticated SSH listening on TCP port %u", SSH_TRANSPORT_PORT); } } ESP_LOGI( TAG, "Using %s serial configuration; UART service starts on 'serial start' or native USB open", used_stored_config ? "stored" : "default"); esp_console_repl_config_t repl_config = ESP_CONSOLE_REPL_CONFIG_DEFAULT(); repl_config.prompt = "serial-tool> "; repl_config.max_cmdline_length = 160; repl_config.task_stack_size = 8192; /* * UART0 remains dedicated to development and diagnostics. The external * RS-232 data path uses UART1 on GPIO17/18 and cannot disturb this REPL. */ esp_console_dev_uart_config_t uart_config = ESP_CONSOLE_DEV_UART_CONFIG_DEFAULT(); uart_config.channel = UART_NUM_0; uart_config.baud_rate = CONSOLE_BAUD_RATE; uart_config.tx_gpio_num = CONSOLE_TX_GPIO; uart_config.rx_gpio_num = CONSOLE_RX_GPIO; esp_console_repl_t *repl = NULL; ESP_ERROR_CHECK(esp_console_new_repl_uart(&uart_config, &repl_config, &repl)); /* The REPL constructor initializes esp_console and installs `help`. */ ESP_ERROR_CHECK(rs232_hw_test_register_console_commands()); ESP_ERROR_CHECK(serial_console_register_commands()); ESP_ERROR_CHECK(session_console_register_commands()); ESP_ERROR_CHECK(usb_console_register_commands()); ESP_ERROR_CHECK(wifi_console_register_commands()); ESP_ERROR_CHECK(web_console_register_commands()); ESP_ERROR_CHECK(ssh_console_register_commands()); ESP_ERROR_CHECK(network_console_register_root_commands()); ESP_ERROR_CHECK(system_console_register_commands()); /* Upgrade late UART terminals safely and add nested completion. */ console_completion_install(); ESP_ERROR_CHECK(esp_console_start_repl(repl)); ESP_LOGI(TAG, "Interactive test console ready at %d baud", CONSOLE_BAUD_RATE); ESP_LOGI(TAG, "Type 'help' for commands; native USB starts UART1 only when its host port opens"); }