Skip to content

Commit f278293

Browse files
Update to 4.1.1
Bug fixes including wonky release. Fixed some issues surrounding end of kine characters. Automatically set stripCR to false if end of line character is CR.
1 parent 21f376b commit f278293

File tree

5 files changed

+18
-190
lines changed

5 files changed

+18
-190
lines changed

Changelog.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
Commander changelog
22
(Changelog started at 1.2.3)
3+
4.1.1
4+
Minor bugfix related to wonky resease.
5+
Fixed missing references to endOfLineCharacter that were still '/n'. **Changing endOfLineCharacter may have implications for how streams get printed.
6+
Changed endOfLineChar(char) so that if the end of line character is set to a carriage return, stripCR is automatically set to false.
37

48
4.1.0
59

README.md

Lines changed: 1 addition & 184 deletions
Original file line numberDiff line numberDiff line change
@@ -205,187 +205,4 @@ We can use any available delimiter in the command string so the command 'set int
205205
_Disclaimer: I'm not the best software engineer in the world so there may be some bits of silliness in my code. I welcome contributions that will improve Commander so long as they maintain a good balance between features and efficiency._
206206
207207
Written by Bill Bigge.
208-
MIT license, all text above must be included in any redistributionle via USB and Bluetooth Serial. It also allows different command lists to be dynamically loaded so multiple command lists, and multiple Commander objects can be combined to produce hierarchical command structures.
209-
210-
Commands can be chained together in a single line and Commander incorporates a set of functions for extracting any payload that comes after a command and for sequentially parsing variables in the payload. It can also augment responses sent to its output and auxiliary Streams by adding prefix and postfix text, for example each line can be enclosed with opening and closing html tags, or prefixed with a command.
211-
212-
Built in commands like Help will generate a help page that lists all the commands and any help text. Additional built in commands can be used to toggle error reporting and port echoing and all built in commands can be overridden by the user with their own handler. Lock and Unlock commands can be used to impliment a password system with two levels. A soft lock will allow internal commands to be used (including help) but will not run any user commands. A hard lock will block all commands except unlock. An optional password can be used and is stored outside the Commander object in the users sketch.
213-
214-
Commander can use an optional command prompt with user defined text to emulate the feel of a command line, this prompt can be changed dynamically to indicate the current context, for example it can show the working directory of a file system, or the title of a sub command list. Commander also has a user defined 'reload' character that will reload the last command. By default this is / so, for example, if you sent a command called 'print sensors' and then want to send the same command again, you just need to type / to repeat it. A user defined 'comment' character (# by default) can be put in front of a line to tell Commander to ignore it. This can be handy when reading SD card files if you want to put comments into the file. Comments can also be placed after commands as well as on their own lines.
215-
216-
Visit the [API](https://github.com/CreativeRobotics/Commander/wiki/API) page for a complete list of Commanders methods.
217-
218-
### The following list of examples demonstrate various ways to use Commander
219-
220-
__BasicCommands:__ Demonstrates setting and getting integer and float values with a command list and setting multiple values with a single command.
221-
222-
__QuickSet:__ Demonstrates an in built method for packing some commands in a single command handler for faster coding whilst retaining the help system for listing commands.
223-
224-
__ESP32-SerialBTCommands:__ Uses a BluetoothSerial object so commands can be sent vial Bluetooth.
225-
226-
__FileRead:__ Open an SD card file that contains a set of commands and read the contents into Commander. Responses to commands are fed back to a Serial port.
227-
228-
__FileReadLog:__ Open an SD card file that contains a set of commands and read the contents into Commander. Responses to commands are written to another file and copied to a Serial port.
229-
230-
__FileNavigation:__ Used SDFat and a set of commands for listing files, navigating and creating directories, renaming and deleting files and directories and printing out files.
231-
232-
__FormattedReplies:__ Shows how to use the pre and postfix formatting, and command chaining so formatting for another command can be invoked.
233-
234-
__SimpleMultiLayer:__ Shows how three command lists can be used with one Commander object to create a multi-level command structure. This example has sub commands for setting variable, and more for reading back variables. These commands can be invoked from the top level (e.g 'get int') or the sub level can be invoked ('get') and then commands from that level invoked directly ('int') before an 'exit' command returns you to the top level. The help command can be used to get help for every level.
235-
236-
__FullMultiLayer:__ This example behaves in an almost identical way to SimpleMultiLayer but uses three Commander objects. Navigating between different levels is handled by passing control from one Commander object to another rather than loading different command lists into the same object.
237-
238-
__PrefabFileExplorer:__ Demonstrates the use of a prefabricated command structure (in PrefabFileNavigator.h) to create a sub menu for navigating and manipulating files on an SD card. The prefab allows files to be created and written to but a suitable terminal application needs to be used - The terminal application needs to be able to send the ASCII value 4 in order to terminate the file download and return control to the command system. The Arduino serial terminal will not allow this so we do not recommend using it with the 'write' command.
239-
240-
__NumberCommand:__ (To Be Done!) Demonstrates a special class of command for handling numbers. It is designed to allow data files to be uploaded and unpacked into an array.
241-
242-
__TelnetCommand:__ (To Be Done) Interface a Telnet session to Commander so that commands can be accessed remotely via WiFi.
243-
244-
__htmlCommand:__ (To Be Done) Feed HTML page requests to Commander and generate HTML formatted responses in reply.
245-
246-
### How it works (roughly speaking)
247-
248-
The command list is an array of structures and each element contains the command string, a function pointer, and a help text string. These are all defined before the code is compiles, rather than being assigned dynamically when your code starts in order to reduce the amount of dynamic memory allocation and ensure the sketch starts quickly, particularly if using very large command sets. When you load a command list into a Commander object it scans the list and records the lengths of all the commands - this is used as part of the command matching algorithm.
249-
250-
When Commander reads a Stream or is fed a String it places it in a buffer and tries to match the start of the string to a command (unless it was rejected as a comment or the reload character was detected). If a command match is found it invokes the users command handler function and waits for it to finish. The buffer is a String object and is public so it can be read and manipulated by the users code from their handler function, and all the Arduino String methods can be used with it.
251-
252-
If it can't find a match it looks for a built in command and will execute the handler if a match is found. When Commander is finished it will check to see if the command prompt is enabled and if so, it will print out the prompt on a new line.
253-
254-
Because Commander checks the user commands first you can override any of the built in commands with your own.
255-
256-
There are a full set of Stream print and write functions that can be used, and they ensure that printed responses will be routed to the Commander objects specified output port, and to the aux port if enabled, and they ensure that any pre or postfix formatting is applied.
257-
258-
The command match system relies on each command ending with either a delimiter chgaracter or an end of line character. If the command doesn't have any arguments it will normally end in an end of line but if it has any arguments then they must be separated by one of the delimiters (The defaults are COMMA FWDSLASH BWDSLASH EQUALS OR TAB and SPACE) - The delimiters allow the use of key=value properties like this: 'myvariable=3' where myvariable is the command and 3 is the argument. Delimiters can be changed by the user, or added to.
259-
260-
Any data that comes after a recognised command is called the payload, and this can be easily extracted using the getPayload() and getPayloadString() commands. Commander can also process the payload to extract individual items. An item is any group of characters with a delimiter or end of line at either end. A set of mathods can be used to extract integers and, floats as well as strings. Commander keeps track of where it is in the payload so you can call getInt() repeatedly to extract a series of values, or getString() to extract individual items. Commander will ignore delimiters if they are insude quote marks so enclosing a whole phrase insude quotes will cause COmmander to treat it as a single item.
261-
262-
An autoChain setting will make Commander attempt to reload any part of the buffer left over after processing as a new command line. This allows commands to be chained together on a single line.
263-
264-
### Basic code structure
265-
266-
Visit the wiki [Getting Started](https://github.com/CreativeRobotics/Commander/wiki/GettingStarted) page for more information.
267-
268-
To create a command system the user needs to create the command list array, and all the command function handlers. A command list array will look something like this (This is all taken from the BasicCommands example):
269-
270-
```c++
271-
const commandList_t masterCommands[] = {
272-
{"hello", helloHandler, "hello"},
273-
{"get int", getIntHandler, "get an int"},
274-
{"set int", setIntHandler, "set an int"},
275-
{"get float", getFloatHandler, "get a float"},
276-
{"set float", setFloatHandler, "set a float"},
277-
{"myint", setIntHandler, "try myint=23"},
278-
{"myfloat", setFloatHandler, "try myfloat=23.5"},
279-
{"set ints", setIntsHandler, "set up to four ints"},
280-
{"set floats", setFloatsHandler, "set up to four floats"},
281-
{"set strings", setStringsHandler,"set up to four Strings"},
282-
};
283-
```
284-
Each line specifies one command (and is one element in the command array). The first text string is the actual command, the second is the name of the function that will handle the command and the third string is the help text that will print out when you type help. Sometimes you might want a command to be available, but not appear in the help text, in which case you can simply place a '-' character at the start of the help text.
285-
286-
To add a command simply copy and paste in a new line, edit the text and create a command handler that matches the template below.
287-
288-
__Command Handler Functions__
289-
290-
The command handlers need to follow the same template. Each must return a boolean value, and take a Commander object as an argument - When the Commander object calls the function it will pass a reference to its self to the function so the users code can access that Commander object and all its methods and variables.
291-
292-
The function template looks like this:
293-
294-
```c++
295-
bool myFunc(Commander &Cmdr){
296-
//put your command handler code here
297-
return 0;
298-
}
299-
```
300-
301-
When you write your command handler you can access the Commanders methods and the command buffer using the Cmdr reference.
302-
303-
In this example the command handler simply used the Cmdr objects print methods to reply with a message that includes the contents of the buffer.
304-
305-
```c++
306-
bool helloHandler(Commander &Cmdr){
307-
Cmdr.print("Hello! this is ");
308-
Cmdr.println(Cmdr.commanderName);
309-
Cmdr.print("This is my buffer: ");
310-
Cmdr.print(Cmdr.bufferString);
311-
return 0;
312-
}
313-
```
314-
315-
Commander has a built in method of parsing integer and float values, this can be used to extract numeric values from a commands payload.
316-
317-
```c++
318-
bool setIntHandler(Commander &Cmdr){
319-
if(Cmdr.getInt(myInt)){
320-
Cmdr.print("myInt set to ");
321-
Cmdr.println(myInt);
322-
}
323-
return 0;
324-
}
325-
```
326-
The method Cmdr.getInt(myInt) checks to see if it can find the start of a number in the payload (the part of the command buffer after the actual command) If it finds one then it converts it into an int and assigns it to the variable referenced in the function call - in this case _myInt_ - The function will return a boolean value when it finishes, this will be TRUE if the attempt was successful, and false if it was not (if your variable was not updated).
327-
328-
Commander can also extract Strings from the payload. A string is any series of characters in quotes, or seperated by delimiters.
329-
330-
```c++
331-
bool setStringHandler(Commander &Cmdr){
332-
if(Cmdr.getString(myString)){
333-
Cmdr.print("myString set to ");
334-
Cmdr.println(String);
335-
}
336-
return 0;
337-
}
338-
```
339-
340-
The method Cmdr.getString(myString) attempts to extract the next item in the payload and assign it to your String object (the myString variable) The function will return a boolean value when it finishes, this will be TRUE if the attempt was successful, and false if it was not (if your variable was not updated). An item is anything seperated by delimiters, or encloded in quote marks. For example sending the command 'setString hello there' would place the text 'hello' in the variable myString because 'hello' is the first item and 'there' is the second. Using the command 'setString "hello there"' would place the text 'hello there' in the variable myString because the text is in quotes.
341-
342-
The _getInt()_ and _getFloat()_ and _getString()_ methods keep track of where they are in the buffer so you can use them to extract a series of numbers with one command. The following code shows how to unpack up to four ints into an array. If you include less than four ints after the command, it will unpack the ones you did send, and if you include too many it will unpack only the first four.
343-
344-
```c++
345-
bool getIntsHandler(Commander &Cmdr){
346-
//create an array to store any values we find
347-
int values[4] = {0,0,0,0};
348-
for(int n = 0; n < 4; n++){
349-
//try and unpack an int, if it fails there are no more left so exit the loop
350-
if(Cmdr.getInt(values[n])){
351-
Cmdr.print("unpacked ");
352-
Cmdr.println(values[n]);
353-
}else break;
354-
}
355-
//print it out
356-
Cmdr.println("Array contents after processing:");
357-
for(int n = 0; n < 4; n++){
358-
Cmdr.print(n);
359-
Cmdr.print(" = ");
360-
Cmdr.println(values[n]);
361-
}
362-
return 0;
363-
}
364-
```
365-
In the example we are using the command _set ints_ which has been defined in the command array. Sending the command string 'set ints 12 34 56 78' will produce the following output on the serial port:
366-
367-
> unpacked 12
368-
369-
> unpacked 34
370-
371-
> unpacked 56
372-
373-
> unpacked 78
374-
375-
> Array contents after processing:
376-
377-
> 0 = 12
378-
379-
> 1 = 34
380-
381-
> 2 = 56
382-
383-
> 3 = 78
384-
385-
386-
We can use any available delimiter in the command string so the command 'set ints 12,34,56,78' will produce exactly the same result, as will 'set ints 12/34/56/78' and 'set ints 12=34\56/78'
387-
388-
_Disclaimer: I'm not the best software engineer in the world so there may be some bits of silliness in my code. I welcome contributions that will improve Commander so long as they maintain a good balance between features and efficiency._
389-
390-
Written by Bill Bigge.
391-
MIT license, all text above must be included in any redistribution
208+
MIT license, all text above must be included in any redistributionle via USB and Bluetooth Serial. It also allows different command lists to be dynamically loaded so multiple command lists, and multiple Commander objects can be combined to produce hierarchical command structures.

library.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
name=Commander
2-
version=4.1.0
2+
version=4.1.1
33
author=Bill Bigge
44
maintainer=Bill Bigge <[email protected]>
55
sentence=Command line library for Arduino.

src/Commander.cpp

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@ bool Commander::streamData(){
136136
//println("EOF Found, tidying up");
137137
commandState.bit.dataStreamOn = false;
138138
//get rid of any newlines or CRs in the stream
139-
while(ports.inPort->peek() == '\n' || ports.inPort->peek() == '\r') ports.inPort->read();
139+
while(ports.inPort->peek() == endOfLineCharacter || ports.inPort->peek() == '\r') ports.inPort->read();
140140
//call the handler again so it can clean up and close anything that needs closing
141141
commandState.bit.commandHandled = !handleCustomCommand();
142142
resetBuffer();
@@ -215,7 +215,7 @@ String Commander::getPayload(){
215215
String Commander::getPayloadString(){
216216
//return the payload minus any newline
217217
if(hasPayload()){
218-
return bufferString.substring(dataReadIndex, bufferString.indexOf('\n'));
218+
return bufferString.substring(dataReadIndex, bufferString.indexOf(endOfLineCharacter));
219219
}
220220
return "";
221221
/*
@@ -625,6 +625,13 @@ bool Commander::containsOff(){
625625
return false;
626626
}
627627
//==============================================================================================================
628+
629+
Commander& Commander::endOfLineChar(char eol){
630+
endOfLineCharacter = eol;
631+
if(endOfLineCharacter == '\r') return stripCR(false);
632+
return *this;
633+
}
634+
//==============================================================================================================
628635
void Commander::computeLengths(){
629636
//compute the length of each command
630637
if(commandListEntries == 0) return;
@@ -661,7 +668,7 @@ bool Commander::handleCommand(){
661668
return 0;
662669
}
663670
//write a newline if the command prompt is enabled so reply messages appear on a new line
664-
if(ports.settings.bit.commandPromptEnabled && !ports.settings.bit.echoTerminal) write('\n');
671+
if(ports.settings.bit.commandPromptEnabled && !ports.settings.bit.echoTerminal) write(endOfLineCharacter);
665672
if(ports.settings.bit.autoFormat) startFormatting();
666673

667674
//Match command will handle internal commands

src/Commander.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ typedef union {
9595
//const String CommanderVersionNumber = "3.0.0";
9696
const uint8_t majorVersion = 4;
9797
const uint8_t minorVersion = 1;
98-
const uint8_t subVersion = 0;
98+
const uint8_t subVersion = 1;
9999

100100
typedef enum streamType_t{
101101
UNDEFINED_STREAM = 0,
@@ -365,7 +365,7 @@ class Commander{
365365
char commentChar() {return commentCharacter;}
366366
Commander& reloadChar(char reloadChar) {reloadCommandCharacter = reloadChar; return *this;}
367367
char reloadChar() {return reloadCommandCharacter;}
368-
Commander& endOfLineChar(char eol) {endOfLineCharacter = eol; return *this;}
368+
Commander& endOfLineChar(char eol);
369369
char endOfLineChar() {return endOfLineCharacter;}
370370
Commander& promptChar(char eol) {promptCharacter = eol; return *this;}
371371
char promptChar() {return promptCharacter;}

0 commit comments

Comments
 (0)