/** ********************************************************************* * * @file console.c * @brief * * @date 2024-04-14 19:12:40 * @author CT * * @details * ************************************************************************* **/ /* Operational Concept: Sending enter displays the current menu Simple 1 char command interface */ #include #include #include "main.h" #include "console.h" #include "NVmem.h" #include "version.h" #include "usbd_cdc_if.h" #define CONSOLE_BUF_SIZE (256u) static cbuf_handle_t m_console_input; static cbuf_handle_t m_console_output; static uint8_t m_console_input_buf[CONSOLE_BUF_SIZE] = {0}; static uint8_t m_console_output_buf[CONSOLE_BUF_SIZE] = {0}; static uint8_t tx_buf[CONSOLE_BUF_SIZE]; static uint8_t m_command_buf[16] = {0}; static uint8_t m_command_len = 0; static bool m_command_ready = false; static bool m_echo_console = false; config_data_u_t* configuration_data; enum console_menu_state{main_menu, slider_1_menu, slider_2_menu, slider_3_menu, slider_4_menu, save_menu, version_menu}; enum console_menu_state console_menu = main_menu; static void process_incoming(void); static void process_outgoing(void); static void process_command(void); static int console_send(uint8_t* buf, uint32_t len); static void menu_state_machine(void); static int parse_input(int32_t* data); static int32_t read_line(uint8_t data); // menus static void print_main_menu(void); static void print_slider_menu(uint32_t slider); static void print_saved_menu(int32_t val); // console control static void set_console_reset(void); static void set_console_red(void); static void set_console_green(void); static void reset_console(void); /* PUBLIC FUNCTIONS */ void console_init(config_data_u_t* config_data) { configuration_data = config_data; m_console_input = circular_buf_init(m_console_input_buf, CONSOLE_BUF_SIZE); m_console_output = circular_buf_init(m_console_output_buf, CONSOLE_BUF_SIZE); } void console_service(void) { // if there is received data waiting process_incoming(); // if there is data waiting to be transmitted process_outgoing(); // Process command process_command(); } /// @brief Attempts to push one byte into the console input buffer /// @param data byte to be pushed to input /// @return 0 on success, -1 on failure int console_push(uint8_t data) { int ret = 0; ret = circular_buf_try_put(m_console_input, data); return (ret); } /* PRIVATE FUNCTIONS */ void process_incoming(void) { if (!circular_buf_empty(m_console_input)) { uint8_t data; while (!circular_buf_empty(m_console_input)) { circular_buf_get(m_console_input, &data); m_command_ready = read_line(data); if (m_echo_console) { circular_buf_try_put(m_console_output, data); } } } } void process_outgoing(void) { if(!circular_buf_empty(m_console_output)) { size_t tx_len = circular_buf_size(m_console_output); for (uint32_t i = 0; i < tx_len; i++) { circular_buf_get(m_console_output, &tx_buf[i]); } // Note: directly interacts with interface CDC_Transmit_FS(tx_buf, tx_len); } } void process_command(void) { if(m_command_ready) { m_command_ready = false; menu_state_machine(); } } int console_send(uint8_t* buf, uint32_t len) { int ret = 0; for (uint32_t i = 0; i < len; i++) { ret |= circular_buf_try_put(m_console_output, buf[i]); } return (ret); } /// @brief Reads in a line terminated by \\n /// @param data /// @return 1 if EOL found, 0 otherwise int32_t read_line(uint8_t data) { int32_t ret = 0; static uint8_t m_command_buf_index = 0; // if EOL, return on and set length if((data == '\n') || (data == '\r')) { m_command_buf[m_command_buf_index++] = '\n'; // set length of command for use within rest of module m_command_len = m_command_buf_index; // reset index m_command_buf_index = 0; ret = 1; } else if (data == 0x1B) // ESCAPE { m_command_len = 1; m_command_buf[0] = 0x1B; // reset index m_command_buf_index = 0; ret = 1; } else { if (m_command_buf_index < 16) { m_command_buf[m_command_buf_index++] = data; } else { m_command_buf_index = 0; } } return (ret); } // Called when a complete line has been received void menu_state_machine(void) { int32_t success = 1; switch (console_menu) { case main_menu: if (m_command_buf[0] == '1') { print_slider_menu(1); console_menu = slider_1_menu; } else if (m_command_buf[0] == '2') { print_slider_menu(2); console_menu = slider_2_menu; } else if (m_command_buf[0] == '3') { print_slider_menu(3); console_menu = slider_3_menu; } else if (m_command_buf[0] == '4') { print_slider_menu(4); console_menu = slider_4_menu; } else if (m_command_buf[0] == 'S') { success = NVmem_write_immediate(configuration_data->data, 0, sizeof(config_data_t)); print_saved_menu(success); // need to call service because we are blocking all normal execution here process_outgoing(); // Delay for user to read result HAL_Delay(3000); console_menu = main_menu; print_main_menu(); } else { print_main_menu(); } break; case slider_1_menu: if (m_command_buf[0] == 0x1B) // ESCAPE { console_menu = main_menu; print_main_menu(); } else { // Take input and immediately return to main menu parse_input(&configuration_data->config.slider_1); console_menu = main_menu; print_main_menu(); } break; case slider_2_menu: if (m_command_buf[0] == 0x1B) // ESCAPE { console_menu = main_menu; print_main_menu(); } else { // Take input and immediately return to main menu parse_input(&configuration_data->config.slider_2); console_menu = main_menu; print_main_menu(); } break; case slider_3_menu: if (m_command_buf[0] == 0x1B) // ESCAPE { console_menu = main_menu; print_main_menu(); } else { // Take input and immediately return to main menu parse_input(&configuration_data->config.slider_3); console_menu = main_menu; print_main_menu(); } break; case slider_4_menu: if (m_command_buf[0] == 0x1B) // ESCAPE { console_menu = main_menu; print_main_menu(); } else { // Take input and immediately return to main menu parse_input(&configuration_data->config.slider_4); console_menu = main_menu; print_main_menu(); } break; case save_menu: break; default: break; } } /// @brief Parses line for integer /// @param data parsed out integer /// @return 1 on success, otherwise fail...kinda int parse_input(int32_t* data) { // TODO check and limit input within bounds // *data = atoi((char*)m_command_buf); int ret = sscanf((char*)m_command_buf, "%ld", data); return (ret); } // Also acts as initialization for state void print_main_menu(void) { char buf[256]; m_echo_console = true; reset_console(); set_console_red(); uint32_t len = snprintf(buf, 256, "Main Menu:\r\nSlider [1]: \"%li\"\r\nSlider [2]: \"%li\"\r\nSlider [3]: \"%li\"\r\nSlider [4]: \"%li\"\r\n[S]ave\r\nVersion: %s\r\nEnter Selection: ", configuration_data->config.slider_1, configuration_data->config.slider_2, configuration_data->config.slider_3, configuration_data->config.slider_4, VERSION_STR); console_send((uint8_t*)buf, len); } void print_slider_menu(uint32_t slider) { char buf[128]; m_echo_console = true; reset_console(); set_console_reset(); uint32_t len = snprintf(buf, 128, "Slider %lu Max Value (0-100): ", slider); console_send((uint8_t*)buf, len); } void print_saved_menu(int32_t val) { char buf[128]; uint32_t len; reset_console(); set_console_green(); if (0 == val) { len = snprintf(buf, 128, "SAVED!!1!"); } else { len = snprintf(buf, 128, "SAVE FAILED!"); } console_send((uint8_t*)buf, len); } void set_console_reset(void) { char buf[16] = {0}; uint32_t len = snprintf(buf, 16, "\x1b[1;0m"); console_send((uint8_t*)buf, len); } void set_console_red(void) { char buf[16] = {0}; uint32_t len = snprintf(buf, 16, "\x1b[1;31m"); console_send((uint8_t*)buf, len); } void set_console_green(void) { char buf[16] = {0}; uint32_t len = snprintf(buf, 16, "\x1b[1;32m"); console_send((uint8_t*)buf, len); } void reset_console(void) { char buf[16] = {0}; uint32_t len = snprintf(buf, 16, "\x1b[2J\x1b[H"); console_send((uint8_t*)buf, len); }