qCommand
qCommand is a class object for linking data with commands. It can be used to get and set variables with serial commands (see Serial Commands example). It can also be used to plot data and set variables via the web application qControl. Some commands and features are only relevant when qCommand is used with qControl , while others are only relevant when used with Serial Commands. To use the class, add the following line to the top your program:
#include "qCommand.h"
Instantiation
qCommand is designed to be instantiated as a global object. It takes an optional boolean argument, to set if input commands are case sensitive (the default is false)
qCommand(bool caseSensitive);
Example
qCommand qC; // qCommand object named qC that is case insensitive
qCommand another_qC(true); // qCommand object named another_qC that is case sensitive
qCommand functions
The instantiated qCommand object has a variety of functions that can be run. These functions can be grouped into the following categories:
Interaction Functions
These functions are designed to be run in the main Arduino loop and allow the Quarto to respond to any incoming data and to send data out as necessary.
readBinary
void readBinary(void);
This function takes no arguments and returns no data. It listens for data on the Quarto's binary USB port and sends updates as needed.
Example
#include "qCommand.h"
qCommand qC;
void loop(void) {
qC.readBinary();
}
readSerial
void readSerial(Stream &inputStream);
Similar to readBinary, readSerial
should be run in Arduino's loop function so it can handle input output to the argument Stream
. Stream
should be one of the Quarto's serial ports: Serial
or Serial2
. If you want the Quarto to respond to commands on both serial ports, run this function twice, once per serial port:
#include "qCommand.h"
qCommand qC;
void loop(void) {
qC.readSerial(Serial);
qC.readSerial(Serial2);
}
Setup Functions
While the Interaction Functions let the Quarto communicate via the serial port or qControl, without any setup commands, there is nothing for the Quarto to communicate about. These setup functions typically are run from Arduino's setup function.
assignVariable
void assignVariable(const char* command, T variable, bool read_only = false);
where T can be a variety of types but it is always a pointer to data. It can be a pointer to a boolean, int and uint, float or double or arrays of any of those types. It can also be a pointer to a SmartData object. (Under the hood, the function is a templated function to these different types). assignVariable
links the data set with variable
to a command
. While running the function produces no output, it enables the Interaction Functions to respond to the command
with the data given in the variable
. Because the variable
will be accessed in other contexts by other qCommand functions, the variable needs to be declared globally so it is always accessible. The last argument is optional and sets if the data is read_only for qControl. When set to true, the variable can only be viewed, not edited from qControl. Note that all data arrays and const variables are automatically set to read_only.
Example
#include "qCommand.h"
qCommand qC; // qCommand object named qC that is case insensitive
double Jupiter = 1.234;
bool Mars = false;
uint16_t Saturn[4];
float PlutoRaw[100];
SmartData<float*> Pluta(PlutoRaw); //SmartData object with PlutoRaw data array under the hood
void setup() {
qC.assignVariable("Jupiter", &Jupiter); // Map the command "Jupiter" to the double Jupiter
qC.assignVariable("J", &Jupiter); // You can map multiple commands to the same underlying data
qC.assignVariable("Mars", &Mars, true); // Set Mars to be read-only to qControl
qC.assignVariable("Sat", &Saturn); // Map the command Sat to a data array
qC.assignVariable("Pluto", &Pluto); // Map command Pluto to SmartData object
}
void loop(void) {
qC.readSerial(Serial);
qC.readBinary();
}
addCommand
void addCommand(const char *command,
void (*function)(qCommand &streamCommandParser, Stream &stream));
addCommand
is similar to assignVariable but maps a command to a custom function instead of a variable. It is only used with serial commands as qControl ignores commands that are mapped to functions.
The syntax looks a little strange, but it is similar to the syntax of assignVariable except the second argument is a pointer to a function instead of a variable. Also, the 3rd argument, read_only
is gone as that argument is only relevant to qControl, which ignores this command anyway. The function that is passed to this function must be a function that takes two arguments: a qCommand object and a Stream. The qCommand object is used to parse the string that is passed to the function and the Stream object it used for outputting a response. See the Serial Commands Example for details on how to use custom functions.
Example
#include "qCommand.h"
qCommand qC;
double loopGain = 1.021; // global for the gain
void gain(qCommand& qC, Stream& S) {
if ( qC.next() != NULL) {
loopGain = atof(qC.current());
if (loopGain < 0) {
loopGain = 0;
} else if (loopGain > 10) {
loopGain = 10;
}
}
S.printf("The gain is %f\n",loopGain);
}
void setup() {
qC.addCommand("Gain", &gain);
}
void loop() {
qC.readSerial(Serial);
}
setDefaultHandler
void setDefaultHandler(void (*function)( const char *, qCommand &streamCommandParser,Stream &stream));
setDefaultHandler
sets the default function to run when a serial command is received that does not match any known commands. This function is only relevent when qCommand with serial commands as qControl does not allow sending unknown commands. The argument to this function is the same as the second argument in addCommand: a pointer to a function that has arguments for a qCommand object and a Stream. This command is used to set a custom message about an unknown command.
Example
#include "qCommand.h"
qCommand qC;
void UnknownCommand(const char* command, qCommand& qC, Stream& S) {
S.printf("I'm sorry, I didn't understand that. (You said '%s'?)\n",command);
S.println("You can type 'help' for a list of commands");
}
void help(qCommand& qC, Stream& S) {
S.println("Available commands are:");
qC.printAvailableCommands(S);
}
void setup() {
qC.setDefaultHandler(UnknownCommand);
qC.addCommand("help", help);
}
void loop() {
qC.readSerial(Serial);
}
String Helper Functions
These functions are for use in functions that are passed to the addCommand and setDefaultHandler functions. These are only relevant when using serial commands with the Quarto.
str2Bool
bool str2Bool(const char *string);
Converts a string like "true" or "false" to a boolean value. Default is false. The function is case insensitive and returns true for "1", "on" and "true".
Example
#include "qCommand.h"
qCommand qC;
void setTrigger(qCommand& qC, Stream& S) {
if ( qC.next() != NULL) {
bool enable = qC.str2Bool(qC.current());
triggerWrite(1,enable);
}
S.printf("Trigger set to %s\n", triggerRead(1) ? "on" : "off");
}
void setup() {
qC.addCommand("Trig", &setTrigger);
}
void loop() {
qC.readSerial(Serial);
}
next
char* next(void);
Returns a pointer to the next command argument. When a string input command is "TheCommand First Second Last", it gets separated by spaces. The first parse is the command ("TheCommand" in this example). When next is run, it will return a pointer to "First". When next is run again, it will return a pointer to "Second". Then "Last". After that, next will return a null pointer.
void setTrigger(qCommand& qC, Stream& S) {
if ( qC.next() != NULL) {
bool enable = qC.str2Bool(qC.current());
triggerWrite(1,enable);
}
S.printf("Trigger set to %s\n", triggerRead(1) ? "on" : "off");
}
current
char* current(void);
Very similar command to next except it returns a pointer to the current argument. This is useful as often next is called to check if there is an argument (does next return a non-null value), but if there is, then you want to do something with that pointer and you can use current
as calling next again would cause it to return the next argument.
void setTrigger(qCommand& qC, Stream& S) {
if ( qC.next() != NULL) {
bool enable = qC.str2Bool(qC.current());
triggerWrite(1,enable);
}
S.printf("Trigger set to %s\n", triggerRead(1) ? "on" : "off");
}
printAvailableCommands
void printAvailableCommands(Stream &outputStream);
Prints a list of available commands to the Stream outputStream
.
Example
void help(qCommand& qC, Stream& S) {
S.println("Available commands are:");
qC.printAvailableCommands(S);
}
Misc Functions
reset
void reset(void);
Rarely needed, but it clears the serial buffer and resets the internal state of all variables. The latter is occasionally useful if qControl gets out of sync with the Quarto and is no longer updating data.
Example
void clearEverything(qCommand& qC, Stream& S) {
qC.reset();
S.println("Running reset.");
}