├── .github └── workflows │ └── main.yml ├── LICENSE ├── README.md ├── examples ├── SerialCommandsArguments │ └── SerialCommandsArguments.ino ├── SerialCommandsOneKeySimple │ └── SerialCommandsOneKeySimple.ino └── SerialCommandsSimple │ └── SerialCommandsSimple.ino ├── keywords.txt ├── library.json ├── library.properties └── src ├── SerialCommands.cpp └── SerialCommands.h /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: Arduino_CI 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | arduino_ci: 7 | name: "Arduino CI - OS(Ubuntu)" 8 | runs-on: ubuntu-latest 9 | steps: 10 | - uses: actions/checkout@v2 11 | - uses: Arduino-CI/action@master 12 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2017, Pedro Pereira 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | * Neither the name of SerialCommands nor the names of its 15 | contributors may be used to endorse or promote products derived from 16 | this software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 22 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 24 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 25 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SerialCommands 2 | An Arduino library to tokenize and parse commands received over a serial port. 3 | 4 | Inspired by (original version): 5 | https://github.com/scogswell/ArduinoSerialCommand 6 | 7 | This library is for handling text commands over a serial port. 8 | 9 | Goals: 10 | * Small footprint 11 | * No dynamic memory allocation 12 | * Stream class's Serial Ports 13 | * Multiple Serial Ports callback methods 14 | * Custom Command termination default is `CR & LF` 15 | * Custom Arguments Delimeter default is `SPACE` 16 | * Optional _OneKey_ commands that are single characters without the need for 17 | additional command termination 18 | 19 | ## SerialCommand object 20 | 21 | SerialCommand creates a binding between a const string token with a callback function. 22 | 23 | Note: 24 | SerialCommand is used in a Linked List. Do not share SerialCommand objects between SerialCommands. 25 | 26 | ```c++ 27 | #include 28 | 29 | void cmd_hello(SerialCommands* sender) 30 | { 31 | //Do not use Serial.Print! 32 | //Use sender->GetSerial this allows sharing the callback method with multiple Serial Ports 33 | sender->GetSerial()->println("HELLO from arduino!"); 34 | } 35 | 36 | SerialCommand cmd_hello_("hello", cmd_hello); 37 | ``` 38 | 39 | ### OneKey commands 40 | 41 | When sending commands from a keyboard, it is often useful to be able to use 42 | single keypresses to perform a specific action, like adjusting a motor speed up 43 | or down, for example. 44 | 45 | To make any command a _OneKey_ command, instantiate the command with a **single 46 | character** command string, and add `true` as a third argument: 47 | 48 | ```c++ 49 | SerialCommand cmd_faster("+", cmd_faster, true); 50 | SerialCommand cmd_slower("-", cmd_slower, true); 51 | ``` 52 | 53 | The `cmd_faster` and `cmd_slower` command functions has the same signature as 54 | for multiple character commands, except that arguments are not allowed for 55 | _OneKey_ commands (they don't make sense), so calling `sender->Next()` is not 56 | supported. 57 | 58 | _OneKey_ commands are only active when they come in as the _first_ character, 59 | either on startup, or after processing a previous command. In other words, if 60 | another command string contains a `+` like `set+go` for example, the command 61 | callback for the one key `+` command will not be called when `set+go` is 62 | entered, and the `set+go` command callback will be called as expected. 63 | 64 | Any multi character command that _starts_ with the same character as a one key 65 | command, will however never be activated as the one key command will always 66 | consume the single character command first, i.e: 67 | 68 | Multi char command: `up` 69 | OneKey command: `u` 70 | 71 | The `up` command will never be executed since the `u` will be consumed by the 72 | one key command first, which leaves the `p` as the remainder for the command 73 | which will not match (unless of course there *is* in fact a `p` one key or multi char 74 | command). 75 | 76 | ## SerialCommands object 77 | 78 | SerialCommands is the main object creates a binding between a Serial Port and a list of SerialCommand 79 | 80 | ```c++ 81 | #include 82 | 83 | //Create a 32 bytes static buffer to be used exclusive by SerialCommands object. 84 | //The size should accomodate command token, arguments, termination sequence and string delimeter \0 char. 85 | char serial_command_buffer_[32]; 86 | 87 | //Creates SerialCommands object attached to Serial 88 | //working buffer = serial_command_buffer_ 89 | //command delimeter: Cr & Lf 90 | //argument delimeter: SPACE 91 | SerialCommands serial_commands_(&Serial, serial_command_buffer_, sizeof(serial_command_buffer_), "\r\n", " "); 92 | ``` 93 | 94 | ### Arduino setup 95 | 96 | ```c++ 97 | void setup() 98 | { 99 | Serial.begin(57600); 100 | serial_commands_.AddCommand(&cmd_hello); 101 | } 102 | ``` 103 | ### Default handler 104 | 105 | ```c++ 106 | void cmd_unrecognized(SerialCommands* sender, const char* cmd) 107 | { 108 | sender->GetSerial()->print("ERROR: Unrecognized command ["); 109 | sender->GetSerial()->print(cmd); 110 | sender->GetSerial()->println("]"); 111 | } 112 | void setup() 113 | { 114 | serial_commands_.SetDefaultHandler(&cmd_unrecognized); 115 | } 116 | ``` 117 | ### Arduino loop 118 | 119 | ```c++ 120 | void loop() 121 | { 122 | serial_commands_.ReadSerial(); 123 | } 124 | ``` 125 | 126 | ### Multiple Serial Ports 127 | ```c++ 128 | #include 129 | 130 | char serial_commands_0_buffer_[32]; 131 | SerialCommands serial_commands_0_(&Serial, serial_commands_0_buffer_, sizeof(serial_commands_0_buffer_)); 132 | 133 | char serial_commands_1_buffer_[32]; 134 | SerialCommands serial_commands_1_(&Serial1, serial_commands_1_buffer_, sizeof(serial_commands_1_buffer_)); 135 | 136 | void cmd_hello(SerialCommands* sender) 137 | { 138 | sender->GetSerial()->println("HELLO from arduino!"); 139 | } 140 | 141 | void cmd_unrecognized(SerialCommands* sender, const char* cmd) 142 | { 143 | sender->GetSerial()->print("ERROR: Unrecognized command ["); 144 | sender->GetSerial()->print(cmd); 145 | sender->GetSerial()->println("]"); 146 | } 147 | 148 | SerialCommand cmd_hello_0_("hello", cmd_hello); 149 | SerialCommand cmd_hello_1_("hello", cmd_hello); 150 | 151 | void setup() 152 | { 153 | Serial.begin(57600); 154 | serial_commands_0_.AddCommand(&cmd_hello_0_); 155 | serial_commands_0_.SetDefaultHandler(&cmd_hello); 156 | 157 | Serial1.begin(57600); 158 | serial_commands_1_.AddCommand(&cmd_hello_1_); 159 | serial_commands_1_.SetDefaultHandler(&cmd_hello); 160 | } 161 | 162 | void loop() 163 | { 164 | serial_commands_0_.ReadSerial(); 165 | serial_commands_1_.ReadSerial(); 166 | } 167 | ``` 168 | -------------------------------------------------------------------------------- /examples/SerialCommandsArguments/SerialCommandsArguments.ino: -------------------------------------------------------------------------------- 1 | /*-------------------------------------------------------------------- 2 | Author : Pedro Pereira 3 | License : BSD 4 | Repository : https://github.com/ppedro74/Arduino-SerialCommands 5 | 6 | SerialCommands - Playing with Arguments 7 | 8 | --------------------------------------------------------------------*/ 9 | #include 10 | #include "SerialCommands.h" 11 | 12 | //Arduino UNO PWM pins 13 | const int kRedLedPin = 3; 14 | const int kGreenLedPin = 5; 15 | const int kBlueLedPin = 9; 16 | 17 | char serial_command_buffer_[32]; 18 | SerialCommands serial_commands_(&Serial, serial_command_buffer_, sizeof(serial_command_buffer_), "\r\n", " "); 19 | 20 | //This is the default handler, and gets called when no other command matches. 21 | void cmd_unrecognized(SerialCommands* sender, const char* cmd) 22 | { 23 | sender->GetSerial()->print("Unrecognized command ["); 24 | sender->GetSerial()->print(cmd); 25 | sender->GetSerial()->println("]"); 26 | } 27 | 28 | //expects one single parameter 29 | void cmd_analog_read(SerialCommands* sender) 30 | { 31 | //Note: Every call to Next moves the pointer to next parameter 32 | 33 | char* port_str = sender->Next(); 34 | if (port_str == NULL) 35 | { 36 | sender->GetSerial()->println("ERROR NO_PORT"); 37 | return; 38 | } 39 | 40 | int port = atoi(port_str); 41 | int value = -1; 42 | switch(port) 43 | { 44 | case 0: 45 | value = analogRead(A0); 46 | break; 47 | case 1: 48 | value = analogRead(A1); 49 | break; 50 | case 2: 51 | value = analogRead(A2); 52 | break; 53 | } 54 | 55 | sender->GetSerial()->print("ANALOG_READ "); 56 | sender->GetSerial()->print(port); 57 | sender->GetSerial()->print(" "); 58 | sender->GetSerial()->print(value); 59 | sender->GetSerial()->println(); 60 | } 61 | 62 | //helper function pass led string (lower case) and pwm value 63 | bool set_led(char* led, int pwm) 64 | { 65 | int pin = -1; 66 | 67 | //note: compare is case sensitive 68 | if (strcmp(led, "red") == 0) 69 | { 70 | pin = kRedLedPin; 71 | } 72 | else if (strcmp(led, "green") == 0) 73 | { 74 | pin = kGreenLedPin; 75 | } 76 | else if (strcmp(led, "blue") == 0) 77 | { 78 | pin = kBlueLedPin; 79 | } 80 | 81 | if (pin<0) 82 | { 83 | //invalid pin 84 | return false; 85 | } 86 | 87 | if (pwm <= 0) 88 | { 89 | digitalWrite(pin, LOW); 90 | } 91 | else if (pwm >= 255) 92 | { 93 | digitalWrite(pin, HIGH); 94 | } 95 | else 96 | { 97 | analogWrite(pin, pwm); 98 | } 99 | 100 | } 101 | 102 | 103 | //First parameter pwm value is required 104 | //Optional parameters: led colors 105 | //e.g. LED 128 red 106 | // LED 128 red blue 107 | // LED 0 red blue green 108 | void cmd_rgb_led(SerialCommands* sender) 109 | { 110 | //Note: Every call to Next moves the pointer to next parameter 111 | 112 | char* pwm_str = sender->Next(); 113 | if (pwm_str == NULL) 114 | { 115 | sender->GetSerial()->println("ERROR NO_PWM"); 116 | return; 117 | } 118 | int pwm = atoi(pwm_str); 119 | 120 | char* led_str; 121 | while ((led_str = sender->Next()) != NULL) 122 | { 123 | if (set_led(led_str, pwm)) 124 | { 125 | sender->GetSerial()->print("LED_STATUS "); 126 | sender->GetSerial()->print(led_str); 127 | sender->GetSerial()->print(" "); 128 | sender->GetSerial()->println(pwm); 129 | } 130 | else 131 | { 132 | sender->GetSerial()->print("ERROR "); 133 | sender->GetSerial()->println(led_str); 134 | } 135 | } 136 | } 137 | 138 | 139 | SerialCommand cmd_analog_read_("AREAD", cmd_analog_read); 140 | SerialCommand cmd_rgb_led_("LED", cmd_rgb_led); 141 | 142 | void setup() 143 | { 144 | Serial.begin(57600); 145 | 146 | pinMode(kRedLedPin, OUTPUT); 147 | pinMode(kGreenLedPin, OUTPUT); 148 | pinMode(kBlueLedPin, OUTPUT); 149 | 150 | set_led("red", 0); 151 | set_led("green", 0); 152 | set_led("blue", 0); 153 | 154 | serial_commands_.SetDefaultHandler(cmd_unrecognized); 155 | serial_commands_.AddCommand(&cmd_analog_read_); 156 | serial_commands_.AddCommand(&cmd_rgb_led_); 157 | 158 | Serial.println("Ready!"); 159 | } 160 | 161 | void loop() 162 | { 163 | serial_commands_.ReadSerial(); 164 | } -------------------------------------------------------------------------------- /examples/SerialCommandsOneKeySimple/SerialCommandsOneKeySimple.ino: -------------------------------------------------------------------------------- 1 | /*-------------------------------------------------------------------- 2 | Author : Pedro Pereira 3 | License : BSD 4 | Repository : https://github.com/ppedro74/Arduino-SerialCommands 5 | 6 | Modified : Fitzterra 7 | : to demonstrate usage of the one_key commands 8 | 9 | SerialCommands Simple Demo 10 | 11 | --------------------------------------------------------------------*/ 12 | #include 13 | #include 14 | 15 | // Pin 13 has an LED connected on most Arduino boards. 16 | // Pin 11 has the LED on Teensy 2.0 17 | // Pin 6 has the LED on Teensy++ 2.0 18 | // Pin 13 has the LED on Teensy 3.0 19 | const int kLedPin = 13; 20 | 21 | char serial_command_buffer_[32]; 22 | SerialCommands serial_commands_(&Serial, serial_command_buffer_, sizeof(serial_command_buffer_), "\r\n", " "); 23 | 24 | //This is the default handler, and gets called when no other command matches. 25 | // Note: It does not get called for one_key commands that do not match 26 | void cmd_unrecognized(SerialCommands* sender, const char* cmd) 27 | { 28 | sender->GetSerial()->print("Unrecognized command ["); 29 | sender->GetSerial()->print(cmd); 30 | sender->GetSerial()->println("]"); 31 | } 32 | 33 | //called for ON command, or one_key '1' command 34 | void cmd_led_on(SerialCommands* sender) 35 | { 36 | digitalWrite(kLedPin, HIGH); 37 | sender->GetSerial()->println("Led is on"); 38 | } 39 | 40 | //called for OFF command, or one_key '0' command 41 | void cmd_led_off(SerialCommands* sender) 42 | { 43 | digitalWrite(kLedPin, LOW); 44 | sender->GetSerial()->println("Led is off"); 45 | } 46 | 47 | //Note: Commands are case sensitive 48 | SerialCommand cmd_led_on_("ON", cmd_led_on); 49 | SerialCommand cmd_led_off_("OFF", cmd_led_off); 50 | // Add one_key commands to call the same on and off function but by simply 51 | // pressing '1' or '0' without needing a terminating key stroke. 52 | // One_key commands are ONLY tested when they are the first key pressed on 53 | // startup, or the first key after a previous terminating keystroke. 54 | SerialCommand cmd_led_on_ok_("1", cmd_led_on, true); 55 | SerialCommand cmd_led_off_ok_("0", cmd_led_off, true); 56 | 57 | 58 | void setup() 59 | { 60 | Serial.begin(115200); 61 | 62 | //Configure the LED for output and sets the intial state to off 63 | pinMode(kLedPin, OUTPUT); 64 | digitalWrite(kLedPin, LOW); 65 | 66 | serial_commands_.SetDefaultHandler(cmd_unrecognized); 67 | serial_commands_.AddCommand(&cmd_led_on_); 68 | serial_commands_.AddCommand(&cmd_led_off_); 69 | serial_commands_.AddCommand(&cmd_led_on_ok_); 70 | serial_commands_.AddCommand(&cmd_led_off_ok_); 71 | 72 | Serial.println("Ready!"); 73 | Serial.println("\nLED On : type: 'ON' + press Enter, or only '1' without enter\n" 74 | "LED Off: type 'OFF' + press Enter, or only '0' without enter.\n"); 75 | } 76 | 77 | void loop() 78 | { 79 | serial_commands_.ReadSerial(); 80 | } 81 | -------------------------------------------------------------------------------- /examples/SerialCommandsSimple/SerialCommandsSimple.ino: -------------------------------------------------------------------------------- 1 | /*-------------------------------------------------------------------- 2 | Author : Pedro Pereira 3 | License : BSD 4 | Repository : https://github.com/ppedro74/Arduino-SerialCommands 5 | 6 | SerialCommands Simple Demo 7 | 8 | --------------------------------------------------------------------*/ 9 | #include 10 | #include 11 | 12 | // Pin 13 has an LED connected on most Arduino boards. 13 | // Pin 11 has the LED on Teensy 2.0 14 | // Pin 6 has the LED on Teensy++ 2.0 15 | // Pin 13 has the LED on Teensy 3.0 16 | const int kLedPin = 13; 17 | 18 | char serial_command_buffer_[32]; 19 | SerialCommands serial_commands_(&Serial, serial_command_buffer_, sizeof(serial_command_buffer_), "\r\n", " "); 20 | 21 | //This is the default handler, and gets called when no other command matches. 22 | void cmd_unrecognized(SerialCommands* sender, const char* cmd) 23 | { 24 | sender->GetSerial()->print("Unrecognized command ["); 25 | sender->GetSerial()->print(cmd); 26 | sender->GetSerial()->println("]"); 27 | } 28 | 29 | //called for ON command 30 | void cmd_led_on(SerialCommands* sender) 31 | { 32 | digitalWrite(kLedPin, HIGH); 33 | sender->GetSerial()->println("Led is on"); 34 | } 35 | 36 | //called for OFF command 37 | void cmd_led_off(SerialCommands* sender) 38 | { 39 | digitalWrite(kLedPin, LOW); 40 | sender->GetSerial()->println("Led is off"); 41 | } 42 | 43 | //Note: Commands are case sensitive 44 | SerialCommand cmd_led_on_("ON", cmd_led_on); 45 | SerialCommand cmd_led_off_("OFF", cmd_led_off); 46 | 47 | void setup() 48 | { 49 | Serial.begin(57600); 50 | 51 | //Configure the LED for output and sets the intial state to off 52 | pinMode(kLedPin, OUTPUT); 53 | digitalWrite(kLedPin, LOW); 54 | 55 | serial_commands_.SetDefaultHandler(cmd_unrecognized); 56 | serial_commands_.AddCommand(&cmd_led_on_); 57 | serial_commands_.AddCommand(&cmd_led_off_); 58 | 59 | Serial.println("Ready!"); 60 | } 61 | 62 | void loop() 63 | { 64 | serial_commands_.ReadSerial(); 65 | } 66 | -------------------------------------------------------------------------------- /keywords.txt: -------------------------------------------------------------------------------- 1 | SERIAL_COMMANDS_ERRORS KEYWORD1 2 | SerialCommand KEYWORD1 3 | SerialCommands KEYWORD1 4 | 5 | AddCommand KEYWORD2 6 | ReadSerial KEYWORD2 7 | GetSerial KEYWORD2 8 | AttachSerial KEYWORD2 9 | DetachSerial KEYWORD2 10 | SetDefaultHandler KEYWORD2 11 | ClearBuffer KEYWORD2 12 | Next KEYWORD2 13 | -------------------------------------------------------------------------------- /library.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "SerialCommands", 3 | "authors": 4 | { 5 | "name": "Pedro Tiago Pereira", 6 | "email": "tiago.private@gmail.com", 7 | "url": "https://github.com/ppedro74" 8 | }, 9 | "keywords": "serial, command, commands, cmd, parse, callback", 10 | "description": "A Library to tokenize and parse commands received over a serial port. Simple and small memory footprint (no dynamic memory allocation)", 11 | "repository": 12 | { 13 | "type": "git", 14 | "url": "https://github.com/ppedro74/Arduino-SerialCommands.git" 15 | }, 16 | "version": "2.2.0", 17 | "frameworks": "arduino", 18 | "platforms": "*" 19 | } 20 | -------------------------------------------------------------------------------- /library.properties: -------------------------------------------------------------------------------- 1 | name=SerialCommands 2 | version=2.2.0 3 | author=Pedro Tiago Pereira 4 | maintainer=Pedro Tiago Pereira 5 | sentence=An Arduino library to tokenize and parse commands received over a serial port. 6 | paragraph=Simple, small footprint, no dynamic memory allocation 7 | category=Data Processing 8 | url=https://github.com/ppedro74/Arduino-SerialCommands 9 | architectures=* 10 | -------------------------------------------------------------------------------- /src/SerialCommands.cpp: -------------------------------------------------------------------------------- 1 | /*-------------------------------------------------------------------- 2 | Author : Pedro Pereira 3 | License : BSD 4 | Repository : https://github.com/ppedro74/Arduino-SerialCommands 5 | --------------------------------------------------------------------*/ 6 | 7 | #include "SerialCommands.h" 8 | 9 | void SerialCommands::AddCommand(SerialCommand* command) 10 | { 11 | #ifdef SERIAL_COMMANDS_DEBUG 12 | Serial.print("Adding #"); 13 | Serial.print(commands_count_); 14 | Serial.print(" cmd=["); 15 | Serial.print(command->command); 16 | Serial.print("]"); 17 | if(command->one_key) 18 | { 19 | Serial.print(" as one-key"); 20 | } 21 | Serial.println(); 22 | #endif 23 | // Default linked list is commands_???? 24 | SerialCommand** cmd_head=&commands_head_; 25 | SerialCommand** cmd_tail=&commands_tail_; 26 | uint8_t *cmd_count = &commands_count_; 27 | 28 | if(command->one_key) 29 | { 30 | // For a one_key command, we switch to the onek_cmds_???? list 31 | cmd_head = &onek_cmds_head_; 32 | cmd_tail = &onek_cmds_tail_; 33 | cmd_count = &onek_cmds_count_; 34 | } 35 | command->next = NULL; 36 | if (*cmd_head == NULL) 37 | { 38 | *cmd_head = *cmd_tail = command; 39 | } 40 | else 41 | { 42 | (*cmd_tail)->next = command; 43 | *cmd_tail = command; 44 | } 45 | (*cmd_count)++; 46 | } 47 | 48 | SERIAL_COMMANDS_ERRORS SerialCommands::ReadSerial() 49 | { 50 | if (serial_ == NULL) 51 | { 52 | return SERIAL_COMMANDS_ERROR_NO_SERIAL; 53 | } 54 | 55 | while (serial_->available() > 0) 56 | { 57 | int ch = serial_->read(); 58 | #ifdef SERIAL_COMMANDS_DEBUG 59 | Serial.print("Read: bufLen="); 60 | Serial.print(buffer_len_); 61 | Serial.print(" bufPos="); 62 | Serial.print(buffer_pos_); 63 | Serial.print(" termPos="); 64 | Serial.print(term_pos_); 65 | if (ch<32) 66 | { 67 | Serial.print(" ch=#"); 68 | Serial.print(ch); 69 | } 70 | else 71 | { 72 | Serial.print(" ch=["); 73 | Serial.print((char)ch); 74 | Serial.print("]"); 75 | } 76 | Serial.println(); 77 | #endif 78 | if (ch <= 0) 79 | { 80 | continue; 81 | } 82 | 83 | if (buffer_pos_ < buffer_len_) 84 | { 85 | buffer_[buffer_pos_++] = ch; 86 | } 87 | else 88 | { 89 | #ifdef SERIAL_COMMANDS_DEBUG 90 | Serial.println("Buffer full"); 91 | #endif 92 | ClearBuffer(); 93 | return SERIAL_COMMANDS_ERROR_BUFFER_FULL; 94 | } 95 | 96 | if(buffer_pos_==1 && CheckOneKeyCmd()) 97 | { 98 | return SERIAL_COMMANDS_SUCCESS; 99 | } 100 | 101 | if (term_[term_pos_] != ch) 102 | { 103 | term_pos_ = 0; 104 | continue; 105 | } 106 | 107 | if (term_[++term_pos_] == 0) 108 | { 109 | buffer_[buffer_pos_ - strlen(term_)] = '\0'; 110 | 111 | #ifdef SERIAL_COMMANDS_DEBUG 112 | Serial.print("Received: ["); 113 | Serial.print(buffer_); 114 | Serial.println("]"); 115 | #endif 116 | char* command = strtok_r(buffer_, delim_, &last_token_); 117 | if (command != NULL) 118 | { 119 | boolean matched = false; 120 | int cx; 121 | SerialCommand* cmd; 122 | for (cmd = commands_head_, cx = 0; cmd != NULL; cmd = cmd->next, cx++) 123 | { 124 | #ifdef SERIAL_COMMANDS_DEBUG 125 | Serial.print("Comparing ["); 126 | Serial.print(command); 127 | Serial.print("] to ["); 128 | Serial.print(cmd->command); 129 | Serial.println("]"); 130 | #endif 131 | 132 | if (strncmp(command, cmd->command, strlen(cmd->command) + 1) == 0) 133 | { 134 | #ifdef SERIAL_COMMANDS_DEBUG 135 | Serial.print("Matched #"); 136 | Serial.println(cx); 137 | #endif 138 | cmd->function(this); 139 | matched = true; 140 | break; 141 | } 142 | } 143 | if (!matched && default_handler_ != NULL) 144 | { 145 | (*default_handler_)(this, command); 146 | } 147 | } 148 | 149 | ClearBuffer(); 150 | } 151 | } 152 | 153 | return SERIAL_COMMANDS_SUCCESS; 154 | } 155 | 156 | bool SerialCommands::CheckOneKeyCmd() 157 | { 158 | #ifdef SERIAL_COMMANDS_DEBUG 159 | Serial.println("Testing for one_key commands."); 160 | #endif 161 | 162 | int cx; 163 | SerialCommand* cmd; 164 | for (cmd = onek_cmds_head_, cx = 0; cmd != NULL; cmd = cmd->next, cx++) 165 | { 166 | #ifdef SERIAL_COMMANDS_DEBUG 167 | Serial.print("Testing ["); 168 | Serial.print(buffer_[0]); 169 | Serial.print("] to ["); 170 | Serial.print(cmd->command[0]); 171 | Serial.println("]"); 172 | #endif 173 | if (buffer_[0] == cmd->command[0]) 174 | { 175 | #ifdef SERIAL_COMMANDS_DEBUG 176 | Serial.print("Matched #"); 177 | Serial.println(cx); 178 | #endif 179 | cmd->function(this); 180 | ClearBuffer(); 181 | return true; 182 | } 183 | } 184 | 185 | return false; 186 | } 187 | 188 | 189 | Stream* SerialCommands::GetSerial() 190 | { 191 | return serial_; 192 | } 193 | 194 | void SerialCommands::AttachSerial(Stream* serial) 195 | { 196 | serial_ = serial; 197 | } 198 | 199 | void SerialCommands::DetachSerial() 200 | { 201 | serial_ = NULL; 202 | } 203 | 204 | void SerialCommands::SetDefaultHandler(void(*function)(SerialCommands*, const char*)) 205 | { 206 | default_handler_ = function; 207 | } 208 | 209 | void SerialCommands::ClearBuffer() 210 | { 211 | buffer_[0] = '\0'; 212 | buffer_pos_ = 0; 213 | term_pos_ = 0; 214 | } 215 | 216 | char* SerialCommands::Next() 217 | { 218 | return strtok_r(NULL, delim_, &last_token_); 219 | } 220 | 221 | -------------------------------------------------------------------------------- /src/SerialCommands.h: -------------------------------------------------------------------------------- 1 | /*-------------------------------------------------------------------- 2 | Author : Pedro Pereira 3 | License : BSD 4 | Repository : https://github.com/ppedro74/Arduino-SerialCommands 5 | --------------------------------------------------------------------*/ 6 | 7 | //#define SERIAL_COMMANDS_DEBUG 8 | 9 | #ifndef SERIAL_COMMANDS_H 10 | #define SERIAL_COMMANDS_H 11 | 12 | #include 13 | 14 | typedef enum ternary 15 | { 16 | SERIAL_COMMANDS_SUCCESS = 0, 17 | SERIAL_COMMANDS_ERROR_NO_SERIAL, 18 | SERIAL_COMMANDS_ERROR_BUFFER_FULL 19 | } SERIAL_COMMANDS_ERRORS; 20 | 21 | class SerialCommands; 22 | 23 | typedef class SerialCommand SerialCommand; 24 | class SerialCommand 25 | { 26 | public: 27 | SerialCommand(const char* cmd, void(*func)(SerialCommands*), bool one_k=false) 28 | : command(cmd), 29 | function(func), 30 | next(NULL), 31 | one_key(one_k) 32 | { 33 | } 34 | 35 | const char* command; 36 | void(*function)(SerialCommands*); 37 | SerialCommand* next; 38 | bool one_key; 39 | }; 40 | 41 | class SerialCommands 42 | { 43 | public: 44 | SerialCommands(Stream* serial, char* buffer, int16_t buffer_len, const char* term = "\r\n", const char* delim = " ") : 45 | serial_(serial), 46 | buffer_(buffer), 47 | buffer_len_(buffer!=NULL && buffer_len > 0 ? buffer_len - 1 : 0), //string termination char '\0' 48 | term_(term), 49 | delim_(delim), 50 | default_handler_(NULL), 51 | buffer_pos_(0), 52 | last_token_(NULL), 53 | term_pos_(0), 54 | commands_head_(NULL), 55 | commands_tail_(NULL), 56 | onek_cmds_head_(NULL), 57 | onek_cmds_tail_(NULL), 58 | commands_count_(0), 59 | onek_cmds_count_(0) 60 | { 61 | } 62 | 63 | 64 | /** 65 | * \brief Adds a command handler (Uses a linked list) 66 | * \param command 67 | */ 68 | void AddCommand(SerialCommand* command); 69 | 70 | /** 71 | * \brief Checks the Serial port, reads the input buffer and calls a matching command handler. 72 | * \return SERIAL_COMMANDS_SUCCESS when successful or SERIAL_COMMANDS_ERROR_XXXX on error. 73 | */ 74 | SERIAL_COMMANDS_ERRORS ReadSerial(); 75 | 76 | /** 77 | * \brief Returns the source stream (Serial port) 78 | * \return 79 | */ 80 | Stream* GetSerial(); 81 | 82 | /** 83 | * \brief Attaches a Serial Port to this object 84 | * \param serial 85 | */ 86 | void AttachSerial(Stream* serial); 87 | 88 | /** 89 | * \brief Detaches the serial port, if no serial port nothing will be done at ReadSerial 90 | */ 91 | void DetachSerial(); 92 | 93 | /** 94 | * \brief Sets a default handler can be used for a catch all or unrecognized commands 95 | * \param function 96 | */ 97 | void SetDefaultHandler(void(*function)(SerialCommands*, const char*)); 98 | 99 | /** 100 | * \brief Clears the buffer, and resets the indexes. 101 | */ 102 | void ClearBuffer(); 103 | 104 | /** 105 | * \brief Gets the next argument 106 | * \return returns NULL if no argument is available 107 | */ 108 | char* Next(); 109 | 110 | private: 111 | Stream* serial_; 112 | char* buffer_; 113 | int16_t buffer_len_; 114 | const char* term_; 115 | const char* delim_; 116 | void(*default_handler_)(SerialCommands*, const char*); 117 | int16_t buffer_pos_; 118 | char* last_token_; 119 | int8_t term_pos_; 120 | SerialCommand* commands_head_; 121 | SerialCommand* commands_tail_; 122 | SerialCommand* onek_cmds_head_; 123 | SerialCommand* onek_cmds_tail_; 124 | uint8_t commands_count_; 125 | uint8_t onek_cmds_count_; 126 | 127 | /** 128 | * \brief Tests for any one_key command and execute it if found 129 | * \return false if this was not a one_key command, true otherwise with 130 | * also clearing the buffer 131 | **/ 132 | bool CheckOneKeyCmd(); 133 | }; 134 | 135 | #endif 136 | --------------------------------------------------------------------------------