├── .gitattributes ├── Changelog.txt ├── LICENSE ├── README.md ├── examples ├── Advanced │ ├── CommandTextChange │ │ ├── CommandTextChange.ino │ │ └── masterCommands.ino │ ├── FunctionSwap │ │ ├── FunctionSwap.ino │ │ └── masterCommands.ino │ ├── customMenu │ │ ├── customMenu.ino │ │ └── masterCommands.ino │ └── customMenu_Static │ │ ├── customMenu_Static.ino │ │ └── masterCommands.ino ├── BasicCommands │ ├── BasicCommands.ino │ └── masterCommands.ino ├── ESP32 │ ├── SerialBTCommands │ │ ├── Commands.ino │ │ └── SerialBTCommands.ino │ └── TelnetCommands │ │ ├── Commands.ino │ │ └── TelnetCommands.ino ├── FileNavigation │ ├── Commands.ino │ └── FileNavigation.ino ├── FileRead │ ├── Commands.ino │ └── FileRead.ino ├── FileReadLog │ ├── Commands.ino │ └── FileReadLog.ino ├── FormattedReplies │ ├── FormattedReplies.ino │ └── masterCommands.ino ├── FullMultiLayer │ ├── FullMultiLayer.ino │ ├── getCommands.ino │ ├── masterCommands.ino │ └── setCommands.ino ├── LockedCommands │ ├── LockedCommands.ino │ └── masterCommands.ino ├── PrefabFileExplorer │ ├── Commands.ino │ └── PrefabFileExplorer.ino ├── TEST_SUITE1 │ ├── TEST_SUITE1.ino │ ├── TEST_SUITE1 │ │ ├── TEST_SUITE1.ino │ │ └── masterCommands.ino │ └── masterCommands.ino ├── Template1SingleTab │ └── Template1SingleTab.ino ├── Template2ForwardDeclaration │ └── Template2ForwardDeclaration.ino ├── Template3InitFunction │ └── Template3InitFunction.ino ├── quickSet │ ├── masterCommands.ino │ └── quickSet.ino └── simpleMultiLayer │ ├── getCommands.ino │ ├── masterCommands.ino │ ├── setCommands.ino │ └── simpleMultiLayer.ino ├── keywords.txt ├── library.properties └── src ├── Commander.cpp ├── Commander.h ├── prefabs ├── Display │ ├── gfxPrefab.h │ └── gfxPrefab.txt └── SDCards │ └── PrefabFileNavigator.h └── utilities ├── CommandHelpTags.cpp ├── CommandHelpTags.h └── htmlUtilities.h /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /Changelog.txt: -------------------------------------------------------------------------------- 1 | Commander changelog 2 | (Changelog started at 1.2.3) 3 | 4.3.0 4 | Added getCommandIndex that returns the index of the last used command in the command array. Calling this from a handler will inform you which command in the command array was used to invoke the handler. 5 | 6 | 4.2.3 7 | Update *char to const *char to remove compiler warnings. 8 | Update some examples to remove casting from const *char to *char. 9 | Removed ideosyncratic spellings from examples. 10 | 11 | 4.2.2 12 | Updated to 4.2.2 because I messed up the last releases on Github. Needs a whole new release tag to fix. 13 | 14 | 15 | 4.2.0 16 | Added reloadCommands() function. This re-computes the command lengths so that command strings can be changed at run time. If the command list is declared as a non const, then the string and function pointers can be reassigned. In order to change a command string to one of a different length commander needs to recompute the command lengths, so this function needs to be called if a command string pointer is assigned to a string of a different length. 17 | Added attachCommandArray() method similar to attachCommands but takes the array length as an argument instead of sizeof(). 18 | Added an extra help system. You can attach an array of strings. If this is attached and help has a payload it will try and match the payload to a command and print out the extra help in the array index that matches the command. Example 'help status' would print the extra help for the command 'status'. 19 | Extra help availability is indicated in the help and query information. 20 | Updated to inherit Arduino print class and replaced template methods with methods for the print class. Thanks to Louis Beaudoin of Embedded creations for lots of work on this and some other up coming features https://github.com/embedded-creations 21 | Changed write() to add() to fix conflict with print class. 22 | Added example showing reallocation of command Handler, command text and help text to different character arrays and functions. 23 | Added example showing how to use one command to edit the help text and command text of another command stored as a String object. 24 | Added example of a dynamically generated command list: A command can be used to create a custom submenu of items. 25 | Added example of using a fixed length command array, and populating it with commands to create a bespoke menu of variable length. 26 | Fixed getLength() to return a length in all circumstances. 27 | Replaced NULL with '\0' when checking chars for null character. 28 | Removed unused variables and fixed issue causing compiler warnings on platformIO and ESP boards. 29 | 30 | 4.1.1 31 | Minor bugfix related to wonky resease. 32 | Fixed missing references to endOfLineCharacter that were still '/n'. **Changing endOfLineCharacter may have implications for how streams get printed. 33 | Changed endOfLineChar(char) so that if the end of line character is set to a carriage return, stripCR is automatically set to false. 34 | 35 | 4.1.0 36 | 37 | Added setUserString(&String) and printUserString(). A String can be created with user defined information, for example a product name, web URL and firmware version. This String will be printed at the start of the help page and status query page (the '?' command). This allows the user to incorporate their own device or product information into the command system. The string can be formatted however the user wants, including with newlines, tabs etc, and will be printed exactly as is - Other help page items print the comment characterat the start of each line, if the user wants the user String to also appear as comment lines then they need to incorporate the comment characters in the String themselves. 38 | Updated test suite sketch with example use. 39 | 40 | Commander 4.0.0 - Method act 41 | 42 | Method CHAIN'O'RAMA! All methods that used to return void now return a *this reference allowing them to be chained. 43 | Changed some configuration commands such as setCommentChar to commentChar and overloaded them so the same function name can be used to set or get the property. 44 | **Quite a few method names have changed so there is better API consistency. 45 | Added setPending(). If you directly write commands to the bufferString, call setPending() before update to tell Commander there is a command waiting there. 46 | Updated keyword list. 47 | Fixed some errors in examples. 48 | 49 | Commander 3.2.0 50 | 51 | Re-wrote containsOn/Off/True/False methods so they search the entire buffer for case insensitive tokens (on, ON, On, true, True, TRUE etc) rather than just checking the next item. The search method looks for the start char (e.g t or T) and if found checks that the target phrase is bounded by a delimiter/end of line before checking for the rest of the characters... possibly not optimal yet? ... 52 | Changed how update works so that when the inport is NULL, Commander will just look for pending commands. This is to allow Commander to be used without an inport so it can be fed Strings from another service (e.g an MQTT payload) 53 | Added another begin() method that does not need ports to be specified. 54 | Added method that returns the command list pointer. 55 | Added a quick method to compliment the quickSet methods. Quick does not set any variables, but can be used to add sub commands with a quick help system in the same way as quickSet and Get. It is helpful for rapid prototyping where you want to add simple commands for debugging without creating whole command handlers. 56 | Updated the quickSet example with the new quick commands 57 | Added write(uint8_t) method to add a single character to the buffer 58 | Changed how feed and feedString work so the command prompt state is saved and reinstated at the end. 59 | 60 | Commander 3.1.1 61 | 62 | Corrected some keywords. 63 | Fixed backslash error in #include. 64 | 65 | Commander 3.1.0 66 | 67 | Changed streamData to private method. 68 | Made pre and postfix Strings private. 69 | Changed set and get autoformat methods to autoformat(arg) and returnval autoformat() for consistency. 70 | Changed most examples to use an initialiser function after the command list instead of forward declarations when starting Commander. Multi layer examples still use forward declarations. 71 | Added template examples to match wiki. 72 | 73 | Commander 3.0.0 74 | 75 | Made some major changes under the hood to the way payloads and items are processed including adding support for chaining multiple commands together in a single line: 76 | 77 | Changed help and query formatting so the comment char appears at the start of every line for help, query and errors. 78 | Changed delimiter char to a String. The defaults are | = , : \ / plus SPACE and TAB. 79 | Removed setDelimChar() method. 80 | Created setDelimiters(String), getDelimiters() and addDelimiter(char) methods. 81 | Added a method where delims are ignored for items in quotes when using getString. 82 | Added a settings bit to allow quotes to be ignored. 83 | Added containsFalse() and containsOff() to compliment ON and TRUE. 84 | Added public method of reading the dataReadIndex variable. This marks the start of the payload, or any section of the payload that has not been extracted with the getInt, getFloat or getString methods. 85 | Added chain() method, breakChain(), autoChain settings bit and autoChain() methods to allow commands to be chained on a single line. Unless set to autoChain mode, if chain() is called at the end of a handler, Commander will find the start of the next item in the payload (anything left over after calls to getInt etc), and reload everything from that point into the buffer as a pending command for processing on the next call to update. If autochaining is enables, then placing breakChain at the end of a handler will prevent any other commands in that chain from being handled. 86 | Chaining will work on any user command provided that any payload for that command has been extracted using the getInt, getFloat or getString methods. These methods will place the dataReadIndex at the end of the commands payload, and before the start of any chained command. 87 | Setting autoChain(true) will automatically allow all commands to be chained. 88 | A chaining state bit can be used to surpress the unrecognised command handler so if chained command does not turn out to be a valid command it gets ignored instead of generating an error. 89 | Comment characters break chains, so comments can be left at the end of a command line, for example in command files. 90 | Added autoChainSurpressErrors bit and methods to control if error messages are displayed when chaining commands. 91 | Added quickSet and get methods for Strings. 92 | Chains will break when a quickset or quickget command is found - this prevents chaining errors. 93 | Added a help tag [C] that indicates if the command is chainable. 94 | Fixed a lock bug printing the unlock message when it didn't unlock. 95 | Fixed internal commands recognising bad commands due to sloppy optimisation. 96 | Inproved contaonsOn,off,true and false so they only with for recognisable items, not when the characters appear in a phrase. They now check the next item in the payload, but do not move dataReadIndex. 97 | 98 | Commander 2.2.0 99 | 100 | Optimised the command search. The command search used to use the String method startsWith(). This has been replaced with a new algorithm: 101 | - When checking each command against the buffer it first checks if the buffer is too short (shorter than the command being checked) and returns false if it is. 102 | - If the command is a single character it performs a specific test and returns true if it matches the buffer, and has a valid end of command char (' ', end of line or delim) 103 | - Otherwise it checks the last command character against the buffer, then the first, and works its way in until it reaches the middle. 104 | - It returns true if it reaches the middle with all characters matching. 105 | - It returns false the moment a character fails to match. 106 | - If it returns true for a match of that command against the buffer, it logs the index and the command length and keeps searching. 107 | - If it finds another match against a longer command it updates the index. 108 | - For internal commands, a switch statement was implimented and optimised to check each item as efficiently as possible. This also increased speed. 109 | -- This reduced the time to search significantly, for example command match of 227 microsecs was reduced to 58 microsecs in a 16 item command list. 110 | -- Theory: The probablility of the last and first characters of a command and the buffer matching should be much lower than for the first and second or third (etc). For example commands 'set int 1234' and 'set float 1.2' match the first four characters but not the last character, so by checking the last character first they are rejected on the first iteration rather than the fourth. 111 | Swapped query and help in internal commands so they can be searched faster 112 | Removed enable command from internal commands because ... its stupid, and you should use locks instead. 113 | Moved printing of reload and comment characters so they are visible even if internal commands are not. 114 | Added a method of hiding commands from the command menu. Putting the '-' character at the start of the help text will prevent the command from printing. 115 | 116 | Commander 2.1.0 117 | 118 | Fixed a bug where CR was not ignored on a blank line. 119 | Fixed a bug where getString was not recognising the custom delimiter. 120 | Changes streamType_t enum so undefines streamtype is zero. 121 | Expanded the settings register to 32 bits and moved autoFormat and locked bits from state register to settings register. 122 | Removed PRINT_LINE_DELAY and replaced with setPrintDelay(uint8_t dTime) uint8_t getPrintDelay() and printDelay(bool enable) to allow a delay from 0-255 millis after a line is printed. Default is off with zero time. 123 | Changed commandListEntries to a uint8_t - Command lists must have no more than 256 items! 124 | Fixed a bug where countItems() will return 1 if there are no items. 125 | Added a benchmarking system using macros. 126 | Added a streamMode option so raw data streaming can be supported. 127 | Changed start stop and set stream method names to Streaming (instead of stream) to remove ambiguity over methods relating to the Stream objects 128 | Added setStreamingMode() and getStreamingMode() methods. 129 | Removed the isCommandStart() and isCommandChar() methods and rearranged the command buffer state machine to use one bit. Commands can now start with any ASCII character. 130 | Removed redundant settings register values. 131 | Renames streamMode and streamOn bits as dataStreamMode and dataStreamOn. 132 | 133 | Commander 2.0.0 134 | 135 | Re-worked several methods and method names and probably broke several things, for example setLockType(bool) replaces enableHardLock(), commandPrompt(bool) replaces enablePrompt() and disablePrompt(). Setting parameters like these are now all done by passing an argument, and macros for ON and OFF are defined. 136 | errorMessagesEnabled now controls all internal messages so disabling them will prevent replies from internal commands like 'echo' or 'U' 137 | bugfix: internal commands did not check length so, for example, sending helpaxfgqerg was recognised as a help command 138 | Added PRINT_LINE_DELAY macro - if defined at the start of a sketch it will insert the defined marco before a line of text is printed - used to slow down transmission when sending many lines of data so a slow reciever can handle it. 139 | Added a defaultHandler function pointer that will be called whenever an unknown command is found. The user can now attach their own command handler for dealing with unrecognised commands. If not specified, the internal handler will be used. 140 | Added a method that returns the number of items in the command list. 141 | Added a method to return a String with the command help text for a specified item in the command list. 142 | Added a method that returns the number of items in the internal command list. 143 | Added a method to return a String with the command and help text for a specified item in the internal command list. 144 | Added a hide Internal Commands bit to prevent internal commands being displayed in help. 145 | Added an enable internal commands bit to stop internal commands from working. 146 | Added getString() method to extract sequential strings seperated by a space or special delimiter 147 | Added a countItems method to return the number of items in the payload. An idem is defined as a string with a space or delimChar at each end (or an end of line char) 148 | Renamed the eocChar into a delimChar, it is used as a delimiter between any items in the payload alongside the space character 149 | Added methods to set the delimChar and endOfLine char. 150 | Added two bit StreamType code to indicate if the attached stream is a serial port, file or web - this is to allow handlers to interrogate the Commander object calling the handler to adjust its response for that stream, for example by adding formatting to a response. 151 | Methods containsOn and containsTrue now work for both lower case and capitals. 152 | Updated the basic example sketch to demonstrate countItems() and getString() methods. These count and display all items in the payload. 153 | Added an HTML web server example using Commander to deliver formatted web pages. 154 | Removed updatePending() because this function is performed within update() 155 | 156 | Utilities: 157 | Added a function to convert an HTTP GET line to a command string - It finds the start and end of the GET message and converts and / or + chars to spaces. A line with "GET /help HTTP/1.1" will return a string containing "help" or "GET /setLED/on HTTP/1.1" will return "setLED on" The function can return a default string if nothing is found. 158 | String GET_CommandString(String serverGetLine, String defaultString) 159 | 160 | Commander 1.3.2: 161 | Changed getInt() to a template method so it will work for any int type (byte, long, unsigned etc) 162 | 163 | Commander 1.3.1: 164 | Restructured directories so everything is in src 165 | Prefabs now have their own directory and can be arranged into sub categories 166 | 167 | Commander 1.3.0: 168 | Fixed adding help system enable bit so help and query (?) commands can be disabled (this had not been added to the repo in 1.2.3) 169 | Added keywords.txt for syntax highlighting. 170 | Added includes=Commander.h and depends= to properties file 171 | Minor edits to readme 172 | Changes to how help is formatted 173 | 174 | Commander 1.2.3: 175 | Added enable bit for help system and query command. The help system can now be disabled if required. -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 CreateiveRobotics 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Commander is a system for handling commands sent over serial ports or other Stream objects. 2 | 3 | ## Easily create complex and powerful text based interfaces for controlling your sketch. 4 | 5 | # [Visit the wiki](https://github.com/CreativeRobotics/Commander/wiki) 6 | 7 | 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. 8 | 9 | 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. 10 | 11 | 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. 12 | 13 | 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. 14 | 15 | 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. 16 | 17 | 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. 18 | 19 | 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. 20 | 21 | Visit the [API](https://github.com/CreativeRobotics/Commander/wiki/API) page for a complete list of Commanders methods. 22 | 23 | ### The following list of examples demonstrate various ways to use Commander 24 | 25 | __BasicCommands:__ Demonstrates setting and getting integer and float values with a command list and setting multiple values with a single command. 26 | 27 | __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. 28 | 29 | __ESP32-SerialBTCommands:__ Uses a BluetoothSerial object so commands can be sent vial Bluetooth. 30 | 31 | __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. 32 | 33 | __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. 34 | 35 | __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. 36 | 37 | __FormattedReplies:__ Shows how to use the pre and postfix formatting, and command chaining so formatting for another command can be invoked. 38 | 39 | __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. 40 | 41 | __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. 42 | 43 | __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. 44 | 45 | __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. 46 | 47 | __TelnetCommand:__ (To Be Done) Interface a Telnet session to Commander so that commands can be accessed remotely via WiFi. 48 | 49 | __htmlCommand:__ (To Be Done) Feed HTML page requests to Commander and generate HTML formatted responses in reply. 50 | 51 | ### How it works (roughly speaking) 52 | 53 | 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. 54 | 55 | 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. 56 | 57 | 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. 58 | 59 | Because Commander checks the user commands first you can override any of the built in commands with your own version. 60 | 61 | 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. 62 | 63 | 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. 64 | 65 | 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. 66 | 67 | 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. 68 | 69 | ### Basic code structure 70 | 71 | Visit the wiki [Getting Started](https://github.com/CreativeRobotics/Commander/wiki/GettingStarted) page for more information. 72 | 73 | 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): 74 | 75 | ```c++ 76 | const commandList_t masterCommands[] = { 77 | {"hello", helloHandler, "hello"}, 78 | {"get int", getIntHandler, "get an int"}, 79 | {"set int", setIntHandler, "set an int"}, 80 | {"get float", getFloatHandler, "get a float"}, 81 | {"set float", setFloatHandler, "set a float"}, 82 | {"myint", setIntHandler, "try myint=23"}, 83 | {"myfloat", setFloatHandler, "try myfloat=23.5"}, 84 | {"set ints", setIntsHandler, "set up to four ints"}, 85 | {"set floats", setFloatsHandler, "set up to four floats"}, 86 | {"set strings", setStringsHandler,"set up to four Strings"}, 87 | }; 88 | ``` 89 | 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. 90 | 91 | 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. 92 | 93 | __Command Handler Functions__ 94 | 95 | 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. 96 | 97 | The function template looks like this: 98 | 99 | ```c++ 100 | bool myFunc(Commander &Cmdr){ 101 | //put your command handler code here 102 | return 0; 103 | } 104 | ``` 105 | 106 | When you write your command handler you can access the Commanders methods and the command buffer using the Cmdr reference. 107 | 108 | 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. 109 | 110 | ```c++ 111 | bool helloHandler(Commander &Cmdr){ 112 | Cmdr.print("Hello! this is "); 113 | Cmdr.println(Cmdr.commanderName); 114 | Cmdr.print("This is my buffer: "); 115 | Cmdr.print(Cmdr.bufferString); 116 | return 0; 117 | } 118 | ``` 119 | 120 | Commander has a built in method of parsing integer and float values, this can be used to extract numeric values from a commands payload. 121 | 122 | ```c++ 123 | bool setIntHandler(Commander &Cmdr){ 124 | if(Cmdr.getInt(myInt)){ 125 | Cmdr.print("myInt set to "); 126 | Cmdr.println(myInt); 127 | } 128 | return 0; 129 | } 130 | ``` 131 | 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). 132 | 133 | Commander can also extract Strings from the payload. A string is any series of characters in quotes, or seperated by delimiters. 134 | 135 | ```c++ 136 | bool setStringHandler(Commander &Cmdr){ 137 | if(Cmdr.getString(myString)){ 138 | Cmdr.print("myString set to "); 139 | Cmdr.println(String); 140 | } 141 | return 0; 142 | } 143 | ``` 144 | 145 | 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. 146 | 147 | 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. 148 | 149 | ```c++ 150 | bool getIntsHandler(Commander &Cmdr){ 151 | //create an array to store any values we find 152 | int values[4] = {0,0,0,0}; 153 | for(int n = 0; n < 4; n++){ 154 | //try and unpack an int, if it fails there are no more left so exit the loop 155 | if(Cmdr.getInt(values[n])){ 156 | Cmdr.print("unpacked "); 157 | Cmdr.println(values[n]); 158 | }else break; 159 | } 160 | //print it out 161 | Cmdr.println("Array contents after processing:"); 162 | for(int n = 0; n < 4; n++){ 163 | Cmdr.print(n); 164 | Cmdr.print(" = "); 165 | Cmdr.println(values[n]); 166 | } 167 | return 0; 168 | } 169 | ``` 170 | 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: 171 | 172 | > unpacked 12 173 | 174 | > unpacked 34 175 | 176 | > unpacked 56 177 | 178 | > unpacked 78 179 | 180 | > Array contents after processing: 181 | 182 | > 0 = 12 183 | 184 | > 1 = 34 185 | 186 | > 2 = 56 187 | 188 | > 3 = 78 189 | 190 | 191 | 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' 192 | 193 | _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._ 194 | 195 | Written by Bill Bigge. 196 | MIT license, all text above must be included in any redistribution. 197 | -------------------------------------------------------------------------------- /examples/Advanced/CommandTextChange/CommandTextChange.ino: -------------------------------------------------------------------------------- 1 | /*Commander example - Changing command text and help text with other commands 2 | */ 3 | #include 4 | Commander cmd; 5 | 6 | //User string - this can be anything you want, and is printed when the help and ? commands are used 7 | //Its a good idea to put the # symbol in front of each line so that if the response to these commands is fet to another commander, they will be interpreted as comments. 8 | String deviceInfo = "#\t(Start of user string)\n#\tChanging command text and help text\n#\tDevice firmware version x.x.x revision x\n#\thttps://github.com/CreativeRobotics/Commander\n#\t(End of user string)"; 9 | //SETUP --------------------------------------------------------------------------- 10 | void setup() { 11 | Serial.begin(115200); 12 | delay(100); 13 | while(!Serial){;} 14 | delay(100); 15 | initialiseCommander(); 16 | cmd.printUserString(); 17 | cmd.println(); 18 | Serial.println("Type 'help' to get help"); 19 | 20 | 21 | cmd.printCommandPrompt(); 22 | } 23 | 24 | //MAIN LOOP --------------------------------------------------------------------------- 25 | void loop() { 26 | cmd.update(); 27 | } 28 | -------------------------------------------------------------------------------- /examples/Advanced/CommandTextChange/masterCommands.ino: -------------------------------------------------------------------------------- 1 | //All commands for 'master' 2 | //COMMAND ARRAY ------------------------------------------------------------------------------ 3 | 4 | String helpStr = "This is the normal help text"; 5 | String exHelpStr = "This is the extended help text for this command. It can be changed with another command."; 6 | String cmdStr = "test"; 7 | 8 | commandList_t masterCommands[] = { 9 | {cmdStr.c_str(), helloHandler, helpStr.c_str()}, 10 | {"set help", setStringHandler, "Set help text for the first command"}, 11 | {"set extra help", setExtraStringHandler, "Set extended help for first command"}, 12 | {"set command", setCommandHandler, "Set command text for the first command"}, 13 | }; 14 | 15 | 16 | const char * extraHelp[] = { 17 | (char*)exHelpStr.c_str(), 18 | "'set help'\tSet the text that will display for the normal help of the first command.\nMake sure you don't exceed Commander buffer size!", 19 | "'set extra help'\tSet the text that will display for the extended help of the first command.\nMake sure you don't exceed Commander buffer size!", 20 | "'set command'\tThis will set the actual command text used for the first command in the list.", 21 | }; 22 | /* Command handler template 23 | bool myFunc(Commander &Cmdr){ 24 | //put your command handler code here 25 | return 0; 26 | } 27 | */ 28 | 29 | 30 | //Initialisation function that avoids having to forward declare the command array and a size variable. 31 | void initialiseCommander(){ 32 | cmd.begin(&Serial, masterCommands, sizeof(masterCommands)) 33 | .commandPrompt(ON) 34 | .echo(true) 35 | .errorMessages(ON) 36 | .autoChain(OFF) 37 | .setUserString(deviceInfo) 38 | .setExtraHelp(extraHelp); 39 | } 40 | 41 | 42 | //These are the command handlers, there needs to be one for each command in the command array myCommands[] 43 | //The command array can have multiple commands strings that all call the same function 44 | bool helloHandler(Commander &Cmdr){ 45 | Cmdr.print("Hello! this is "); 46 | Cmdr.println(Cmdr.commanderName); 47 | Cmdr.print("This is my buffer: "); 48 | Cmdr.print(Cmdr.bufferString); 49 | //Cmdr.printDiagnostics(); 50 | return 0; 51 | } 52 | 53 | bool setStringHandler(Commander &Cmdr){ 54 | //String myString = ""; 55 | 56 | if(Cmdr.hasPayload()){ 57 | helpStr = Cmdr.getPayloadString(); 58 | //The pointer may have changed so get a new one 59 | masterCommands[0].manualString = helpStr.c_str(); 60 | Cmdr.print("Help string changed to:"); 61 | Cmdr.println(helpStr); 62 | } 63 | else{ 64 | Cmdr.println("No payload. String is still:"); 65 | Cmdr.println(helpStr); 66 | } 67 | return 0; 68 | } 69 | 70 | 71 | bool setExtraStringHandler(Commander &Cmdr){ 72 | //String myString = ""; 73 | 74 | if(Cmdr.hasPayload()){ 75 | exHelpStr = Cmdr.getPayloadString(); 76 | //The pointer may have changed so get a new one 77 | extraHelp[0] = exHelpStr.c_str(); 78 | Cmdr.print("Extended help string changed to:"); 79 | Cmdr.println(exHelpStr); 80 | } 81 | else{ 82 | Cmdr.println("No payload. String is still:"); 83 | Cmdr.println(exHelpStr); 84 | } 85 | return 0; 86 | } 87 | 88 | 89 | bool setCommandHandler(Commander &Cmdr){ 90 | //String myString = ""; 91 | 92 | if(Cmdr.hasPayload()){ 93 | cmdStr = Cmdr.getPayloadString(); 94 | //The pointer may have changed so get a new one 95 | masterCommands[0].commandString = cmdStr.c_str(); 96 | Cmdr.print("Command string changed to:"); 97 | Cmdr.println(cmdStr); 98 | Cmdr.reloadCommands(); 99 | } 100 | else{ 101 | Cmdr.println("No payload. Command string is still:"); 102 | Cmdr.println(cmdStr); 103 | } 104 | return 0; 105 | } 106 | -------------------------------------------------------------------------------- /examples/Advanced/FunctionSwap/FunctionSwap.ino: -------------------------------------------------------------------------------- 1 | /*Commander example - Swapping function handlers and command text 2 | * (I used google translate for the French text, my apologies if it is wrong!) 3 | */ 4 | #include 5 | Commander cmd; 6 | bool swapstate = 0; 7 | //User string - this can be anything you want, and is printed when the help and ? commands are used 8 | //Its a good idea to put the # symbol in front of each line so that if the response to these commands is fet to another commander, they will be interpreted as comments. 9 | String deviceInfo = "#\t(Start of user string)\n#\tCommander swop function example\n#\tDevice firmware version x.x.x revision x\n#\thttps://github.com/CreativeRobotics/Commander\n#\t(End of user string)"; 10 | //SETUP --------------------------------------------------------------------------- 11 | void setup() { 12 | Serial.begin(115200); 13 | delay(100); 14 | while(!Serial){;} 15 | delay(100); 16 | initialiseCommander(); 17 | cmd.printUserString(); 18 | cmd.println(); 19 | Serial.println("Type 'help' to get help"); 20 | 21 | 22 | cmd.printCommandPrompt(); 23 | } 24 | 25 | //MAIN LOOP --------------------------------------------------------------------------- 26 | void loop() { 27 | cmd.update(); 28 | } 29 | -------------------------------------------------------------------------------- /examples/Advanced/FunctionSwap/masterCommands.ino: -------------------------------------------------------------------------------- 1 | //All commands for 'master' 2 | //COMMAND ARRAY ------------------------------------------------------------------------------ 3 | //All the strings are const, so the are stored in flash 4 | //The two different commands we are using 5 | const char* normalCommand = "hello"; 6 | const char* normalHelp = "say hello"; 7 | 8 | //Extended help strings, one in French and one in English 9 | const char* extendedHelp = "Prints out a hello message in English.\nThe 'swop' command will toggle the function pointer between two handlers, one for French and one for English, every time it is used."; 10 | const char* otherExtendedHelp = "Imprime un message hello dans Français.\nLa commande 'swop' bascule le pointeur fonction entre deux gestionnaires, l'un pour Français et l'autre pour l'anglais, chaque fois qu'il est utilisé."; 11 | 12 | //Normal help strings, one French and one English 13 | const char* otherCommand = "bonjour"; 14 | const char* otherHelp = "saluer"; 15 | 16 | commandList_t masterCommands[] = { 17 | {normalCommand, helloHandler, normalHelp}, 18 | {"swap", swapHandler, "swap the command strings and function handler"}, 19 | }; 20 | 21 | //Extended help text. This is an array of pointers to strings so we can assign one of the char arrays from above, and use the swop function to change the text. 22 | //(There needs to me the same number of elements in this array as in the command list.) 23 | const char * extraHelp[] = { 24 | extendedHelp, 25 | "'swap'\tSwap between two different function handlers for the first command.\nWhen this is called the function handler, command text and help text for the first command in the list will be swapped.", 26 | }; 27 | /* Command handler template 28 | bool myFunc(Commander &Cmdr){ 29 | //put your command handler code here 30 | return 0; 31 | } 32 | */ 33 | 34 | 35 | //Initialisation function that avoids having to forward declare the command array and a size variable. 36 | void initialiseCommander(){ 37 | cmd.begin(&Serial, masterCommands, sizeof(masterCommands)) 38 | .commandPrompt(ON) 39 | .echo(true) 40 | .errorMessages(ON) 41 | .autoChain(OFF) 42 | .setUserString(deviceInfo) 43 | .setExtraHelp(extraHelp); 44 | } 45 | 46 | //Three command handlers. 47 | bool helloHandler(Commander &Cmdr){ 48 | Cmdr.print("Hello! this is "); 49 | Cmdr.println(Cmdr.commanderName); 50 | Cmdr.print("This is my buffer: "); 51 | Cmdr.print(Cmdr.bufferString); 52 | //Cmdr.printDiagnostics(); 53 | return 0; 54 | } 55 | bool bonjourHandler(Commander &Cmdr){ 56 | Cmdr.print("Bonjour! c'est"); 57 | Cmdr.println(Cmdr.commanderName); 58 | Cmdr.print("C'est mon tampon de données: "); 59 | Cmdr.print(Cmdr.bufferString); 60 | //Cmdr.printDiagnostics(); 61 | return 0; 62 | } 63 | 64 | bool swapHandler(Commander &Cmdr){ 65 | Cmdr.println("Swapping command handler"); 66 | if(swapstate){ 67 | masterCommands[0].handler = helloHandler; 68 | masterCommands[0].commandString = normalCommand; 69 | masterCommands[0].manualString = normalHelp; 70 | extraHelp[0] = extendedHelp; 71 | }else{ 72 | masterCommands[0].handler = bonjourHandler; 73 | masterCommands[0].commandString = otherCommand; 74 | masterCommands[0].manualString = otherHelp; 75 | extraHelp[0] = otherExtendedHelp; 76 | } 77 | //ESSENTIAL: You must call this if the command string text is changed otherwide Commander might not be able to recognise the new command 78 | Cmdr.reloadCommands(); 79 | swapstate = !swapstate; 80 | return 0; 81 | } 82 | -------------------------------------------------------------------------------- /examples/Advanced/customMenu/customMenu.ino: -------------------------------------------------------------------------------- 1 | /*Commander example - Dynamic creation of a custom menu 2 | */ 3 | #include 4 | Commander cmd; 5 | bool swopstate = 0; 6 | //User string - this can be anything you want, and is printed when the help and ? commands are used 7 | //Its a good idea to put the # symbol in front of each line so that if the response to these commands is fet to another commander, they will be interpreted as comments. 8 | String deviceInfo = "#\t(Start of user string)\n#\tCommander dynamic menu creation\n#\tDevice firmware version x.x.x revision x\n#\thttps://github.com/CreativeRobotics/Commander\n#\t(End of user string)"; 9 | //SETUP --------------------------------------------------------------------------- 10 | void setup() { 11 | Serial.begin(115200); 12 | delay(100); 13 | while(!Serial){;} 14 | delay(100); 15 | initialiseCommander(); 16 | cmd.printUserString(); 17 | cmd.println(); 18 | Serial.println("Type 'help' to get help"); 19 | 20 | 21 | cmd.printCommandPrompt(); 22 | } 23 | 24 | //MAIN LOOP --------------------------------------------------------------------------- 25 | void loop() { 26 | cmd.update(); 27 | } 28 | -------------------------------------------------------------------------------- /examples/Advanced/customMenu/masterCommands.ino: -------------------------------------------------------------------------------- 1 | //All commands for 'master' 2 | //COMMAND ARRAY ------------------------------------------------------------------------------ 3 | //All the strings are const, so the are stored in flash 4 | //We need a string for each command and its help text 5 | const char* command0 = "hello"; 6 | const char* help0 = "say hello"; 7 | const char* command1 = "hello1"; 8 | const char* help1 = "say hello1"; 9 | const char* command2 = "hello2"; 10 | const char* help2 = "say hello2"; 11 | const char* command3 = "hello3"; 12 | const char* help3 = "say hello3"; 13 | const char* command4 = "hello4"; 14 | const char* help4 = "say hello4"; 15 | 16 | 17 | //These are some other commands we need 18 | const char* listCommand = "list"; 19 | const char* listhelp = "List available commands"; 20 | 21 | 22 | const char* createCommand = "create"; 23 | const char* createHelp = "Create a command list (eg 'create 2 4 3')"; 24 | 25 | const char* revertCommand = "revert"; 26 | const char* revertHelp = "revert to the old list"; 27 | 28 | const commandList_t masterCommands[] = { 29 | {listCommand, listHandler, listhelp}, 30 | {createCommand, createHandler, createHelp}, 31 | }; 32 | 33 | commandList_t *newCmds; 34 | /* Command handler template 35 | bool myFunc(Commander &Cmdr){ 36 | //put your command handler code here 37 | return 0; 38 | } 39 | */ 40 | /* 41 | {(char*)command0, helloHandler, (char*)help0}, 42 | {(char*)command1, helloHandler, (char*)help1}, 43 | {(char*)command2, helloHandler, (char*)help2}, 44 | {(char*)command3, helloHandler, (char*)help3}, 45 | {(char*)command4, helloHandler, (char*)help4}, 46 | */ 47 | //Initialisation function that avoids having to forward declare the command array and a size variable. 48 | void initialiseCommander(){ 49 | cmd.begin(&Serial, masterCommands, sizeof(masterCommands)) 50 | .commandPrompt(ON) 51 | .echo(true) 52 | .errorMessages(ON) 53 | .autoChain(OFF) 54 | .setUserString(deviceInfo); 55 | } 56 | 57 | //Three command handlers. 58 | bool helloHandler(Commander &Cmdr){ 59 | Cmdr.print("Hello! this is "); 60 | Cmdr.println(Cmdr.commanderName); 61 | Cmdr.print("This is my buffer: "); 62 | Cmdr.print(Cmdr.bufferString); 63 | //Cmdr.printDiagnostics(); 64 | return 0; 65 | } 66 | bool hello1Handler(Commander &Cmdr){ 67 | Cmdr.print("Hello 1! this is "); 68 | Cmdr.println(Cmdr.commanderName); 69 | Cmdr.print("This is my buffer: "); 70 | Cmdr.print(Cmdr.bufferString); 71 | //Cmdr.printDiagnostics(); 72 | return 0; 73 | } 74 | bool hello2Handler(Commander &Cmdr){ 75 | Cmdr.print("Hello 2! this is "); 76 | Cmdr.println(Cmdr.commanderName); 77 | Cmdr.print("This is my buffer: "); 78 | Cmdr.print(Cmdr.bufferString); 79 | //Cmdr.printDiagnostics(); 80 | return 0; 81 | } 82 | bool hello3Handler(Commander &Cmdr){ 83 | Cmdr.print("Hello 3! this is "); 84 | Cmdr.println(Cmdr.commanderName); 85 | Cmdr.print("This is my buffer: "); 86 | Cmdr.print(Cmdr.bufferString); 87 | //Cmdr.printDiagnostics(); 88 | return 0; 89 | } 90 | bool hello4Handler(Commander &Cmdr){ 91 | Cmdr.print("Hello 4! this is "); 92 | Cmdr.println(Cmdr.commanderName); 93 | Cmdr.print("This is my buffer: "); 94 | Cmdr.print(Cmdr.bufferString); 95 | //Cmdr.printDiagnostics(); 96 | return 0; 97 | } 98 | bool listHandler(Commander &Cmdr){ 99 | Cmdr.println("List of available functions:"); 100 | Cmdr.println("0 = hello"); 101 | Cmdr.println("1 = hello1"); 102 | Cmdr.println("2 = hello2"); 103 | Cmdr.println("3 = hello3"); 104 | Cmdr.println("4 = hello4"); 105 | return 0; 106 | } 107 | 108 | bool revertHandler(Commander &Cmdr){ 109 | Cmdr.println("Reverting to old list"); 110 | //delete the old list. 111 | delete [] newCmds; 112 | Cmdr.attachCommands(masterCommands, sizeof(masterCommands)); 113 | return 0; 114 | } 115 | 116 | bool createHandler(Commander &Cmdr){ 117 | int items = Cmdr.countItems(); 118 | int *values = new int[items]; 119 | for(int n = 0; n < items; n++){ 120 | //try and unpack an int, if it fails there are no more left so exit the loop 121 | if(Cmdr.getInt(values[n])){ 122 | Cmdr.print("unpacked "); 123 | Cmdr.println(values[n]); 124 | }else{ 125 | Cmdr.println("Syntax Error"); 126 | return 0; 127 | } 128 | } 129 | //create a new array for the custom list, with one extra item for the revert option 130 | newCmds = new commandList_t[items+1]; 131 | 132 | 133 | //now assign the correct strings and handlers 134 | for(uint8_t n = 0; n < items; n++){ 135 | newCmds[n].commandString = getCommand(values[n]); 136 | newCmds[n].handler = getHander(values[n]); 137 | newCmds[n].manualString = getHelp(values[n]); 138 | } 139 | //tack the revert option to the end of the array 140 | newCmds[items].commandString = revertCommand; 141 | newCmds[items].handler = revertHandler; 142 | newCmds[items].manualString = revertHelp; 143 | //Attach it to commander using the attachCommandArray method 144 | Cmdr.attachCommandArray(newCmds, items+1); 145 | Cmdr.println("New command list created and activated"); 146 | return 0; 147 | } 148 | 149 | //Functions to get the appropriate text and function pointers when creating the command array. 150 | char* getCommand(int item){ 151 | switch(item){ 152 | case 0: return (char*)command0; 153 | case 1: return (char*)command1; 154 | case 2: return (char*)command2; 155 | case 3: return (char*)command3; 156 | case 4: return (char*)command4; 157 | default: return (char*)command0; 158 | } 159 | } 160 | 161 | char* getHelp(int item){ 162 | switch(item){ 163 | case 0: return (char*)help0; 164 | case 1: return (char*)help1; 165 | case 2: return (char*)help2; 166 | case 3: return (char*)help3; 167 | case 4: return (char*)help4; 168 | default: return (char*)help0; 169 | } 170 | } 171 | 172 | cmdHandler getHander(int item){ 173 | switch(item){ 174 | case 0: return helloHandler; 175 | case 1: return hello1Handler; 176 | case 2: return hello2Handler; 177 | case 3: return hello3Handler; 178 | case 4: return hello4Handler; 179 | default: return helloHandler; 180 | } 181 | } 182 | -------------------------------------------------------------------------------- /examples/Advanced/customMenu_Static/customMenu_Static.ino: -------------------------------------------------------------------------------- 1 | /*Commander example - Create a custom menu from an SD card 2 | * This example shows how to populate a command list and then load the list into Commander to use it. 3 | * The command list length is defined at compile time, so there is no dynamic memory allocation, but you don't need to use the whole list. 4 | * The sketch starts with a command list of two items, add and finish. Add will add a command (specified by a number) from the list of 24 possible commands) 5 | * Finish will complete the process and load the new command list. 6 | * The sketch will first try to open an SD card called menu.txt. If it opens the file then it reads it and any commands in the file are executed. 7 | * Once it is read, or if no file is found, control switches over the the serial port. 8 | * 9 | * This demonstrates how to use a configuration file on the SD card to create a bespoke list of menu items that are available over the serial port when the device boots up. 10 | * To change the menu items, just edit the SD card file. 11 | * 12 | * This could be used to run a 'boot script' using a command list when booting the device, which then creates a menu of commands that depend on features that are detected during the boot process. 13 | */ 14 | #include 15 | #include 16 | #include 17 | File myFile; 18 | Commander cmd; 19 | 20 | bool SDOK = false; 21 | bool fileOpen = false; 22 | const int cardSelect = 4; 23 | 24 | int commandCount = 0; 25 | 26 | //User string - this can be anything you want, and is printed when the help and ? commands are used 27 | //Its a good idea to put the # symbol in front of each line so that if the response to these commands is fet to another commander, they will be interpreted as comments. 28 | String deviceInfo = "#\t(Start of user string)\n#\tCustom menu at boot from SD card\n#\tDevice firmware version x.x.x revision x\n#\thttps://github.com/CreativeRobotics/Commander\n#\t(End of user string)"; 29 | //SETUP --------------------------------------------------------------------------- 30 | void setup() { 31 | Serial.begin(115200); 32 | while(!Serial){;} 33 | delay(100); 34 | //See if an SD card is inserted 35 | if(!SD.begin(cardSelect)) { 36 | Serial.println("SDCard Init Error"); 37 | }else{ 38 | SDOK = true; 39 | Serial.println("SDCard Started"); 40 | } 41 | Serial.println("Starting Commander ... "); 42 | 43 | 44 | initialiseCommander(); 45 | cmd.printUserString(); 46 | 47 | if(SDOK && SD.exists("menu.txt")){ 48 | Serial.println("Opening File"); 49 | myFile = SD.open("commands.txt" , FILE_READ); 50 | fileOpen = true; 51 | }else if(SDOK){ 52 | Serial.println("No File Found"); 53 | } 54 | 55 | 56 | cmd.println(); 57 | Serial.println("Type 'help' to get help"); 58 | 59 | 60 | cmd.printCommandPrompt(); 61 | } 62 | 63 | void loop() { 64 | //If the file was opened, read it 65 | if(fileOpen){ 66 | cmd.attachInputPort(&myFile); 67 | Serial.println("---------------------------------------"); 68 | readFile(); 69 | myFile.close(); 70 | fileOpen = false; 71 | Serial.println("---------------------------------------"); 72 | Serial.println("File read finished"); 73 | //Now redirect commander to the serial port so we can type commands in there: 74 | cmd.attachInputPort(&Serial); 75 | //Enable command prompts and echo characters 76 | cmd.commandPrompt(ON); 77 | cmd.echo(true); 78 | //Now print out the prompt with a message 79 | cmd.println("You have control - Enter a command"); 80 | cmd.printCommandPrompt(); 81 | } 82 | cmd.update(); 83 | delay(1); 84 | } 85 | 86 | void readFile(){ 87 | //Call update for as long as there are still characters left to read 88 | //cmd will return 1 when it handles a command but there is more in the buffer and 0 when it reaches the end of the file 89 | while(cmd.update()){delay(10);} 90 | //If there is no newline at the end of the file then any command on the last line won't be handled 91 | //Add a newline to the buffer and handle any commands found there 92 | cmd.endLine(); 93 | } 94 | -------------------------------------------------------------------------------- /examples/Advanced/customMenu_Static/masterCommands.ino: -------------------------------------------------------------------------------- 1 | //All commands for 'master' 2 | //COMMAND ARRAY ------------------------------------------------------------------------------ 3 | //You can have up to maxItems number of commands in the command list at once 4 | //There are a total of numberOfCommands command handlers available to use. 5 | const int maxItems = 16; 6 | const int numberOfCommands = 24; 7 | 8 | 9 | const char* commandList[numberOfCommands] = { 10 | "cmd0", 11 | "cmd1", 12 | "cmd2", 13 | "cmd3", 14 | "cmd4", 15 | "cmd5", 16 | "cmd6", 17 | "cmd7", 18 | "cmd8", 19 | "cmd9", 20 | "cmd10", 21 | "cmd11", 22 | "cmd12", 23 | "cmd13", 24 | "cmd14", 25 | "cmd15", 26 | "cmd16", 27 | "cmd17", 28 | "cmd18", 29 | "cmd19", 30 | "cmd20", 31 | "cmd21", 32 | "cmd22", 33 | "cmd23", 34 | }; 35 | 36 | 37 | const char* helpList[numberOfCommands] = { 38 | "help text for command cmd0", 39 | "help text for command cmd1", 40 | "help text for command cmd2", 41 | "help text for command cmd3", 42 | "help text for command cmd4", 43 | "help text for command cmd5", 44 | "help text for command cmd6", 45 | "help text for command cmd7", 46 | "help text for command cmd8", 47 | "help text for command cmd9", 48 | "help text for command cmd10", 49 | "help text for command cmd11", 50 | "help text for command cmd12", 51 | "help text for command cmd13", 52 | "help text for command cmd14", 53 | "help text for command cmd15", 54 | "help text for command cmd16", 55 | "help text for command cmd17", 56 | "help text for command cmd18", 57 | "help text for command cmd19", 58 | "help text for command cmd20", 59 | "help text for command cmd21", 60 | "help text for command cmd22", 61 | "help text for command cmd23", 62 | }; 63 | 64 | 65 | 66 | 67 | cmdHandler handlers[numberOfCommands] = { 68 | Handler0, 69 | Handler1, 70 | Handler2, 71 | Handler3, 72 | Handler4, 73 | Handler5, 74 | Handler6, 75 | Handler7, 76 | Handler8, 77 | Handler9, 78 | Handler10, 79 | Handler11, 80 | Handler12, 81 | Handler13, 82 | Handler14, 83 | Handler15, 84 | Handler16, 85 | Handler17, 86 | Handler18, 87 | Handler19, 88 | Handler20, 89 | Handler21, 90 | Handler22, 91 | Handler23, 92 | }; 93 | 94 | const commandList_t startupCommands[] = { 95 | {"add", addHandler, "Add a command (add [handlerNo])"}, 96 | {"finish", finishHandler, "Finish adding commands and load the list"}, 97 | }; 98 | 99 | //uninitialised array to be filled 100 | commandList_t myCommands[maxItems]; 101 | 102 | 103 | /* Command handler template 104 | bool myFunc(Commander &Cmdr){ 105 | //put your command handler code here 106 | return 0; 107 | } 108 | */ 109 | 110 | //Initialisation function that avoids having to forward declare the command array and a size variable. 111 | void initialiseCommander(){ 112 | //Initialise with inport and outport pointing to serial 113 | //inport will be reassigned to the file if it is found, the directed back to serial at the end of the file read 114 | //Any messages will be printed to Serial 115 | cmd.begin(&Serial, &Serial, startupCommands, sizeof(startupCommands)) 116 | .commandPrompt(ON) 117 | .echo(true) 118 | .errorMessages(ON) 119 | .autoChain(OFF) 120 | .setUserString(deviceInfo); 121 | } 122 | 123 | //commandString; 124 | // cmdHandler handler; 125 | // char* manualString; 126 | 127 | 128 | 129 | 130 | bool finishHandler(Commander &Cmdr){ 131 | Cmdr.println("Switching to custom command list"); 132 | Cmdr.attachCommandArray(myCommands, commandCount); 133 | return 0; 134 | } 135 | 136 | 137 | bool Handler0(Commander &Cmdr){ 138 | Cmdr.println("Called command handler number 0"); 139 | return 0; 140 | } 141 | bool Handler1(Commander &Cmdr){ 142 | Cmdr.println("Called command handler number 1"); 143 | return 0; 144 | } 145 | bool Handler2(Commander &Cmdr){ 146 | Cmdr.println("Called command handler number 2"); 147 | return 0; 148 | } 149 | bool Handler3(Commander &Cmdr){ 150 | Cmdr.println("Called command handler number 3"); 151 | return 0; 152 | } 153 | bool Handler4(Commander &Cmdr){ 154 | Cmdr.println("Called command handler number 4"); 155 | return 0; 156 | } 157 | bool Handler5(Commander &Cmdr){ 158 | Cmdr.println("Called command handler number 05"); 159 | return 0; 160 | } 161 | bool Handler6(Commander &Cmdr){ 162 | Cmdr.println("Called command handler number 6"); 163 | return 0; 164 | } 165 | bool Handler7(Commander &Cmdr){ 166 | Cmdr.println("Called command handler number 7"); 167 | return 0; 168 | } 169 | bool Handler8(Commander &Cmdr){ 170 | Cmdr.println("Called command handler number 8"); 171 | return 0; 172 | } 173 | bool Handler9(Commander &Cmdr){ 174 | Cmdr.println("Called command handler number 9"); 175 | return 0; 176 | } 177 | bool Handler10(Commander &Cmdr){ 178 | Cmdr.println("Called command handler number 10"); 179 | return 0; 180 | } 181 | bool Handler11(Commander &Cmdr){ 182 | Cmdr.println("Called command handler number 11"); 183 | return 0; 184 | } 185 | bool Handler12(Commander &Cmdr){ 186 | Cmdr.println("Called command handler number 12"); 187 | return 0; 188 | } 189 | bool Handler13(Commander &Cmdr){ 190 | Cmdr.println("Called command handler number 13"); 191 | return 0; 192 | } 193 | bool Handler14(Commander &Cmdr){ 194 | Cmdr.println("Called command handler number 14"); 195 | return 0; 196 | } 197 | bool Handler15(Commander &Cmdr){ 198 | Cmdr.println("Called command handler number 15"); 199 | return 0; 200 | } 201 | bool Handler16(Commander &Cmdr){ 202 | Cmdr.println("Called command handler number 16"); 203 | return 0; 204 | } 205 | bool Handler17(Commander &Cmdr){ 206 | Cmdr.println("Called command handler number 17"); 207 | return 0; 208 | } 209 | bool Handler18(Commander &Cmdr){ 210 | Cmdr.println("Called command handler number 18"); 211 | return 0; 212 | } 213 | bool Handler19(Commander &Cmdr){ 214 | Cmdr.println("Called command handler number 19"); 215 | return 0; 216 | } 217 | bool Handler20(Commander &Cmdr){ 218 | Cmdr.println("Called command handler number 20"); 219 | return 0; 220 | } 221 | bool Handler21(Commander &Cmdr){ 222 | Cmdr.println("Called command handler number 21"); 223 | return 0; 224 | } 225 | bool Handler22(Commander &Cmdr){ 226 | Cmdr.println("Called command handler number 22"); 227 | return 0; 228 | } 229 | bool Handler23(Commander &Cmdr){ 230 | Cmdr.println("Called command handler number 23"); 231 | return 0; 232 | } 233 | 234 | 235 | bool addHandler(Commander &Cmdr){ 236 | //There should be just a number in the payload 237 | //Cmdr.printDiagnostics(); 238 | int itemID = -1; 239 | if(commandCount >= maxItems){ 240 | Cmdr.println("Error, menu is full"); 241 | return 0; 242 | } 243 | if(Cmdr.getInt(itemID)){ 244 | if(itemID > -1 && itemID < numberOfCommands){ 245 | myCommands[commandCount].commandString = commandList[itemID]; 246 | myCommands[commandCount].handler = handlers[itemID]; 247 | myCommands[commandCount].manualString = helpList[itemID]; 248 | Cmdr.print("Added the command "); 249 | Cmdr.print(commandList[itemID]); 250 | Cmdr.print(" as menu item "); 251 | Cmdr.println(commandCount); 252 | commandCount++; 253 | }else Cmdr.println("Error, that item is not available"); 254 | } 255 | return 0; 256 | } 257 | -------------------------------------------------------------------------------- /examples/BasicCommands/BasicCommands.ino: -------------------------------------------------------------------------------- 1 | /*Commander example - basic 2 | * Demonstrating commands to get and set an int and a float 3 | */ 4 | #include 5 | Commander cmd; 6 | //Variables we can set or get 7 | int myInt = 0; 8 | float myFloat = 0.0; 9 | 10 | String deviceInfo = "#\tCommander basic commands example\n#\thttps://github.com/CreativeRobotics/Commander"; 11 | //SETUP --------------------------------------------------------------------------- 12 | void setup() { 13 | Serial.begin(115200); 14 | initialiseCommander(); 15 | while(!Serial){;} 16 | cmd.printUserString(); 17 | cmd.println(); 18 | Serial.println("Type 'help' to get help"); 19 | cmd.printCommandPrompt(); 20 | } 21 | 22 | //MAIN LOOP --------------------------------------------------------------------------- 23 | void loop() { 24 | cmd.update(); 25 | } 26 | -------------------------------------------------------------------------------- /examples/BasicCommands/masterCommands.ino: -------------------------------------------------------------------------------- 1 | //All commands for 'master' 2 | //COMMAND ARRAY ------------------------------------------------------------------------------ 3 | const commandList_t masterCommands[] = { 4 | {"hello", helloHandler, "hello"}, 5 | {"get int", getIntHandler, "get an int"}, 6 | {"set int", setIntHandler, "set an int"}, 7 | {"get float", getFloatHandler, "get a float"}, 8 | {"set float", setFloatHandler, "set a float"}, 9 | {"hidden1", hiddenHandler, "-Command hidden from help"}, 10 | {"myint", setIntHandler, "try myint=23"}, 11 | {"myfloat", setFloatHandler, "try myfloat=23.5"}, 12 | {"set ints", setIntsHandler, "set up to four ints"}, 13 | {"set floats", setFloatsHandler, "set up to four floats"}, 14 | {"set strings", setStringsHandler,"set up to four Strings"}, 15 | {"hidden2", hiddenHandler, "-Command hidden from help"}, 16 | }; 17 | 18 | /* Command handler template 19 | bool myFunc(Commander &Cmdr){ 20 | //put your command handler code here 21 | return 0; 22 | } 23 | */ 24 | void initialiseCommander(){ 25 | cmd.begin(&Serial, masterCommands, sizeof(masterCommands)); 26 | cmd.commandPrompt(ON); //enable the command prompt 27 | cmd.echo(true); //Echo incoming characters to theoutput port 28 | cmd.errorMessages(ON); //error messages are enabled - it will tell us if we issue any unrecognised commands 29 | cmd.autoChain(ON); 30 | cmd.setUserString(deviceInfo); 31 | } 32 | 33 | //These are the command handlers, there needs to be one for each command in the command array myCommands[] 34 | //The command array can have multiple commands strings that all call the same function 35 | bool helloHandler(Commander &Cmdr){ 36 | Cmdr.print("Hello! this is "); 37 | Cmdr.println(Cmdr.commanderName); 38 | Cmdr.print("This is my buffer: "); 39 | Cmdr.print(Cmdr.bufferString); 40 | //Cmdr.printDiagnostics(); 41 | return 0; 42 | } 43 | bool getIntHandler(Commander &Cmdr){ 44 | Cmdr.print("myInt = "); 45 | Cmdr.println(myInt); 46 | //Cmdr.printDiagnostics(); 47 | return 0; 48 | } 49 | 50 | bool setIntHandler(Commander &Cmdr){ 51 | if(Cmdr.getInt(myInt)){ 52 | Cmdr.print("myInt set to "); 53 | Cmdr.println(myInt); 54 | } 55 | //Cmdr.printDiagnostics(); 56 | return 0; 57 | } 58 | bool getFloatHandler(Commander &Cmdr){ 59 | Cmdr.print("myFloat = "); 60 | Cmdr.println(myFloat); 61 | 62 | //Cmdr.printDiagnostics(); 63 | return 0; 64 | } 65 | 66 | bool setFloatHandler(Commander &Cmdr){ 67 | if(Cmdr.getFloat(myFloat)){ 68 | Cmdr.print("myFloat set to "); 69 | Cmdr.println(myFloat, 4); //print with 4 decimal places 70 | } 71 | //Cmdr.printDiagnostics(); 72 | return 0; 73 | } 74 | 75 | bool setIntsHandler(Commander &Cmdr){ 76 | //create an array to store any values we find 77 | int values[4] = {0,0,0,0}; 78 | 79 | int itms = Cmdr.countItems(); 80 | Cmdr.print("There are "); 81 | Cmdr.print(itms); 82 | Cmdr.println(" items in the payload"); 83 | 84 | for(int n = 0; n < 4; n++){ 85 | //try and unpack an int, if it fails there are no more left so exit the loop 86 | if(Cmdr.getInt(values[n])){ 87 | Cmdr.print("unpacked "); 88 | Cmdr.println(values[n]); 89 | }else break; 90 | } 91 | //print it out 92 | Cmdr.println("Array contents after processing:"); 93 | for(int n = 0; n < 4; n++){ 94 | Cmdr.print(n); 95 | Cmdr.print(" = "); 96 | Cmdr.println(values[n]); 97 | } 98 | //Cmdr.chain(); 99 | //Cmdr.printDiagnostics(); 100 | return 0; 101 | } 102 | 103 | bool setFloatsHandler(Commander &Cmdr){ 104 | float values[4] = {0.0,0.0,0.0,0.0}; 105 | int itms = Cmdr.countItems(); 106 | Cmdr.print("There are "); 107 | Cmdr.print(itms); 108 | Cmdr.println(" items in the payload"); 109 | 110 | for(int n = 0; n < 4; n++){ 111 | if(Cmdr.getFloat(values[n])){ 112 | Cmdr.print("unpacked "); 113 | Cmdr.println(values[n]); 114 | }else break; 115 | } 116 | Cmdr.println("Array contents after processing:"); 117 | for(int n = 0; n < 4; n++){ 118 | Cmdr.print(n); 119 | Cmdr.print(" = "); 120 | Cmdr.println(values[n]); 121 | } 122 | //Cmdr.chain(); 123 | //Cmdr.printDiagnostics(); 124 | return 0; 125 | } 126 | 127 | bool setStringsHandler(Commander &Cmdr){ 128 | String myString = ""; 129 | int itms = Cmdr.countItems(); 130 | Cmdr.print("There are "); 131 | Cmdr.print(itms); 132 | Cmdr.println(" items in the payload"); 133 | for(int n = 0; n < itms; n++){ 134 | if(Cmdr.getString(myString)){ 135 | Cmdr.print("String "); 136 | Cmdr.print(n); 137 | Cmdr.print(" = "); 138 | Cmdr.println(myString); 139 | }else Cmdr.println("Operation failed"); 140 | } 141 | //Cmdr.chain(); 142 | //Cmdr.printDiagnostics(); 143 | return 0; 144 | } 145 | 146 | bool hiddenHandler(Commander &Cmdr){ 147 | Cmdr.println("This command is hidden from the help system"); 148 | //Cmdr.printDiagnostics(); 149 | return 0; 150 | } 151 | -------------------------------------------------------------------------------- /examples/ESP32/SerialBTCommands/Commands.ino: -------------------------------------------------------------------------------- 1 | //All commands for 'master' 2 | //COMMAND ARRAY ------------------------------------------------------------------------------ 3 | const commandList_t masterCommands[] = { 4 | {"hello", helloHandler, "hello"}, 5 | {"get int", getIntHandler, "get an int"}, 6 | {"set int", setIntHandler, "set an int"}, 7 | {"get float", getFloatHandler, "get a float"}, 8 | {"set float", setFloatHandler, "set a float"}, 9 | }; 10 | /* Command handler template 11 | bool myFunc(Commander &Cmdr){ 12 | //put your command handler code here 13 | return 0; 14 | } 15 | */ 16 | void initialiseCommander(){ 17 | cmd.begin(&SerialBT, masterCommands, sizeof(masterCommands)); 18 | cmd.commandPrompt(ON);; //enable the command prompt 19 | } 20 | //These are the command handlers, there needs to be one for each command in the command array myCommands[] 21 | //The command array can have multiple commands strings that all call the same function 22 | bool helloHandler(Commander &Cmdr){ 23 | Cmdr.print("Hello! this is "); 24 | Cmdr.println(Cmdr.commanderName); 25 | Cmdr.println("Communicating with Bluetooth"); 26 | Cmdr.println("With an ESP32"); 27 | Cmdr.println("2Communicating with Bluetooth"); 28 | Cmdr.println("2With an ESP32"); 29 | Cmdr.println("3Communicating with Bluetooth"); 30 | Cmdr.println("4With an ESP32"); 31 | return 0; 32 | } 33 | 34 | bool getIntHandler(Commander &Cmdr){ 35 | Cmdr.print("myInt = "); 36 | Cmdr.println(myInt); 37 | return 0; 38 | } 39 | 40 | bool setIntHandler(Commander &Cmdr){ 41 | if(Cmdr.getInt(myInt)){ 42 | Cmdr.print("myInt set to "); 43 | Cmdr.println(myInt); 44 | } 45 | return 0; 46 | } 47 | bool getFloatHandler(Commander &Cmdr){ 48 | Cmdr.print("myFloat = "); 49 | Cmdr.println(myFloat); 50 | return 0; 51 | } 52 | 53 | bool setFloatHandler(Commander &Cmdr){ 54 | if(Cmdr.getFloat(myFloat)){ 55 | Cmdr.print("myFloat set to "); 56 | Cmdr.println(myFloat, 4); //print with 4 decimal places 57 | } 58 | return 0; 59 | } 60 | -------------------------------------------------------------------------------- /examples/ESP32/SerialBTCommands/SerialBTCommands.ino: -------------------------------------------------------------------------------- 1 | //This example demonstrated a commander object attached to a Bluetooth Serial Port. 2 | //Use a bluetooth terminal on a smartphone or laptop to connect and enter commands. 3 | //Based on the ESP32 BluetoothSerial example By Evandro Copercini - 2018 4 | // 5 | 6 | #include "BluetoothSerial.h" 7 | #include 8 | Commander cmd; 9 | //Variables we can set or get 10 | int myInt = 0; 11 | float myFloat = 0.0; 12 | 13 | #if !defined(CONFIG_BT_ENABLED) || !defined(CONFIG_BLUEDROID_ENABLED) 14 | #error Bluetooth is not enabled! Please run `make menuconfig` to and enable it 15 | #endif 16 | 17 | BluetoothSerial SerialBT; 18 | 19 | void setup() { 20 | Serial.begin(115200); 21 | SerialBT.begin("ESP32test"); //Bluetooth device name 22 | Serial.println("The device started, now you can pair it with bluetooth!"); 23 | initialiseCommander(); 24 | cmd.printCommandPrompt(); 25 | } 26 | 27 | void loop() { 28 | cmd.update(); 29 | delay(20); 30 | } 31 | -------------------------------------------------------------------------------- /examples/ESP32/TelnetCommands/Commands.ino: -------------------------------------------------------------------------------- 1 | //All commands for 'master' 2 | //COMMAND ARRAY ------------------------------------------------------------------------------ 3 | const commandList_t masterCommands[] = { 4 | {"hello", helloHandler, "hello"}, 5 | {"get int", getIntHandler, "get an int"}, 6 | {"set int", setIntHandler, "set an int"}, 7 | {"get float", getFloatHandler, "get a float"}, 8 | {"set float", setFloatHandler, "set a float"}, 9 | }; 10 | /* Command handler template 11 | bool myFunc(Commander &Cmdr){ 12 | //put your command handler code here 13 | return 0; 14 | } 15 | */ 16 | 17 | //Initialisation function that avoids having to forward declare the command array and a size variable. 18 | void initialiseCommander(){ 19 | cmd.begin(&serverClient, masterCommands, sizeof(masterCommands)); 20 | cmd.commandPrompt(ON);; //enable the command prompt 21 | } 22 | 23 | //These are the command handlers, there needs to be one for each command in the command array myCommands[] 24 | //The command array can have multiple commands strings that all call the same function 25 | bool helloHandler(Commander &Cmdr){ 26 | Cmdr.print("Hello! this is "); 27 | Cmdr.println(Cmdr.commanderName); 28 | Cmdr.println("Communicating with Bluetooth"); 29 | Cmdr.println("With an ESP32"); 30 | Cmdr.println("2Communicating with Bluetooth"); 31 | Cmdr.println("2With an ESP32"); 32 | Cmdr.println("3Communicating with Bluetooth"); 33 | Cmdr.println("4With an ESP32"); 34 | return 0; 35 | } 36 | 37 | bool getIntHandler(Commander &Cmdr){ 38 | Cmdr.print("myInt = "); 39 | Cmdr.println(myInt); 40 | return 0; 41 | } 42 | 43 | bool setIntHandler(Commander &Cmdr){ 44 | if(Cmdr.getInt(myInt)){ 45 | Cmdr.print("myInt set to "); 46 | Cmdr.println(myInt); 47 | } 48 | return 0; 49 | } 50 | bool getFloatHandler(Commander &Cmdr){ 51 | Cmdr.print("myFloat = "); 52 | Cmdr.println(myFloat); 53 | return 0; 54 | } 55 | 56 | bool setFloatHandler(Commander &Cmdr){ 57 | if(Cmdr.getFloat(myFloat)){ 58 | Cmdr.print("myFloat set to "); 59 | Cmdr.println(myFloat, 4); //print with 4 decimal places 60 | } 61 | return 0; 62 | } 63 | -------------------------------------------------------------------------------- /examples/ESP32/TelnetCommands/TelnetCommands.ino: -------------------------------------------------------------------------------- 1 | //This example demonstrated a commander object attached to a Wifi Telnet server 2 | //Use a bluetooth terminal on a smartphone or laptop to connect and enter commands. 3 | 4 | 5 | #include 6 | 7 | Commander cmd; 8 | //Variables we can set or get 9 | int myInt = 0; 10 | float myFloat = 0.0; 11 | #include 12 | #include 13 | 14 | WiFiMulti wifiMulti; 15 | 16 | //how many clients should be able to telnet to this ESP32 17 | #define MAX_SRV_CLIENTS 1 18 | const char* ssid = "**********"; 19 | const char* password = "**********"; 20 | 21 | WiFiServer server(23); 22 | WiFiClient serverClient; 23 | 24 | void setup() { 25 | 26 | Serial.begin(115200); 27 | Serial.println("\nConnecting"); 28 | 29 | wifiMulti.addAP(ssid, password); 30 | 31 | Serial.println("Connecting Wifi "); 32 | for (int loops = 10; loops > 0; loops--) { 33 | if (wifiMulti.run() == WL_CONNECTED) { 34 | Serial.println(""); 35 | Serial.print("WiFi connected "); 36 | Serial.print("IP address: "); 37 | Serial.println(WiFi.localIP()); 38 | break; 39 | } 40 | else { 41 | Serial.println(loops); 42 | delay(1000); 43 | } 44 | } 45 | if (wifiMulti.run() != WL_CONNECTED) { 46 | Serial.println("WiFi connect failed"); 47 | delay(1000); 48 | ESP.restart(); 49 | } 50 | server.begin(); 51 | server.setNoDelay(true); 52 | 53 | Serial.print("Ready! Use 'telnet "); 54 | Serial.print(WiFi.localIP()); 55 | Serial.println(" 23' to connect"); 56 | 57 | initialiseCommander(); 58 | cmd.printCommandPrompt(); 59 | } 60 | 61 | void loop() { 62 | 63 | if (wifiMulti.run() == WL_CONNECTED) { 64 | //check if there are any new clients 65 | if (server.hasClient()){ 66 | //find free/disconnected spot 67 | if (!serverClient || !serverClient.connected()){ 68 | if(serverClient) serverClient.stop(); 69 | serverClient = server.available(); 70 | if (!serverClient) Serial.println("available broken"); 71 | Serial.print("New client: "); 72 | Serial.println(serverClient.remoteIP()); 73 | } 74 | } 75 | //check clients for data 76 | 77 | if (serverClient && serverClient.connected()){ 78 | if(serverClient.available()){ 79 | //get data from the telnet client and push it to the UART 80 | 81 | cmd.update(); 82 | //while(serverClient.available()) Serial2.write(serverClient.read()); 83 | } 84 | } else { 85 | if (serverClient) { 86 | serverClient.stop(); 87 | } 88 | } 89 | 90 | } else { 91 | Serial.println("WiFi not connected!"); 92 | if (serverClient) serverClient.stop(); 93 | delay(1000); 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /examples/FileNavigation/Commands.ino: -------------------------------------------------------------------------------- 1 | //All commands for 'master' 2 | //COMMAND ARRAY ------------------------------------------------------------------------------ 3 | const commandList_t masterCommands[] = { 4 | {"mkdir", makeDirectory, "make a sub directory"}, 5 | {"chdir", changeDirectory, "Change directory - use / for root"}, 6 | {"ls", printDirectory, "Print directory"}, 7 | {"remove", removeFile, "Delete a file"}, 8 | {"removedir", removeDirectory, "Delete an empty directory"}, 9 | {"rename", renameFile, "rename a file or sub directory"}, 10 | {"print", printFileContents, "Print file contents to terminal"}, 11 | {"status", noCardError, "check card status"}, 12 | 13 | }; 14 | 15 | /* Command handler template 16 | bool myFunc(Commander &Cmdr){ 17 | //put your command handler code here 18 | return 0; 19 | } 20 | */ 21 | void initialiseCommander(){ 22 | //Start Commander and attach the incoming port to the File stream 23 | //Attach the outgoing port to Serial 24 | //Attach the command list and the list size variable 25 | cmd.begin(&Serial, &Serial, masterCommands, sizeof(masterCommands)); 26 | cmd.commandPrompt(ON); 27 | cmd.commanderName = cmdName + "/"; 28 | cmd.echo(true); 29 | } 30 | 31 | 32 | //These are the command handlers, there needs to be one for each command in the command array myCommands[] 33 | //The command array can have multiple commands strings that all call the same function 34 | bool makeDirectory(Commander &Cmdr){ 35 | if(!SDOK) return noCardError(Cmdr); 36 | if(SD.mkdir( Cmdr.getPayloadString().c_str() )){ 37 | Cmdr.print("Created: "); 38 | Cmdr.println(Cmdr.getPayloadString()); 39 | } 40 | return 0; 41 | } 42 | bool changeDirectory(Commander &Cmdr){ 43 | if(!SDOK) return noCardError(Cmdr); 44 | if(SD.chdir( Cmdr.getPayloadString().c_str() )){ 45 | Cmdr.print("In: "); 46 | Cmdr.println(Cmdr.getPayloadString()); 47 | if(Cmdr.getPayloadString() == "/" ) Cmdr.commanderName = cmdName + Cmdr.getPayloadString(); 48 | else Cmdr.commanderName = cmdName + " /" + Cmdr.getPayloadString(); 49 | }else Cmdr.println("Error - no such directory"); 50 | return 0; 51 | } 52 | bool printDirectory(Commander &Cmdr){ 53 | if(!SDOK) return noCardError(Cmdr); 54 | 55 | SD.ls(Cmdr.getOutputPort(), LS_R); 56 | return 0; 57 | } 58 | 59 | bool removeFile(Commander &Cmdr){ 60 | if(!SDOK) return noCardError(Cmdr); 61 | if(SD.remove( Cmdr.getPayloadString().c_str())){ 62 | Cmdr.print("Removed: "); 63 | Cmdr.println(Cmdr.getPayloadString()); 64 | }else{ 65 | Cmdr.println("operation failed"); 66 | } 67 | return 0; 68 | } 69 | bool removeDirectory(Commander &Cmdr){ 70 | if(!SDOK) return noCardError(Cmdr); 71 | if(SD.rmdir( Cmdr.getPayloadString().c_str())){ 72 | Cmdr.print("Removed: "); 73 | Cmdr.println(Cmdr.getPayloadString()); 74 | }else{ 75 | Cmdr.println("operation failed"); 76 | } 77 | return 0; 78 | } 79 | bool renameFile(Commander &Cmdr){ 80 | if(!SDOK) return noCardError(Cmdr); 81 | String pld = Cmdr.getPayloadString(); //get the payload without any newline 82 | int idx = pld.indexOf(" "); //find the first space 83 | if(idx == -1){ 84 | Cmdr.println("Error, unable to handle command"); 85 | return 0; 86 | } 87 | String sub1 = pld.substring(0, idx); //get the first word. 88 | String sub2 = pld.substring(idx+1, pld.length()); 89 | Cmdr.print("Changing "); 90 | Cmdr.print(sub1); 91 | Cmdr.print(" to "); 92 | Cmdr.println(sub2); 93 | if(SD.rename(sub1.c_str(), sub2.c_str())){ 94 | Cmdr.println("File or directory has been renamed"); 95 | }else Cmdr.println("Error: Operation failed"); 96 | return 0; 97 | } 98 | 99 | bool printFileContents(Commander &Cmdr){ 100 | if(!SDOK) return noCardError(Cmdr); 101 | File tmpFile = SD.open(Cmdr.getPayloadString().c_str() , O_RDONLY); 102 | while(tmpFile.available()){ 103 | Cmdr.write(tmpFile.read()); 104 | } 105 | Cmdr.write('\n'); 106 | tmpFile.close(); 107 | return 0; 108 | } 109 | 110 | bool noCardError(Commander &Cmdr){ 111 | if(!SDOK) Cmdr.println("ERR: No SD Card"); 112 | else Cmdr.println("Card detected"); 113 | return 0; 114 | } 115 | -------------------------------------------------------------------------------- /examples/FileNavigation/FileNavigation.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * Commander file navigation example 3 | * Demonstrates listing files, navigating and creating directories, renaming and deleting files and directories and printing out files. 4 | */ 5 | #include 6 | #include 7 | #include 8 | #include 9 | SdFat SD; 10 | Commander cmd; 11 | 12 | bool SDOK = false; 13 | bool fileOpen = false; 14 | const int cardSelect = 4; 15 | String cmdName = "CMD: "; 16 | 17 | void setup() { 18 | Serial.begin(115200); 19 | while(!Serial){} //Wait for the serial port to open before running the example 20 | Serial.println("Commander File navigator Example"); 21 | 22 | //See if an SD card is inserted 23 | if(!SD.begin(cardSelect, SD_SCK_MHZ(50))) { 24 | Serial.println("SDCard Init Error"); 25 | }else{ 26 | SDOK = true; 27 | Serial.println("SDCard Started"); 28 | } 29 | Serial.println("Starting Commander ... type help to see a command list"); 30 | 31 | initialiseCommander(); 32 | //cmd.println("Files on card:"); 33 | //SD.ls(cmd.getOutputPort(), LS_R); 34 | 35 | cmd.printCommandPrompt(); 36 | } 37 | 38 | void loop() { 39 | cmd.update(); 40 | } 41 | -------------------------------------------------------------------------------- /examples/FileRead/Commands.ino: -------------------------------------------------------------------------------- 1 | //All commands for 'master' 2 | //COMMAND ARRAY ------------------------------------------------------------------------------ 3 | const commandList_t masterCommands[] = { 4 | {"hello", helloHandler, "Say hello"}, 5 | {"set int 1", setInt1Handler, "Set an int"}, 6 | {"get int 1", getInt1Handler, "Get an int"}, 7 | {"set int 2", setInt2Handler, "Set an int"}, 8 | {"get int 2", getInt2Handler, "Get an int"}, 9 | {"set float 1", setFloat1Handler, "Set a float"}, 10 | {"get float 1", getFloat1Handler, "Get a float"}, 11 | {"set float 2", setFloat2Handler, "Set a float"}, 12 | {"get float 2", getFloat2Handler, "Get a float"} 13 | }; 14 | 15 | 16 | /* Command handler template 17 | bool myFunc(Commander &Cmdr){ 18 | //put your command handler code here 19 | return 0; 20 | } 21 | */ 22 | 23 | void initialiseCommander(){ 24 | //Start Commander and attach the file stream object, and the serial port for replies 25 | //begin(Stream object for input, stream object for output, command array, size of command array) 26 | //Attach the outgoing port to Serial 27 | //Attach the command list and the list size variable 28 | cmd.begin(&myFile, &Serial, masterCommands, sizeof(masterCommands)); 29 | //enable printing of comment lines 30 | cmd.printComments(true); 31 | } 32 | 33 | //These are the command handlers, there needs to be one for each command in the command array myCommands[] 34 | //The command array can have multiple commands strings that all call the same function 35 | bool helloHandler(Commander &Cmdr){ 36 | Cmdr.print("Hello! this is "); 37 | Cmdr.println(Cmdr.commanderName); 38 | return 0; 39 | } 40 | bool setInt1Handler(Commander &Cmdr){ 41 | if(Cmdr.getInt(myInt1)){ 42 | Cmdr.print("Set Int 1 to "); 43 | Cmdr.println(myInt1); 44 | }else Cmdr.println("Set Int 1 failed"); 45 | return 0; 46 | } 47 | bool getInt1Handler(Commander &Cmdr){ 48 | Cmdr.print("Int 1 is "); 49 | Cmdr.println(myInt1); 50 | return 0; 51 | } 52 | bool setInt2Handler(Commander &Cmdr){ 53 | if(Cmdr.getInt(myInt2)){ 54 | Cmdr.print("Set Int 2 to "); 55 | Cmdr.println(myInt2); 56 | }else Cmdr.println("Set Int 2 failed"); 57 | return 0; 58 | } 59 | bool getInt2Handler(Commander &Cmdr){ 60 | Cmdr.print("Int 2 is "); 61 | Cmdr.println(myInt2); 62 | return 0; 63 | } 64 | bool setFloat1Handler(Commander &Cmdr){ 65 | if(Cmdr.getFloat(myFloat1)){ 66 | Cmdr.print("Set Float 1 to "); 67 | Cmdr.println(myFloat1); 68 | }else Cmdr.println("Set Float 1 failed"); 69 | return 0; 70 | } 71 | bool getFloat1Handler(Commander &Cmdr){ 72 | Cmdr.print("Float 1 is "); 73 | Cmdr.println(myFloat1); 74 | return 0; 75 | } 76 | bool setFloat2Handler(Commander &Cmdr){ 77 | if(Cmdr.getFloat(myFloat2)){ 78 | Cmdr.print("Set Float 2 to "); 79 | Cmdr.println(myFloat2); 80 | }else Cmdr.println("Set Float 2 failed"); 81 | return 0; 82 | } 83 | bool getFloat2Handler(Commander &Cmdr){ 84 | Cmdr.print("Float 2 is "); 85 | Cmdr.println(myFloat2); 86 | return 0; 87 | } 88 | -------------------------------------------------------------------------------- /examples/FileRead/FileRead.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * Commander file read example: 3 | * Open an SD file using the SD library, attach it to a Commander object and let it read the contents 4 | * Replies are redirected to the Serial port. 5 | * By default this is written for the Adafruit Feather M0 Adalogger 6 | */ 7 | //#include 8 | //#include 9 | #include 10 | #include 11 | 12 | #include 13 | 14 | //SdFat SD; 15 | File myFile; 16 | 17 | extern const uint16_t numOfMasterCmds; //This is a forward declarationso the compiler knows we are going to declare this variable properly later 18 | extern const commandList_t masterCommands[]; //forward declare the master command list as well 19 | Commander cmd; 20 | 21 | //Some variables that can be read from and written to 22 | int myInt1 = 0; 23 | int myInt2 = 0; 24 | float myFloat1 = 0.0; 25 | float myFloat2 = 0.0; 26 | 27 | bool SDOK = false; 28 | bool fileOpen = false; 29 | const int cardSelect = 4; 30 | 31 | void setup() { 32 | Serial.begin(115200); 33 | while(!Serial){} //Wait for the serial port to open before running the example 34 | Serial.println("Commander File Read Example"); 35 | 36 | //See if an SD card is inserted 37 | if(!SD.begin(cardSelect)) { 38 | Serial.println("SDCard Init Error"); 39 | }else{ 40 | SDOK = true; 41 | Serial.println("SDCard Started"); 42 | } 43 | Serial.println("Starting Commander ... "); 44 | 45 | initialiseCommander(); 46 | //Check that a file called "commands.txt" exists 47 | if(SDOK && SD.exists("commands.txt")){ 48 | Serial.println("Opening File"); 49 | myFile = SD.open("commands.txt" , FILE_READ); 50 | fileOpen = true; 51 | }else if(SDOK){ 52 | Serial.println("No File Found"); 53 | } 54 | } 55 | 56 | void loop() { 57 | //If the file was opened, read it and finsh 58 | if(fileOpen){ 59 | Serial.println("---------------------------------------"); 60 | readFile(); 61 | myFile.close(); 62 | fileOpen = false; 63 | Serial.println("---------------------------------------"); 64 | Serial.println("File read finished"); 65 | //Now redirect commander to the serial port so we can type commands in there: 66 | cmd.attachInputPort(&Serial); 67 | //Enable command prompts and echo characters 68 | cmd.commandPrompt(ON); 69 | cmd.echo(true); 70 | //Now print out te prompt with a message 71 | cmd.println("You have control - Enter a command"); 72 | cmd.printCommandPrompt(); 73 | } 74 | cmd.update(); 75 | delay(1); 76 | } 77 | 78 | void readFile(){ 79 | //Call update for as long as there are still characters left to read 80 | //cmd will return 1 when it handles a command but there is more in the buffer and 0 when it reaches the end of the file 81 | while(cmd.update()){delay(10);} 82 | //If there is no newline at the end of the file then any command on the last line won't be handled 83 | //Add a newline to the buffer and handle any commands found there 84 | cmd.endLine(); 85 | } 86 | -------------------------------------------------------------------------------- /examples/FileReadLog/Commands.ino: -------------------------------------------------------------------------------- 1 | //All commands for 'master' 2 | //COMMAND ARRAY ------------------------------------------------------------------------------ 3 | const commandList_t masterCommands[] = { 4 | {"hello", helloHandler, "Say hello"}, 5 | {"set int 1", setInt1Handler, "Set an int"}, 6 | {"get int 1", getInt1Handler, "Get an int"}, 7 | {"set int 2", setInt2Handler, "Set an int"}, 8 | {"get int 2", getInt2Handler, "Get an int"}, 9 | {"set float 1", setFloat1Handler, "Set a float"}, 10 | {"get float 1", getFloat1Handler, "Get a float"}, 11 | {"set float 2", setFloat2Handler, "Set a float"}, 12 | {"get float 2", getFloat2Handler, "Get a float"}, 13 | {"stop log", closeLogFile, "Stop command log"}, 14 | {"print log", printLogFile, "Print the command log"} 15 | }; 16 | 17 | 18 | /* Command handler template 19 | bool myFunc(Commander &Cmdr){ 20 | //put your command handler code here 21 | return 0; 22 | } 23 | */ 24 | 25 | void initialiseCommander(){ 26 | //Start Commander and attach the incoming port to the File stream 27 | //Attach the outgoing port to Serial 28 | //Attach the command list and the list size variable 29 | cmd.begin(&myFile, &myLogFile, masterCommands, sizeof(masterCommands)); 30 | //attach Serial to the alt port and enable echoing of messages to alt so we can see whats happening 31 | cmd.attachAltPort(&Serial); 32 | //enable printing of comment lines 33 | cmd.printComments(true); 34 | //Echo to alt will copy any characters arriving on the input port to the Alt port - any commands sent to Commander will be copied to the Alt port 35 | cmd.echoToAlt(true); 36 | //CopyReplyAlt will print any replies to the alt port - Anything sent using the internal print commands will be sent to the Alt port as well as the Output port 37 | cmd.copyRepyAlt(true); 38 | //Uncomment this to have the actual commands logged to the log file as well as just the responses 39 | //cmd.echo(true); 40 | /* 41 | * With both the options above enabled some things will appear twice on the alt port, 42 | * for example with comment printing enabled the comment will be sent to alt once as the command input (echoed to alt) 43 | * and again as the response (copied reply to alt) 44 | * With normal echoing disabled the log file will only be sent responses to commands, not the actual commands themselves. 45 | */ 46 | } 47 | 48 | 49 | //These are the command handlers, there needs to be one for each command in the command array myCommands[] 50 | //The command array can have multiple commands strings that all call the same function 51 | bool helloHandler(Commander &Cmdr){ 52 | Cmdr.print("Hello! this is "); 53 | Cmdr.println(Cmdr.commanderName); 54 | return 0; 55 | } 56 | bool setInt1Handler(Commander &Cmdr){ 57 | if(Cmdr.getInt(myInt1)){ 58 | Cmdr.print("Set Int 1 to "); 59 | Cmdr.println(myInt1); 60 | }else Cmdr.println("Set Int 1 failed"); 61 | return 0; 62 | } 63 | bool getInt1Handler(Commander &Cmdr){ 64 | Cmdr.print("Int 1 is "); 65 | Cmdr.println(myInt1); 66 | return 0; 67 | } 68 | bool setInt2Handler(Commander &Cmdr){ 69 | if(Cmdr.getInt(myInt2)){ 70 | Cmdr.print("Set Int 2 to "); 71 | Cmdr.println(myInt2); 72 | }else Cmdr.println("Set Int 2 failed"); 73 | return 0; 74 | } 75 | bool getInt2Handler(Commander &Cmdr){ 76 | Cmdr.print("Int 2 is "); 77 | Cmdr.println(myInt2); 78 | return 0; 79 | } 80 | bool setFloat1Handler(Commander &Cmdr){ 81 | if(Cmdr.getFloat(myFloat1)){ 82 | Cmdr.print("Set Float 1 to "); 83 | Cmdr.println(myFloat1); 84 | }else Cmdr.println("Set Float 1 failed"); 85 | return 0; 86 | } 87 | bool getFloat1Handler(Commander &Cmdr){ 88 | Cmdr.print("Float 1 is "); 89 | Cmdr.println(myFloat1); 90 | return 0; 91 | } 92 | bool setFloat2Handler(Commander &Cmdr){ 93 | if(Cmdr.getFloat(myFloat2)){ 94 | Cmdr.print("Set Float 2 to "); 95 | Cmdr.println(myFloat2); 96 | }else Cmdr.println("Set Float 2 failed"); 97 | return 0; 98 | } 99 | bool getFloat2Handler(Commander &Cmdr){ 100 | Cmdr.print("Float 2 is "); 101 | Cmdr.println(myFloat2); 102 | return 0; 103 | } 104 | 105 | bool closeLogFile(Commander &Cmdr){ 106 | //!!! This crashes at the moment ... 107 | Cmdr.println("Closing Log File ..."); 108 | myLogFile.close(); 109 | //Detach the port and disable alt port copying 110 | //cmd.detachAltPort(); 111 | cmd.echoToAlt(false); 112 | cmd.copyRepyAlt(false); 113 | return 0; 114 | } 115 | 116 | 117 | bool printLogFile(Commander &Cmdr){ 118 | Cmdr.println("Printing Log File:"); 119 | if(myLogFile){ 120 | myLogFile.close(); 121 | cmd.echoToAlt(false); 122 | cmd.copyRepyAlt(false); 123 | } 124 | if(!SD.exists("log.txt")){ 125 | Cmdr.println("No log file found"); 126 | return 0; 127 | } 128 | myLogFile = SD.open("log.txt", O_RDONLY); 129 | Cmdr.println("===================== LOG FILE START ==================="); 130 | while(myLogFile.available()){ 131 | Cmdr.write(myLogFile.read()); 132 | } 133 | myLogFile.close(); 134 | Cmdr.println("====================== LOG FILE END ===================="); 135 | return 0; 136 | } 137 | -------------------------------------------------------------------------------- /examples/FileReadLog/FileReadLog.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * Commander file read example: 3 | * Open an SD file using SDFat, attach it to a Commander object and let it read the contents 4 | * Create a second SD file and attach to the outgoing port so replies are logged to a file 5 | * Attach the Serial port to the Auxiluary port so replies are copied to the Serial port. 6 | * When the file read is complete, close the file and attach Commander to the Serial port 7 | * so that commands can be sent from there, and attach the log file to the Aux port so responses are still logged 8 | * A 'stop log' command can be used to close this log file. 9 | * A 'print log' command can be used to print out the file to the serial port 10 | * The print log command will close the log file before printing. 11 | */ 12 | #include 13 | #include 14 | #include 15 | #include 16 | #define ENABLE_EXTENDED_TRANSFER_CLASS true 17 | SdFat SD; 18 | File myFile; 19 | File myLogFile; 20 | Commander cmd; 21 | 22 | //Some variables that can be read from and written to 23 | int myInt1 = 0; 24 | int myInt2 = 0; 25 | float myFloat1 = 0.0; 26 | float myFloat2 = 0.0; 27 | 28 | bool SDOK = false; 29 | bool fileOpen = false; 30 | bool readOK = false; 31 | const int cardSelect = 4; 32 | 33 | void setup() { 34 | Serial.begin(115200); 35 | while(!Serial){} //Wait for the serial port to open before running the example 36 | Serial.println("Commander File Read Example"); 37 | 38 | //See if an SD card is inserted 39 | if(!SD.begin(cardSelect, SD_SCK_MHZ(50))) { 40 | Serial.println("SDCard Init Error"); 41 | }else{ 42 | SDOK = true; 43 | Serial.println("SDCard Started"); 44 | } 45 | Serial.println("Starting Commander ... "); 46 | initialiseCommander(); 47 | 48 | //Check that a file called "commands.txt" exists 49 | if(SDOK && SD.exists("commands.txt")){ 50 | Serial.println("Opening File"); 51 | myFile = SD.open("commands.txt" , O_RDONLY); 52 | fileOpen = true; 53 | }else if(SDOK){ 54 | Serial.println("No File Found"); 55 | } 56 | //Create the log file 57 | if(SDOK){ 58 | //Delete any existing log file for convenience 59 | if(SD.exists("log.txt")){ 60 | Serial.println("deleting old log file"); 61 | SD.remove("log.txt"); 62 | } 63 | myLogFile = SD.open("log.txt", O_WRONLY | O_CREAT); 64 | } 65 | } 66 | 67 | void loop() { 68 | //If the file was opened, read it and finsh 69 | if(fileOpen){ 70 | readFile(); 71 | myFile.close(); 72 | fileOpen = false; 73 | Serial.println("File read finished"); 74 | //now attach the Commander object to the serial port ... 75 | cmd.attachInputPort(&Serial); 76 | cmd.attachOutputPort(&Serial); 77 | cmd.attachAltPort(&myLogFile); //attach the log file to the alt port 78 | readOK = true; 79 | //Enable command prompts and echo characters 80 | cmd.commandPrompt(ON); 81 | cmd.echo(true); 82 | //Now print out te prompt with a message 83 | cmd.println("----------------------------------------------------------------"); 84 | cmd.println("File logging is still active. You have control - Enter a command"); 85 | cmd.printCommandPrompt(); 86 | }else if(readOK){ 87 | //If the file was handled then the commander is now attached to the serial port and can accept commands from there 88 | cmd.update(); 89 | } 90 | } 91 | 92 | void readFile(){ 93 | //Call update for as long as there are still characters left to read 94 | //cmd will return 1 when it handles a command but there is more in the buffer and 0 when it reaches the end of the file 95 | while(cmd.update()){;} 96 | //If there is no newline at the end of the file then any command on the last line won't be handled 97 | //Add a newline to the buffer and handle any commands found there 98 | cmd.endLine(); 99 | } 100 | -------------------------------------------------------------------------------- /examples/FormattedReplies/FormattedReplies.ino: -------------------------------------------------------------------------------- 1 | /*Commander example - single object, multi layer command structure 2 | * Demonstrates how to use the reply pre and post fix for formatting 3 | * This extendes the 'quickset' example. 4 | */ 5 | #include 6 | Commander cmd; 7 | //Variables we can set or get 8 | int myInt1 = 0; 9 | int myInt2 = 0; 10 | float myFloat1 = 0.0; 11 | float myFloat2 = 0.0; 12 | //SETUP --------------------------------------------------------------------------- 13 | void setup() { 14 | // put your setup code here, to run once: 15 | Serial.begin(115200); 16 | initialiseCommander(); 17 | 18 | while(!Serial){;} 19 | Serial.println("Hello: Type 'help' to get help"); 20 | cmd.printCommandPrompt(); 21 | } 22 | 23 | //MAIN LOOP --------------------------------------------------------------------------- 24 | void loop() { 25 | //Call the update functions using the activeCommander pointer 26 | cmd.update(); 27 | } 28 | -------------------------------------------------------------------------------- /examples/FormattedReplies/masterCommands.ino: -------------------------------------------------------------------------------- 1 | //All commands for 'master' 2 | //COMMAND ARRAY ------------------------------------------------------------------------------ 3 | const commandList_t masterCommands[] = { 4 | {"hello", helloHandler, "Say hello"}, 5 | {"set", setHandler, "Quick set commands. Help: \"set help\""}, 6 | {"get", getHandler, "Quick get commands. Help: \"get help\""}, 7 | {"set prefix", setPrefixHandler, "Sets the prefix string. EG: 'set prefix PREFIX'"}, 8 | {"set postfix",setPostfixHandler, "Sets the postfix string. EG: 'set prefix POSTFIX'"}, 9 | {"prefix", prefixHandler, "Changes the prefix text to 'PREFIXED REPLY:' - use 'prefix hello' to send the hello command"}, 10 | {"HTML", formatHTMLHandler, "Set pre and postfix lines to '
' and reloads any payload for handling EG:'HTML help' gets a formatted help page"}, 11 | {"reload", formatAgainHandler, "Passes any payload to commander and enables formatting - uses any existing pre and postfix text"}, 12 | {"toggle", toggleFmtHandler, "toggle formatting between always on or always off"}, 13 | }; 14 | /* Command handler template 15 | bool myFunc(Commander &Cmdr){ 16 | //put your command handler code here 17 | return 0; 18 | } 19 | */ 20 | 21 | void initialiseCommander(){ 22 | cmd.begin(&Serial, masterCommands, sizeof(masterCommands)); 23 | cmd.commandPrompt(ON); //enable the command prompt 24 | cmd.echo(true); //Echo incoming characters to theoutput port 25 | cmd.errorMessages(ON); //error messages are enabled - it will tell us if we issue any unrecognised commands 26 | } 27 | 28 | //These are the command handlers, there needs to be one for each command in the command array myCommands[] 29 | //The command array can have multiple commands strings that all call the same function 30 | bool helloHandler(Commander &Cmdr){ 31 | 32 | Cmdr.print("Hello! this is "); 33 | Cmdr.println(Cmdr.commanderName); 34 | return 0; 35 | } 36 | 37 | 38 | bool setHandler(Commander &Cmdr){ 39 | //quickget function 40 | //Call quickSetHelp() first to handle any help command 41 | Cmdr.quickSetHelp(); 42 | if( Cmdr.quickSet("int1", myInt1) ) Cmdr.println("int1 set to " + String(myInt1)); 43 | if( Cmdr.quickSet("int2", myInt2) ) Cmdr.println("int2 set to " + String(myInt2)); 44 | if( Cmdr.quickSet("float1", myFloat1) ) Cmdr.println("float1 set to " + String(myFloat1)); 45 | if( Cmdr.quickSet("float2", myFloat2) ) Cmdr.println("float2 set to " + String(myFloat2)); 46 | return 0; 47 | } 48 | bool getHandler(Commander &Cmdr){ 49 | //quickset function 50 | //Call quickSetHelp() first to handle any help command 51 | Cmdr.quickSetHelp(); 52 | Cmdr.quickGet("int1", myInt1); 53 | Cmdr.quickGet("int2", myInt2) ; 54 | Cmdr.quickGet("float1", myFloat1); 55 | Cmdr.quickGet("float2", myFloat2) ; 56 | return 0; 57 | } 58 | 59 | bool setPrefixHandler(Commander &Cmdr){ 60 | //Extract any payload and use it as the prefix message 61 | //Uses getPayloadString to extract the payload without any newline 62 | Cmdr.setPrefix(Cmdr.getPayloadString()); 63 | Cmdr.print("Set prefix to: "); 64 | Cmdr.println(Cmdr.getPayloadString()); 65 | } 66 | 67 | bool setPostfixHandler(Commander &Cmdr){ 68 | //Extract any payload and use it as the postfix message 69 | //Uses getPayloadString to extract the payload without any newline 70 | Cmdr.setPostfix(Cmdr.getPayloadString()); 71 | Cmdr.print("Set postfix to: "); 72 | Cmdr.println(Cmdr.getPayloadString()); 73 | } 74 | 75 | bool prefixHandler(Commander &Cmdr){ 76 | //The setPrefix or setPostfix commands always enable the pre or postfix behaviour for the command being handled 77 | //If autoformat is NOT enabled then the pre/postfix behaviour will end when commander returns control to the user code 78 | //Using just the setPrefix command will cause ONLY the prefix message to be printed whilst the command is being handled. 79 | //Here we are checking to see if this command came with a payload, and if so we send the payload back to Commander for processing 80 | //If the payload was a command then it will get handled, and responses will be formatted. 81 | Cmdr.setPrefix("PREFIXED REPLY:"); 82 | if(Cmdr.hasPayload()){ 83 | Cmdr.feedString(Cmdr.getPayload()); 84 | }else{ 85 | Cmdr.print("here is part of my reply"); 86 | Cmdr.println(" here is eol"); 87 | Cmdr.println("Next line ..."); 88 | Cmdr.print("Here is an int: "); 89 | Cmdr.println(myInt1); 90 | Cmdr.print("Here is a float: "); 91 | Cmdr.print(myFloat1); 92 | Cmdr.println(" and another line ... "); 93 | } 94 | return 0; 95 | } 96 | 97 | bool formatHTMLHandler(Commander &Cmdr){ 98 | //The setPrefix or setPostfix commands always enable the pre or postfix behaviour for the command being handled 99 | //If autoformat is NOT enabled then the pre/postfix behaviour will end when commander returns control to the user code 100 | //Here we are checking to see if this command came with a payload, and if so we send the payload back to Commander for processing 101 | //If the payload was a command then it will get handled, and responses will be formatted. 102 | Cmdr.setPrefix("

"); 103 | Cmdr.setPostfix("

"); 104 | if(Cmdr.hasPayload()){ 105 | Cmdr.feedString(Cmdr.getPayload()); 106 | }else{ 107 | Cmdr.print("here is part of my reply"); 108 | Cmdr.println(" here is eol"); 109 | Cmdr.println("Next line ..."); 110 | Cmdr.print("Here is an int: "); 111 | Cmdr.println(myInt1); 112 | Cmdr.print("Here is a float: "); 113 | Cmdr.print(myFloat1); 114 | Cmdr.println(" and another line ... "); 115 | } 116 | return 0; 117 | } 118 | 119 | bool formatAgainHandler(Commander &Cmdr){ 120 | //The startFormatting function will enable pre and postfix formatting for the commadn heing handled. 121 | //If autoformat is NOT enabled then the pre/postfix behaviour will end when commander returns control to the user code 122 | //Here we are checking to see if this command came with a payload, and if so we send the payload back to Commander for processing 123 | //If the payload was a command then it will get handled, and responses will be formatted. 124 | Cmdr.startFormatting(); 125 | if(Cmdr.hasPayload()){ 126 | Cmdr.feedString(Cmdr.getPayload()); 127 | }else{ 128 | Cmdr.print("here is part of my reply"); 129 | Cmdr.println(" here is eol"); 130 | Cmdr.println("Next line ..."); 131 | Cmdr.print("Here is an int: "); 132 | Cmdr.println(myInt1); 133 | Cmdr.print("Here is a float: "); 134 | Cmdr.print(myFloat1); 135 | Cmdr.println(" and another line ... "); 136 | } 137 | return 0; 138 | } 139 | 140 | 141 | bool toggleFmtHandler(Commander &Cmdr){ 142 | //Setting autoFormat to true will make Commander add the pre and postfix messages to every response. 143 | //Pre and Postfix messages are Strings, if they are empty then nothing will be printed 144 | //EG: If you need autoformatting and only want a postfix message then you can set the prefix message to "" 145 | Cmdr.autoFormat( !Cmdr.autoFormat() ); //Get current autoformat state, invert it and set it as the new autoformat state. 146 | Cmdr.autoFormat() ? Cmdr.println("Autoformat ON") : Cmdr.println("Autoformat OFF"); 147 | return 0; 148 | } 149 | -------------------------------------------------------------------------------- /examples/FullMultiLayer/FullMultiLayer.ino: -------------------------------------------------------------------------------- 1 | /*Commander example - multi object, multi layer command structure 2 | * Demonstrates how to use many commander objects each with its own command array to create a layerd command structure 3 | * Top level commands can be invoked by typing them 4 | * Lower commands can be directly invoked from the top layer by typing the top command followed by the lower command 5 | * For example 'get help' calls the 'help' function in the command set called 'get' 6 | * Lower command structures can be entered by typing their command, and then the lower level commands can be directly invoked 7 | * An exit command will return control to the top level 8 | * For example, 'get' will transfer control to the 'get' command set. 'help' will then call the help function for the 'get' commands. 9 | */ 10 | #include 11 | 12 | extern const commandList_t masterCommands[]; //forward declare the master command list 13 | extern const uint16_t numOfMasterCmds; //A forward declaration so the compiler knows we are going to initialise this variable properly later 14 | extern const commandList_t getCommands[]; //forward declare the get command list 15 | extern const uint16_t numOfGetCmds; //A forward declaration so the compiler knows we are going to initialise this variable properly later 16 | extern const commandList_t setCommands[]; //forward declare the set command list 17 | extern const uint16_t numOfSetCmds; //A forward declaration so the compiler knows we are going to initialise this variable properly later 18 | 19 | portSettings_t savedSettings; //Create a portSettings_t variable to store Commander settings in when we switch to a different command object. 20 | 21 | //Declare the three commander objects 22 | Commander masterCmd; 23 | Commander getcmd; 24 | Commander setcmd; 25 | 26 | //Create a pointer to a commander object, and point it to the master object 27 | //We will change this later to point to a sub command object when we transfer control. 28 | Commander* activeCommander = &masterCmd; 29 | 30 | //Variables we can set or get 31 | int myInt = 0; 32 | float myFloat = 0.0; 33 | //SETUP --------------------------------------------------------------------------- 34 | void setup() { 35 | Serial.begin(9600); 36 | //initialise the commander objects 37 | masterCmd.begin(&Serial, masterCommands, numOfMasterCmds); 38 | getcmd.begin(&Serial, getCommands, numOfGetCmds); 39 | setcmd.begin(&Serial, setCommands, numOfSetCmds); 40 | //give each one a name 41 | masterCmd.commanderName = "Master commander"; 42 | getcmd.commanderName = "get commander"; 43 | setcmd.commanderName = "set commander"; 44 | //enable command prompts for the master command object 45 | masterCmd.commandPrompt(ON); 46 | //enable multi commander mode for all command objects 47 | masterCmd.multiCommander(true);//enable multicommander so command prompts work properly with multiple commander objects 48 | getcmd.multiCommander(true);//enable multicommander so command prompts work properly with multiple commander objects 49 | setcmd.multiCommander(true);//enable multicommander so command prompts work properly with multiple commander objects 50 | //enable echoing - any characters we send will be echoed back 51 | //We only need to do this for the master because the settings will be copied to any sub command object we use 52 | masterCmd.echo(true); 53 | //wait for a serial port to open 54 | while(!Serial){;} 55 | Serial.println("Hello: Type 'help' to get help"); 56 | //print the command prompt 57 | masterCmd.printCommandPrompt(); 58 | } 59 | 60 | //MAIN LOOP --------------------------------------------------------------------------- 61 | void loop() { 62 | //Call the update function using the activeCommander pointer 63 | //This will call the update() function on whatever commander object it is currently pointing to. 64 | activeCommander->update(); 65 | } 66 | -------------------------------------------------------------------------------- /examples/FullMultiLayer/getCommands.ino: -------------------------------------------------------------------------------- 1 | //All commands for 'get' 2 | //COMMAND ARRAY ------------------------------------------------------------------------------ 3 | const commandList_t getCommands[] = { 4 | {"hello", getHelloHandler, "Say hello"}, 5 | {"int", getIntVariable, "get my int variable"}, 6 | {"float", getFloatVariable,"get my float variable"}, 7 | {"exit", exitGet, "Exit get command mode"}, 8 | }; 9 | /* 10 | * This needs to be passed to the commander object so it knows how big the array of commands is, but this happens earlier in setup(). 11 | * This has already been forward declared before setup() so the compiler knows it exists 12 | */ 13 | /* Command handler template 14 | bool myFunc(Commander &Cmdr){ 15 | //put your command handler code here 16 | return 0; 17 | } 18 | */ 19 | //initialise the numOfGetCmds variable after creating the getCommands[] array - numOfGetCmds now indicates the length of the array 20 | const uint16_t numOfGetCmds = sizeof(getCommands); 21 | 22 | //COMMAND HANDLERS --------------------------------------------------------------------------- 23 | 24 | bool getHelloHandler(Commander &Cmdr){ 25 | Cmdr.print("Hello! this is "); 26 | Cmdr.println(Cmdr.commanderName); 27 | return 0; 28 | } 29 | 30 | bool getIntVariable(Commander &Cmdr){ 31 | Cmdr.print("my int is "); 32 | Cmdr.println(myInt); 33 | return 0; 34 | } 35 | 36 | bool getFloatVariable(Commander &Cmdr){ 37 | Cmdr.print("My float is "); 38 | Cmdr.println(myFloat); 39 | return 0; 40 | } 41 | 42 | bool exitGet(Commander &Cmdr){ 43 | //had over control to the sub commander 44 | Cmdr.println("Passing control back to main command handler"); 45 | //Set the Commander pointer to the sub commander 1 object 46 | activeCommander = &masterCmd; 47 | //Transfer this Cmdr to the new active commander 48 | activeCommander->transfer(Cmdr); 49 | //Restore this commanders settings from the backup before we exit 50 | Cmdr.portSettings(savedSettings); 51 | return 0; 52 | } 53 | -------------------------------------------------------------------------------- /examples/FullMultiLayer/masterCommands.ino: -------------------------------------------------------------------------------- 1 | //All commands for 'master' 2 | //COMMAND ARRAY ------------------------------------------------------------------------------ 3 | const commandList_t masterCommands[] = { 4 | {"hello", helloHandler, "Say hello"}, 5 | {"get", getHandler, "call get commands"}, 6 | {"set", setHandler, "call set commands"}, 7 | }; 8 | /* 9 | * This needs to be passed to the commander object so it knows how big the array of commands is, but this happens earlier in setup(). 10 | * This has already been forward declared before setup() so the compiler knows it exists 11 | */ 12 | /* Command handler template 13 | bool myFunc(Commander &Cmdr){ 14 | //put your command handler code here 15 | return 0; 16 | } 17 | */ 18 | 19 | //initialise the numOfMasterCmds variable after creating the masterCommands[] array - numOfMasterCmds now indicates the length of the array 20 | const uint16_t numOfMasterCmds = sizeof(masterCommands); 21 | 22 | 23 | //These are the command handlers, there needs to be one for each command in the command array myCommands[] 24 | //The command array can have multiple commands strings that all call the same function 25 | bool helloHandler(Commander &Cmdr){ 26 | Cmdr.print("Hello! this is "); 27 | Cmdr.println(Cmdr.commanderName); 28 | return 0; 29 | } 30 | 31 | bool getHandler(Commander &Cmdr){ 32 | //This handles commands that have a list of 'get' commands. 33 | //sending just the command will pass control to the get command handler 34 | //Sending the command, plus one of the 'get' commands will pass this command to the get command handler, 35 | //the command will be carried out, and control will return to the master. 36 | //For example: 37 | //Sending 'get' passes control to get. Sending 'help' will then call gets help command. 38 | //Sending 'get help' will call gets help command, but if you then send 'help', it will call masters help command because we never transferred control to get 39 | bool retVal = 0; 40 | if(Cmdr.hasPayload()){ 41 | Cmdr.println("handing payload to get command object"); 42 | retVal = getcmd.feed(Cmdr); 43 | }else{ 44 | Cmdr.println("handing control to get command object"); 45 | //backup the port setting from the commander we are about to transfer control to. 46 | savedSettings = getcmd.portSettings(); 47 | //Set the Commander pointer to the sub commander 1 object 48 | activeCommander = &getcmd; 49 | //Transfer this Cmdr to the new active commander 50 | activeCommander->transfer(Cmdr); 51 | } 52 | return retVal; 53 | } 54 | 55 | bool setHandler(Commander &Cmdr){ 56 | //This handles commands that have a list of 'set' commands. 57 | //sending just the command will pass control to the set command handler 58 | //Sending the command, plus one of the 'set' commands will pass this command to the cub command handler, 59 | //the command will be carried out, and control will return to the master. 60 | //For example: 61 | //Sending 'set' passes control to set. Sending 'help' will then call sets help command. 62 | //Sending 'set help' will call sets help command, but if you then send 'help', it will call masters help command because we never transferred control to set 63 | bool retVal = 0; 64 | if(Cmdr.hasPayload()){ 65 | Cmdr.println("handing payload to set command object"); 66 | retVal = setcmd.feed(Cmdr); 67 | }else{ 68 | Cmdr.println("handing control to set command object"); 69 | //backup the port setting from the commander we are about to transfer control to. 70 | savedSettings = setcmd.portSettings(); 71 | //Set the Commander pointer to the sub commander 1 object 72 | activeCommander = &setcmd; 73 | //Transfer this Cmdr to the new active commander 74 | activeCommander->transfer(Cmdr); 75 | } 76 | return retVal; 77 | } 78 | -------------------------------------------------------------------------------- /examples/FullMultiLayer/setCommands.ino: -------------------------------------------------------------------------------- 1 | //All commands for 'set' 2 | //COMMAND ARRAY ------------------------------------------------------------------------------ 3 | const commandList_t setCommands[] = { 4 | {"hello", setHelloHandler, "Say hello"}, 5 | {"int", setIntVariable, "set my int variable"}, 6 | {"float", setfloatVariable, "set my float variable"}, 7 | {"exit", exitSet, "Exit sub command mode"}, 8 | }; 9 | /* 10 | * This needs to be passed to the commander object so it knows how big the array of commands is, but this happens earlier in setup(). 11 | * This has already been forward declared before setup() so the compiler knows it exists 12 | */ 13 | /* Command handler template 14 | bool myFunc(Commander &Cmdr){ 15 | //put your command handler code here 16 | return 0; 17 | } 18 | */ 19 | //initialise the numOfSetCmds variable after creating the setCommands[] array - numOfSetCmds now indicates the length of the array 20 | const uint16_t numOfSetCmds = sizeof(setCommands); 21 | 22 | //COMMAND HANDLERS --------------------------------------------------------------------------- 23 | 24 | bool setHelloHandler(Commander &Cmdr){ 25 | Cmdr.print("Hello! this is "); 26 | Cmdr.println(Cmdr.commanderName); 27 | return 0; 28 | } 29 | 30 | bool setIntVariable(Commander &Cmdr){ 31 | if(Cmdr.getInt(myInt)){ 32 | Cmdr.print("Setting my int to "); 33 | Cmdr.println(myInt); 34 | }else Cmdr.println("Error"); 35 | return 0; 36 | } 37 | 38 | bool setfloatVariable(Commander &Cmdr){ 39 | if(Cmdr.getFloat(myFloat)){ 40 | Cmdr.print("Setting my float to "); 41 | Cmdr.println(myFloat); 42 | }else Cmdr.println("Error"); 43 | return 0; 44 | } 45 | 46 | bool exitSet(Commander &Cmdr){ 47 | //had over control to the sub commander 48 | Cmdr.println("Passing control back to main command handler"); 49 | //Set the Commander pointer to the sub commander 1 object 50 | activeCommander = &masterCmd; 51 | //Transfer this Cmdr to the new active commander 52 | activeCommander->transfer(Cmdr); 53 | //Restore this commanders settings from the backup before we exit 54 | Cmdr.portSettings(savedSettings); 55 | return 0; 56 | } 57 | -------------------------------------------------------------------------------- /examples/LockedCommands/LockedCommands.ino: -------------------------------------------------------------------------------- 1 | /*Commander example - Using locks and passwords 2 | * Commander can be locked using a soft of a hard lock. 3 | * When soft locked commander will respond to internal commands only 4 | * When hard locked Commander will only respond to the unlock command 5 | * If a password is not set then 'U' will unlock 6 | * If a password is set then 'U mypassword' will unlock it 7 | * The password is not stored inside Commander and must be in the users sketch 8 | * A 'set password' command can be used to change the password when Commander is unlocked - the password is stored in RAM so it will not be retained after power off. 9 | */ 10 | #include 11 | 12 | Commander cmd; 13 | //Variables we can set or get 14 | int myInt = 0; 15 | float myFloat = 0.0; 16 | String myPassphrase = "default"; 17 | //SETUP --------------------------------------------------------------------------- 18 | void setup() { 19 | Serial.begin(115200); 20 | initialiseCommander(); 21 | while(!Serial){;} 22 | Serial.println("Hello: Type 'help' to get help"); 23 | 24 | Serial.println("Type 'X' to soft lock commander"); 25 | Serial.println("Type 'U default' to unlock commander"); 26 | 27 | cmd.print("Password is "); 28 | cmd.printPassPhrase(); 29 | cmd.println(); 30 | cmd.printCommandPrompt(); 31 | } 32 | 33 | //MAIN LOOP --------------------------------------------------------------------------- 34 | void loop() { 35 | cmd.update(); 36 | } 37 | -------------------------------------------------------------------------------- /examples/LockedCommands/masterCommands.ino: -------------------------------------------------------------------------------- 1 | //All commands for 'master' 2 | //COMMAND ARRAY ------------------------------------------------------------------------------ 3 | const commandList_t masterCommands[] = { 4 | {"hello", helloHandler, "hello"}, 5 | {"get int", getIntHandler, "get an int"}, 6 | {"set int", setIntHandler, "set an int"}, 7 | {"get float", getFloatHandler, "get a float"}, 8 | {"set float", setFloatHandler, "set a float"}, 9 | {"myint", setIntHandler, "try myint=23"}, 10 | {"myfloat", setFloatHandler, "try myfloat=23.5"}, 11 | {"set password", setPassHandler, "set password"}, 12 | {"toggle hl", toggleHLHandler, "toggle hard lock"}, 13 | }; 14 | 15 | /* Command handler template 16 | bool myFunc(Commander &Cmdr){ 17 | //put your command handler code here 18 | return 0; 19 | } 20 | */ 21 | 22 | void initialiseCommander(){ 23 | cmd.begin(&Serial, masterCommands, sizeof(masterCommands)); 24 | cmd.commandPrompt(ON); //enable the command prompt 25 | cmd.echo(true); //Echo incoming characters to theoutput port 26 | cmd.errorMessages(ON); //error messages are enabled - it will tell us if we issue any unrecognised commands 27 | cmd.setPassPhrase(myPassphrase); 28 | } 29 | 30 | //These are the command handlers, there needs to be one for each command in the command array myCommands[] 31 | //The command array can have multiple commands strings that all call the same function 32 | bool helloHandler(Commander &Cmdr){ 33 | Cmdr.print("Hello! this is "); 34 | Cmdr.println(Cmdr.commanderName); 35 | Cmdr.print("This is my buffer: "); 36 | Cmdr.print(Cmdr.bufferString); 37 | return 0; 38 | } 39 | 40 | bool getIntHandler(Commander &Cmdr){ 41 | Cmdr.print("myInt = "); 42 | Cmdr.println(myInt); 43 | return 0; 44 | } 45 | 46 | bool setIntHandler(Commander &Cmdr){ 47 | if(Cmdr.getInt(myInt)){ 48 | Cmdr.print("myInt set to "); 49 | Cmdr.println(myInt); 50 | } 51 | return 0; 52 | } 53 | bool getFloatHandler(Commander &Cmdr){ 54 | Cmdr.print("myFloat = "); 55 | Cmdr.println(myFloat); 56 | return 0; 57 | } 58 | 59 | bool setFloatHandler(Commander &Cmdr){ 60 | if(Cmdr.getFloat(myFloat)){ 61 | Cmdr.print("myFloat set to "); 62 | Cmdr.println(myFloat, 4); //print with 4 decimal places 63 | } 64 | return 0; 65 | } 66 | 67 | bool setPassHandler(Commander &Cmdr){ 68 | if(Cmdr.hasPayload()){ 69 | myPassphrase = Cmdr.getPayloadString(); 70 | Cmdr.print("Set password to: "); 71 | Cmdr.println(myPassphrase); 72 | } 73 | return 0; 74 | } 75 | 76 | bool toggleHLHandler(Commander &Cmdr){ 77 | Cmdr.setLockType(!Cmdr.getLockType()); 78 | Cmdr.getLockType() ? Cmdr.println("Hard Locking enabled") : Cmdr.println("Hard locking disabled"); 79 | return 0; 80 | } 81 | -------------------------------------------------------------------------------- /examples/PrefabFileExplorer/Commands.ino: -------------------------------------------------------------------------------- 1 | //All commands for 'master' 2 | //COMMAND ARRAY ------------------------------------------------------------------------------ 3 | const commandList_t masterCommands[] = { 4 | {"status", sdStatusHandler, "Check SD Card status"}, 5 | {"SD", sdFileHandler, "Open SD explorer"}, 6 | }; 7 | 8 | 9 | /* 10 | * This needs to be passed to the commander object so it knows how big the array of commands is, but this happens earlier in setup(). 11 | * This has already been forward declared before setup() so the compiler knows it exists 12 | */ 13 | /* Command handler template 14 | bool myFunc(Commander &Cmdr){ 15 | //put your command handler code here 16 | return 0; 17 | } 18 | */ 19 | 20 | //initialise the numOfMasterCmds variable after creating the masterCommands[] array - numOfMasterCmds now indicates the length of the array 21 | const uint16_t numOfMasterCmds = sizeof(masterCommands); 22 | //These are the command handlers, there needs to be one for each command in the command array myCommands[] 23 | //The command array can have multiple commands strings that all call the same function 24 | bool sdStatusHandler(Commander &Cmdr){ 25 | if(!SDOK){ 26 | Cmdr.println("No SD Card"); 27 | }else{ 28 | Cmdr.println("SD Card detected"); 29 | //Cmdr.println(Cmdr.getPayloadString()); 30 | } 31 | return 0; 32 | } 33 | 34 | bool sdFileHandler(Commander &Cmdr){ 35 | if(!SDOK){ 36 | Cmdr.println("No SD Card"); 37 | return 0; 38 | } 39 | //For the moment - block commands if they are chained 40 | if(Cmdr.hasPayload()){ 41 | Cmdr.println("Chained commands are disabled"); 42 | //This is blocking when using coolterm ...? 43 | return 0; 44 | } 45 | Cmdr.println("Opening SD Navigator"); 46 | //Cmdr.attachCommands(getCommands, numOfGetCmds); 47 | cmdName = "SD:"; 48 | if(Cmdr.transferTo(fileCommands, numOfFileCommands, cmdName)){ 49 | //commander returns true if it is passing back control; 50 | Cmdr.transferBack(masterCommands, numOfMasterCmds, prompt); 51 | } 52 | return 0; 53 | } 54 | -------------------------------------------------------------------------------- /examples/PrefabFileExplorer/PrefabFileExplorer.ino: -------------------------------------------------------------------------------- 1 | /* Prefab command example 2 | * Prefabs are command sets and command handlers that have been pre defined for specific scenarios 3 | * This demonstrates the FileNavigator prefab that impliments an SDFat file navigation system. 4 | * The prefab is implimented as a sub command and can be invoked with the SD command. 5 | * 6 | * IMPORTANT: 7 | * The prefab includes a file write command that lets you stream text files to a file on the SD card or open a file and sent typed text to the file. 8 | * When this command is activated the command processor will stop working until the ASCII value 4 is received. 9 | * This can be sent from a terminal application such as coolTerm by pressing CONTROL+D 10 | * The Arduino Serial terminal does NOT allow you to send this character and so you cannot terminate the file download when using the Arduino Serial terminal. 11 | */ 12 | #include 13 | #include 14 | 15 | #include 16 | SdFat SD; 17 | Commander cmd; 18 | extern const uint16_t numOfMasterCmds; //This is a forward declarationso the compiler knows we are going to declare this variable properly later 19 | extern const commandList_t masterCommands[]; //forward declare the master command list 20 | 21 | //These variables are defined in PrefabFileNavigator.h so they need to be initialised here 22 | bool SDOK = false; //indicates that the SD card is present and initialised 23 | String cmdName = "SD:"; //String for the SD Explored prompt 24 | 25 | 26 | //String for the top level commander prompt 27 | String prompt = "CMD"; 28 | 29 | //SPI Chip select pin 30 | const int cardSelect = 4; 31 | 32 | void setup() { 33 | Serial.begin(115200); 34 | while(!Serial){yield();} //Wait for the serial port to open before running the example 35 | Serial.println("Commander prefab File navigator Example"); 36 | 37 | //See if an SD card is inserted 38 | if(!SD.begin(cardSelect, SD_SCK_MHZ(50))) { 39 | Serial.println("SDCard Init Error"); 40 | }else{ 41 | SDOK = true; 42 | Serial.println("SDCard Started"); 43 | } 44 | Serial.println("Starting Commander ... type help to see a command list"); 45 | //Attach the command list and the list size variable 46 | //tell the SD prefab what the top layer command list is called and how large it is 47 | setTopLayer(masterCommands, numOfMasterCmds, prompt); 48 | //Attach the predefined special handler - this is required for writing files 49 | cmd.attachSpecialHandler(streamToFileHandler); 50 | cmd.begin(&Serial, &Serial, masterCommands, numOfMasterCmds); 51 | //cmd.begin(&Serial, &Serial, fileCommands, numOfFileCmds); 52 | cmd.commandPrompt(ON); 53 | cmd.commanderName = prompt; 54 | cmd.echo(true); 55 | //cmd.println("Files on card:"); 56 | //SD.ls(cmd.getOutputPort(), LS_R); 57 | 58 | cmd.printCommandPrompt(); 59 | } 60 | 61 | void loop() { 62 | cmd.update(); 63 | } 64 | -------------------------------------------------------------------------------- /examples/TEST_SUITE1/TEST_SUITE1.ino: -------------------------------------------------------------------------------- 1 | /*Commander example - Test Suite 2 | */ 3 | #include 4 | Commander cmd; 5 | //Variables we can set or get 6 | int myInt = 0; 7 | int myInt1, myInt2; 8 | float myFloat1, myFloat2; 9 | float myFloat = 0.0; 10 | String myString1 = ""; 11 | String myString2 = ""; 12 | //User string - this can be anything you want, and is printed when the help and ? commands are used 13 | //Its a good idea to put the # symbol in front of each line so that if the response to these commands is fet to another commander, they will be interpreted as comments. 14 | String deviceInfo = "#\t(Start of user string)\n#\tCommander test suite\n#\tDevice firmware version 1.0.0 revision x\n#\thttps://github.com/CreativeRobotics/Commander\n#\t(End of user string)"; 15 | //SETUP --------------------------------------------------------------------------- 16 | void setup() { 17 | Serial.begin(115200); 18 | while(!Serial){;} 19 | initialiseCommander(); 20 | cmd.printUserString(); 21 | cmd.println(); 22 | Serial.println("Type 'help' to get help"); 23 | cmd.printCommandPrompt(); 24 | } 25 | 26 | //MAIN LOOP --------------------------------------------------------------------------- 27 | void loop() { 28 | cmd.update(); 29 | } 30 | -------------------------------------------------------------------------------- /examples/TEST_SUITE1/TEST_SUITE1/TEST_SUITE1.ino: -------------------------------------------------------------------------------- 1 | /*Commander example - Test Suite 2 | */ 3 | #include 4 | Commander cmd; 5 | //Variables we can set or get 6 | int myInt = 0; 7 | int myInt1, myInt2; 8 | float myFloat1, myFloat2; 9 | float myFloat = 0.0; 10 | String myString1 = ""; 11 | String myString2 = ""; 12 | //User string - this can be anything you want, and is printed when the help and ? commands are used 13 | //Its a good idea to put the # symbol in front of each line so that if the response to these commands is fet to another commander, they will be interpreted as comments. 14 | String deviceInfo = "#\t(Start of user string)\n#\tCommander test suite\n#\tDevice firmware version 1.0.0 revision x\n#\thttps://github.com/CreativeRobotics/Commander\n#\t(End of user string)"; 15 | //SETUP --------------------------------------------------------------------------- 16 | void setup() { 17 | Serial.begin(115200); 18 | while(!Serial){;} 19 | initialiseCommander(); 20 | cmd.printUserString(); 21 | cmd.println(); 22 | Serial.println("Type 'help' to get help"); 23 | cmd.printCommandPrompt(); 24 | } 25 | 26 | //MAIN LOOP --------------------------------------------------------------------------- 27 | void loop() { 28 | cmd.update(); 29 | } 30 | -------------------------------------------------------------------------------- /examples/TEST_SUITE1/TEST_SUITE1/masterCommands.ino: -------------------------------------------------------------------------------- 1 | //All commands for 'master' 2 | //COMMAND ARRAY ------------------------------------------------------------------------------ 3 | 4 | const commandList_t masterCommands[] = { 5 | {"hello", helloHandler, "hello"}, 6 | {"set", setHandler, "Quick set commands. Help: \"set help\""}, 7 | {"get", getHandler, "Quick get commands. Help: \"get help\""}, 8 | {"get int", getIntHandler, "get an int"}, 9 | {"set int", setIntHandler, "set an int"}, 10 | {"get float", getFloatHandler, "get a float"}, 11 | {"set float", setFloatHandler, "set a float"}, 12 | {"hidden1", hiddenHandler, "-Command hidden from help"}, 13 | {"myint", setIntHandler, "try myint=23"}, 14 | {"myfloat", setFloatHandler, "try myfloat=23.5"}, 15 | {"set ints", setIntsHandler, "set up to four ints"}, 16 | {"set floats", setFloatsHandler, "set up to four floats"}, 17 | {"set strings", setStringsHandler,"set up to four Strings"}, 18 | {"hidden2", hiddenHandler, "-Command hidden from help"}, 19 | }; 20 | 21 | /* Command handler template 22 | bool myFunc(Commander &Cmdr){ 23 | //put your command handler code here 24 | return 0; 25 | } 26 | */ 27 | 28 | 29 | //Initialisation function that avoids having to forward declare the command array and a size variable. 30 | void initialiseCommander(){ 31 | cmd.begin(&Serial, masterCommands, sizeof(masterCommands)) 32 | .commandPrompt(ON) 33 | .echo(true) 34 | .errorMessages(ON) 35 | .autoChain(ON) 36 | .setUserString(deviceInfo); 37 | } 38 | 39 | 40 | //These are the command handlers, there needs to be one for each command in the command array myCommands[] 41 | //The command array can have multiple commands strings that all call the same function 42 | bool helloHandler(Commander &Cmdr){ 43 | Cmdr.print("Hello! this is "); 44 | Cmdr.println(Cmdr.commanderName); 45 | Cmdr.print("This is my buffer: "); 46 | Cmdr.print(Cmdr.bufferString); 47 | //Cmdr.printDiagnostics(); 48 | return 0; 49 | } 50 | 51 | bool setHandler(Commander &Cmdr){ 52 | //quickget function 53 | //Call quickSetHelp() first to handle any help command 54 | Cmdr.quickSetHelp(); 55 | if( Cmdr.quickSet("int1", myInt1) ) Cmdr.println("int1 set to " + String(myInt1)); 56 | if( Cmdr.quickSet("int2", myInt2) ) Cmdr.println("int2 set to " + String(myInt2)); 57 | if( Cmdr.quickSet("float1", myFloat1) ) Cmdr.println("float1 set to " + String(myFloat1)); 58 | if( Cmdr.quickSet("float2", myFloat2) ) Cmdr.println("float2 set to " + String(myFloat2)); 59 | if( Cmdr.quickSet("str1", myString1) ) Cmdr.println("str1 set to " + String(myString1)); 60 | if( Cmdr.quickSet("str2", myString2) ) Cmdr.println("str2 set to " + String(myString2)); 61 | return 0; 62 | } 63 | bool getHandler(Commander &Cmdr){ 64 | //quickset function 65 | //Call quickSetHelp() first to handle any help command 66 | Cmdr.quickSetHelp(); 67 | Cmdr.quickGet("int1", myInt1); 68 | Cmdr.quickGet("int2", myInt2) ; 69 | Cmdr.quickGet("float1", myFloat1); 70 | Cmdr.quickGet("float2", myFloat2) ; 71 | Cmdr.quickGet("str1", myString1); 72 | Cmdr.quickGet("str2", myString2) ; 73 | return 0; 74 | } 75 | 76 | 77 | bool getIntHandler(Commander &Cmdr){ 78 | Cmdr.print("myInt = "); 79 | Cmdr.println(myInt); 80 | //Cmdr.printDiagnostics(); 81 | return 0; 82 | } 83 | 84 | bool setIntHandler(Commander &Cmdr){ 85 | if(Cmdr.getInt(myInt)) Cmdr.print("myInt set to "); 86 | else Cmdr.print("myInt = "); 87 | Cmdr.println(myInt); 88 | //Cmdr.printDiagnostics(); 89 | return 0; 90 | } 91 | bool getFloatHandler(Commander &Cmdr){ 92 | Cmdr.print("myFloat = "); 93 | Cmdr.println(myFloat); 94 | 95 | //Cmdr.printDiagnostics(); 96 | return 0; 97 | } 98 | 99 | bool setFloatHandler(Commander &Cmdr){ 100 | if(Cmdr.getFloat(myFloat)) Cmdr.print("myFloat set to "); 101 | else Cmdr.print("My int = "); 102 | Cmdr.println(myFloat, 4); //print with 4 decimal places 103 | //Cmdr.printDiagnostics(); 104 | return 0; 105 | } 106 | 107 | bool setIntsHandler(Commander &Cmdr){ 108 | //create an array to store any values we find 109 | int values[4] = {0,0,0,0}; 110 | 111 | int itms = Cmdr.countItems(); 112 | Cmdr.print("There are "); 113 | Cmdr.print(itms); 114 | Cmdr.println(" items in the payload"); 115 | 116 | for(int n = 0; n < 4; n++){ 117 | //try and unpack an int, if it fails there are no more left so exit the loop 118 | if(Cmdr.getInt(values[n])){ 119 | Cmdr.print("unpacked "); 120 | Cmdr.println(values[n]); 121 | }else break; 122 | } 123 | //print it out 124 | Cmdr.println("Array contents after processing:"); 125 | for(int n = 0; n < 4; n++){ 126 | Cmdr.print(n); 127 | Cmdr.print(" = "); 128 | Cmdr.println(values[n]); 129 | } 130 | //Cmdr.chain(); 131 | //Cmdr.printDiagnostics(); 132 | return 0; 133 | } 134 | 135 | bool setFloatsHandler(Commander &Cmdr){ 136 | float values[4] = {0.0,0.0,0.0,0.0}; 137 | int itms = Cmdr.countItems(); 138 | Cmdr.print("There are "); 139 | Cmdr.print(itms); 140 | Cmdr.println(" items in the payload"); 141 | 142 | for(int n = 0; n < 4; n++){ 143 | if(Cmdr.getFloat(values[n])){ 144 | Cmdr.print("unpacked "); 145 | Cmdr.println(values[n]); 146 | }else break; 147 | } 148 | Cmdr.println("Array contents after processing:"); 149 | for(int n = 0; n < 4; n++){ 150 | Cmdr.print(n); 151 | Cmdr.print(" = "); 152 | Cmdr.println(values[n]); 153 | } 154 | //Cmdr.chain(); 155 | //Cmdr.printDiagnostics(); 156 | return 0; 157 | } 158 | 159 | bool setStringsHandler(Commander &Cmdr){ 160 | String myString = ""; 161 | int itms = Cmdr.countItems(); 162 | Cmdr.print("There are "); 163 | Cmdr.print(itms); 164 | Cmdr.println(" items in the payload"); 165 | for(int n = 0; n < itms; n++){ 166 | if(Cmdr.getString(myString)){ 167 | Cmdr.print("String "); 168 | Cmdr.print(n); 169 | Cmdr.print(" = "); 170 | Cmdr.println(myString); 171 | }else Cmdr.println("Operation failed"); 172 | } 173 | //Cmdr.chain(); 174 | //Cmdr.printDiagnostics(); 175 | return 0; 176 | } 177 | 178 | bool hiddenHandler(Commander &Cmdr){ 179 | Cmdr.println("This command is hidden from the help system"); 180 | //Cmdr.printDiagnostics(); 181 | Cmdr.unchain(); 182 | return 0; 183 | } 184 | -------------------------------------------------------------------------------- /examples/TEST_SUITE1/masterCommands.ino: -------------------------------------------------------------------------------- 1 | //All commands for 'master' 2 | //COMMAND ARRAY ------------------------------------------------------------------------------ 3 | 4 | const commandList_t masterCommands[] = { 5 | {"hello", helloHandler, "hello"}, 6 | {"set", setHandler, "Quick set commands. Help: \"set help\""}, 7 | {"get", getHandler, "Quick get commands. Help: \"get help\""}, 8 | {"get int", getIntHandler, "get an int"}, 9 | {"set int", setIntHandler, "set an int"}, 10 | {"get float", getFloatHandler, "get a float"}, 11 | {"set float", setFloatHandler, "set a float"}, 12 | {"hidden1", hiddenHandler, "-Command hidden from help"}, 13 | {"myint", setIntHandler, "try myint=23"}, 14 | {"myfloat", setFloatHandler, "try myfloat=23.5"}, 15 | {"set ints", setIntsHandler, "set up to four ints"}, 16 | {"set floats", setFloatsHandler, "set up to four floats"}, 17 | {"set strings", setStringsHandler,"set up to four Strings"}, 18 | {"hidden2", hiddenHandler, "-Command hidden from help"}, 19 | }; 20 | 21 | /* Command handler template 22 | bool myFunc(Commander &Cmdr){ 23 | //put your command handler code here 24 | return 0; 25 | } 26 | */ 27 | 28 | 29 | //Initialisation function that avoids having to forward declare the command array and a size variable. 30 | void initialiseCommander(){ 31 | cmd.begin(&Serial, masterCommands, sizeof(masterCommands)) 32 | .commandPrompt(ON) 33 | .echo(true) 34 | .errorMessages(ON) 35 | .autoChain(ON) 36 | .setUserString(deviceInfo); 37 | } 38 | 39 | 40 | //These are the command handlers, there needs to be one for each command in the command array myCommands[] 41 | //The command array can have multiple commands strings that all call the same function 42 | bool helloHandler(Commander &Cmdr){ 43 | Cmdr.print("Hello! this is "); 44 | Cmdr.println(Cmdr.commanderName); 45 | Cmdr.print("This is my buffer: "); 46 | Cmdr.print(Cmdr.bufferString); 47 | //Cmdr.printDiagnostics(); 48 | return 0; 49 | } 50 | 51 | bool setHandler(Commander &Cmdr){ 52 | //quickget function 53 | //Call quickSetHelp() first to handle any help command 54 | Cmdr.quickSetHelp(); 55 | if( Cmdr.quickSet("int1", myInt1) ) Cmdr.println("int1 set to " + String(myInt1)); 56 | if( Cmdr.quickSet("int2", myInt2) ) Cmdr.println("int2 set to " + String(myInt2)); 57 | if( Cmdr.quickSet("float1", myFloat1) ) Cmdr.println("float1 set to " + String(myFloat1)); 58 | if( Cmdr.quickSet("float2", myFloat2) ) Cmdr.println("float2 set to " + String(myFloat2)); 59 | if( Cmdr.quickSet("str1", myString1) ) Cmdr.println("str1 set to " + String(myString1)); 60 | if( Cmdr.quickSet("str2", myString2) ) Cmdr.println("str2 set to " + String(myString2)); 61 | return 0; 62 | } 63 | bool getHandler(Commander &Cmdr){ 64 | //quickset function 65 | //Call quickSetHelp() first to handle any help command 66 | Cmdr.quickSetHelp(); 67 | Cmdr.quickGet("int1", myInt1); 68 | Cmdr.quickGet("int2", myInt2) ; 69 | Cmdr.quickGet("float1", myFloat1); 70 | Cmdr.quickGet("float2", myFloat2) ; 71 | Cmdr.quickGet("str1", myString1); 72 | Cmdr.quickGet("str2", myString2) ; 73 | return 0; 74 | } 75 | 76 | 77 | bool getIntHandler(Commander &Cmdr){ 78 | Cmdr.print("myInt = "); 79 | Cmdr.println(myInt); 80 | //Cmdr.printDiagnostics(); 81 | return 0; 82 | } 83 | 84 | bool setIntHandler(Commander &Cmdr){ 85 | if(Cmdr.getInt(myInt)){ 86 | Cmdr.print("myInt set to "); 87 | Cmdr.println(myInt); 88 | } 89 | //Cmdr.printDiagnostics(); 90 | return 0; 91 | } 92 | bool getFloatHandler(Commander &Cmdr){ 93 | Cmdr.print("myFloat = "); 94 | Cmdr.println(myFloat); 95 | 96 | //Cmdr.printDiagnostics(); 97 | return 0; 98 | } 99 | 100 | bool setFloatHandler(Commander &Cmdr){ 101 | if(Cmdr.getFloat(myFloat)){ 102 | Cmdr.print("myFloat set to "); 103 | Cmdr.println(myFloat, 4); //print with 4 decimal places 104 | } 105 | //Cmdr.printDiagnostics(); 106 | return 0; 107 | } 108 | 109 | bool setIntsHandler(Commander &Cmdr){ 110 | //create an array to store any values we find 111 | int values[4] = {0,0,0,0}; 112 | 113 | int itms = Cmdr.countItems(); 114 | Cmdr.print("There are "); 115 | Cmdr.print(itms); 116 | Cmdr.println(" items in the payload"); 117 | 118 | for(int n = 0; n < 4; n++){ 119 | //try and unpack an int, if it fails there are no more left so exit the loop 120 | if(Cmdr.getInt(values[n])){ 121 | Cmdr.print("unpacked "); 122 | Cmdr.println(values[n]); 123 | }else break; 124 | } 125 | //print it out 126 | Cmdr.println("Array contents after processing:"); 127 | for(int n = 0; n < 4; n++){ 128 | Cmdr.print(n); 129 | Cmdr.print(" = "); 130 | Cmdr.println(values[n]); 131 | } 132 | //Cmdr.chain(); 133 | //Cmdr.printDiagnostics(); 134 | return 0; 135 | } 136 | 137 | bool setFloatsHandler(Commander &Cmdr){ 138 | float values[4] = {0.0,0.0,0.0,0.0}; 139 | int itms = Cmdr.countItems(); 140 | Cmdr.print("There are "); 141 | Cmdr.print(itms); 142 | Cmdr.println(" items in the payload"); 143 | 144 | for(int n = 0; n < 4; n++){ 145 | if(Cmdr.getFloat(values[n])){ 146 | Cmdr.print("unpacked "); 147 | Cmdr.println(values[n]); 148 | }else break; 149 | } 150 | Cmdr.println("Array contents after processing:"); 151 | for(int n = 0; n < 4; n++){ 152 | Cmdr.print(n); 153 | Cmdr.print(" = "); 154 | Cmdr.println(values[n]); 155 | } 156 | //Cmdr.chain(); 157 | //Cmdr.printDiagnostics(); 158 | return 0; 159 | } 160 | 161 | bool setStringsHandler(Commander &Cmdr){ 162 | String myString = ""; 163 | int itms = Cmdr.countItems(); 164 | Cmdr.print("There are "); 165 | Cmdr.print(itms); 166 | Cmdr.println(" items in the payload"); 167 | for(int n = 0; n < itms; n++){ 168 | if(Cmdr.getString(myString)){ 169 | Cmdr.print("String "); 170 | Cmdr.print(n); 171 | Cmdr.print(" = "); 172 | Cmdr.println(myString); 173 | }else Cmdr.println("Operation failed"); 174 | } 175 | //Cmdr.chain(); 176 | //Cmdr.printDiagnostics(); 177 | return 0; 178 | } 179 | 180 | bool hiddenHandler(Commander &Cmdr){ 181 | Cmdr.println("This command is hidden from the help system"); 182 | //Cmdr.printDiagnostics(); 183 | Cmdr.unchain(); 184 | return 0; 185 | } 186 | -------------------------------------------------------------------------------- /examples/Template1SingleTab/Template1SingleTab.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * Commander template example 3 | * Use this file as a starter for your own projects 4 | */ 5 | #include 6 | Commander cmd; 7 | 8 | //Command handlers 9 | bool helloHandler(Commander &Cmdr){ 10 | Cmdr.print("Hello! this is "); 11 | Cmdr.println(Cmdr.commanderName); 12 | return 0; 13 | } 14 | 15 | bool goodbyeHandler(Commander &Cmdr){ 16 | Cmdr.print("Goodbye from "); 17 | Cmdr.println(Cmdr.commanderName); 18 | return 0; 19 | } 20 | 21 | //Now the command list must be defined 22 | const commandList_t commands[] = { 23 | {"hello", helloHandler, "Say hello"}, 24 | {"set", goodbyeHandler, "Say goodbye"}, 25 | }; 26 | 27 | //Now the sketch setup, loop and any other functions 28 | void setup() { 29 | Serial.begin(115200); 30 | while(!Serial){;} //Wait for the serial port to open (if using USB) 31 | cmd.begin(&Serial, commands, sizeof(commands)); //start Commander on Serial 32 | cmd.commandPrompt(ON); //enable the command prompt 33 | cmd.echo(true); //Echo incoming characters to theoutput port 34 | Serial.println("Hello: Type 'help' to get help"); 35 | cmd.printCommandPrompt(); 36 | } 37 | 38 | void loop() { 39 | // put your main code here, to run repeatedly: 40 | cmd.update(); 41 | } 42 | -------------------------------------------------------------------------------- /examples/Template2ForwardDeclaration/Template2ForwardDeclaration.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * Commander template example 3 | * Use this file as a starter for your own projects 4 | */ 5 | #include 6 | Commander cmd; 7 | 8 | //forward declare the command list and the size variable 9 | extern const commandList_t commands[]; 10 | extern const uint16_t sizeOfCommands; 11 | 12 | //Now the sketch setup, loop and any other functions 13 | void setup() { 14 | Serial.begin(115200); 15 | while(!Serial){;} //Wait for the serial port to open (if using USB) 16 | cmd.begin(&Serial, commands, sizeOfCommands); //start Commander on Serial 17 | cmd.commandPrompt(ON); //enable the command prompt 18 | cmd.echo(true); //Echo incoming characters to theoutput port 19 | Serial.println("Hello: Type 'help' to get help"); 20 | cmd.printCommandPrompt(); 21 | } 22 | 23 | void loop() { 24 | // put your main code here, to run repeatedly: 25 | cmd.update(); 26 | } 27 | 28 | //Now the command list must be defined 29 | const commandList_t commands[] = { 30 | {"hello", helloHandler, "Say hello"}, 31 | {"set", goodbyeHandler, "Say goodbye"}, 32 | }; 33 | const uint16_t sizeOfCommands = sizeof(commands); //get the size of the command list 34 | 35 | bool helloHandler(Commander &Cmdr){ 36 | Cmdr.print("Hello! this is "); 37 | Cmdr.println(Cmdr.commanderName); 38 | return 0; 39 | } 40 | 41 | bool goodbyeHandler(Commander &Cmdr){ 42 | Cmdr.print("Goodbye from "); 43 | Cmdr.println(Cmdr.commanderName); 44 | return 0; 45 | } 46 | -------------------------------------------------------------------------------- /examples/Template3InitFunction/Template3InitFunction.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * Commander template example 3 | * Use this file as a starter for your own projects 4 | */ 5 | #include 6 | Commander cmd; 7 | 8 | //Now the sketch setup, loop and any other functions 9 | void setup() { 10 | Serial.begin(115200); 11 | while(!Serial){;} //Wait for the serial port to open (if using USB) 12 | initialiseCommander(); 13 | cmd.commandPrompt(ON); //enable the command prompt 14 | cmd.echo(true); //Echo incoming characters to theoutput port 15 | Serial.println("Hello: Type 'help' to get help"); 16 | cmd.printCommandPrompt(); 17 | } 18 | 19 | void loop() { 20 | // put your main code here, to run repeatedly: 21 | cmd.update(); 22 | } 23 | 24 | //Now the command list must be defined 25 | const commandList_t commands[] = { 26 | {"hello", helloHandler, "Say hello"}, 27 | {"set", goodbyeHandler, "Say goodbye"}, 28 | }; 29 | 30 | //Initialisation function 31 | void initialiseCommander(){ 32 | cmd.begin(&Serial, commands, sizeof(commands)); //start Commander on Serial 33 | } 34 | 35 | bool helloHandler(Commander &Cmdr){ 36 | Cmdr.print("Hello! this is "); 37 | Cmdr.println(Cmdr.commanderName); 38 | return 0; 39 | } 40 | 41 | bool goodbyeHandler(Commander &Cmdr){ 42 | Cmdr.print("Goodbye from "); 43 | Cmdr.println(Cmdr.commanderName); 44 | return 0; 45 | } 46 | -------------------------------------------------------------------------------- /examples/quickSet/masterCommands.ino: -------------------------------------------------------------------------------- 1 | //All commands for 'master' 2 | //COMMAND ARRAY ------------------------------------------------------------------------------ 3 | const commandList_t masterCommands[] = { 4 | {"hello", helloHandler, "Say hello"}, 5 | {"set", setHandler, "Quick set commands. Help: \"set help\""}, 6 | {"get", getHandler, "Quick get commands. Help: \"get help\""}, 7 | {"print", printHandler, "Print items. Help: \"print help\""}, 8 | {"set int", getIntHandler, "Get many ints"}, 9 | }; 10 | /* Command handler template 11 | bool myFunc(Commander &Cmdr){ 12 | //put your command handler code here 13 | return 0; 14 | } 15 | */ 16 | void initialiseCommander(){ 17 | cmd.begin(&Serial, masterCommands, sizeof(masterCommands)); 18 | cmd.commandPrompt(ON); //enable the command prompt 19 | cmd.echo(true); //Echo incoming characters to theoutput port 20 | cmd.errorMessages(ON); //error messages are enabled - it will tell us if we issue any unrecognised commands 21 | //Error messaged do NOT work for quick set and get commands 22 | 23 | } 24 | //These are the command handlers, there needs to be one for each command in the command array myCommands[] 25 | //The command array can have multiple commands strings that all call the same function 26 | bool helloHandler(Commander &Cmdr){ 27 | Cmdr.print("Hello! this is "); 28 | Cmdr.println(Cmdr.commanderName); 29 | return 0; 30 | } 31 | 32 | 33 | bool setHandler(Commander &Cmdr){ 34 | //quickget function 35 | //Call quickSetHelp() first to handle any help command 36 | Cmdr.quickSetHelp(); 37 | if( Cmdr.quickSet("int1", myInt1) ) Cmdr.println("int1 set to " + String(myInt1)); 38 | if( Cmdr.quickSet("int2", myInt2) ) Cmdr.println("int2 set to " + String(myInt2)); 39 | if( Cmdr.quickSet("float1", myFloat1) ) Cmdr.println("float1 set to " + String(myFloat1)); 40 | if( Cmdr.quickSet("float2", myFloat2) ) Cmdr.println("float2 set to " + String(myFloat2)); 41 | return 0; 42 | } 43 | bool getHandler(Commander &Cmdr){ 44 | //quickset function 45 | //Call quickSetHelp() first to handle any help command 46 | Cmdr.quickSetHelp(); 47 | Cmdr.quickGet("int1", myInt1); 48 | Cmdr.quickGet("int2", myInt2) ; 49 | Cmdr.quickGet("float1", myFloat1); 50 | Cmdr.quickGet("float2", myFloat2) ; 51 | return 0; 52 | } 53 | 54 | 55 | bool printHandler(Commander &Cmdr){ 56 | //quickset function 57 | //Call quickSetHelp() first to handle any help command 58 | Cmdr.quickSetHelp(); 59 | if(Cmdr.quick("int1")) Cmdr.println(myInt1); 60 | if(Cmdr.quick("int2")) Cmdr.println(myInt2); 61 | if(Cmdr.quick("float1")) Cmdr.println(myFloat1); 62 | if(Cmdr.quick("float2")) Cmdr.println(myFloat2); 63 | if(Cmdr.quick("hello")) Cmdr.println("Hello world"); 64 | return 0; 65 | } 66 | bool getIntHandler(Commander &Cmdr){ 67 | int mInt = 0; 68 | while(Cmdr.getInt(mInt)){ 69 | Cmdr.println(mInt); 70 | } 71 | Cmdr.println(mInt); 72 | return 0; 73 | } 74 | -------------------------------------------------------------------------------- /examples/quickSet/quickSet.ino: -------------------------------------------------------------------------------- 1 | /*Commander example - single object, multi layer command structure 2 | * Demonstrates how to use quick set and quick get commands 3 | * Quick set and get allow multiple commands in a single command handler specifically for setting or getting variables 4 | * For example if you have six integer values in your sketch and you want to use commands to read and write to them 5 | * you can use the quick set commands. This reduces the ammount of code you have to type. 6 | * Quick set and get has a special help file method as well. 7 | * 8 | * Quick set and get commands can be chained so you can set and get all the variables in a group with one command line 9 | * In this example there are four variables, myInt1, myInt2, myFloat1 and myFloat2 10 | * You can set myInt1 to the value 23 by sending 'set int1 23' 11 | * You can read back the value by sending 'get int1' 12 | * You can get a list of all the quick set or get commands by typing 'set help' or 'get help' 13 | * 14 | * You can set all four variables at once by sending 'set int1 23 int2 34 float1 56.7 float2 89.0' 15 | * You can read back the ones you want with a single line: 'get int2 float2 int1' 16 | * When reading back multiple variables Remember: The order with which you request the variables will NOT be the order in which they are sent. This is determined by the get command handler 17 | */ 18 | 19 | #include 20 | Commander cmd; 21 | //Variables we can set or get 22 | int myInt1 = 0; 23 | int myInt2 = 0; 24 | float myFloat1 = 0.0; 25 | float myFloat2 = 0.0; 26 | //SETUP --------------------------------------------------------------------------- 27 | void setup() { 28 | // put your setup code here, to run once: 29 | Serial.begin(115200); 30 | initialiseCommander(); 31 | while(!Serial){;} 32 | Serial.println("Hello: Type 'help' to get help"); 33 | cmd.printCommandPrompt(); 34 | } 35 | 36 | //MAIN LOOP --------------------------------------------------------------------------- 37 | void loop() { 38 | //Call the update functions using the activeCommander pointer 39 | cmd.update(); 40 | } 41 | -------------------------------------------------------------------------------- /examples/simpleMultiLayer/getCommands.ino: -------------------------------------------------------------------------------- 1 | //All commands for 'get' 2 | //COMMAND ARRAY ------------------------------------------------------------------------------ 3 | const commandList_t getCommands[] = { 4 | {"hello", getHelloHandler, "Say hello"}, 5 | {"int", getIntVariable, "get my int variable"}, 6 | {"float", getFloatVariable,"get my float variable"}, 7 | {"exit", exitGet, "Exit get command mode"}, 8 | }; 9 | /* 10 | * This needs to be passed to the commander object so it knows how big the array of commands is, but this happens earlier in setup(). 11 | * This has already been forward declared before setup() so the compiler knows it exists 12 | */ 13 | /* Command handler template 14 | bool myFunc(Commander &Cmdr){ 15 | //put your command handler code here 16 | return 0; 17 | } 18 | */ 19 | //initialise the numOfGetCmds variable after creating the getCommands[] array - numOfGetCmds now indicates the length of the array 20 | const uint16_t numOfGetCmds = sizeof(getCommands); 21 | 22 | //COMMAND HANDLERS --------------------------------------------------------------------------- 23 | 24 | bool getHelloHandler(Commander &Cmdr){ 25 | Cmdr.print("Hello! this is "); 26 | Cmdr.println(Cmdr.commanderName); 27 | return 0; 28 | } 29 | 30 | bool getIntVariable(Commander &Cmdr){ 31 | Cmdr.print("my int is "); 32 | Cmdr.println(myInt); 33 | return 0; 34 | } 35 | 36 | bool getFloatVariable(Commander &Cmdr){ 37 | Cmdr.print("My float is "); 38 | Cmdr.println(myFloat); 39 | return 0; 40 | } 41 | 42 | bool exitGet(Commander &Cmdr){ 43 | //had over control to the sub commander 44 | Cmdr.println("Passing control back to main command handler"); 45 | //transfer back to the master command list 46 | Cmdr.transferBack(masterCommands, numOfMasterCmds, "Cmd"); 47 | return 0; 48 | } 49 | -------------------------------------------------------------------------------- /examples/simpleMultiLayer/masterCommands.ino: -------------------------------------------------------------------------------- 1 | //All commands for 'master' 2 | //COMMAND ARRAY ------------------------------------------------------------------------------ 3 | const commandList_t masterCommands[] = { 4 | {"hello", helloHandler, "Say hello"}, 5 | {"get", getHandler, "call get commands"}, 6 | {"set", setHandler, "call set commands"}, 7 | }; 8 | /* 9 | * This needs to be passed to the commander object so it knows how big the array of commands is, but this happens earlier in setup(). 10 | * This has already been forward declared before setup() so the compiler knows it exists 11 | */ 12 | /* Command handler template 13 | bool myFunc(Commander &Cmdr){ 14 | //put your command handler code here 15 | return 0; 16 | } 17 | */ 18 | //initialise the numOfMasterCmds variable after creating the masterCommands[] array - numOfMasterCmds now indicates the length of the array 19 | const uint16_t numOfMasterCmds = sizeof(masterCommands); 20 | 21 | //These are the command handlers, there needs to be one for each command in the command array myCommands[] 22 | //The command array can have multiple commands strings that all call the same function 23 | bool helloHandler(Commander &Cmdr){ 24 | Cmdr.print("Hello! this is "); 25 | Cmdr.println(Cmdr.commanderName); 26 | return 0; 27 | } 28 | 29 | bool getHandler(Commander &Cmdr){ 30 | //This handles commands that have a list of 'get' commands. 31 | //Sending the command, plus one of the 'get' commands will pass this command to the get command handler, 32 | //the command will be carried out, and control will return to the master. 33 | //Sending just the 'get' command will transfer control down to the set commad list. 34 | //For example: Sending 'get help' will call gets help command, but if you then send 'help', it will call masters help command. 35 | //Sending 'get' will transfer control to the set command list, then sending 'help' will print gets command help 36 | //get must impliment its own function to return control, for exampe an 'exit' command. 37 | Cmdr.println("Passing control to get command handler"); 38 | //Cmdr.attachCommands(getCommands, numOfGetCmds); 39 | if(Cmdr.transferTo(getCommands, numOfGetCmds, "get")){ 40 | //commander returns true if it is passing back control; 41 | Cmdr.transferBack(masterCommands, numOfMasterCmds, "Cmd"); 42 | } 43 | 44 | //if(Cmdr.hasPayload()){ 45 | // Serial.println("handing payload to get command list"); 46 | // retVal = Cmdr.feed(Cmdr); 47 | //} 48 | return 0; 49 | } 50 | 51 | bool setHandler(Commander &Cmdr){ 52 | //This handles commands that are sub commands of the 'set' command. 53 | //Sending the command, plus one of the 'set' commands will pass this command to the sub command handler, 54 | //the command will be carried out, and control will return to the master. 55 | //Sending just the 'set' command will transfer control down to the set commad list. 56 | //For example: Sending 'set help' will call sets help command, but if you then send 'help', it will call masters help command. 57 | //Sending 'set' will transfer control to the set command list, then sending 'help' will print sets command help 58 | //set must impliment its own function to return control, for exampe an 'exit' command. 59 | bool retVal = 0; 60 | Cmdr.println("Passing to set command handler"); 61 | if(Cmdr.transferTo(setCommands, numOfSetCmds, "set")){ 62 | //Cmdr.commanderName = "set"; 63 | //commander returns true if it is passing back control; 64 | Cmdr.transferBack(masterCommands, numOfMasterCmds, "Cmd"); 65 | } 66 | return 0; 67 | } 68 | -------------------------------------------------------------------------------- /examples/simpleMultiLayer/setCommands.ino: -------------------------------------------------------------------------------- 1 | //All commands for 'set' 2 | //COMMAND ARRAY ------------------------------------------------------------------------------ 3 | const commandList_t setCommands[] = { 4 | {"hello", setHelloHandler, "Say hello"}, 5 | {"int", setIntVariable, "set my int variable"}, 6 | {"float", setfloatVariable, "set my float variable"}, 7 | {"exit", exitSet, "Exit sub command mode"}, 8 | }; 9 | /* 10 | * This needs to be passed to the commander object so it knows how big the array of commands is, but this happens earlier in setup(). 11 | * This has already been forward declared before setup() so the compiler knows it exists 12 | */ 13 | /* Command handler template 14 | bool myFunc(Commander &Cmdr){ 15 | //put your command handler code here 16 | return 0; 17 | } 18 | */ 19 | //initialise the numOfSetCmds variable after creating the setCommands[] array - numOfSetCmds now indicates the length of the array 20 | const uint16_t numOfSetCmds = sizeof(setCommands); 21 | 22 | //COMMAND HANDLERS --------------------------------------------------------------------------- 23 | 24 | bool setHelloHandler(Commander &Cmdr){ 25 | Cmdr.print("Hello! this is "); 26 | Cmdr.println(Cmdr.commanderName); 27 | return 0; 28 | } 29 | 30 | bool setIntVariable(Commander &Cmdr){ 31 | 32 | if(Cmdr.getInt(myInt)){ 33 | Cmdr.print("Setting my int to "); 34 | Cmdr.println(myInt); 35 | }else Cmdr.println("Error"); 36 | return 0; 37 | } 38 | 39 | bool setfloatVariable(Commander &Cmdr){ 40 | if(Cmdr.getFloat(myFloat)){ 41 | Cmdr.print("Setting my float to "); 42 | Cmdr.println(myFloat); 43 | }else Cmdr.println("Error"); 44 | return 0; 45 | } 46 | 47 | bool exitSet(Commander &Cmdr){ 48 | //had over control to the sub commander 49 | Cmdr.println("Passing control back to main command handler"); 50 | //transfer back to the master command list 51 | Cmdr.transferBack(masterCommands, numOfMasterCmds, "Cmd"); 52 | return 0; 53 | } 54 | -------------------------------------------------------------------------------- /examples/simpleMultiLayer/simpleMultiLayer.ino: -------------------------------------------------------------------------------- 1 | /*Commander example - single object, multi layer command structure 2 | * Demonstrates how to use one commander object with multiple command arrays to create a layerd command structure 3 | * Top level commands can be invoked by typing them 4 | * Lower commands can be directly invoked from the top layer by typing the top command followed by the lower command 5 | * For example 'get help' calls the 'help' function in the command set called 'get' 6 | * Lower command structures can be entered by typing their command, and then the lower level commands can be directly invoked 7 | * An exit command will return control to the top level 8 | * For example, 'get' will transfer control to the 'get' command set. 'help' will then call the help function for the 'get' commands. 9 | */ 10 | #include 11 | extern const uint16_t numOfMasterCmds; //This is a forward declarationso the compiler knows we are going to declare this variable properly later 12 | extern const uint16_t numOfGetCmds; //This is a forward declaration so the compiler knows we are going to declare this variable properly later 13 | extern const uint16_t numOfSetCmds; //This is a forward declaration so the compiler knows we are going to declare this variable properly later 14 | extern const commandList_t masterCommands[]; //forward declare the master command list 15 | extern const commandList_t getCommands[]; //forward declare the get command list 16 | extern const commandList_t setCommands[]; //forward declare the set command list 17 | //portSettings_t savedSettings; 18 | Commander cmd; 19 | //Variables we can set or get 20 | int myInt = 0; 21 | float myFloat = 0.0; 22 | 23 | //SETUP --------------------------------------------------------------------------- 24 | void setup() { 25 | Serial.begin(115200); 26 | cmd.begin(&Serial, masterCommands, numOfMasterCmds); 27 | cmd.commanderName = "Cmd"; 28 | cmd.commandPrompt(ON); 29 | cmd.echo(true); 30 | while(!Serial){;} 31 | Serial.println("Hello: Type 'help' to get help"); 32 | cmd.printCommandPrompt(); 33 | } 34 | 35 | //MAIN LOOP --------------------------------------------------------------------------- 36 | void loop() { 37 | //Call the update functions using the activeCommander pointer 38 | cmd.update(); 39 | } 40 | -------------------------------------------------------------------------------- /keywords.txt: -------------------------------------------------------------------------------- 1 | ######################################################## 2 | # Syntax Coloring Map for Commander Library # 3 | ######################################################## 4 | # Class 5 | ################################################################### 6 | 7 | Commander KEYWORD1 8 | 9 | ################################################################### 10 | # Methods and Functions 11 | ################################################################### 12 | 13 | begin KEYWORD2 14 | update KEYWORD2 15 | printCommands KEYWORD2 16 | setPassPhrase KEYWORD2 17 | printPassPhrase KEYWORD2 18 | setUserString KEYWORD2 19 | printUserString KEYWORD2 20 | lock KEYWORD2 21 | unlock KEYWORD2 22 | setLockType KEYWORD2 23 | getLockType KEYWORD2 24 | isLocked KEYWORD2 25 | feed KEYWORD2 26 | hasPayload KEYWORD2 27 | getPayload KEYWORD2 28 | getPayloadString KEYWORD2 29 | feedString KEYWORD2 30 | loadString KEYWORD2 31 | setPending KEYWORD2 32 | endLine KEYWORD2 33 | startStreaming KEYWORD2 34 | stopStreaming KEYWORD2 35 | setStream KEYWORD2 36 | isStreaming KEYWORD2 37 | transfer KEYWORD2 38 | transferTo KEYWORD2 39 | transferBack KEYWORD2 40 | attachOutputPort KEYWORD2 41 | getOutputPort KEYWORD2 42 | attachAltPort KEYWORD2 43 | getAltPort KEYWORD2 44 | attachInputPort KEYWORD2 45 | getInputPort KEYWORD2 46 | deleteAltPort KEYWORD2 47 | attachSpecialHandler KEYWORD2 48 | setBuffer KEYWORD2 49 | attachCommands KEYWORD2 50 | reloadCommands KEYWORD2 51 | quick KEYWORD2 52 | quickSetHelp KEYWORD2 53 | quickSet KEYWORD2 54 | quickGet KEYWORD2 55 | print KEYWORD2 56 | println KEYWORD2 57 | add KEYWORD2 58 | setPrefix KEYWORD2 59 | startPrefix KEYWORD2 60 | setPostfix KEYWORD2 61 | startPostfix KEYWORD2 62 | startFormatting KEYWORD2 63 | autoFormat KEYWORD2 64 | printCommandPrompt KEYWORD2 65 | containsTrue KEYWORD2 66 | containsOn KEYWORD2 67 | containsOff KEYWORD2 68 | containsFalse KEYWORD2 69 | delimiters KEYWORD2 70 | addDelimiter KEYWORD2 71 | commentChar KEYWORD2 72 | reloadChar KEYWORD2 73 | endOfLineChar KEYWORD2 74 | setPrintDelay KEYWORD2 75 | printDelay KEYWORD2 76 | echo KEYWORD2 77 | printComments KEYWORD2 78 | echoToAlt KEYWORD2 79 | copyRepyAlt KEYWORD2 80 | commandProcessor KEYWORD2 81 | stripCR KEYWORD2 82 | multiCommander KEYWORD2 83 | errorMessages KEYWORD2 84 | commandPrompt KEYWORD2 85 | showHelp KEYWORD2 86 | internalCommands KEYWORD2 87 | showInternalCommands KEYWORD2 88 | settings KEYWORD2 89 | portSettings KEYWORD2 90 | getInt KEYWORD2 91 | getFloat KEYWORD2 92 | getDouble KEYWORD2 93 | countItems KEYWORD2 94 | printCommandList KEYWORD2 95 | printCommanderVersion KEYWORD2 96 | printAlt KEYWORD2 97 | printAltln KEYWORD2 98 | writeAlt KEYWORD2 99 | cmdHandler KEYWORD2 100 | getCommandList KEYWORD2 101 | getCommandListLength KEYWORD2 102 | getCommandItem KEYWORD2 103 | getInternalCommandLength KEYWORD2 104 | getInternalCommandItem KEYWORD2 105 | getReadIndex KEYWORD2 106 | chain KEYWORD2 107 | unchain KEYWORD2 108 | autoChain KEYWORD2 109 | setStreamType KEYWORD2 110 | getStreamType KEYWORD2 111 | 112 | ################################################################### 113 | # Variables 114 | ################################################################### 115 | 116 | bufferString KEYWORD1 117 | commanderName KEYWORD1 118 | promptCharacter KEYWORD1 119 | eocCharacter KEYWORD1 120 | prefixString KEYWORD1 121 | postfixString KEYWORD1 122 | portSettings_t KEYWORD3 123 | commandList_t KEYWORD3 124 | 125 | ################################################################### 126 | # Constants 127 | ################################################################### 128 | 129 | UNDEFINED_STREAM KEYWORD3 130 | SERIAL_STREAM KEYWORD3 131 | FILE_STREAM KEYWORD3 132 | WEB_STREAM KEYWORD3 -------------------------------------------------------------------------------- /library.properties: -------------------------------------------------------------------------------- 1 | name=Commander 2 | version=4.3.0 3 | author=Bill Bigge 4 | maintainer=Bill Bigge 5 | sentence=Command line library for Arduino. 6 | paragraph=Attaches to Stream objects (Serial ports, SD Cards, Bluetooth, WiFi . . .) and allows text based command prompt style interfaces to be created. 7 | category=Communication 8 | url=https://github.com/CreativeRobotics/Commander 9 | architectures=* 10 | includes=Commander.h 11 | depends= 12 | -------------------------------------------------------------------------------- /src/Commander.h: -------------------------------------------------------------------------------- 1 | //Commander 2 | 3 | #ifndef Commander_h 4 | #define Commander_h 5 | 6 | #include 7 | #include 8 | #include "utilities/CommandHelpTags.h" 9 | 10 | class Commander; 11 | 12 | const uint8_t majorVersion = 4; 13 | const uint8_t minorVersion = 3; 14 | const uint8_t subVersion = 0; 15 | 16 | 17 | //#define BENCHMARKING_ON 18 | 19 | #define DEBUG_INDEXER false 20 | 21 | #ifndef ON 22 | #define ON true 23 | #endif 24 | #ifndef OFF 25 | #define OFF false 26 | #endif 27 | #ifndef ENABLED 28 | #define ENABLED true 29 | #endif 30 | #ifndef DISABLED 31 | #define DISABLED false 32 | #endif 33 | typedef bool (*cmdHandler)(Commander& Cmdr); //command handler function pointer type 34 | //Command handler array type - contains command string and function pointer 35 | 36 | typedef struct commandList_t{ 37 | const char* commandString; 38 | cmdHandler handler; 39 | const char* manualString; 40 | } commandList_t; 41 | 42 | //extern const commandList_t myCommands[]; 43 | 44 | typedef union { 45 | struct { 46 | uint16_t newLine:1; //end of line was detected 47 | uint16_t bufferFull:1; //buffer is full 48 | uint16_t isCommandPending:1; //another command is in the pending command string 49 | uint16_t bufferState:1; //the state of the command buffer 50 | uint16_t commandHandled:1; //flag set if a command was handled during the last update 51 | uint16_t quickHelp:1; //flag set to true if quick help requested 52 | uint16_t quickSetCalled:1; //Flags that the current command is a quickset - chaining must break here 53 | uint16_t prefixMessage:1; //flag to indicate that any replies should be prefixed with the prefix string 54 | uint16_t postfixMessage:1; //flag to indicate that any replies should be appended with the postfix string 55 | uint16_t newlinePrinted:1; //flag to indicate that a newline has been sent - used with the prefixMessage flag to ensure prefix string appears at the start of every line 56 | uint16_t dataStreamOn:1; //indicates that a data stream is active and in one of two modes 57 | uint16_t chain:1; //Chain commands - reload the buffer after a command to see if there are more commands 58 | uint16_t chaining:1; //Flags that the current command is an attempt to chain and so errors should be surpressed. 59 | uint16_t commandType:3; //Indicates which command type was last identified 60 | } bit; // used for bit access 61 | uint16_t reg; //used for register access 62 | } cmdState_t; 63 | #define STREAM_MODE_EOF false 64 | #define STREAM_MODE_PURE true 65 | 66 | #define BUFFER_WAITING_FOR_START 0 67 | #define BUFFER_BUFFERING_PACKET 1 68 | //#define BUFFER_PACKET_RECEIVED 2 69 | 70 | 71 | typedef union { 72 | struct { 73 | uint32_t echoTerminal:1; //0 74 | uint32_t echoToAlt:1; //1 75 | uint32_t copyResponseToAlt:1; //2 76 | uint32_t commandParserEnabled:1; //3 77 | uint32_t errorMessagesEnabled:1; //4 78 | uint32_t commandPromptEnabled:1; //5 79 | uint32_t helpEnabled:1; //6 enable the help system 80 | uint32_t internalCommandsEnabled:1; //7 disables the internal commands - stops them working 81 | uint32_t printInternalCommands:1; //8 enable printing of internal commands with help 82 | uint32_t multiCommanderMode:1; //9 set to true when using multiple commander objects for multilayerd commands. Prevents multiple command prompts from appearing 83 | uint32_t printComments:1; //10 set to true and lines prefixed with the comment char will print to the out and alt ports 84 | uint32_t dataStreamMode:1; //11 indicates the stream mode. mode 0 looks for an end of line, 1 is pure stream mode and cannot be terminated using a command char. 85 | uint32_t useHardLock:1; //12 use hard or soft lock (0 or 1) 86 | uint32_t locked:1; //13 Indicates if Commander is in a locked or unlocked state 87 | uint32_t stripCR:1; //14 Strip carriage returns from the buffer 88 | uint32_t streamType:2; //15-16 A two bit code indicating the stream type (Serial, File, HTML, OTHER) 89 | uint32_t autoFormat:1; //17 bflag to indicate that all replies should be formatted with pre and postfix text 90 | uint32_t useDelay:1; //18 Insert a delay before println 91 | uint32_t autoChain:1; //19 Automatically chain commands, and to hell with the consequences 92 | uint32_t autoChainSurpressErrors:1; //20 Prevent error messages when chaining commands 93 | uint32_t ignoreQuotes:1; //21 don't treat items in quotes as special 94 | } bit; // used for bit access 95 | uint32_t reg; //used for register access 96 | } cmdSettings_t; 97 | //Settings register: 98 | // 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 99 | //default is 0b 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 1 0 1 1 1 0 1 1 0 0 0 100 | 101 | 102 | typedef enum streamType_t{ 103 | UNDEFINED_STREAM = 0, 104 | SERIAL_STREAM = 1, 105 | FILE_STREAM = 2, 106 | WEB_STREAM = 3, 107 | } streamType_t; 108 | 109 | typedef struct portSettings_t{ 110 | Stream *inPort = NULL; 111 | Stream *outPort = NULL; 112 | 113 | Stream *altPort = NULL; 114 | 115 | cmdSettings_t settings; 116 | } portSettings_t; 117 | 118 | /* 119 | #define INTERNAL_U -20 120 | #define INTERNAL_X -19 121 | #define INTERNAL_? -18 122 | #define INTERNAL_help -17 123 | #define INTERNAL_echo -16 124 | #define INTERNAL_echox -15 125 | #define INTERNAL_errors -14 126 | */ 127 | #define UNKNOWN_COMMAND 0 128 | #define USER_COMMAND 1 129 | #define CUSTOM_COMMAND 2 130 | #define INTERNAL_COMMAND 3 131 | #define COMMENT_COMMAND 4 132 | 133 | 134 | #define INTERNAL_COMMAND_ITEMS 7 135 | 136 | #define COMMANDER_DEFAULT_REGISTER_SETTINGS 0b00000000000000000100010111011000 137 | //Default settings: 138 | //commandParserEnabled = true 139 | //errorMessagesEnabled = true 140 | //stripCR = true 141 | #define COMMANDER_DEFAULT_STATE_SETTINGS 0x0 142 | #define DEFAULT_DELIMITER_ITEMS 6 143 | // =:\/| 144 | 145 | 146 | #define printAlt ports.altPort->print 147 | #define printAltln ports.altPort->println 148 | #define writeAlt ports.altPort->write 149 | 150 | #define HARD_LOCK true 151 | #define SOFT_LOCK false 152 | const uint16_t SBUFFER_DEFAULT = 128; 153 | 154 | //some const strings for common messages 155 | const String onString = "on"; 156 | const String offString = "off"; 157 | const String okString = "OK"; 158 | const String nullString = "NULL"; 159 | const String lockMessage = "Locked"; 160 | const String unlockMessage = "Unlocked"; 161 | 162 | const char EOFChar = 4; //end of file character for terminal 163 | 164 | //Commander Class =========================================================================================== 165 | class Commander : public Stream { 166 | public: 167 | Commander(); 168 | Commander(uint16_t reservedBuffer); 169 | Commander& begin(Stream *sPort); 170 | Commander& begin(Stream *sPort, const commandList_t *commands, uint32_t size); 171 | Commander& begin(Stream *sPort, Stream *oPort, const commandList_t *commands, uint32_t size); 172 | Commander& begin(const commandList_t *commands, uint32_t size); 173 | bool update(); 174 | Commander& setPassPhrase(String& phrase) {passPhrase = &phrase; return *this;} 175 | Commander& printPassPhrase() {print(*passPhrase); return *this;} 176 | Commander& setUserString(String& str) {userString = &str; return *this;} 177 | Commander& printUserString() {print(*userString); return *this;} 178 | Commander& setExtraHelp(const char* ptr[]){extraHelp = ptr; return *this;} 179 | Commander& lock() {ports.settings.bit.locked = true; return *this;} 180 | Commander& unlock() {ports.settings.bit.locked = false; return *this;} 181 | Commander& setLockType(bool hlState) {ports.settings.bit.useHardLock = hlState; return *this;} 182 | bool isLocked() {return ports.settings.bit.locked;} 183 | bool getLockType() {return ports.settings.bit.useHardLock;} 184 | bool feed(Commander& Cmdr); 185 | bool hasPayload(); 186 | String getPayload(); 187 | String getPayloadString(); 188 | bool feedString(String newString); 189 | Commander& loadString(String newString); 190 | Commander& setPending(bool pState) {commandState.bit.isCommandPending = pState; return *this;} //sets the pending command bit - used if manually writing to the buffer 191 | Commander& add(uint8_t character) {bufferString += character; return *this;} 192 | bool endLine(); 193 | Commander& startStreaming() {commandState.bit.dataStreamOn = true; return *this;} //set the streaming function ON 194 | Commander& stopStreaming() {commandState.bit.dataStreamOn = false; return *this;} //set the streaming function OFF 195 | Commander& setStreaming(bool streamState) {commandState.bit.dataStreamOn = streamState; return *this;} 196 | bool isStreaming() {return commandState.bit.dataStreamOn;} 197 | Commander& setStreamingMode(bool dataStreamMode) {ports.settings.bit.dataStreamMode = dataStreamMode; return *this;} 198 | bool getStreamingMode() {return ports.settings.bit.dataStreamMode;} 199 | Commander& transfer(Commander& Cmdr); 200 | bool transferTo(const commandList_t *commands, uint32_t size, String newName); 201 | Commander& transferBack(const commandList_t *commands, uint32_t size, String newName); 202 | Commander& attachOutputPort(Stream *oPort) {ports.outPort = oPort; return *this;} 203 | Stream* getOutputPort() {return ports.outPort;} 204 | Commander& attachAltPort(Stream *aPort) {ports.altPort = aPort; return *this;} 205 | Stream* getAltPort() {return ports.altPort;} 206 | //void detachAltPort() {ports.altPort = NULL;}//ports.altPort->flush(); 207 | 208 | Commander& attachInputPort(Stream *iPort) {ports.inPort = iPort; return *this;} 209 | Stream* getInputPort() {return ports.inPort;} 210 | Commander& deleteAltPort() {ports.altPort = NULL; return *this;} 211 | Commander& attachSpecialHandler(cmdHandler handler) {customHandler = handler; return *this;} 212 | Commander& attachDefaultHandler(cmdHandler handler) {defaultHandler = handler; return *this;} 213 | Commander& setBuffer(uint16_t buffSize); 214 | Commander& attachCommands(const commandList_t *commands, uint32_t size); 215 | Commander& attachCommandArray(const commandList_t *commands, uint32_t length); 216 | Commander& setStreamType(streamType_t newType) {ports.settings.bit.streamType = (uint16_t)newType; return *this;} 217 | streamType_t getStreamType() {return (streamType_t)ports.settings.bit.streamType;} 218 | 219 | Commander& reloadCommands() {computeLengths(); return *this;} 220 | 221 | int quick(String cmd); 222 | Commander& quickSetHelp(); 223 | bool quickSet(String cmd, int& var); 224 | bool quickSet(String cmd, float& var); 225 | bool quickSet(String cmd, double& var); 226 | bool quickSet(String cmd, String& str); 227 | Commander& quickGet(String cmd, int var); 228 | Commander& quickGet(String cmd, float var); 229 | Commander& quickGet(String cmd, double var); 230 | Commander& quickGet(String cmd, String str); 231 | 232 | size_t write(uint8_t b) { 233 | yield(); 234 | if( ports.settings.bit.copyResponseToAlt ) writeAlt(b); 235 | yield(); 236 | if(ports.outPort){ 237 | doPrefix(); 238 | if(isNewline(b)) ports.outPort->print(postfixString); 239 | return ports.outPort->write(b); 240 | } 241 | return 0; 242 | } 243 | 244 | int available() { return bufferString.length(); } 245 | 246 | int read() { 247 | if(!bufferString.length()) 248 | return -1; 249 | char retchar = bufferString.charAt(0); 250 | bufferString.remove(0, 1); 251 | return retchar; 252 | } 253 | 254 | int peek() { 255 | if(!bufferString.length()) 256 | return -1; 257 | return bufferString.charAt(0); 258 | } 259 | 260 | int availableForWrite() { 261 | #if !defined(ESP8266) && !defined(ESP32) 262 | return ports.outPort ? ports.outPort->availableForWrite() : 0; 263 | #else 264 | // availableForWrite() is not part of the Stream class on ESP32 (and probably ESP8266) 265 | return 0; 266 | #endif 267 | } 268 | 269 | 270 | 271 | //int availableForWrite() { return ports.outPort ? ports.outPort->availableForWrite() : 0; } 272 | 273 | void flush() { if(ports.outPort) ports.outPort->flush(); } 274 | 275 | using Print::write; // pull in write(String) and write(buf, size) from Print 276 | 277 | 278 | 279 | Commander& setPrefix(String prfx){ 280 | prefixString = prfx; 281 | commandState.bit.prefixMessage = true; 282 | commandState.bit.newlinePrinted = true; 283 | return *this; 284 | } 285 | //enable prefix for this command with whatever String is already set 286 | Commander& startPrefix(){ 287 | commandState.bit.prefixMessage = true; 288 | commandState.bit.newlinePrinted = true; 289 | return *this; 290 | } 291 | //set postfix String for the command 292 | Commander& setPostfix(String pofx){ 293 | postfixString = pofx; 294 | commandState.bit.postfixMessage = true; 295 | return *this; 296 | //commandState.bit.newlinePrinted = true; 297 | } 298 | //enable Postfix for this command with whatever String is already set 299 | Commander& startPostfix(){ 300 | commandState.bit.postfixMessage = true; 301 | return *this; 302 | //commandState.bit.newlinePrinted = true; 303 | } 304 | Commander& startFormatting(){ 305 | commandState.bit.prefixMessage = true; 306 | commandState.bit.newlinePrinted = true; 307 | commandState.bit.postfixMessage = true; 308 | return *this; 309 | } 310 | Commander& autoFormat(bool state){ ports.settings.bit.autoFormat = state; return *this;} 311 | bool autoFormat() { return ports.settings.bit.autoFormat;} 312 | 313 | Commander& printCommandPrompt(); 314 | 315 | bool containsTrue(); 316 | bool containsFalse(); 317 | bool containsOn(); 318 | bool containsOff(); 319 | 320 | Commander& delimiters(String myDelims) { if(myDelims != "") delimiterChars = myDelims; return *this;} 321 | String delimiters() {return delimiterChars;} 322 | Commander& addDelimiter(char newDelim) {delimiterChars += newDelim; return *this;} 323 | 324 | Commander& commentChar(char cmtChar) {commentCharacter = cmtChar; return *this;} 325 | char commentChar() {return commentCharacter;} 326 | Commander& reloadChar(char reloadChar) {reloadCommandCharacter = reloadChar; return *this;} 327 | char reloadChar() {return reloadCommandCharacter;} 328 | Commander& endOfLineChar(char eol); 329 | char endOfLineChar() {return endOfLineCharacter;} 330 | Commander& promptChar(char eol) {promptCharacter = eol; return *this;} 331 | char promptChar() {return promptCharacter;} 332 | //void setDelimChar(char eol) {delimChar = eol;} 333 | 334 | Commander& echo(bool sState) {ports.settings.bit.echoTerminal = sState; return *this;} 335 | bool echo() {return ports.settings.bit.echoTerminal;} 336 | Commander& printComments(bool cState) {ports.settings.bit.printComments = cState; return *this;} 337 | bool printComments() {return ports.settings.bit.printComments;} 338 | Commander& echoToAlt(bool sState) {if(ports.altPort) ports.settings.bit.echoToAlt = sState; return *this;} //only allow this if the altPort exists 339 | bool echoToAlt() {return ports.settings.bit.echoToAlt;} //only allow this if the altPort exists 340 | Commander& copyRepyAlt(bool sState) {if(ports.altPort) ports.settings.bit.copyResponseToAlt = sState; return *this;} //only allow this if the altPort exists 341 | bool copyRepyAlt() {return ports.settings.bit.copyResponseToAlt;} //only allow this if the altPort exists 342 | 343 | Commander& commandProcessor(bool state) {ports.settings.bit.commandParserEnabled = state; return *this;} 344 | bool commandProcessor() {return ports.settings.bit.commandParserEnabled;} 345 | 346 | Commander& stripCR(bool sState) {ports.settings.bit.stripCR = sState; return *this;} 347 | bool stripCR() {return ports.settings.bit.stripCR;} 348 | 349 | Commander& multiCommander(bool enable) {ports.settings.bit.multiCommanderMode = enable; return *this;} 350 | bool multiCommander() {return ports.settings.bit.multiCommanderMode;} 351 | 352 | Commander& errorMessages(bool state) {ports.settings.bit.errorMessagesEnabled = state; return *this;} 353 | bool errorMessages() {return ports.settings.bit.errorMessagesEnabled;} 354 | 355 | Commander& commandPrompt(bool state) {ports.settings.bit.commandPromptEnabled = state; return *this;} 356 | bool commandPrompt() {return ports.settings.bit.commandPromptEnabled;} 357 | 358 | Commander& showHelp(bool state) {ports.settings.bit.helpEnabled = state; return *this;} 359 | bool showHelp() {return ports.settings.bit.helpEnabled;} 360 | 361 | Commander& internalCommands(bool state) {ports.settings.bit.internalCommandsEnabled = state; return *this;} 362 | bool internalCommands() {return ports.settings.bit.internalCommandsEnabled;} 363 | 364 | Commander& showInternalCommands(bool state) {ports.settings.bit.printInternalCommands = state; return *this;} 365 | bool showInternalCommands() {return ports.settings.bit.printInternalCommands;} 366 | 367 | Commander& autoChain(bool state) {ports.settings.bit.autoChain = state; return *this;} 368 | bool autoChain() {return ports.settings.bit.autoChain;} 369 | 370 | Commander& autoChainErrors(bool state) {ports.settings.bit.autoChainSurpressErrors = state; return *this;} 371 | bool autoChainErrors() {return ports.settings.bit.autoChainSurpressErrors;} 372 | 373 | 374 | Commander& chain() {commandState.bit.chain = true; return *this;} 375 | Commander& unchain(); 376 | 377 | cmdSettings_t settings() {return ports.settings;} 378 | Commander& settings(cmdSettings_t newSet) {ports.settings = newSet; return *this;} 379 | 380 | portSettings_t portSettings() {return ports;} 381 | Commander& portSettings(portSettings_t newPorts) {ports = newPorts; return *this;} 382 | 383 | Commander& printDelayTime(uint8_t dTime) {primntDelayTime = dTime; return *this;} 384 | uint8_t printDelayTime() {return primntDelayTime;} 385 | 386 | Commander& printDelay(bool enable) {ports.settings.bit.useDelay = enable; return *this;} 387 | bool printDelay() {return ports.settings.bit.useDelay;} 388 | 389 | Commander& printDiagnostics(); 390 | 391 | template 392 | bool getInt(iType &myIvar) { 393 | if(tryGet()){ 394 | //Parse it to the variable 395 | String subStr = bufferString.substring(dataReadIndex); 396 | myIvar = (iType)subStr.toInt(); 397 | //if there is no space next, set dataReadIndex to zero and return true - you parsed an int, but next time it will fail. 398 | if(!findNextItem()) dataReadIndex = 0;//nextNumberDelimiter(); 399 | return true; //nextSpace(); 400 | }else return 0; 401 | } 402 | bool getFloat(float &myFloat); //Returns true if myFloat was updated with a value parsed from the command String 403 | bool getDouble(double &myDouble); //Returns true if myDouble was updated with a value parsed from the command String 404 | bool getString(String &myString); //returns the next string in the payload - determined by the delimiters space and special char 405 | uint8_t countItems(); //Returns the number of items in the payload. An item is any string with a space or delimiterChar at each end (or the end of line) 406 | uint16_t getCommandListLength() {return commandListEntries;} //returns the number of commands 407 | const commandList_t* getCommandList() {return commandList;} 408 | String getCommandItem(uint16_t commandItem); //returns a String containing the specified command and help text 409 | uint8_t getInternalCommandLength() {return INTERNAL_COMMAND_ITEMS;} 410 | String getInternalCommandItem(uint8_t internalItem); 411 | uint16_t getReadIndex() {return dataReadIndex;} 412 | Commander& rewind(); 413 | Commander& printCommandList(); 414 | Commander& printCommanderVersion(); 415 | int16_t getCommandIndex() {return commandIndex;} 416 | String bufferString = ""; //the buffer - public so user functions can read it 417 | String commanderName = "CMD"; 418 | 419 | #if defined BENCHMARKING_ON 420 | unsigned long benchmarkStartTime1 = 0, benchmarkStartTime2 = 0, benchmarkStartTime3 = 0, benchmarkStartTime4 = 0; 421 | unsigned long benchmarkTime1 = 0, benchmarkTime2 = 0, benchmarkTime3 = 0, benchmarkTime4 = 0; 422 | int benchmarkCounter = 0; 423 | #endif 424 | 425 | private: 426 | bool processPending(); 427 | bool streamData(); 428 | void echoPorts(int portByte); 429 | void bridgePorts(); 430 | void doPrefix(){ //handle prefixes for command replies 431 | if(commandState.bit.prefixMessage && commandState.bit.newlinePrinted) ports.outPort->print(prefixString); 432 | commandState.bit.newlinePrinted = false; 433 | } 434 | void doPrefixln(){ //handle prefixes for command replies with newlines 435 | if( ports.settings.bit.useDelay ) delay(primntDelayTime); 436 | if(commandState.bit.prefixMessage && commandState.bit.newlinePrinted) ports.outPort->print(prefixString); 437 | commandState.bit.newlinePrinted = true; 438 | } 439 | bool qSetHelp(String &cmd); 440 | int qSetSearch(String &cmd); 441 | void computeLengths(); 442 | uint8_t getLength(uint8_t indx); 443 | bool handleCommand(); 444 | bool handleUnknown(); 445 | void tryUnlock(); 446 | bool checkPass(); 447 | void handleComment(); 448 | bool processBuffer(int dataByte); 449 | void writeToBuffer(int dataByte); 450 | void resetBuffer(); 451 | int matchCommand(); 452 | bool checkCommand(uint16_t cmdIdx); 453 | bool checkInternalCommand(uint16_t cmdIdx); 454 | bool qcheckInternal(uint8_t itm); 455 | int handleInternalCommand(uint16_t internalCommandIndex); 456 | bool handleCustomCommand(); 457 | bool tryGet(); 458 | bool findNextDelim(); 459 | bool findNextItem(); 460 | bool delimToNextItem(); 461 | bool itemToNextDelim(); 462 | bool isDelimiter(char ch); 463 | bool isItem(char ch); 464 | bool isNumber(String &str); 465 | bool isNumeral(char ch); 466 | bool isEndOfLine(char dataByte); 467 | bool isEndOfCommand(char dataByte); 468 | //bool isStartOfItem(char dataByte); 469 | bool isNewline(char var) { return (var == '\n') ? true : false;} 470 | bool isNewline(int var) { return (var == '\n') ? true : false;} 471 | //bool isNewline(char var[]) { return false;} //fix this so it looks for a newline at the end of the string ... 472 | String getWhiteSpace(uint8_t spaces); 473 | uint8_t getInternalCmdLength(const char intCmd[]); 474 | //utility to print the buffer contents as integer values 475 | void printBuffer(){ 476 | for(uint32_t n = 0; n < bufferString.length(); n++){ 477 | write('['); 478 | print((int)bufferString.charAt(n)); 479 | write(']'); 480 | } 481 | } 482 | String prefixString = ""; 483 | String postfixString = ""; 484 | const commandList_t* commandList; 485 | uint8_t commandListEntries = 0; 486 | cmdHandler customHandler; 487 | cmdHandler defaultHandler; 488 | cmdState_t commandState; 489 | portSettings_t ports; 490 | //int8_t commandType = UNKNOWN_COMMAND; 491 | int16_t commandIndex = -1; 492 | uint8_t* commandLengths; 493 | uint8_t endIndexOfLastCommand = 0; 494 | const char** extraHelp; 495 | uint8_t longestCommand = 0; 496 | char commentCharacter = '#'; //marks a line as a comment - ignored by the command parser 497 | char reloadCommandCharacter = '/'; //send this character to automatically reprocess the old buffer - same as resending the last command from the users POV. 498 | char promptCharacter = '>'; 499 | //char* delimiters; 500 | String delimiterChars = "= :,\t\\/|"; 501 | //char delimChar = '='; //special delimiter character - Is used IN ADDITION to the default space char to mark the end of a command or seperation between items 502 | char endOfLineCharacter = '\n'; 503 | uint16_t bytesWritten = 0; //overflow check for bytes written into the buffer 504 | uint16_t bufferSize = SBUFFER_DEFAULT; 505 | uint16_t dataReadIndex = 0; //for parsing many numbers 506 | //const char* internalCommandArray[INTERNAL_COMMAND_ITEMS]; 507 | const char* internalCommandArray[INTERNAL_COMMAND_ITEMS] = { "U", "X", "?", "help", "echo", "echox", "errors"}; 508 | String *passPhrase = NULL; 509 | String *userString = NULL; 510 | uint8_t primntDelayTime = 0; // 511 | }; 512 | 513 | #endif //Commander_h -------------------------------------------------------------------------------- /src/prefabs/Display/gfxPrefab.h: -------------------------------------------------------------------------------- 1 | //GFX prefab 2 | 3 | void drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint16_t color); 4 | 5 | drawLine x0 y0 xl yl col 6 | 7 | drawFastVLine(uint16_t x0, uint16_t y0, uint16_t length, uint16_t color); 8 | 9 | drawFastVLine x0 y0 len col 10 | 11 | 12 | drawFastHLine(uint16_t x0, uint16_t y0, uint16_t length, uint16_t color); 13 | 14 | drawFastHLine x0 y0 len col 15 | 16 | void drawRect(uint16_t x0, uint16_t y0, uint16_t w, uint16_t h, uint16_t color); 17 | 18 | drawRect x0 y0 w h col 19 | 20 | void fillRect(uint16_t x0, uint16_t y0, uint16_t w, uint16_t h, uint16_t color); 21 | 22 | fillRect x0 y0 w h col 23 | 24 | void drawCircle(uint16_t x0, uint16_t y0, uint16_t r, uint16_t color); 25 | 26 | drawCircle 27 | 28 | void fillCircle(uint16_t x0, uint16_t y0, uint16_t r, uint16_t color); 29 | 30 | fillCircle 31 | 32 | void drawRoundRect(uint16_t x0, uint16_t y0, uint16_t w, uint16_t h, uint16_t radius, uint16_t color); 33 | 34 | drawRoundRect 35 | 36 | void fillRoundRect(uint16_t x0, uint16_t y0, uint16_t w, uint16_t h, uint16_t radius, uint16_t color); 37 | 38 | fillRoundRect 39 | 40 | void drawChar(uint16_t x, uint16_t y, char c, uint16_t color, uint16_t bg, uint8_t size); 41 | 42 | drawChar 43 | 44 | void setCursor(uint16_t x0, uint16_t y0); 45 | 46 | setCursor 47 | 48 | void setTextColor(uint16_t color); 49 | 50 | setTextColor 51 | 52 | void setTextColor(uint16_t color, uint16_t backgroundcolor); 53 | 54 | setTextColor 55 | 56 | void setTextSize(uint8_t size); 57 | 58 | setTextSize 59 | 60 | void setTextWrap(boolean w); 61 | 62 | setTextWrap 63 | 64 | void drawBitmap(int16_t x, int16_t y, uint8_t *bitmap, int16_t w, int16_t h, uint16_t color); 65 | 66 | drawBitmap 67 | 68 | //The bitmap data must be located in program memory using the PROGMEM directive. 69 | 70 | void fillScreen(uint16_t color); 71 | 72 | fillScreen 73 | 74 | void setRotation(uint8_t rotation); 75 | 76 | setRotation -------------------------------------------------------------------------------- /src/prefabs/Display/gfxPrefab.txt: -------------------------------------------------------------------------------- 1 | //GFX prefab 2 | 3 | void drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint16_t color); 4 | 5 | line x0 y0 xl yl col 6 | 7 | drawFastVLine(uint16_t x0, uint16_t y0, uint16_t length, uint16_t color); 8 | 9 | vline x0 y0 len col 10 | 11 | 12 | drawFastHLine(uint16_t x0, uint16_t y0, uint16_t length, uint16_t color); 13 | 14 | hline x0 y0 len col 15 | 16 | void drawRect(uint16_t x0, uint16_t y0, uint16_t w, uint16_t h, uint16_t color); 17 | 18 | rect x0 y0 w h col 19 | 20 | void fillRect(uint16_t x0, uint16_t y0, uint16_t w, uint16_t h, uint16_t color); 21 | 22 | frect x0 y0 w h col 23 | 24 | -------------------------------------------------------------------------------- /src/prefabs/SDCards/PrefabFileNavigator.h: -------------------------------------------------------------------------------- 1 | //Commander prefab: SDFat file navigator 2 | #ifndef PrefabFileNavigator_h 3 | #define PrefabFileNavigator_h 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | extern bool SDOK; 11 | extern SdFat SD; 12 | 13 | File file1, file2; 14 | bool fileOpen = false; //indicates if a file is open 15 | extern String cmdName; 16 | 17 | const commandList_t* topLayerCommands; 18 | uint16_t topLayerCommandSize = 0; 19 | String topPrompt = ""; 20 | /* 21 | * This needs to be passed to the commander object so it knows how big the array of commands is, but this happens earlier in setup(). 22 | * This has already been forward declared before setup() so the compiler knows it exists 23 | */ 24 | /* Command handler template 25 | bool myFunc(Commander &Cmdr){ 26 | //put your command handler code here 27 | return 0; 28 | } 29 | */ 30 | 31 | //some utility functions 32 | //close any open files 33 | void closeFiles(){ 34 | if(file1.isOpen()) file1.close(); 35 | if(file2.isOpen()) file2.close(); 36 | } 37 | //----------------------------------------------------------------------------------------------- 38 | 39 | void setTopLayer(const commandList_t* topCommands, uint16_t sizes, String prompt){ 40 | topLayerCommands = topCommands; 41 | topLayerCommandSize = sizes; 42 | topPrompt = prompt; 43 | } 44 | //----------------------------------------------------------------------------------------------- 45 | 46 | bool noCardError(Commander &Cmdr){ 47 | if(!SDOK) Cmdr.println("ERR: No SD Card"); 48 | else Cmdr.println("Card detected"); 49 | return 0; 50 | } 51 | //----------------------------------------------------------------------------------------------- 52 | 53 | //These are the command handlers, there needs to be one for each command in the command array myCommands[] 54 | //The command array can have multiple commands strings that all call the same function 55 | bool makeDirectory(Commander &Cmdr){ 56 | if(!SDOK) return noCardError(Cmdr); 57 | if(SD.mkdir( Cmdr.getPayloadString().c_str() )){ 58 | Cmdr.print("Created: "); 59 | Cmdr.println(Cmdr.getPayloadString()); 60 | } 61 | return 0; 62 | } 63 | //----------------------------------------------------------------------------------------------- 64 | bool changeDirectory(Commander &Cmdr){ 65 | if(!SDOK) return noCardError(Cmdr); 66 | if(SD.chdir( Cmdr.getPayloadString().c_str() )){ 67 | Cmdr.print("In: "); 68 | Cmdr.println(Cmdr.getPayloadString()); 69 | if(Cmdr.getPayloadString() == "/" ) Cmdr.commanderName = cmdName + Cmdr.getPayloadString(); 70 | else Cmdr.commanderName = cmdName + " /" + Cmdr.getPayloadString(); 71 | }else Cmdr.println("Error - no such directory"); 72 | return 0; 73 | } 74 | //----------------------------------------------------------------------------------------------- 75 | bool printDirectory(Commander &Cmdr){ 76 | if(!SDOK) return noCardError(Cmdr); 77 | 78 | SD.ls(Cmdr.getOutputPort(), LS_R); 79 | return 0; 80 | } 81 | //----------------------------------------------------------------------------------------------- 82 | 83 | bool removeFile(Commander &Cmdr){ 84 | if(!SDOK) return noCardError(Cmdr); 85 | closeFiles(); 86 | if(SD.remove( Cmdr.getPayloadString().c_str())){ 87 | Cmdr.print("Removed: "); 88 | Cmdr.println(Cmdr.getPayloadString()); 89 | }else{ 90 | Cmdr.println("operation failed"); 91 | } 92 | return 0; 93 | } 94 | //----------------------------------------------------------------------------------------------- 95 | bool removeDirectory(Commander &Cmdr){ 96 | if(!SDOK) return noCardError(Cmdr); 97 | if(SD.rmdir( Cmdr.getPayloadString().c_str())){ 98 | Cmdr.print("Removed: "); 99 | Cmdr.println(Cmdr.getPayloadString()); 100 | }else{ 101 | Cmdr.println("operation failed"); 102 | } 103 | return 0; 104 | } 105 | //----------------------------------------------------------------------------------------------- 106 | bool renameFile(Commander &Cmdr){ 107 | if(!SDOK) return noCardError(Cmdr); 108 | closeFiles(); 109 | String pld = Cmdr.getPayloadString(); //get the payload without any newline 110 | int idx = pld.indexOf(" "); //find the first space 111 | if(idx == -1){ 112 | Cmdr.println("Error, unable to handle command"); 113 | return 0; 114 | } 115 | String sub1 = pld.substring(0, idx); //get the first word. 116 | String sub2 = pld.substring(idx+1, pld.length()); 117 | Cmdr.print("Changing "); 118 | Cmdr.print(sub1); 119 | Cmdr.print(" to "); 120 | Cmdr.println(sub2); 121 | if(SD.rename(sub1.c_str(), sub2.c_str())){ 122 | Cmdr.println("File or directory has been renamed"); 123 | }else Cmdr.println("Error: Operation failed"); 124 | return 0; 125 | } 126 | //----------------------------------------------------------------------------------------------- 127 | 128 | bool readFileContents(Commander &Cmdr){ 129 | if(!SDOK) return noCardError(Cmdr); 130 | closeFiles(); 131 | File tmpFile = SD.open(Cmdr.getPayloadString().c_str(), O_RDONLY); 132 | if(!tmpFile.isOpen()){ 133 | Cmdr.println("Unable to open file"); 134 | return 0; 135 | } 136 | Cmdr.print("Reading from file "); 137 | Cmdr.println(Cmdr.getPayloadString()); 138 | while(tmpFile.available()){ 139 | Cmdr.write(tmpFile.read()); 140 | } 141 | Cmdr.write('\n'); 142 | tmpFile.close(); 143 | return 0; 144 | } 145 | //----------------------------------------------------------------------------------------------- 146 | bool writeToFileHandler(Commander &Cmdr){ 147 | /* Open a file and write data to it. 148 | Use the payload as the filename 149 | Set Commander to Stream mode and wait for contents to be downloaded 150 | */ 151 | closeFiles(); 152 | if(Cmdr.hasPayload()){ 153 | //create file 154 | file1 = SD.open(Cmdr.getPayloadString().c_str(), O_WRITE | O_CREAT); 155 | Cmdr.print(F("Created file: ")); 156 | Cmdr.println(Cmdr.getPayloadString()); 157 | Cmdr.startStreaming(); 158 | }else Cmdr.print(F("Error - Filename required")); 159 | return 0; 160 | } 161 | //----------------------------------------------------------------------------------------------- 162 | 163 | bool exitHandler(Commander &Cmdr){ 164 | if(topLayerCommands == NULL || topLayerCommandSize == 0){ 165 | Cmdr.println("Exit not functional"); 166 | return 0; 167 | } 168 | //close any open files ... 169 | Cmdr.println("Closing SD Navigator"); 170 | Cmdr.transferBack(topLayerCommands, topLayerCommandSize, topPrompt); 171 | return 0; 172 | } 173 | //----------------------------------------------------------------------------------------------- 174 | bool streamToFileHandler(Commander &Cmdr){ 175 | //special handler for file streams 176 | if(!file1.isOpen()){ 177 | //file is not open so tell the streamer to stop 178 | Cmdr.println("Error: Streaming to closed file"); 179 | Cmdr.stopStreaming(); 180 | return 0; 181 | } 182 | if(Cmdr.bufferString.length() > 0){ 183 | //write to the file and flush the buffer. 184 | Cmdr.print("WRITING:"); 185 | Cmdr.println(Cmdr.bufferString); 186 | file1.print(Cmdr.bufferString); 187 | file1.flush(); 188 | } 189 | if(Cmdr.isStreaming() == false && file1.isOpen()){ 190 | //File is open but streaming has stopped - indicated end of file and time to tidy up. 191 | Cmdr.println("Flushing and closing file"); 192 | file1.flush(); 193 | file1.close(); 194 | 195 | } 196 | return 0; 197 | } 198 | //----------------------------------------------------------------------------------------------- 199 | 200 | 201 | //All commands for 'master' 202 | //COMMAND ARRAY ------------------------------------------------------------------------------ 203 | const commandList_t fileCommands[] = { 204 | {"mkdir", makeDirectory, "make a sub directory"}, 205 | {"chdir", changeDirectory, "Change directory - use / for root"}, 206 | {"ls", printDirectory, "Print directory"}, 207 | {"remove", removeFile, "Delete a file"}, 208 | {"removedir", removeDirectory, "Delete an empty directory"}, 209 | {"rename", renameFile, "rename a file or sub directory"}, 210 | {"read", readFileContents, "Read file and send to terminal"}, 211 | {"write", writeToFileHandler, "Create file and open for writing - Send ASCII code 0x04 to terminate"}, 212 | {"status", noCardError, "check card status"}, 213 | {"exit", exitHandler, "Exit sub command"} 214 | 215 | }; 216 | 217 | //initialise the numOfMasterCmds variable after creating the masterCommands[] array - numOfMasterCmds now indicates the length of the array 218 | const uint16_t numOfFileCommands = sizeof(fileCommands); 219 | #endif //PrefabFileNavigator_h -------------------------------------------------------------------------------- /src/utilities/CommandHelpTags.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "CommandHelpTags.h" 3 | 4 | 5 | bool getCommandArgCode(char helpText[], cmdArgs_t commandArguments){ 6 | //look for brackets: 7 | int16_t firstBracket = -1; 8 | int16_t lastBracket = -1; 9 | uint8_t idx = 0; 10 | //find the opening and closing brackets 11 | while(helpText[idx] != '\0'){ 12 | //step through the help string 13 | if(helpText[idx] == CMD_ARG_START_BRACKET){ 14 | if(idx > 1) break; //if the bracket is not in the 1st or 2nd place, exit. 15 | firstBracket = idx; //first bracket found 16 | } 17 | else if(helpText[idx] == CMD_ARG_END_BRACKET){ 18 | lastBracket = idx; 19 | break; 20 | } 21 | idx++; 22 | } 23 | //Tags MUST appear at the start of a line, or directly after a hide character ('-') return false because there were no tags 24 | if(firstBracket !=0 || lastBracket < 1 || firstBracket != 1) return 0; 25 | switch(helpText[firstBracket+1]){ 26 | case CMD_ARG_NONE: 27 | commandArguments.argumentType = CMD_NO_ARGS; 28 | break; 29 | case CMD_ARG_INT: 30 | commandArguments.argumentType = CMD_INT; 31 | break; 32 | case CMD_ARG_FLOAT: 33 | commandArguments.argumentType = CMD_FLOAT; 34 | break; 35 | case CMD_ARG_STRING: 36 | commandArguments.argumentType = CMD_STRING; 37 | break; 38 | case CMD_ARG_BINARY: 39 | commandArguments.argumentType = CMD_BOOL; 40 | break; 41 | case CMD_ARG_ONOFF: 42 | commandArguments.argumentType = CMD_ONOFF; 43 | break; 44 | case CMD_ARG_TOGGLE: 45 | commandArguments.argumentType = CMD_TOGGLE; 46 | break; 47 | case CMD_ARG_DATASTREAM: 48 | commandArguments.argumentType = CMD_DATASTREAM; 49 | break; 50 | } 51 | commandArguments.numberOfArguments = 1; 52 | if( helpText[firstBracket+2] > 47 && helpText[firstBracket+2] < 58 ){ 53 | commandArguments.numberOfArguments = helpText[firstBracket+2] -48 ; 54 | if(helpText[firstBracket+3] < 48 || helpText[firstBracket+3] > 57 ) commandArguments.numberOfArguments = (commandArguments.numberOfArguments*10) + (helpText[idx]-48); 55 | } 56 | if( helpText[lastBracket] == CMD_CHAINABLE ) commandArguments.chainable = true; 57 | return true; 58 | } 59 | 60 | -------------------------------------------------------------------------------- /src/utilities/CommandHelpTags.h: -------------------------------------------------------------------------------- 1 | //Commander help tags for auto GUI generation 2 | /* 3 | Argument tags can be placed at the start of a help string and indicate what type of argument should come with the command 4 | This can be used to either generate extended help, or to generate user interface elements based on each command 5 | For example a line of HTML can be generated from a command that includes a button, and data entry field, for issuing a command 6 | 7 | Tags MUST be at the start of a help message and enclosed in square brackets 8 | Tags can have a number after them to indicate the number of arguments 9 | For int and float arguments this indicates the number of values, EG [I3] indicates an argument with three ints 10 | For text artuments it indicates the number of words or phrases, for example a tag [T2] indicates two text fields (for example for setting an SSID and password) 11 | 12 | By default, no tag should result in any GUI generator producing a button with a generic text entry field because arguments appearing after commands that should not have an argument are still handled (the argument is ditched) 13 | The [X] tag explicitly marks a command as having no arguments and therefore any GUI element should have no text field. 14 | Tags can be added together, for example [TS] indicates a toggle for streaming data. 15 | */ 16 | #ifndef CommandHelpTags_h 17 | #define CommandHelpTags_h 18 | 19 | 20 | #include 21 | #include 22 | 23 | //Hide command when printing help 24 | #define CMD_HIDE_HELP '-' 25 | //Command is chainable 26 | #define CMD_CHAINABLE 'C' 27 | 28 | #define CMD_ARG_START_BRACKET '[' 29 | #define CMD_ARG_END_BRACKET ']' 30 | //No arguments for this command 31 | #define CMD_ARG_NONE 'X' 32 | //n Int arguments (defaults to 1 if no number is included with the tag) 33 | #define CMD_ARG_INT 'I' 34 | //n Float arguments (defaults to 1 if no number is included after the tag) 35 | #define CMD_ARG_FLOAT 'F' 36 | //String argument. A number after the string indicates the number of words 37 | #define CMD_ARG_STRING 'S' 38 | //Binary argument consisting of the word 'true' or 'false' 39 | #define CMD_ARG_BINARY 'B' 40 | //Binary argument consisting of the word 'on' or 'off' 41 | #define CMD_ARG_ONOFF 'O' 42 | //Command will toggle a setting when issued 43 | #define CMD_ARG_TOGGLE 'T' 44 | //Command is associated with streaming data 45 | #define CMD_ARG_DATASTREAM 'D' 46 | 47 | 48 | 49 | typedef enum cmdArgType_t{ 50 | CMD_NO_TAGS = 0, 51 | CMD_NO_ARGS, 52 | CMD_INT, 53 | CMD_FLOAT, 54 | CMD_STRING, 55 | CMD_BOOL, 56 | CMD_ONOFF, 57 | CMD_TOGGLE, 58 | CMD_DATASTREAM, 59 | } cmdArgType_t; 60 | 61 | //data strtucture to hold an argument type 62 | typedef struct cmdArgs_t{ 63 | uint8_t numberOfArguments = 0; 64 | cmdArgType_t argumentType = CMD_NO_TAGS; 65 | bool chainable = false; 66 | }cmdArgs_t; 67 | 68 | bool getCommandArgCode(char helpText[], cmdArgs_t commandArguments); 69 | 70 | #endif //CommandHelpTags_h 71 | -------------------------------------------------------------------------------- /src/utilities/htmlUtilities.h: -------------------------------------------------------------------------------- 1 | //web page utilities for Commander 2 | 3 | //Returns a string containing the command from a GET request 4 | 5 | String GET_CommandString(String serverGetLine, String defaultString){ 6 | int startOf = serverGetLine.indexOf("GET /"); 7 | int endOf = serverGetLine.indexOf(" HTTP"); 8 | if(startOf == -1) return ""; //return an empty string if GET is not in the line 9 | 10 | startOf += 5; //startOf now corresponds to the space after the slash 11 | if(startOf == endOf) return defaultString; //return the default string if there is nothing found 12 | int equalsIndex = serverGetLine.indexOf("="); 13 | if(equalsIndex > -1) startOf = equalsIndex+1; //if it was a form submission, get the submitted text 14 | 15 | 16 | String returnString = serverGetLine.substring(startOf, endOf); 17 | returnString.replace("%3F", "?"); //replace special characters 18 | for(int n = 0; n < returnString.length(); n++){ 19 | if(returnString.charAt(n) == '+' || returnString.charAt(n) == '/') returnString.setCharAt(n, ' '); 20 | } 21 | return returnString; 22 | } 23 | --------------------------------------------------------------------------------