The serial command handler is an Arduino library to decode and process commands sent to the Arduino using a serial port by MegunoLink (or any serial program). When the library receives a command it can either:
- Call an Arduino function
- Update an Arduino variable
Serial commands can be sent from MegunoLink’s Raw Serial Visualizer, Message Monitor Visualizer or you can build an Interface Panel that sends a serial command when you click a button. Commands can include multiple parameter values, limited only by the memory you set aside for the command handler in your Arduino sketch.
Serial Command Format
By default, commands start with a ‘!’ character and end with a carriage return (‘\r’) though both can be customized. Parameters can be included in the serial message and are separated using a space. Example commands:
!SetPoint 4\r
!StartMotor\r
!TurnLEDsOn 1 2 3\r
Using the Serial Command Handler
To use the serial command handler in your program you need to:
#include "CommandHandler.h"
to make the library available to your program- Create a
CommandHandler<> SerialCommandHandler;
variable - Add the commands you want to handle
- To call a function when a command is received use
SerialCommandHandler.AddCommand(F("CommandName"), Cmd_FunctionToCall)
- To update a variable when a command is received use
SerialCommandHandler.AddVariable(F("CommandName"), VariableName)
- To call a function when a command is received use
- Implement the command functions. Each must take a single
CommandParameter
reference, even if it isn’t used. - Call
SerialCommandHandler.Process()
in your main loop function to receive and decode commands
For a detailed walk through see Getting started processing serial commands
Command limits
The maximum number of commands and variables that can be registered, and the maximum length of a command (including all parameters) is set when the command handler variable is created. The default is 10 commands and variables and 30 character commands.
The command and variable limits can be increased to register more commands and/or variables. Decreasing the limits will save memory. Each additional command uses 4 bytes of RAM on an Arduino Uno; each additional variable uses 8 bytes of RAM on an Arduino Uno. See command handler construction and example declarations below.
The command handler holds a buffer to assemble new commands. This buffer must be large enough to handle the command and all of the parameters. It can be increased to handler longer commands or decreased to save memory. See command handler construction and example declarations below.
Serial Command Handler Reference
The CommandHandler
is implemented as a C++ template:
template <int MAX_COMMANDS = 10, int CP_SERIAL_BUFFER_SIZE = 30, int MAX_VARIABLES=10> class CommandHandler{…}
Command Store Size
The CommandHandler
uses a fixed size array to store the command names and functions to call. By default 10 commands can be added to the handler. This can be changed using the first template parameter. You would need to increase this if you want to handle more commands. You could decrease this to save a little bit of memory. Each command slot uses 4 bytes.
See command handler constructor for examples.
Command Buffer Size
The CommandHandler
uses a fixed-size buffer to store characters as they are received from the serial port until a complete message is received. By default this buffer is 30 characters long. This can be changed using the second template parameter. You could increase this if you are sending long commands, or commands with lots of parameters. You could decrease this to save memory. The buffer has to be large enough to contain the start of message character (‘!’) and the terminator (‘\r’) and all the characters in between.
See command handler constructor for examples.
Variable Store Size
The CommandHandler
uses a fixed-size buffer to store the map from command name to variable. By default 10 variables can be added to the handler. This can be changed using the third template parameter. You’d need to increase this if you want to handle more variables. Decreasing this would save a little bit of memory. Each command slot uses 7 bytes.
See command handler constructor for examples.
Command Handler construction
The CommandHandler
constructor takes three (optional) arguments:
CommandHandler(Stream &SourceStream = Serial, char StartOfMessage = '!', char EndOfMessage = '\r')
SourceStream
the serial port that the command handler should listen to. Normally this isSerial
, the Arduino’s default serial port. But some devices (such as the Mega support additional ports. To receive data on the second Mega serial port, you’d passSerial2
as the first argument when creating the command handler variable,StartOfMessage
is the character that the command handler looks for to mark the start of a new message. By default it is a ‘!’. Whenever the command handler encounters the start character it will discard whatever it has so far and begin building a new message. So you need to make sure the start character will never be found in the message itself.EndOfMesasge
is the character that marks the end of a message. By default it is a carriage return (‘\r’). When the end character is found, the command handler tries to match the command it has received and call the matching function. You need to make sure the end character is never found as part of the message itself.
Example Serial Command Handler declarations
Declaration | Maximum number of Commands | Maximum command length | Maximum number of Variables | Start of Message | End of Message |
CommandHandler<> SerialCommandHandler; |
10 (default) | 30 characters (default) | 10 (default) | ! |
\r |
CommandHandler<15> SerialCommandHandler; |
15 | 30 characters (default) | 10 (default) | ! |
\r |
CommandHandler<12,20> SerialCommandHandler; |
12 | 20 characters | 10 (default) | ! |
\r |
CommandHandler<10, 30, 15> SerialCommandHandler; |
10 (default) | 30 characters (default) | 15 | ! |
\r |
CommandHandler<> SerialCommandHandler(Serial1); |
10 (default) | 30 characters (default) | 10 (default) | ! |
\r |
CommandHandler<> SerialCommandHandler(Serial2, '#', '^'); |
10 (default) | 30 characters (default) | 10 (default) | # |
^ |
Adding Commands
To add a command, call SerialCommandHandler.AddCommand(F("CommandName"), Cmd_CommandFunction);
. This would typically be used in the setup
function of your program.
CommandName
is the text the command handler will look for between the start of a command message and the first space (or end of the command message). Normally a ‘!’ marks the start of the command message and a ‘\r’ marks the end of the command message. So, for example, On
is the command in each of these messages: !On\r
, !On 23\r
. But not in this one: !Onguard 12\r
. The command handler will print AddCommand: full
if there isn’t enough space to store another command.
Cmd_CommandFunction
is the Arduino function in your program that will be called whenever the command message is found. It can have any name you like, but it must take one parameter, a CommandParameter &
. So the function should look like this:
1 2 3 4 |
void Cmd_CommandFunction(CommandParameter ¶meter) { // so something exciting } |
The parameter
variable lets you get any parameters that came with the command. See working with parameters below.
Adding Variables
To add a variable, call SerialCommandHandler.AddVariable(F("VariableName"), Variable);
. This would typically be used in the setup
function of your program. The command handler will print AddVariable: full
if there isn’t enough space to store the variable.
VariableName
is the text the command handler will look for between the start of a command message and the first space (or end of the command message). Normally a ‘!’ marks the start of the command message and a ‘\r’ marks the end of the command message.
Variable
is the variable you want the command handler to update when the variable name in the command matches a registered variable. This variable should be in the global scope of your program. For example, a variable declared at the top of your program outside any functions.
So, for example, if you register SerialCommandHandler.AddVariable(F("SetPoint"), SetPoint)
the command !SetPoint 20\r
would update the SetPoint
variable to 20.
The following types of variables are supported:
- float
- double
- byte
- short
- int
- long
- uint8_t
- uint16_t
- uint32_t
- int8_t
- int16_t
- int32_t
Default Handler
When first setting up a new program it can be helpful to know if messages are received that don’t match any known commands. This can point to errors in the commands you are sending, differences in spelling or case (capital vs lower case letters) or too many commands/variables were registered (by default only 10 commands and 10 variables are supported). By default, the command handler will print Unknown command
if an unrecognized command is encountered.
Use SerialCommandHandler.SetDefaultHandler(UnknownMessageHandler);
to register a default message handler. UnknownMessageHandler
is a function in your program that will be called whenever an unrecognized command is encountered. It shouldn’t take any parameters. A typically unknown message handler would be:
1 2 3 4 |
void UnknownMessageHandler() { Serial.println(F("I don't know that command")); } |
Clearing Registered Commands
You can clear all the registered commands using the SerialCommandHandler.ClearCommands();
function.
Error Messages
Error messages reported by the command handler are summarized in the table below.
Message | Description |
---|---|
AddCommand: full | The command store is full. Increase the store size or reduce the number of commands registered |
AddVariable: full | The variable store is full. Increase the store size or reduce the number of variables registered |
Unknown command | An unknown command/variable was encountered. Check the command was registered (with AddCommand(…) or AddVariable(…) ) and the store isn’t full (see AddCommand: full and/or AddVariable: full above. |
Ovrflw | The command received was too long to fit in the buffer. Reduce the command length or increase the size of the command buffer |
Working with Parameters
When the handler matches a command it will call the function registered for that command. This function must take a single parameter, a CommandParameter &
. So a typical command handler function would look like this:
1 2 3 4 |
void Cmd_HandleCommand(CommandParameter ¶meter) { Serial.println(F("the command is handled")); } |
You need to include the parameter, even it is not used in the command handler function. Otherwise your program won’t build.
The CommandParameter
provides access to any parameters that were included in the serial message. Parameters are separated by spaces.
A CommandParameter
supports the following methods:
NextParameter()
returns the next parameter (as text) in the serial messageRemainingParameters()
returns the rest of the message as textNextParameterAsInteger(Default)
interprets the next parameter as an integer (16 bit) value; returns Default if the next parameter isn’t a valid integerNextParameterAsLong(Default)
interprets the next parameter as an long (32 bit) value; returns Default if the next parameter isn’t a valid integerNextParameterAsUnsignedLong(Default)
interprets the next parameter as an unsigned long (32 bit) value; returns Default if the next parameter isn’t validNextParameterAsDouble(Default)
interprets the next parameter as an double value; returns Default if the next parameter isn’t valid
Example
Sending !SetTurtleCount 5\r
from MegunoLink Pro will update the NumberOfTurtles
variable in this simple example. Sending !GetTurtleCount\r
will print the current value of the NumberOfTurtles
variable to the serial port.
These commands can be sent from the Arduino serial monitor or MegunoLink’s serial monitor. Or you can build a custom user interface with buttons that send commands so you don’t have to remember them all.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
#include "MegunoLink.h" #include "CommandHandler.h" int NumberOfTurtles = 0; CommandHandler<> SerialCommandHandler; void Cmd_GetTurtleCount(CommandParameter &Parameters) { Parameters.GetSource().print(F("Number of turtles = ")); Parameters.GetSource().println(NumberOfTurtles); } void Cmd_SetTurtleCount(CommandParameter &Parameters) { NumberOfTurtles=Parameters.NextParameterAsInteger(); } void setup() { Serial.begin(9600); Serial.println("MegunoLink Pro Turtle Monitor"); Serial.println("-----------------------------"); SerialCommandHandler.AddCommand(F("SetTurtleCount"), Cmd_SetTurtleCount); SerialCommandHandler.AddCommand(F("GetTurtleCount"), Cmd_GetTurtleCount); } void loop() { SerialCommandHandler.Process(); } |