AceRoutine  0.1
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 * `StreamReader` - a class that provided non-blocking methods for
7  reading various types of input tokens from the `Serial` port. Supported tokens
8  include whole lines (terminated by newline), words (separated by whitespace),
9  and integers.
10 * `CommandDispatcher` - a class that uses the `StreamReader` and
11  the `AceRoutine` library to read command lines from the `Serial` port, parse
12  the command line string, then dispatch to the appropriate `CommandHandler` to
13  perform the associated command.
14 
15 These classes were initially an experiment to validate the `AceRoutine` macros
16 and classes but they seem to be useful as an independent library. They may be
17 moved to a separate project/repository later.
18 
19 Version: (2018-07-20)
20 
21 ## Usage
22 
23 The basic steps for adding a command line interface to an Arduino sketch
24 using the `cli/` library is the following:
25 
26 1. Create a `StreamReader`, giving it the buffers that it needs.
27 1. Create a `DispatchTable` containing the list of commands whose signature
28  matches `CommandHandler`.
29 1. Create a `CommanDispatcher` which reads a whole line from the Serial port,
30  and dispatches to the appropriate command.
31 1. Run the `CommandDispatcher` as an `AceRoutine` coroutine in the global
32  `loop()`.
33 
34 ### Command Dispatcher
35 
36 The `CommandDispatcher` is a subclass of the `Coroutine` class and implements a
37 coroutine in the `run()` method. This is a manually created coroutine,
38 not managed by the `COROUTINE()` macro, so the
39 the `resume()` method must be called to add it to the `CoroutineScheduler`.
40 
41 The calling client is expected to create one of 2 subclasses of
42 `CommandDispatcher`:
43 * `CommandDispatcherC`: accepts an array of `DispatchRecordC` objects which
44  hold normal C-strings (i.e. `const char*`)
45 * `CommandDispatcherF`: accepts an array of `DispatchRecordF` objects which
46  hold
47  [PROGMEM](https://arduino.cc/reference/en/language/variables/utilities/progmem/)
48  strings stored in flash memory (i.e. `const __FlashStringHelper*`)
49 
50 On devices with limited static RAM like AVR boards, using flash strings can be
51 critical to allowing the program to run.
52 
53 ### Command Handler and Arguments
54 
55 The `CommandHandler` typedef is a pointer to a user-defined function that has
56 the following signature:
57 ```
58 typedef void (*CommandHandler)(Print& printer, int argc, const char** argv);
59 ```
60 
61 * `printer` is the output device, which will normally be the global `Serial`
62  object
63 * `argc` is the number of `argv` arguments
64 * `argv` is the array of `cont char*` pointers, each pointing to the words
65  of the command line delimited by whitespaces. These are identical to
66  the `argc` and `argv` parameters passed to the C-language `main(argc, argv)`
67  function. For example, `argv[0]` is the name of the command, and `argv[1]`
68  is the first argument after the command (if it exists).
69 
70 ### Memory Allocation
71 
72 Both the `StreamReader` and `CommandDispatcher` perform no memory allocations
73 internally (no `malloc()`, no `new` operator) to avoid memory problems. All
74 data structures used by these classes must be pre-allocated by the calling code.
75 Normally they will be created at static initialization time (before the global
76 `setup()` method is called), but the calling code is allowed to create them on
77 the heap if it wants or needs to.
78 
79 ### Structure of Client Calling Code
80 
81 An Arduino `.ino` file that uses the CLI classes to implement a commmand line
82 shell will look something like this:
83 
84 ```
85 #include <AceRoutine.h>
86 #include <ace_routine/cli/StreamReader.h>
87 #include <ace_routine/cli/CommandDispatcher.h>
88 
89 using namespace ace_routine;
90 using namespace ace_routine::cli;
91 
92 // Create the StreamReader with buffers.
93 const int BUF_SIZE = 64;
94 char lineBuffer[BUF_SIZE];
95 StreamReader streamReader(Serial, lineBuffer, BUF_SIZE);
96 
97 // Define the command handlers.
98 void newCommand(Print& printer, int argc, const char** argv) {
99  ...
100 }
101 
102 void anotherCommand(Print& printer, int argc, const char** argv) {
103  ...
104 }
105 
106 // Create the dispatch table of commands (using C-strings)
107 const DispatchRecordC dispatchTable[] = {
108  {&newCommand, "name-of-command", "help-string"},
109  {&anotherCommand, "name-of-other-command", "help-string"},
110  ...
111 };
112 const uint8_t NUM_COMMANDS = sizeof(dispatchTable) / sizeof(DispatchRecordC);
113 
114 // Create CommandDispatcher with buffers.
115 const int8_t ARGV_SIZE = 10;
116 const char* argv[ARGV_SIZE];
117 CommandDispatcher dispatcher(streamReader, Serial,
118  dispatchTable, NUM_COMMANDS argv, ARGV_SIZE);
119 
120 void setup() {
121  Serial.begin(115200);
122  while (!Serial); // micro/leonardo
123 
124  dispatcher.resume(); // insert into the scheduler
125  CoroutineScheduler::setup();
126 }
127 
128 void loop() {
129  CoroutineScheduler::loop();
130 }
131 ```
132 
133 ## Example
134 
135 See [examples/CommandLineInterface/](../../../examples/CommandLineInterface/)
136 for an demo program that implements 5 commands:
137 
138 * `help [command]`
139 * `list`
140 * `free`
141 * `echo [args ...]`
142 * `delay (on | off) millis`