diff --git a/README.md b/README.md index d19a189..0444dff 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,6 @@ -# SerialCommands +# SerialCommands - MODIFIED +This is a fork that allows you to inject arbitrary commands as if they were typed at the serial console. Useful if you want to use SerialCommands as a central dispatch for quick n dirty message processing from external sources (like MQTT/HTTP/etc) + An Arduino library to tokenize and parse commands received over a serial port. Inspired by (original version): diff --git a/library.json b/library.json index 5fce926..a4034e4 100644 --- a/library.json +++ b/library.json @@ -2,16 +2,16 @@ "name": "SerialCommands", "authors": { - "name": "Pedro Tiago Pereira", + "name": "Pedro Tiago Pereira (Original Author)", "email": "tiago.private@gmail.com", "url": "https://github.com/ppedro74" }, "keywords": "serial, command, commands, cmd, parse, callback", - "description": "A Library to tokenize and parse commands received over a serial port. Simple and small memory footprint (no dynamic memory allocation)", + "description": "[forked] A Library to tokenize and parse commands received over a serial port. Simple and small memory footprint (no dynamic memory allocation)", "repository": { "type": "git", - "url": "https://github.com/ppedro74/Arduino-SerialCommands.git" + "url": "https://github.com/binary1230/Arduino-SerialCommands.git" }, "version": "2.2.0", "frameworks": "arduino", diff --git a/library.properties b/library.properties index e1eda18..e766d72 100644 --- a/library.properties +++ b/library.properties @@ -5,5 +5,5 @@ maintainer=Pedro Tiago Pereira sentence=An Arduino library to tokenize and parse commands received over a serial port. paragraph=Simple, small footprint, no dynamic memory allocation category=Data Processing -url=https://github.com/ppedro74/Arduino-SerialCommands +url=https://github.com/binary1230/Arduino-SerialCommands architectures=* diff --git a/src/SerialCommands.cpp b/src/SerialCommands.cpp index ef8b5ee..c7c531c 100644 --- a/src/SerialCommands.cpp +++ b/src/SerialCommands.cpp @@ -107,50 +107,92 @@ SERIAL_COMMANDS_ERRORS SerialCommands::ReadSerial() if (term_[++term_pos_] == 0) { buffer_[buffer_pos_ - strlen(term_)] = '\0'; + ProcessBuffer(); + } + } -#ifdef SERIAL_COMMANDS_DEBUG - Serial.print("Received: ["); - Serial.print(buffer_); - Serial.println("]"); -#endif - char* command = strtok_r(buffer_, delim_, &last_token_); - if (command != NULL) - { - boolean matched = false; - int cx; - SerialCommand* cmd; - for (cmd = commands_head_, cx = 0; cmd != NULL; cmd = cmd->next, cx++) - { -#ifdef SERIAL_COMMANDS_DEBUG - Serial.print("Comparing ["); - Serial.print(command); - Serial.print("] to ["); - Serial.print(cmd->command); - Serial.println("]"); -#endif + return SERIAL_COMMANDS_SUCCESS; +} - if (strncmp(command, cmd->command, strlen(cmd->command) + 1) == 0) - { -#ifdef SERIAL_COMMANDS_DEBUG - Serial.print("Matched #"); - Serial.println(cx); -#endif - cmd->function(this); - matched = true; - break; - } - } - if (!matched && default_handler_ != NULL) - { - (*default_handler_)(this, command); - } - } +void SerialCommands::ProcessBuffer() +{ + #ifdef SERIAL_COMMANDS_DEBUG + Serial.print("Received: ["); + Serial.print(buffer_); + Serial.println("]"); + #endif - ClearBuffer(); + char* command = strtok_r(buffer_, delim_, &last_token_); + if (command == NULL) { + ClearBuffer(); + return; + } + + boolean matched = false; + int cx; + SerialCommand* cmd; + for (cmd = commands_head_, cx = 0; cmd != NULL; cmd = cmd->next, cx++) + { + #ifdef SERIAL_COMMANDS_DEBUG + Serial.print("Comparing ["); + Serial.print(command); + Serial.print("] to ["); + Serial.print(cmd->command); + Serial.println("]"); + #endif + + if (strncmp(command, cmd->command, strlen(cmd->command) + 1) == 0) + { + #ifdef SERIAL_COMMANDS_DEBUG + Serial.print("Matched #"); + Serial.println(cx); + #endif + cmd->function(this); + matched = true; + break; } } + if (!matched && default_handler_ != NULL) + { + (*default_handler_)(this, command); + } - return SERIAL_COMMANDS_SUCCESS; + ClearBuffer(); +} + +// instead of reading from serial input, manually process this line as if it was sent +// on the serial input. only works for non-OneKey commands. +// any previous buffer is discarded, any in-progress parsing is restarted. +// do not call from inside another command handler. +bool SerialCommands::ProcessCommandLine(const char* line) +{ + if (!line || strlen(line) == 0) + return false; // invalid input + + if (strlen(line) >= buffer_len_-1) + return false; // not enough buffer space to copy into + + // lock so we can't accidentally be called from a command handler + // strtok uses global state and weird recursive calls could potentially mess it up. + // if you do need this capability, this can be rewritten to deal with it. + if (is_processing_cmdline_) + return false; + is_processing_cmdline_ = true; // locked + + // clear out anything in progress on the real serial input + // (warning: discards any serial input already typed) + ClearBuffer(); + + // copy in our line string into the buffer, which is same as what reading from Serial does + strncpy(buffer_, line, buffer_len_ - 1); + buffer_[buffer_len_ - 1] = '\0'; // ensure null-term no matter what + + // process the buffer like as though it was recieved via serial input + ProcessBuffer(); + + is_processing_cmdline_ = false; // unlock + + return true; } bool SerialCommands::CheckOneKeyCmd() diff --git a/src/SerialCommands.h b/src/SerialCommands.h index 89ca23d..5378c53 100644 --- a/src/SerialCommands.h +++ b/src/SerialCommands.h @@ -56,7 +56,8 @@ class SerialCommands onek_cmds_head_(NULL), onek_cmds_tail_(NULL), commands_count_(0), - onek_cmds_count_(0) + onek_cmds_count_(0), + is_processing_cmdline_(false) { } @@ -107,6 +108,19 @@ class SerialCommands */ char* Next(); + /** + * \brief Alternative input processing: Treat 'line' as if it was received from the Serial input. + * NOTE: Do NOT call this from inside any command handler, it will return false + * This disregards anything currently being typed in via real serial, and clears the buffer afterwards. + * Will not call OneKey commands, only regular commands. + * 'line' must be a null-terminated string + * it should be no more than the initial buffer size minus 1 + * 'line' should not end in any delimiters (like newline/etc), just type the string you want. + * example: ProcessCommandLine("set wifi_ssid my_network_name"); + * \ return True if successful, false if error (string too low, buffer too small to hold command, or, recursive call detected from cmd handler) + */ + bool ProcessCommandLine(const char* line); + private: Stream* serial_; char* buffer_; @@ -123,6 +137,7 @@ class SerialCommands SerialCommand* onek_cmds_tail_; uint8_t commands_count_; uint8_t onek_cmds_count_; + bool is_processing_cmdline_; /** * \brief Tests for any one_key command and execute it if found @@ -130,6 +145,8 @@ class SerialCommands * also clearing the buffer **/ bool CheckOneKeyCmd(); + + void ProcessBuffer(); }; #endif