/* * Copyright (C) 2015-2019 Grégoire Passault * Copyright (C) 2019-2025 Adrien Boussicault * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program. If not, see * . */ #include #include "terminal.hpp" #include "serial_print.hpp" #define STRINGIFY(x) #x namespace terminal_n { void Terminal::reset(){ terminal_pos = 0; terminal_last_pos = 0; terminal_buffer[0] = '\0'; terminal_last_ok = false; print_prompt(); } /** * Stops the terminal */ void Terminal::disable(){ disabled = true; } void Terminal::enable(){ terminal_last_ok = false; disabled = false; } /** * Write the terminal prompt */ void Terminal::print_prompt(){ print(PROMPT); } void Terminal::setup(HardwareSerial* serial){ io = serial; if(terminal_command_count >= terminal_max_commands){ println("There are too many commands. Increase the number of commands."); println("Define the macro TERMINAL_MAX_COMMANDS with a bigger number before including the terminal library."); } if(serial){ silent = false; }else{ silent = true; } enable(); print_prompt(); check_eeprom_names(); load_config_from_eeprom(); } void Terminal::terminal_silent(bool silent) { silent = silent; } const terminal_command * Terminal::find_command( char *command_name, uint32_t command_name_length ){ uint32_t i; for(i=0; iname) == command_name_length && strncmp(terminal_buffer, command->name, command_name_length) == 0 ){ return command; } } return NULL; } /*** * Executes the given command with given parameters */ bool Terminal::terminal_execute( char *command_name, uint32_t command_name_length, uint32_t argc, char **argv ){ uint32_t i; const terminal_command *command; // Try to find and execute the command command = find_command(command_name, command_name_length); if(command != NULL){ command->command(argc, argv); if(command->auto_save_after_command){ save_value_to_eeprom(command); } } // If it fails, try to parse the command as an allocation (a=b) if (command == NULL) { for(i=0; iparameter){ argv[0] = command_name+i+1; argv[1] = NULL; argc = 1; command->command(argc, argv); if(command->auto_save_after_command){ save_value_to_eeprom(command); } }else{ command = NULL; } if(!command){ print("Unknown parameter: "); write(command_name, command_name_length); println(""); return false; } } } } // If it fails again, display the "unknown command" message if (command == NULL) { print("Unknown command: "); write(command_name, command_name_length); println(""); return false; } return true; } /*** * Process the receive buffer to parse the command and executes it */ void Terminal::terminal_process(){ char *saveptr; uint32_t command_name_length; uint32_t argc = 0; char* argv[terminal_max_arguments+1]; println(""); strtok_r(terminal_buffer, " ", &saveptr); while ( (argv[argc] = strtok_r(NULL, " ", &saveptr)) != NULL && argc < terminal_max_arguments ){ *(argv[argc]-1) = '\0'; argc++; } if(terminal_max_arguments == argc){ println("Nb. of parameters are limited."); } if (saveptr != NULL) { *(saveptr - 1) = ' '; } command_name_length = strlen(terminal_buffer); if (command_name_length > 0) { terminal_last_ok = terminal_execute( terminal_buffer, command_name_length, argc, argv ); }else{ terminal_last_ok = false; } terminal_last_pos = terminal_pos; terminal_pos = 0; print_prompt(); } /** * Ticking the terminal, this will cause lookup for characters * and eventually a call to the process function on new lines */ void Terminal::update(){ if(disabled || !terminal_has_io()){ return; } char c; uint8_t input; while(io->available()){ input = io->read(); c = (char)input; if(c == '\0'){ continue; } //Return key if(c == '\r' || c == '\n'){ if(terminal_pos == 0 && terminal_last_ok){ // If the user pressed no keys, restore the last // command and run it again uint32_t i; for(i=0; i 0){ terminal_pos--; print("\x8 \x8"); } //Special key }else if(c == '\x1b'){ while(!io->available()); io->read(); while(!io->available()); io->read(); //Others }else{ terminal_buffer[terminal_pos] = c; if(terminal_echo_mode){ print(c); } if(terminal_pos < terminal_buffer_size-1){ terminal_pos++; } } } } /** * Registers a command */ void Terminal::register_command(struct terminal_command *command){ if(terminal_command_count >= terminal_max_commands) return; terminal_commands[terminal_command_count] = command; terminal_command_count = terminal_command_count + 1; } void Terminal::displayHelp(bool parameter){ char buffer[1000]; uint32_t i; if(parameter){ println("Available parameters:"); } else { print(documentation); println("Available commands:"); } println(""); for(i=0; iparameter!=0) != parameter){ continue; } int32_t namesize = strlen(command->name); int32_t descsize = strlen(command->description); int32_t typesize = (command->parameter_type == NULL) ? 0 : strlen(command->parameter_type); memcpy(buffer, command->name, namesize); buffer[namesize++] = ' '; buffer[namesize++] = ' '; buffer[namesize++] = ':'; buffer[namesize++] = ' '; buffer[namesize++] = ' '; // buffer[namesize++] = '\r'; //buffer[namesize++] = '\n'; //buffer[namesize++] = '\t'; memcpy(buffer+namesize, command->description, descsize); if(typesize){ buffer[namesize+descsize++] = ' '; buffer[namesize+descsize++] = '('; memcpy(buffer+namesize+descsize, command->parameter_type, typesize); buffer[namesize+descsize+typesize++] = ')'; } buffer[namesize+descsize+typesize++] = '\r'; buffer[namesize+descsize+typesize++] = '\n'; write(buffer, namesize+descsize+typesize); } } void Terminal::terminal_params_show(){ for(uint32_t i=0; iparameter) { command->command(0, NULL); } } } void Terminal::echo_command(uint32_t argc, char *argv[]){ if( (argc == 1 && strcmp("on", argv[0])==0) || (argc == 0 && terminal_echo_mode==false) ){ terminal_echo_mode = true; println("Echo enabled"); }else{ terminal_echo_mode = false; println("Echo disabled"); } } void Terminal::params_command(uint32_t argc, char *argv[]){ if(argc && strcmp(argv[0], "show")==0){ terminal_params_show(); }else{ displayHelp(true); } } bool Terminal::have_to_be_saved_in_eeprom(const terminal_command *command){ if(command->parameter == NONE_PARAMETER) return false; if(command->eeprom_name[0] == '\0') return false; return true; } void Terminal::save_value_to_eeprom(const terminal_command *command){ if(!have_to_be_saved_in_eeprom(command)) return; const constexpr bool read_only = false; preferences.begin( preferences_namespace, read_only, preferences_nvs_partition ); switch(command->parameter){ case FLOAT_PARAMETER : preferences.putFloat(command->eeprom_name, *static_cast(command->value_ptr)); break; case DOUBLE_PARAMETER : preferences.putDouble(command->eeprom_name, *static_cast(command->value_ptr)); break; case INT_PARAMETER : preferences.putInt(command->eeprom_name, *static_cast(command->value_ptr)); break; case UINT_PARAMETER : preferences.putUInt(command->eeprom_name, *static_cast(command->value_ptr)); break; case LONGINT_PARAMETER : preferences.putLong64(command->eeprom_name, *static_cast(command->value_ptr)); break; case LONGUINT_PARAMETER : preferences.putULong64(command->eeprom_name, *static_cast(command->value_ptr)); break; case BOOL_PARAMETER : preferences.putBool(command->eeprom_name, *static_cast(command->value_ptr)); break; case NONE_PARAMETER : default : println("Error -- line:" STRINGIFY(__LINE__) ", file" __FILE__ ); break; } preferences.end(); } void Terminal::save_config_to_eeprom(){ for(uint32_t i=0; ieeprom_name, eeprom_name, MAX_EEPROM_NAME_LENGTH ) ){ save_value_to_eeprom(terminal_commands[i]); return true; } } return false; } void Terminal::print_value_from_eeprom(const struct terminal_command *command){ if(!have_to_be_saved_in_eeprom(command)) return; const constexpr bool read_only = true; preferences.begin( preferences_namespace, read_only, preferences_nvs_partition ); if( preferences.isKey(command->eeprom_name) ){ print("\""); print(command->eeprom_name); print("\""); print(" : "); switch(command->parameter){ case FLOAT_PARAMETER : print(preferences.getFloat(command->eeprom_name)); break; case DOUBLE_PARAMETER : print(preferences.getDouble(command->eeprom_name)); break; case INT_PARAMETER : print(preferences.getInt(command->eeprom_name)); break; case UINT_PARAMETER : print(preferences.getUInt(command->eeprom_name)); break; case LONGINT_PARAMETER : print(preferences.getLong64(command->eeprom_name)); break; case LONGUINT_PARAMETER : print(preferences.getULong64(command->eeprom_name)); break; case BOOL_PARAMETER : print(preferences.getBool(command->eeprom_name)); break; case NONE_PARAMETER : default : println("Error -- line:" STRINGIFY(__LINE__) ", file" __FILE__ ); break; } println(";"); } preferences.end(); } void Terminal::print_config_from_eeprom(){ println("{"); for (uint32_t i=0; ieeprom_name, eeprom_name, MAX_EEPROM_NAME_LENGTH ) ){ print_value_from_eeprom(terminal_commands[i]); return true; } } return false; } void Terminal::reset_to_constructor_config(){ print("Reseting to constructor configuration ... "); const constexpr bool read_only = false; preferences.begin( preferences_namespace, read_only, preferences_nvs_partition ); preferences.clear(); preferences.end(); println("Done."); println("Please reset the card."); } bool Terminal::reset_to_constructor_config(const char* eeprom_name){ for(uint32_t i=0; ieeprom_name, eeprom_name, MAX_EEPROM_NAME_LENGTH ) ){ const constexpr bool read_only = false; preferences.begin( preferences_namespace, read_only, preferences_nvs_partition ); if( preferences.isKey(eeprom_name) ){ preferences.remove(eeprom_name); } preferences.end(); return true; } } return false; } void Terminal::check_eeprom_names(){ for(int j=1; jeeprom_name, "", MAX_EEPROM_NAME_LENGTH) && strncmp(terminal_commands[j]->eeprom_name, "", MAX_EEPROM_NAME_LENGTH) && !strncmp( terminal_commands[i]->eeprom_name, terminal_commands[j]->eeprom_name, MAX_EEPROM_NAME_LENGTH ) ){ while(true){ print("Error : "); print(terminal_commands[i]->name); print(" and "); print(terminal_commands[i]->name); print(" have the same eeprom name : "); println(terminal_commands[i]->eeprom_name); delay(1000); } } } } } void Terminal::print_saved_parameter(const terminal_command *command){ if(!have_to_be_saved_in_eeprom(command)) return; print(command->name); print(" : "); switch(command->parameter){ case FLOAT_PARAMETER : print(*static_cast(command->value_ptr)); break; case DOUBLE_PARAMETER : print(*static_cast(command->value_ptr)); break; case INT_PARAMETER : print(*static_cast(command->value_ptr)); break; case UINT_PARAMETER : print(*static_cast(command->value_ptr)); break; case LONGINT_PARAMETER : print(*static_cast(command->value_ptr)); break; case LONGUINT_PARAMETER : print(*static_cast(command->value_ptr)); break; case BOOL_PARAMETER : print(*static_cast(command->value_ptr)); break; case NONE_PARAMETER : default : println("Error -- line:" STRINGIFY(__LINE__) ", file" __FILE__ ); break; } } void Terminal::load_value_from_eeprom( const terminal_command *command, bool enable_print ){ if(!have_to_be_saved_in_eeprom(command)) return; const constexpr bool read_only = true; preferences.begin( preferences_namespace, read_only, preferences_nvs_partition ); if( preferences.isKey(command->eeprom_name) ){ switch(command->parameter){ case FLOAT_PARAMETER : *static_cast(command->value_ptr) = preferences.getFloat(command->eeprom_name); break; case DOUBLE_PARAMETER : *static_cast(command->value_ptr) = preferences.getDouble(command->eeprom_name); break; case INT_PARAMETER : *static_cast(command->value_ptr) = preferences.getInt(command->eeprom_name); break; case UINT_PARAMETER : *static_cast(command->value_ptr) = preferences.getUInt(command->eeprom_name); break; case LONGINT_PARAMETER : *static_cast(command->value_ptr) = preferences.getLong64(command->eeprom_name); break; case LONGUINT_PARAMETER : *static_cast(command->value_ptr) = preferences.getULong64(command->eeprom_name); break; case BOOL_PARAMETER : *static_cast(command->value_ptr) = preferences.getBool(command->eeprom_name); break; case NONE_PARAMETER : default : println("Error -- line:" STRINGIFY(__LINE__) ", file" __FILE__ ); break; } if(enable_print){ print("(eeprom ) "); } }else{ if(enable_print){ print("(construction) "); } } if(enable_print){ print_saved_parameter(command); println("");} preferences.end(); } void Terminal::load_config_from_eeprom(bool enable_print){ if(enable_print){ println("Loading config :"); } for (uint32_t i=0; ieeprom_name, eeprom_name, MAX_EEPROM_NAME_LENGTH ) ){ load_value_from_eeprom(terminal_commands[i], enable_print); return true; } } return false; } }