Giter Club home page Giter Club logo

commander's Introduction

Commander is a system for handling commands sent over serial ports or other Stream objects.

Easily create complex and powerful text based interfaces for controlling your sketch.

Commander allows you to define a list of text commands, a function to handle each command, and some help text that can be displayed when the 'help' command is sent. All the work of reading the incoming stream data, identifying the appropriate function and calling the handler is done by the commander object. It will run on most Arduino boards but is more suited to devices with large memory.

Commander is attached to Stream object so it can be used with Serial ports, files on a SD cards, or Bluetooth Serial and other Stream based objects on wireless capable Arduinos such as the ESP32.

Commander can have up to three Stream objects connected at once, an input stream, output stream and auxiliary stream. As well as reading commands and passing them to the handler functions Commander can route or copy data to another port, echo data back to you and redirect or copy responses to a different port. When using SD Cards and the SDFat library, Commanders input Stream can be attached to one file to read commands, the output Stream can be attached to a second file for logging any responses generated by the command handler, and the aux stream can copy all those responses to a serial port.

Commander is designed so that the list of commands, handlers and help text are separate from the Commander object, this allows command lists to be shared between several instances of a Commander object, for example where the same command set needs to be available 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.

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.

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.

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.

Visit the API page for a complete list of Commanders methods.

The following list of examples demonstrate various ways to use Commander

BasicCommands: Demonstrates setting and getting integer and float values with a command list and setting multiple values with a single command.

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.

ESP32-SerialBTCommands: Uses a BluetoothSerial object so commands can be sent vial Bluetooth.

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.

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.

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.

FormattedReplies: Shows how to use the pre and postfix formatting, and command chaining so formatting for another command can be invoked.

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.

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.

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.

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.

TelnetCommand: (To Be Done) Interface a Telnet session to Commander so that commands can be accessed remotely via WiFi.

htmlCommand: (To Be Done) Feed HTML page requests to Commander and generate HTML formatted responses in reply.

How it works (roughly speaking)

The command list is an array of C structures and each element contains the command string, a function pointer, and a help text string. These are all defined before the code is compiled, 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.

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.

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.

Because Commander checks the user commands first you can override any of the built in commands with your own version.

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 also to the aux port if enabled, and they ensure that any pre or postfix formatting is applied.

The command match system relies on each command ending with either a delimiter character or an end of line character. If the command doesn't have any arguments it will normally end in an end of line character, 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.

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 methods 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 inside quote marks so enclosing a whole phrase inside quotes will cause Commander to treat it as a single item.

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.

Basic code structure

Visit the wiki Getting Started page for more information.

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):

const commandList_t masterCommands[] = {
  {"hello",       helloHandler,     "hello"},
  {"get int",     getIntHandler,    "get an int"},
  {"set int",     setIntHandler,    "set an int"},
  {"get float",   getFloatHandler,  "get a float"},
  {"set float",   setFloatHandler,  "set a float"},
  {"myint",       setIntHandler,    "try myint=23"},
  {"myfloat",     setFloatHandler,  "try myfloat=23.5"},
  {"set ints",    setIntsHandler,   "set up to four ints"},
  {"set floats",  setFloatsHandler, "set up to four floats"},
  {"set strings", setStringsHandler,"set up to four Strings"},
};

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.

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.

Command Handler Functions

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.

The function template looks like this:

bool myFunc(Commander &Cmdr){
  //put your command handler code here
  return 0;
}

When you write your command handler you can access the Commanders methods and the command buffer using the Cmdr reference.

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.

bool helloHandler(Commander &Cmdr){
  Cmdr.print("Hello! this is ");
  Cmdr.println(Cmdr.commanderName);
  Cmdr.print("This is my buffer: ");
  Cmdr.print(Cmdr.bufferString);
  return 0;
}

Commander has a built in method of parsing integer and float values, this can be used to extract numeric values from a commands payload.

bool setIntHandler(Commander &Cmdr){
  if(Cmdr.getInt(myInt)){
    Cmdr.print("myInt set to ");
    Cmdr.println(myInt);
  }
  return 0;
}

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).

Commander can also extract Strings from the payload. A string is any series of characters in quotes, or seperated by delimiters.

bool setStringHandler(Commander &Cmdr){
  if(Cmdr.getString(myString)){
    Cmdr.print("myString set to ");
    Cmdr.println(String);
  }
  return 0;
}

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.

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.

bool getIntsHandler(Commander &Cmdr){
  //create an array to store any values we find
  int values[4] = {0,0,0,0};
  for(int n = 0; n < 4; n++){
    //try and unpack an int, if it fails there are no more left so exit the loop
    if(Cmdr.getInt(values[n])){
      Cmdr.print("unpacked ");
      Cmdr.println(values[n]);
    }else break;
  }
  //print it out
  Cmdr.println("Array contents after processing:");
  for(int n = 0; n < 4; n++){
    Cmdr.print(n);
    Cmdr.print(" = ");
    Cmdr.println(values[n]);
  }
  return 0;
}

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:

unpacked 12

unpacked 34

unpacked 56

unpacked 78

Array contents after processing:

0 = 12

1 = 34

2 = 56

3 = 78

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'

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.

Written by Bill Bigge. 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.

commander's People

Contributors

creativerobotics avatar gitneko avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar

commander's Issues

Compile Errors with current Arduino ESP8266 on PlatformIO

Apparently several of the internal functions have unused values and at least one doesn't guarantee a return value for a non-void function, which is now treated as an error in PlatformIO so the library won't compile.
If it helps, here's the relevant errors from pio run

Compiling .pio/build/nodemcu/lib8e8/Commander/Commander.cpp.o
Compiling .pio/build/nodemcu/lib8e8/Commander/utilities/CommandHelpTags.cpp.o
.pio/libdeps/nodemcu/Commander/src/utilities/CommandHelpTags.cpp: In function 'bool getCommandArgCode(char*, cmdArgs_t)':
.pio/libdeps/nodemcu/Commander/src/utilities/CommandHelpTags.cpp:11:25: warning: NULL used in arithmetic [-Wpointer-arith]
   11 |  while(helpText[idx] != NULL){
      |                         ^~~~
.pio/libdeps/nodemcu/Commander/src/Commander.cpp: In constructor 'Commander::Commander()':
.pio/libdeps/nodemcu/Commander/src/Commander.cpp:10:34: warning: list-initializer for non-class type must not be parenthesized
   10 |                         "errors"}){
      |                                  ^
.pio/libdeps/nodemcu/Commander/src/Commander.cpp: In constructor 'Commander::Commander(uint16_t)':
.pio/libdeps/nodemcu/Commander/src/Commander.cpp:24:46: warning: list-initializer for non-class type must not be parenthesized
   24 |                                     "errors"}){
      |                                              ^
.pio/libdeps/nodemcu/Commander/src/Commander.cpp: In member function 'uint8_t Commander::countItems()':
.pio/libdeps/nodemcu/Commander/src/Commander.cpp:527:7: warning: unused variable 'state' [-Wunused-variable]
  527 |  bool state = 0;
      |       ^~~~~
.pio/libdeps/nodemcu/Commander/src/Commander.cpp: In member function 'uint8_t Commander::getLength(uint8_t)':
.pio/libdeps/nodemcu/Commander/src/Commander.cpp:658:44: warning: NULL used in arithmetic [-Wpointer-arith]
  658 |   if(commandList[indx].commandString[n] != NULL) length++;
      |                                            ^~~~
.pio/libdeps/nodemcu/Commander/src/Commander.cpp: In member function 'bool Commander::checkCommand(uint16_t)':
.pio/libdeps/nodemcu/Commander/src/Commander.cpp:940:27: warning: comparison of integer expressions of different signedness: 'unsigned int' and 'int' [-Wsign-compare]
  940 |  if(bufferString.length() < commandLengths[cmdIdx]+1) return false; //no match if the buffer is shorter than the command+1 (buffer will have the end of line char)
      |     ~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~
.pio/libdeps/nodemcu/Commander/src/Commander.cpp: In member function 'bool Commander::itemToNextDelim()':
.pio/libdeps/nodemcu/Commander/src/Commander.cpp:1243:7: warning: unused variable 'endOnQuote' [-Wunused-variable]
 1243 |  bool endOnQuote = false;
      |       ^~~~~~~~~~
.pio/libdeps/nodemcu/Commander/src/Commander.cpp: In member function 'bool Commander::isItem(char)':
.pio/libdeps/nodemcu/Commander/src/Commander.cpp:1282:11: warning: NULL used in arithmetic [-Wpointer-arith]
 1282 |  if(ch == NULL) return false;
      |           ^~~~
.pio/libdeps/nodemcu/Commander/src/Commander.cpp: In member function 'uint8_t Commander::getInternalCmdLength(const char*)':
.pio/libdeps/nodemcu/Commander/src/Commander.cpp:1461:19: warning: NULL used in arithmetic [-Wpointer-arith]
 1461 |   if(intCmd[n] == NULL) return n;
      |                   ^~~~
.pio/libdeps/nodemcu/Commander/src/Commander.cpp: In member function 'uint8_t Commander::getLength(uint8_t)':
.pio/libdeps/nodemcu/Commander/src/Commander.cpp:661:1: error: control reaches end of non-void function [-Werror=return-type]

So, can you make sure that non-void member functions always have a return value?

Arduino Library Manager latest release doesn't compile

Hi, I just found your very impressive library by searching "CLI" in Arduino Library Manager, but when I tried to compile an example it failed because setUserString() was missing. I see from the commit history that the 4.1.0 tag was made for the the commit before setUserString() was added. I believe you need to update library.properties with a new number, commit, and tag that commit to get a working copy of the latest code into Arduino Library Manager.

I was able to checkout the latest from GitHub and compile so I'm not stuck.

README copy/paste error

Last paragraph has some extra text at the end:

Written by Bill Bigge. 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.

endOfLineChar(CR) should also disable stripCR()?

My terminal uses CR as the default EOL character, and I've not had a problem with that setting for years, so I didn't feel like changing it to LF when trying out Commander. I found endOfLineChar() but it didn't work when I changed to CR, and it took some digging to find the stripCR() method.

I think it's probably a common use case to want to change the EOL character to CR, would it be reasonable to automatically disable the stripCR setting if EOL is set to CR?

[RFC] help command not executed

When trying the getting started example
The command doesn't seems to get executed

Once connected to the serial port:

Hello: Type 'help' to get help
CMD>help

The cursor return at the beginning of the line and nothing happens after hitting enter

if I keep entering characters:

aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa#ERR: Buffer Overflow

Hardware: SAMD21G18 Microcontroller

library

arduino-cli lib list
Commander         4.0.1    -           user     - 

serial communication

cu -l /dev/ttyACM0 -s 115200

NOTE: same behavior with minicom

Unable to use library with Putty.

I flashed the 'BasicCommands' example to my ESP32 board. From the Arduino Serial Monitor, I'm able to use the library wonderfully. However, if I try to connect to the serial port using Putty, or the Visual Studio Code "Serial Monitor", entered commands aren't registered.

startStreaming() using Stream class

I'm trying to add YMODEM receive to my project using this library (my fork which allows for setting the Stream port instead of using hardcoded Serial). I can't use the existing streaming options in Commander directly, as the library depends on receiving data via a Stream. I don't really want to modify the library just to work with Commander. I imagine there are more libraries using Stream that could be included in future prefabs, so I'd rather find a good solution to this problem than hack together support for one library to solve my immediate problem.

I see two options here:

  1. Make a mode where Commander gives up control of inPort, and lets other code use it until stopStreaming() is called
  • This wouldn't work with !dataStreamMode as the other code could read from the Stream before Commander gets a chance to look for 0x04
  1. Use PairedPipedStream from this library (my fork where I modified it to optionally output to a Stream on one end automatically). Commander can inspect each byte, and load it into one end of a PairedPipedStream. The other code can read from the other end as if it were reading directly from inPort (though Commander decides when to stop streaming data), and whatever it writes gets automatically written to outPort.
  • This uses more memory as the PairedPipedStream has a fixed buffer, but with a little extra code we could dynamically resize the buffer.
  1. I guess there's a third option where there's no modification to Commander, and I just don't call cmd.update() to make option 1 work inside of my sketch, or I take care of option 2 inside a special handler in my sketch/prefab.

I only thought of the much simpler option 1 after doing all the work upgrading PairedPipedStream and prototyping option 2 and getting YMODEM receive to work. At this point I think the remaining work for option 1 and 2 are about the same.

LMK if you have some thoughts on these options, or want more details on anything

Idea to change sketch structure to avoid Arduino-specific magic and externs

I noticed you're using multiple .ino files for several examples to work around some circular referencing (probably not the right terminology) issues. It seems that's allowed by the Arduino IDE, but not common practice. Even so, you also have to use externs to work around the same issues. I tried to figure out why this is needed, and it seems to come from needing to have a command list containing a function reference while the function also needs a reference to the list, and both definitions are needed at compile time. I couldn't find a good way to avoid using .INOs+externs without dramatically restructuring the code, and also making it more complex. The functions don't need to know the command list and size at compile time though, it can be set during setup(), making the problem easier to solve. I prototyped a solution to get your feedback.

embedded-creations@5547ef6

If it's not obvious to you why this is an improvement, let me know and I'll make a case for why I like it and think it should at least be an option. I'm too tired to write anything more right now ;-)

String in BasicCommands

I've tried several times to use the string as output in BasicCommands for an if function. First, I was told that the variable myString was not defined in the code, so I initialized String myString=""; as with float and int. However, I wasn't able to print myString and the if function wasn't working, so the code wasn't able to show me whether the string was set to the right value. I made a thread about this on the arduino forum and b707 helped me. It seems that initializing the string in BasicCommands is the correct way to use it in this code, but it has already been initialized in the Master, which would block the string from being modified in BasicCommands. The solution was to comment out the initialization in the Master, and it worked!
https://forum.arduino.cc/t/commander-library-set-string/1188811/12

`/*Commander example - basic

  • Demonstrating commands to get and set an int and a float

#include <Commander.h>
Commander cmd;
//Variables we can set or get
int myInt = 0;
float myFloat = 0.0;
String myString="";

String deviceInfo = "#\tCommander basic commands example\n#\thttps://github.com/CreativeRobotics/Commander";
//SETUP ---------------------------------------------------------------------------
void setup() {
Serial.begin(115200);
initialiseCommander();
while(!Serial){;}
cmd.printUserString();
cmd.println();
Serial.println("Type 'help' to get help");
cmd.printCommandPrompt();
}

//MAIN LOOP ---------------------------------------------------------------------------
void loop() {

//command=cmd.getPayloadString();

if(myString =="stop"){
Serial.println("Succes string");
}
else if(myString=="go") {
Serial.println("Succes go");
}
cmd.update();
}
//Serial.println(myInt);
`

`//All commands for 'master'
//COMMAND ARRAY ------------------------------------------------------------------------------
const commandList_t masterCommands[] = {
{"hello", helloHandler, "hello"},
{"get int", getIntHandler, "get an int"},
{"set int", setIntHandler, "set an int"},
{"get float", getFloatHandler, "get a float"},
{"set float", setFloatHandler, "set a float"},
{"hidden1", hiddenHandler, "-Command hidden from help"},
{"myint", setIntHandler, "try myint=23"},
{"myfloat", setFloatHandler, "try myfloat=23.5"},
{"set ints", setIntsHandler, "set up to four ints"},
{"set floats", setFloatsHandler, "set up to four floats"},
{"set strings", setStringsHandler,"set up to four Strings"},
{"hidden2", hiddenHandler, "-Command hidden from help"},
};

/* Command handler template
bool myFunc(Commander &Cmdr){
//put your command handler code here
return 0;
}
*/
void initialiseCommander(){
cmd.begin(&Serial, masterCommands, sizeof(masterCommands));
cmd.commandPrompt(ON); //enable the command prompt
cmd.echo(true); //Echo incoming characters to theoutput port
cmd.errorMessages(ON); //error messages are enabled - it will tell us if we issue any unrecognised commands
cmd.autoChain(ON);
cmd.setUserString(deviceInfo);
}

//These are the command handlers, there needs to be one for each command in the command array myCommands[]
//The command array can have multiple commands strings that all call the same function
bool helloHandler(Commander &Cmdr){
Cmdr.print("Hello! this is ");
Cmdr.println(Cmdr.commanderName);
Cmdr.print("This is my buffer: ");
Cmdr.print(Cmdr.bufferString);
//Cmdr.printDiagnostics();
return 0;
}
bool getIntHandler(Commander &Cmdr){
Cmdr.print("myInt = ");
Cmdr.println(myInt);
//Cmdr.printDiagnostics();
return 0;
}

bool setIntHandler(Commander &Cmdr){
if(Cmdr.getInt(myInt)){
Cmdr.print("myInt set to ");
Cmdr.println(myInt);
}
//Cmdr.printDiagnostics();
return 0;
}
bool getFloatHandler(Commander &Cmdr){
Cmdr.print("myFloat = ");
Cmdr.println(myFloat);

//Cmdr.printDiagnostics();
return 0;
}

bool setFloatHandler(Commander &Cmdr){
if(Cmdr.getFloat(myFloat)){
Cmdr.print("myFloat set to ");
Cmdr.println(myFloat, 4); //print with 4 decimal places
}
//Cmdr.printDiagnostics();
return 0;
}

bool setIntsHandler(Commander &Cmdr){
//create an array to store any values we find
int values[4] = {0,0,0,0};

int itms = Cmdr.countItems();
Cmdr.print("There are ");
Cmdr.print(itms);
Cmdr.println(" items in the payload");

for(int n = 0; n < 4; n++){
//try and unpack an int, if it fails there are no more left so exit the loop
if(Cmdr.getInt(values[n])){
Cmdr.print("unpacked ");
Cmdr.println(values[n]);
}else break;
}
//print it out
Cmdr.println("Array contents after processing:");
for(int n = 0; n < 4; n++){
Cmdr.print(n);
Cmdr.print(" = ");
Cmdr.println(values[n]);
}
//Cmdr.chain();
//Cmdr.printDiagnostics();
return 0;
}

bool setFloatsHandler(Commander &Cmdr){
float values[4] = {0.0,0.0,0.0,0.0};
int itms = Cmdr.countItems();
Cmdr.print("There are ");
Cmdr.print(itms);
Cmdr.println(" items in the payload");

for(int n = 0; n < 4; n++){
if(Cmdr.getFloat(values[n])){
Cmdr.print("unpacked ");
Cmdr.println(values[n]);
}else break;
}
Cmdr.println("Array contents after processing:");
for(int n = 0; n < 4; n++){
Cmdr.print(n);
Cmdr.print(" = ");
Cmdr.println(values[n]);
}
//Cmdr.chain();
//Cmdr.printDiagnostics();
return 0;
}

bool setStringsHandler(Commander &Cmdr){
//String myString = "";
int itms = Cmdr.countItems();
Cmdr.print("There are ");
Cmdr.print(itms);
Cmdr.println(" items in the payload");
for(int n = 0; n < itms; n++){
if(Cmdr.getString(myString)){
Cmdr.print("String ");
Cmdr.print(n);
Cmdr.print(" = ");
Cmdr.println(myString);
}else Cmdr.println("Operation failed");
}
//Cmdr.chain();
//Cmdr.printDiagnostics();
return 0;
}

bool hiddenHandler(Commander &Cmdr){
Cmdr.println("This command is hidden from the help system");
//Cmdr.printDiagnostics();
return 0;
}`

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.