AceRoutine  1.0
A low-memory, fast-switching, cooperative multitasking library using stackless coroutines on Arduino platforms.
/home/brian/dev/AceRoutine/src/ace_routine/cli/README.md
1 # Command Line Interface
2 
3 These classes implement a non-blocking command line interface on the Serial
4 port. In ther words, you can implement a primitive "shell" for the Arduino.
5 
6 These classes were initially an experiment to validate the `AceRoutine` macros
7 and classes but they seem to be useful as an independent library. They may be
8 moved to a separate project/repository later.
9 
10 Version: (2019-07-23)
11 
12 ## Usage
13 
14 The basic steps for adding a command line interface to an Arduino sketch
15 using the `cli/` library is the following:
16 
17 1. Create a `CommandHandler` class for each command, defining its
18  `name` and `helpString`.
19 1. Create a static array of `CommandHandler*` pointers with all the commands
20  that you would like to suport.
21 1. Create a `CommandManager` object, giving it the `CommandHandler*` array,
22  and a number of size parameters for various internal buffers (maximum line
23  buffer length, and maximum number of `argv` parameters for a command).
24 1. Insert the `CommandManager` into the `CoroutineScheduler` by calling
25  `commandManager.setupCoroutine()` just before `CoroutineScheduler::setup()`.
26 1. Run the `CoroutineScheduler::loop` in the global `loop()` method to
27  run the `CommandManager` as a coroutine.
28 
29 ### Command Handler and Arguments
30 
31 The `CommandHandler` class defines the command's `name`, `helpString` and
32 the `run()` method that implements the command. It takes the following
33 parameters:
34 
35 * `printer` is the output device, which will normally be the global `Serial`
36  object
37 * `argc` is the number of `argv` arguments
38 * `argv` is the array of `cont char*` pointers, each pointing to the words
39  of the command line delimited by whitespaces. These are identical to
40  the `argc` and `argv` parameters passed to the C-language `main(argc, argv)`
41  function. For example, `argv[0]` is the name of the command, and `argv[1]`
42  is the first argument after the command (if it exists).
43 
44 ### CommandManager
45 
46 The `CommandManager` is a templatized convenience class that creates all the
47 helper objects and buffers needed to read and parse the command line input.
48 It includes:
49 
50 * a `StreamLineReader` coroutine that reads the input lines from `Serial`
51 * a `CommandDispatcher` coroutine that parses the input lines
52 * a `Channel<InputLine>` from `StreamLineReader` to `CommandDispatcher`
53 * a line buffer for the input lines
54 * a array of `(const char*)` to hold the command line arguments of the command
55 
56 You don't have to use the `CommandManager`, but it greatly simplies the creation
57 and usage of the `CommandDispatcher`.
58 
59 ### CommandHandler Definitions and Setup
60 
61 An Arduino `.ino` file that uses the CLI classes to implement a commmand line
62 shell will look something like this:
63 
64 ```C++
65 #include <AceRoutine.h>
66 #include <ace_routine/cli/CommandManager.h>
67 
68 using namespace ace_routine;
69 using namespace ace_routine::cli;
70 
71 class FooCommand: public CommandHandler {
72  FooCommand():
73  CommandHandler("{fooName}", "{helpString}") {}
74 
75  virtual void run(Print& printer, int argc, const char* const* argv)
76  const override {
77  ...
78  }
79 };
80 
81 class BarCommand: public CommandHandler {
82  BarCommand():
83  CommandHandler(F("{barCommand}"), F("{helpString}")) {}
84 
85  virtual void run(Print& printer, int argc, const char* const* argv)
86  const override {
87  ...
88  }
89 };
90 
91 FooCommand fooCommand;
92 BarCommand barCommand;
93 
94 static const CommandHandler* const COMMANDS[] = {
95  &fooCommand,
96  &barCommand,
97 };
98 static uint8_t const NUM_COMMANDS = sizeof(COMMANDS) / sizeof(CommandHandler*);
99 
100 uint8_t const BUF_SIZE = 64; // maximum size of an input line
101 uint8_t const ARGV_SIZE = 10; // maximum number of tokens in command
102 char const PROMPT[] = "$ ";
103 
104 CommandManager<BUF_SIZE, ARGV_SIZE> commandManager(
105  COMMANDS, NUM_COMMANDS, Serial, PROMPT);
106 
107 void setup() {
108  ...
109  commandManager.setupCoroutine("commandManager");
110  CoroutineScheduler::setup();
111 }
112 
113 void loop() {
114  CoroutineScheduler::loop();
115 }
116 ```
117 
118 ### Argc and Argv Parsing
119 
120 Within the `CommandHandler`, there are several helper routines which are useful
121 for processing the `argc` and `argv` arguments:
122 
123 * `SHIFT_ARGC_ARGV(argc, argv)` macro shifts the input tokens by 1, incrementing
124  `argv` and decrementing `argc`
125 * `bool isArgEqual(const char*, const char*);`
126 * `bool isArgEqual(const char*, const __FlashHelperString*);`
127 
128 Here is the sketch of the Command that will parse a command whose syntax is
129 `delay [(on | off) {millis}]`, where the `on {millis}` and `off {millis}`
130 arguments to the `delay` command are optional:
131 
132 ```C++
133 class DelayCommand: public CommandHandler {
134  DelayCommand():
135  CommandHandler(F("delay"), F("[(on | off) {millis}")) {}
136 
137  virtual void run(Print& printer, int argc, const char* const* argv)
138  const override {
139  if (argc == 1) {
140  printer.println(F("'delay' typed with no arguments"));
141  return;
142  }
143 
144  if (argc != 3) {
145  printer.println(F("Incorrect number of arguments to 'delay' command"));
146  return;
147  }
148 
149  SHIFT_ARGC_ARGV(argc, argv);
150  if (isArgEqual(argv[0], "on")) {
151  int delay = atoi(argv[1]));
152  printer.print(F("Executing: delay on "));
153  printer.println(delay);
154  } else if (isArgEqual(argv[0], F("off"))) {
155  int delay = atoi(argv[1]));
156  printer.print(F("Executing: delay off "));
157  printer.println(delay);
158  } else {
159  printer.println(F("Unknown argument to 'delay'"));
160  }
161  }
162 };
163 ```
164 
165 ## Example
166 
167 See [examples/CommandLineShell/](../../../examples/CommandLineShell/)
168 for an demo program that implements 5 commands:
169 
170 * `help [command]`
171 * `list`
172 * `free`
173 * `echo [args ...]`
174 * `delay [(on | off) millis]`