/** ********************************************************************* * * @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 "console.h" #include "NVmem.h" #include "version.h" #include "usbd_cdc_if.h" #define CONSOLE_BUF_SIZE (256u) typedef struct config_data { int32_t val_1; int32_t val_2; int32_t val_3; } config_data_t; typedef union config_data_u { config_data_t config; uint8_t data[12]; } config_data_u_t; 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; // static int32_t submenu_value_one = 0; // static int32_t submenu_value_two = 0; // static int32_t submenu_value_three = 0; config_data_u_t configuration_data; enum console_menu_state{main_menu, submenu_1, submenu_2, submenu_3, 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); static void print_main_menu(void); static void print_submenu_one(void); static void print_submenu_two(void); static void print_submenu_three(void); static void print_saved_menu(int32_t val); 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(void) { 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); // for this demonstratior, load example values from nvmem NVmem_read(configuration_data.data, 0, 12); } 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; // for(uint32_t i = 0; i < m_command_len; i++) // { // circular_buf_put(m_console_output, m_command_buf[i]); // } 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 = 0; switch (console_menu) { case main_menu: if (m_command_buf[0] == '1') { print_submenu_one(); console_menu = submenu_1; } else if (m_command_buf[0] == '2') { print_submenu_two(); console_menu = submenu_2; } else if (m_command_buf[0] == '3') { print_submenu_three(); console_menu = submenu_3; } else if (m_command_buf[0] == 'S') { success = NVmem_write_immediate(configuration_data.data, 0, 12); 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// if (m_command_buf[0] == '\n') { print_main_menu(); } break; case submenu_1: 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.val_1); console_menu = main_menu; print_main_menu(); } break; case submenu_2: if (m_command_buf[0] == 0x1B) // ESCAPE { console_menu = main_menu; print_main_menu(); } else { parse_input(&configuration_data.config.val_2); console_menu = main_menu; print_main_menu(); } break; case submenu_3: if (m_command_buf[0] == 0x1B) // ESCAPE { console_menu = main_menu; print_main_menu(); } else { parse_input(&configuration_data.config.val_3); 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) { // *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[128]; m_echo_console = true; reset_console(); set_console_red(); uint32_t len = snprintf(buf, 128, "Main Menu:\r\n[1]Option 1: \"%li\"\r\n[2]Option 2: \"%li\"\r\n[3]Option 3: \"%li\"\r\n[S]ave\r\nVersion: %s\r\nEnter Selection: ", configuration_data.config.val_1, configuration_data.config.val_2, configuration_data.config.val_3, VERSION_STR); console_send((uint8_t*)buf, len); } void print_submenu_one(void) { char buf[128]; m_echo_console = true; reset_console(); set_console_reset(); uint32_t len = snprintf(buf, 128, "Submenu 1:\r\n"); console_send((uint8_t*)buf, len); } void print_submenu_two(void) { char buf[128]; m_echo_console = true; reset_console(); set_console_reset(); uint32_t len = snprintf(buf, 128, "Submenu 2:\r\n"); console_send((uint8_t*)buf, len); } void print_submenu_three(void) { char buf[128]; m_echo_console = true; reset_console(); set_console_reset(); uint32_t len = snprintf(buf, 128, "Submenu 3:\r\n"); 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); }