AceUtils
0.6.0
Useful Arduino utilties which are too small as separate libraries, but complex enough to be shared among multiple projects, and often have external dependencies to other libraries.
|
These classes implement a non-blocking command line interface on the Serial port. In other words, you can implement a primitive "shell" for the Arduino.
These classes were initially an experiment to validate the AceRoutine
macros and classes but they seem to be useful as an independent library. They may be moved to a separate project/repository later.
Version: (2021-07-19)
This class is a thin wrapper around a DirectProcessor
and a CommandDispatcher. The
Stream` input is scanned using a non-blocking loop without using coroutines. This is the simplest option if you do not want to depend on the AceRoutine library.
The basic steps for adding a command line interface to an Arduino sketch using the cli/
library is the following:
CommandHandler
class for each command, defining its name
and helpString
.CommandHandler*
pointers with all the commands that you would like to support.DirectProcessorManager
object, giving it the CommandHandler*
array, and a number of size parameters for various internal buffers (maximum line buffer length, and maximum number of argv
parameters for a command).CoroutineScheduler::setup()
in the global setup()
function.CoroutineScheduler::loop()
in the global loop()
function to run the StreamProcessorCoroutine
that was created inside the StreamProcessorManager
.The dependency diagram looks like this:
The StreamProcessorManager
is a templatized convenience class that creates all the helper objects and buffers needed to read and parse the command line input. It includes:
StreamProcessorCoroutine
coroutine that reads lines from the given Stream
object.CommandDispatcher
instance that knows how to tokenize a string line and call the matching CommandHandler
(const char*)
to hold the command line arguments of the commandYou don't have to use the StreamProcessorManager
, but it greatly simplifies the creation and usage of the StreamProcessorCoroutine
.
This class is a thin wrapper around a StreamProcessoCoroutine
and a CommandDispatcher. The
Stream` input is scanned using an AceRoutine coroutine. This may be an option if your application already uses AceRoutine and you want everything handled as a coroutine.
The basic steps for adding a command line interface to an Arduino sketch using the cli/
library is the following:
CommandHandler
class for each command, defining its name
and helpString
.CommandHandler*
pointers with all the commands that you would like to support.StreamProcessorManager
object, giving it the CommandHandler*
array, and a number of size parameters for various internal buffers (maximum line buffer length, and maximum number of argv
parameters for a command).CoroutineScheduler::setup()
in the global setup()
function.CoroutineScheduler::loop()
in the global loop()
function to run the StreamProcessorCoroutine
that was created inside the StreamProcessorManager
.The dependency diagram looks like this:
The StreamProcessorManager
is a templatized convenience class that creates all the helper objects and buffers needed to read and parse the command line input. It includes:
StreamProcessorCoroutine
coroutine that reads lines from the given Stream
object.CommandDispatcher
instance that knows how to tokenize a string line and call the matching CommandHandler
(const char*)
to hold the command line arguments of the commandYou don't have to use the StreamProcessorManager
, but it greatly simplifies the creation and usage of the StreamProcessorCoroutine
.
Deprecated: This uses the experimental ace_routine::Channel
class to allow passing the input string from the StreamReaderCoroutine
to the ChannelProcessorCoroutine
. It turns out that the direct approach of StreamProcessorCoroutine
(above) is simpler with the same functionality, without using the Channel
class.
The basic steps for adding a command line interface to an Arduino sketch using the cli/
library is the following:
CommandHandler
class for each command, defining its name
and helpString
.CommandHandler*
pointers with all the commands that you would like to support.ChannelProcessorManager
object, giving it the CommandHandler*
array, and a number of size parameters for various internal buffers (maximum line buffer length, and maximum number of argv
parameters for a command).CoroutineScheduler::setup()
in the global setup()
function.CoroutineScheduler::loop()
in the global loop()
function to run the ChannelProcessorCoroutine
and StreamReaderCoroutine
which were created inside the ChannelProcessorManager
.The dependency diagram looks like this:
The ChannelProcessorManager
is a templatized convenience class that creates all the helper objects and buffers needed to read and parse the command line input. It includes:
StreamReaderCoroutine
coroutine that reads the input lines from Serial
ChannelProcessorCoroutine
coroutine that parses the input lines inside Channel<InputLine>
, from StreamReaderCoroutine
to ChannelProcessorCoroutine
CommandDispatcher
instance that knows how to tokenize a string line and call the matching CommandHandler
(const char*)
to hold the command line arguments of the commandYou don't have to use the ChannelProcessorManager
, but it greatly simplifies the creation and usage of the ChannelProcessorCoroutine
.
(TBD: Add documentation or example of a command line shell over MQTT messages.)
The CommandHandler
class defines the command's name
, helpString
and the run()
method that implements the command. It takes the following parameters:
printer
is the output device, which will normally be the global Serial
objectargc
is the number of argv
argumentsargv
is the array of cont char*
pointers, each pointing to the words of the command line delimited by whitespaces. These are identical to the argc
and argv
parameters passed to the C-language main(argc, argv)
function. For example, argv[0]
is the name of the command, and argv[1]
is the first argument after the command (if it exists).An Arduino .ino
file that uses the CLI classes to implement a command line shell will look something like this:
Within the CommandHandler
, there are several helper routines which are useful for processing the argc
and argv
arguments:
shiftArgcArgv(argc, argv)
shifts the input tokens by 1 token to the left, by incrementing argv
and decrementing argc
bool isArgEqual(const char*, const char*);
bool isArgEqual(const char*, const __FlashHelperString*);
Here is the sketch of the Command that will parse a command whose syntax is delay [(on | off) {millis}]
, where the on {millis}
and off {millis}
arguments to the delay
command are optional:
Each of the following examples implements 5 commands:
All 3 implement the following commands:
help [command]
list
free
echo [args ...]
delay [(on | off) millis]