#include "serial_console.h" #include #include #include #include #include #include "esp_console.h" #include "esp_err.h" #include "rs232_port_owner.h" #include "serial_config.h" #include "serial_service.h" static void print_usage(void) { printf("Usage:\n"); printf(" serial status\n"); printf(" serial start|stop\n"); printf(" serial set \n"); printf(" serial save|load|defaults|reset\n"); printf(" serial counters|clear-counters\n"); } static void print_config(const serial_config_t *config) { printf("Configuration v%lu: baud=%lu, data-bits=%s, parity=%s, stop-bits=%s, flow=%s, DTR=%s, RTS-threshold=%lu\n", (unsigned long)config->version, (unsigned long)config->baud_rate, serial_config_data_bits_to_string(config->data_bits), serial_config_parity_to_string(config->parity), serial_config_stop_bits_to_string(config->stop_bits), serial_config_flow_control_to_string(config->flow_control), serial_config_dtr_behavior_to_string(config->dtr_behavior), (unsigned long)config->rts_threshold); } static bool parse_unsigned(const char *text, uint32_t minimum, uint32_t maximum, uint32_t *value) { if (text == NULL || *text == '\0') { return false; } for (const char *character = text; *character != '\0'; ++character) { if (*character < '0' || *character > '9') { return false; } } char *end = NULL; errno = 0; unsigned long parsed = strtoul(text, &end, 10); if (errno != 0 || end == text || *end != '\0' || parsed < minimum || parsed > maximum) { return false; } *value = (uint32_t)parsed; return true; } static int show_status(void) { serial_config_t config; esp_err_t err = serial_service_get_config(&config); if (err != ESP_OK) { printf("Could not read serial configuration: %s\n", esp_err_to_name(err)); return 1; } bool running = serial_service_is_running(); rs232_port_owner_t owner = rs232_port_get_owner(); printf("UART service: %s\n", running ? "running" : "stopped"); printf("RS-232 port owner: %s\n", rs232_port_owner_to_string(owner)); print_config(&config); printf("Buffers: RX-available=%u TX-pending=%u\n", (unsigned int)serial_service_rx_available(), (unsigned int)serial_service_tx_pending()); if (running) { serial_modem_state_t modem; serial_service_get_modem_state(&modem); printf("Modem asserted: DCD=%d DSR=%d CTS=%d RI=%d; valid-voltage VLD=%d\n", modem.dcd, modem.dsr, modem.cts, modem.ri, modem.valid); } else if (owner == RS232_PORT_OWNER_NONE) { printf("Phase 0 hardware commands are available while the service is stopped.\n"); } else { printf("Phase 0 commands are unavailable until the current owner releases the port.\n"); } return 0; } static int show_counters(void) { serial_service_counters_t counters; serial_service_get_counters(&counters); printf("Data: RX=%" PRIu64 " RX-dropped=%" PRIu64 " TX-queued=%" PRIu64 " TX-to-UART=%" PRIu64 " TX-dropped=%" PRIu64 "\n", counters.rx_bytes, counters.rx_dropped_bytes, counters.tx_queued_bytes, counters.tx_sent_to_uart_bytes, counters.tx_dropped_bytes); printf("UART errors: frame=%" PRIu64 " parity=%" PRIu64 " FIFO-overflow=%" PRIu64 " buffer-full=%" PRIu64 " break=%" PRIu64 "\n", counters.frame_errors, counters.parity_errors, counters.fifo_overflows, counters.buffer_full_events, counters.breaks); printf("Modem transitions: DCD=%" PRIu64 " DSR=%" PRIu64 " CTS=%" PRIu64 " RI=%" PRIu64 " VLD=%" PRIu64 "\n", counters.dcd_transitions, counters.dsr_transitions, counters.cts_transitions, counters.ri_transitions, counters.valid_transitions); return 0; } static int set_parameter(const char *parameter, const char *value) { serial_config_t candidate; esp_err_t err = serial_service_get_config(&candidate); if (err != ESP_OK) { printf("Could not read current configuration: %s\n", esp_err_to_name(err)); return 1; } if (strcmp(parameter, "baud") == 0) { if (!parse_unsigned( value, SERIAL_CONFIG_MIN_BAUD_RATE, SERIAL_CONFIG_MAX_BAUD_RATE, &candidate.baud_rate)) { printf("Baud rate must be %u..%u.\n", SERIAL_CONFIG_MIN_BAUD_RATE, SERIAL_CONFIG_MAX_BAUD_RATE); return 1; } } else if (strcmp(parameter, "data-bits") == 0) { if (!serial_config_parse_data_bits(value, &candidate.data_bits)) { printf("Data bits must be 7 or 8.\n"); return 1; } } else if (strcmp(parameter, "parity") == 0) { if (!serial_config_parse_parity(value, &candidate.parity)) { printf("Parity must be none, even, or odd.\n"); return 1; } } else if (strcmp(parameter, "stop-bits") == 0) { if (!serial_config_parse_stop_bits(value, &candidate.stop_bits)) { printf("Stop bits must be 1 or 2.\n"); return 1; } } else if (strcmp(parameter, "flow") == 0) { if (!serial_config_parse_flow_control(value, &candidate.flow_control)) { printf("Flow control must be none or rts-cts.\n"); return 1; } } else if (strcmp(parameter, "dtr") == 0) { if (!serial_config_parse_dtr_behavior(value, &candidate.dtr_behavior)) { printf("DTR behavior must be inactive, active, or on-connect.\n"); return 1; } } else if (strcmp(parameter, "rts-threshold") == 0) { if (!parse_unsigned( value, 1, SERIAL_CONFIG_MAX_RTS_THRESHOLD, &candidate.rts_threshold)) { printf("RTS threshold must be 1..%u.\n", SERIAL_CONFIG_MAX_RTS_THRESHOLD); return 1; } } else { printf("Unknown serial parameter '%s'.\n", parameter); print_usage(); return 1; } err = serial_service_apply_config(&candidate); if (err != ESP_OK) { printf("Could not apply configuration: %s\n", esp_err_to_name(err)); return 1; } print_config(&candidate); printf("Applied in RAM%s; run 'serial save' to persist it.\n", serial_service_is_running() ? " after a controlled UART restart" : ""); return 0; } static int command_serial(int argc, char **argv) { if (argc == 1 || (argc == 2 && strcmp(argv[1], "status") == 0)) { return show_status(); } if (argc == 2 && strcmp(argv[1], "start") == 0) { esp_err_t err = serial_service_start(); if (err != ESP_OK) { printf("Could not start UART service: %s\n", esp_err_to_name(err)); return 1; } return show_status(); } if (argc == 2 && strcmp(argv[1], "stop") == 0) { esp_err_t err = serial_service_stop(); if (err != ESP_OK) { printf("Could not stop UART service: %s\n", esp_err_to_name(err)); return 1; } return show_status(); } if (argc == 4 && strcmp(argv[1], "set") == 0) { return set_parameter(argv[2], argv[3]); } if (argc == 2 && strcmp(argv[1], "save") == 0) { serial_config_t config; esp_err_t err = serial_service_get_config(&config); if (err == ESP_OK) { err = serial_config_save(&config); } if (err != ESP_OK) { printf("Could not save configuration: %s\n", esp_err_to_name(err)); return 1; } printf("Serial configuration saved to NVS.\n"); return 0; } if (argc == 2 && strcmp(argv[1], "load") == 0) { serial_config_t config; bool used_stored_config; esp_err_t err = serial_config_load(&config, &used_stored_config); if (err == ESP_OK) { err = serial_service_apply_config(&config); } if (err != ESP_OK) { printf("Could not load configuration: %s\n", esp_err_to_name(err)); return 1; } printf("Loaded %s configuration.\n", used_stored_config ? "stored" : "default"); print_config(&config); return 0; } if (argc == 2 && strcmp(argv[1], "defaults") == 0) { serial_config_t config; serial_config_defaults(&config); esp_err_t err = serial_service_apply_config(&config); if (err != ESP_OK) { printf("Could not apply defaults: %s\n", esp_err_to_name(err)); return 1; } printf("Defaults applied in RAM; run 'serial save' to persist them.\n"); print_config(&config); return 0; } if (argc == 2 && strcmp(argv[1], "reset") == 0) { serial_config_t previous; serial_config_t defaults; serial_config_defaults(&defaults); esp_err_t err = serial_service_get_config(&previous); if (err == ESP_OK) { err = serial_service_apply_config(&defaults); } if (err == ESP_OK) { err = serial_config_reset_storage(); if (err != ESP_OK) { /* Keep runtime and persisted behavior aligned if NVS cannot commit. */ serial_service_apply_config(&previous); } } if (err != ESP_OK) { printf("Could not reset configuration: %s\n", esp_err_to_name(err)); return 1; } printf("Defaults applied and saved to NVS.\n"); print_config(&defaults); return 0; } if (argc == 2 && strcmp(argv[1], "counters") == 0) { return show_counters(); } if (argc == 2 && strcmp(argv[1], "clear-counters") == 0) { serial_service_clear_counters(); printf("Serial counters cleared.\n"); return 0; } print_usage(); return 1; } esp_err_t serial_console_register_commands(void) { const esp_console_cmd_t command = { .command = "serial", .help = "Configure and control the Phase 1 UART service; use 'serial' for usage/status", .hint = NULL, .func = &command_serial, .argtable = NULL, }; return esp_console_cmd_register(&command); }