#include "cli.hpp" #include #include #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "freertos/timers.h" #include "driver/uart.h" #include "esp_log.h" #include "config.hpp" static const char* TAG = "CLI"; #define CLI_UART_PORT UART_NUM_0 #define BUF_SIZE 1024 // Handle a silence timer for suppressing logs during cli actions static TimerHandle_t silence_timer = nullptr; // Dynamic variable holding the current mute runtime static uint32_t current_mute_time_sec = Config::DEFAULT_MUTE_TIME_SEC; static void log_restore_timer_callback(TimerHandle_t xTimer) { // Print a clean new prompt since logs might have disrupted the visual flow printf("\r\n[System] Debug logs unmuted after %lu seconds of inactivity. \r\n%s #\r\n", (unsigned long)current_mute_time_sec, Config::PROJECT_TITLE); fflush(stdout); // Now safely restore global logging back to INFO level after inactivity esp_log_level_set("*", ESP_LOG_INFO); } // Process input strings once the user presses Enter static void process_command(const char* cmd) { if (strlen(cmd) == 0) return; printf("\r\n"); // Move to new line after command execution if (strcmp(cmd, "help") == 0) { printf("Available commands:\r\n"); printf(" help - Show this command overview\r\n"); printf(" version - Display project title and version\r\n"); printf(" status - Print internal hardware statistics\r\n"); printf(" mute - Set custom logging timeout (Current: %lus)\r\n", (unsigned long)current_mute_time_sec); } else if (strcmp(cmd, "version") == 0) { printf("%s System - Running Version: %s\r\n", Config::PROJECT_TITLE, Config::PROJECT_VERSION); } else if (strcmp(cmd, "status") == 0) { printf("--- %s Status ---\r\n", Config::PROJECT_TITLE); printf("Free Internal Heap: %lu bytes\r\n", (unsigned long)esp_get_free_heap_size()); printf("Mute Timer Period : %lu seconds\r\n", (unsigned long)current_mute_time_sec); printf("Target CPU Board : ESP32\r\n"); } // NEW: Handle the "mute" parameter runtime configuration else if (strncmp(cmd, "mute ", 5) == 0) { // Extract the value after the string space int new_time = atoi(cmd + 5); if (new_time > 0 && new_time <= 3600) { // Limit between 1 second and 1 hour current_mute_time_sec = new_time; // Instantly update the running FreeRTOS timer period if (silence_timer != nullptr) { xTimerChangePeriod(silence_timer, pdMS_TO_TICKS(current_mute_time_sec * 1000), pdMS_TO_TICKS(10)); } printf("Success: Mute timer interval updated to %lu seconds.\r\n", (unsigned long)current_mute_time_sec); } else { printf("Error: Please provide a valid timeout between 1 and 3600 seconds.\r\n"); } } else { printf("Unknown command: '%s'. Type 'help' for options.\r\n", cmd); } // Print command prompt again printf("\r\n%s # ", Config::PROJECT_TITLE); fflush(stdout); } // Background listening loop for keyboard input static void cli_worker_task(void *pvParameters) { // Configure UART parameters for native monitoring uart_config_t uart_config = {}; uart_config.baud_rate = 115200; uart_config.data_bits = UART_DATA_8_BITS; uart_config.parity = UART_PARITY_DISABLE; uart_config.stop_bits = UART_STOP_BITS_1; uart_config.flow_ctrl = UART_HW_FLOWCTRL_DISABLE; uart_config.source_clk = UART_SCLK_DEFAULT; // Install UART driver without using resource-heavy interrupt queues ESP_ERROR_CHECK(uart_param_config(CLI_UART_PORT, &uart_config)); ESP_ERROR_CHECK(uart_driver_install(CLI_UART_PORT, BUF_SIZE * 2, 0, 0, NULL, 0)); char line_buffer[BUF_SIZE]; int buffer_index = 0; // Welcome message and initial prompt printf("\r\n=== Interactive %s CLI Console Started ===\r\n", Config::PROJECT_TITLE); printf("Type 'help' to get a list of active instructions.\r\n\r\n"); printf("%s # ", Config::PROJECT_TITLE); fflush(stdout); uint8_t data; while (1) { // Read character by character from serial interface (non-blocking with 20ms timeout) int len = uart_read_bytes(CLI_UART_PORT, &data, 1, pdMS_TO_TICKS(20)); if (len > 0) { esp_log_level_set("*", ESP_LOG_NONE); if (silence_timer != nullptr) { xTimerReset(silence_timer, pdMS_TO_TICKS(10)); } // Handle carriage return or line feed (Enter key pressed) if (data == '\r' || data == '\n') { line_buffer[buffer_index] = '\0'; // Null-terminate string process_command(line_buffer); buffer_index = 0; // Reset buffer index for next command } // Handle Backspace (ASCII 8 or 127) else if ((data == 8 || data == 127) && buffer_index > 0) { buffer_index--; printf("\b \b"); // Clear character visually in user terminal fflush(stdout); } // Add normal printable characters to buffer else if (data >= 32 && data <= 126 && buffer_index < (BUF_SIZE - 1)) { line_buffer[buffer_index++] = data; printf("%c", data); // Terminal Echo (prints what user types) fflush(stdout); } } // Minimal delay to keep watchdog and FreeRTOS internal background tasks happy vTaskDelay(pdMS_TO_TICKS(10)); } } void start_cli_task(void) { // Dynamically initialize the timer with the variable interval using milliseconds conversion silence_timer = xTimerCreate( "MuteTimer", pdMS_TO_TICKS(current_mute_time_sec * 1000), pdFALSE, nullptr, log_restore_timer_callback ); if (silence_timer == nullptr) { ESP_LOGE(TAG, "Failed to create FreeRTOS silence timer."); } xTaskCreatePinnedToCore( cli_worker_task, "CLI_InterfaceTask", 4096, // Slightly bigger stack to safely handle printf string buffers NULL, 5, NULL, 0 // Hosted on Core 0 to separate terminal interactive traffic from Core 1 ); }