diff --git a/andino_firmware/src/app.cpp b/andino_firmware/src/app.cpp index f7d97361..82d3c900 100644 --- a/andino_firmware/src/app.cpp +++ b/andino_firmware/src/app.cpp @@ -126,8 +126,8 @@ void App::setup() { } void App::loop() { - // Process command shell. - shell_.process(); + // Process command prompt input. + shell_.process_input(); // Run a PID calculation at the appropriate intervals if (millis() > nextPID) { @@ -155,25 +155,33 @@ void App::loop() { } } -void App::cmd_unknown_cb(const char*, const char*) { Serial.println("Unknown command."); } +void App::cmd_unknown_cb(int, char**) { Serial.println("Unknown command."); } -void App::cmd_read_analog_gpio_cb(const char* arg1, const char*) { - const int pin = atoi(arg1); +void App::cmd_read_analog_gpio_cb(int argc, char** argv) { + if (argc < 2) { + return; + } + + const int pin = atoi(argv[1]); Serial.println(analogRead(pin)); } -void App::cmd_read_digital_gpio_cb(const char* arg1, const char*) { - const int pin = atoi(arg1); +void App::cmd_read_digital_gpio_cb(int argc, char** argv) { + if (argc < 2) { + return; + } + + const int pin = atoi(argv[1]); Serial.println(digitalRead(pin)); } -void App::cmd_read_encoders_cb(const char*, const char*) { +void App::cmd_read_encoders_cb(int, char**) { Serial.print(left_encoder_.read()); Serial.print(" "); Serial.println(right_encoder_.read()); } -void App::cmd_reset_encoders_cb(const char*, const char*) { +void App::cmd_reset_encoders_cb(int, char**) { left_encoder_.reset(); right_encoder_.reset(); left_pid_controller_.reset(left_encoder_.read()); @@ -181,9 +189,13 @@ void App::cmd_reset_encoders_cb(const char*, const char*) { Serial.println("OK"); } -void App::cmd_set_motors_speed_cb(const char* arg1, const char* arg2) { - const int left_motor_speed = atoi(arg1); - const int right_motor_speed = atoi(arg2); +void App::cmd_set_motors_speed_cb(int argc, char** argv) { + if (argc < 3) { + return; + } + + const int left_motor_speed = atoi(argv[1]); + const int right_motor_speed = atoi(argv[2]); // Reset the auto stop timer. lastMotorCommand = millis(); @@ -206,9 +218,13 @@ void App::cmd_set_motors_speed_cb(const char* arg1, const char* arg2) { Serial.println("OK"); } -void App::cmd_set_motors_pwm_cb(const char* arg1, const char* arg2) { - const int left_motor_pwm = atoi(arg1); - const int right_motor_pwm = atoi(arg2); +void App::cmd_set_motors_pwm_cb(int argc, char** argv) { + if (argc < 3) { + return; + } + + const int left_motor_pwm = atoi(argv[1]); + const int right_motor_pwm = atoi(argv[2]); // Reset the auto stop timer. lastMotorCommand = millis(); @@ -222,7 +238,12 @@ void App::cmd_set_motors_pwm_cb(const char* arg1, const char* arg2) { Serial.println("OK"); } -void App::cmd_set_pid_tuning_gains_cb(const char* arg1, const char*) { +void App::cmd_set_pid_tuning_gains_cb(int argc, char** argv) { + // TODO(jballoffet): Refactor to expect command multiple arguments. + if (argc < 2) { + return; + } + static constexpr int kSizePidArgs{4}; int i = 0; char arg[20]; @@ -230,7 +251,7 @@ void App::cmd_set_pid_tuning_gains_cb(const char* arg1, const char*) { int pid_args[kSizePidArgs]{0, 0, 0, 0}; // Example: "u 30:20:10:50". - strcpy(arg, arg1); + strcpy(arg, argv[1]); char* p = arg; while ((str = strtok_r(p, ":", &p)) != NULL && i < kSizePidArgs) { pid_args[i] = atoi(str); diff --git a/andino_firmware/src/app.h b/andino_firmware/src/app.h index 1c125695..b0ed9aa6 100644 --- a/andino_firmware/src/app.h +++ b/andino_firmware/src/app.h @@ -50,36 +50,28 @@ class App { private: /// Callback method for an unknown command (default). - // TODO(jballoffet): Parse arguments within callback method. - static void cmd_unknown_cb(const char* arg1, const char* arg2); + static void cmd_unknown_cb(int argc, char** argv); /// Callback method for the `Commands::kReadAnalogGpio` command. - // TODO(jballoffet): Parse arguments within callback method. - static void cmd_read_analog_gpio_cb(const char* arg1, const char* arg2); + static void cmd_read_analog_gpio_cb(int argc, char** argv); /// Callback method for the `Commands::kReadDigitalGpio` command. - // TODO(jballoffet): Parse arguments within callback method. - static void cmd_read_digital_gpio_cb(const char* arg1, const char* arg2); + static void cmd_read_digital_gpio_cb(int argc, char** argv); /// Callback method for the `Commands::kReadEncoders` command. - // TODO(jballoffet): Parse arguments within callback method. - static void cmd_read_encoders_cb(const char* arg1, const char* arg2); + static void cmd_read_encoders_cb(int argc, char** argv); /// Callback method for the `Commands::kResetEncoders` command. - // TODO(jballoffet): Parse arguments within callback method. - static void cmd_reset_encoders_cb(const char* arg1, const char* arg2); + static void cmd_reset_encoders_cb(int argc, char** argv); /// Callback method for the `Commands::kSetMotorsSpeed` command. - // TODO(jballoffet): Parse arguments within callback method. - static void cmd_set_motors_speed_cb(const char* arg1, const char* arg2); + static void cmd_set_motors_speed_cb(int argc, char** argv); /// Callback method for the `Commands::kSetMotorsPwm` command. - // TODO(jballoffet): Parse arguments within callback method. - static void cmd_set_motors_pwm_cb(const char* arg1, const char* arg2); + static void cmd_set_motors_pwm_cb(int argc, char** argv); /// Callback method for the `Commands::kSetPidsTuningGains` command. - // TODO(jballoffet): Parse arguments within callback method. - static void cmd_set_pid_tuning_gains_cb(const char* arg1, const char* arg2); + static void cmd_set_pid_tuning_gains_cb(int argc, char** argv); /// Application command shell. static Shell shell_; diff --git a/andino_firmware/src/shell.cpp b/andino_firmware/src/shell.cpp index 736c4f3b..6c8dfdc0 100644 --- a/andino_firmware/src/shell.cpp +++ b/andino_firmware/src/shell.cpp @@ -48,69 +48,57 @@ void Shell::register_command(const char* name, CommandCallback callback) { commands_[commands_count_++] = command; } -// TODO(jballoffet): Modify parsing method to allow command name to be a string. -void Shell::process() { +void Shell::process_input() { while (stream_->available() > 0) { - // Read the next character - char chr = stream_->read(); - - // Terminate a command with a CR - if (chr == 13) { - if (args_count_ == 1) { - arg1_[arg_index_] = 0; - } else if (args_count_ == 2) { - arg2_[arg_index_] = 0; - } - execute_callback(); - reset(); - } - // Use spaces to delimit parts of the command - else if (chr == ' ') { - // Step through the arguments - if (args_count_ == 0) { - args_count_ = 1; - } else if (args_count_ == 1) { - arg1_[arg_index_] = 0; - args_count_ = 2; - arg_index_ = 0; - } - continue; - } else { - if (args_count_ == 0) { - // The first arg is the single-letter command - command_ = chr; - } else if (args_count_ == 1) { - // Subsequent arguments can be more than one character - arg1_[arg_index_] = chr; - arg_index_++; - } else if (args_count_ == 2) { - arg2_[arg_index_] = chr; - arg_index_++; - } + const char input = stream_->read(); + + switch (input) { + case '\r': + // Terminate command prompt message and parse it. + message_buffer_[message_index_++] = '\0'; + parse_message(); + // Reset message buffer. + message_index_ = 0; + break; + + case '\n': + // Ignore newline characters. + break; + + default: + message_buffer_[message_index_++] = input; + // Prevent buffer overflow. + if (message_index_ >= kCommandPromptLengthMax) { + message_index_ = 0; + } + break; } } } -void Shell::reset() { - command_ = 0; - memset(arg1_, 0, sizeof(arg1_)); - memset(arg2_, 0, sizeof(arg2_)); - args_count_ = 0; - arg_index_ = 0; +void Shell::parse_message() { + char* argv[kCommandArgMax]; + int argc = 0; + + argv[argc] = strtok(message_buffer_, " "); + while (argv[argc] != NULL && argc < (kCommandArgMax - 1)) { + argv[++argc] = strtok(NULL, " "); + } + + execute_callback(argc, argv); } -// TODO(jballoffet): Modify parsing method to allow command name to be a string. -void Shell::execute_callback() { +void Shell::execute_callback(int argc, char** argv) { for (size_t i = 0; i < commands_count_; i++) { - if (command_ == commands_[i].name[0]) { - commands_[i].callback(arg1_, arg2_); + if (!strcmp(argv[0], commands_[i].name)) { + commands_[i].callback(argc, argv); return; } } // Unknown command received, executing default callback. if (default_callback_ != nullptr) { - default_callback_(arg1_, arg2_); + default_callback_(argc, argv); } } diff --git a/andino_firmware/src/shell.h b/andino_firmware/src/shell.h index 3b988f83..b4a8212b 100644 --- a/andino_firmware/src/shell.h +++ b/andino_firmware/src/shell.h @@ -39,7 +39,7 @@ namespace andino { class Shell { public: /// @brief Command callback type. - typedef void (*CommandCallback)(const char*, const char*); + typedef void (*CommandCallback)(int argc, char** argv); /// @brief Initializes the shell. /// @@ -59,17 +59,11 @@ class Shell { /// @brief Processes the available input at the command prompt (if any). Meant to be called /// continously. - void process(); + void process_input(); private: - /// Maximum number of commands that can be registered. - static constexpr int kCommandsMax{10}; - /// Maximum command name length. - static constexpr int kCommandNameLengthMax{16}; - - /// Maximum command argument length. - static constexpr int kCommandArgLengthMax{16}; + static constexpr int kCommandNameLengthMax{8}; /// Command registry entry definition. struct Command { @@ -79,11 +73,20 @@ class Shell { CommandCallback callback; }; - /// Resets the command prompt processing variables. - void reset(); + /// Maximum number of commands that can be registered. + static constexpr int kCommandsMax{16}; + + /// Maximum number of command arguments that can be processed. + static constexpr int kCommandArgMax{16}; + + /// Maximum command prompt message length. + static constexpr int kCommandPromptLengthMax{64}; + + /// Parses the command prompt message. + void parse_message(); /// Executes the corresponding command callback function. - void execute_callback(); + void execute_callback(int argc, char** argv); /// Data stream. Stream* stream_{nullptr}; @@ -97,20 +100,11 @@ class Shell { /// Number of registered commands. size_t commands_count_{0}; - /// Holds the received command. - char command_; - - /// Holds the first received command argument. - char arg1_[kCommandArgLengthMax]; - - /// Holds the second received command argument. - char arg2_[kCommandArgLengthMax]; - - /// Argument array index. - int arg_index_{0}; + /// Command prompt message. + char message_buffer_[kCommandPromptLengthMax]; - /// Number of received command arguments. - int args_count_{0}; + /// Command prompt message index. + int message_index_{0}; }; } // namespace andino