Link
On this page

Commander interface

Commander is a simple and flexible interface monitoring, supervision, configuration and control using the G-code like communication protocol. The communication is based on ASCII character command ids which makes simple and efficient to parse on any MCU. After the command id has been received the function attached to this command is called and provided the remaining string of characters received which follows the command character.

This g-code like interface provides callback to configure and tune any:

Furthermore commander enables you to easily create your own commands and extend this interface in any way you might need for your particular application. Here is the link to the docs about how to make your custom commands.

What happens when user sends a command?

When the commander received the string:

It first checks the command id, identifies its M and sends the remaining string to the motor handling callback. Then the motor callback checks what is the coommand id, finds V and sends the remaining string to the PID velocity callbacK. Then the PID velocity callback scans the command id and finds it is the D, so derivative gain and sets the value.

Commander Motor callback (cmd id M ) PID callback (cmd id V )

The other example is if the commander receives:

First id that it finds is O, which is for example motor. It calls the callback that is assigned to this command (which is in this case motor callback) with the string remaining string. Then the motor callback finds the command E and knows its the status (enabled/disabled) either getting or getting. It checks the value and sees that the value is empty, which means the user has sent a get request.

Commander Motor callback (cmd id O )

Using the commander interface

Command interface is implemented in the Commander class.

// Commander interface constructor
// - serial  - optionally receives HardwareSerial/Stream instance
// - eol     - optionally receives eol character - by default it is the newline: "\n" 
// - echo    - option echo last typed character (for command line feedback) - defualt false
Commander commander = Commander(Serial, "\n", false);

The end of line (eol) character is an optional input of the Commander class which represents end of command character. User can define its own end of command characters here, but by default the character used is newline character \n. For example i

BEWARE: EOL characters

Different operating systems have different EOL characters by default. Newline character is probably the most common one but there is also the carriage return '\r' for linux users. Be sure to provide it to the constructor of the Commander class if you wish to use it with your setup!

The echo flag can be used as a debugging feature but it is not recommended to be used for real time motor control and configuration!

Next step would be to add the commander function that reads the Serial instance that you provided into the Arduino loop():

void loop(){
  ...
  commander.run(); // reads Serial instance form constructor
}

If you did not provide the Serial instance to the Commander constructor you can provide it to the run() function.

void loop(){
  ...
  commander.run(Serial); // reads Serial instance form run
}

Or, if you wish to use the commander without Serial and using just string variables then you can provide and char* variables to the run() function:

char* my_string = "user command";
commander.run(my_string); // reads the string

Serial output

The Commander class will always try to print the output to the serial instance provided in the constructor. If it did not receive one in the constructor, then it will use the one provided in the run() function. If it does not any of the two, it will not output anywhere, but the user can still use it.

Adding commands

In order to add the callback for a given command character to the Commander you will need to call the function add() that receives the command character, the function pointer and the commands label:

// creating the command A in the commander
// - command id - character
// - callback   - function pointer - void callback(char* cmd)
// - label      - label of the command (optional) 
commander.add('A',doSomething,"do something");

The only real requirement for the type of the function you can use as the callback is that hey need to return void and they have to receive char* string:

void doSomething(char* cmd){ ... }

With this simple interface you can create your own commands very simply and subscribe them to the Commander using just one line of code.

In addition to this flexible interface for adding generic callbacks the Commander class additionally implements standardized callbacks for:

  • BLDC (BLDCMotor) or Stepper (StepperMotor) motor - commander.motor(&motor, cmd) - see more
  • PID controller (PIDController) - commander.pid(&pid, cmd) - see more
  • Low pass filter (LowPassFilter) - commander.lpf(&lpf, cmd) - see more
  • Any numeric variable (float) - commander.scalar(&variable, cmd) - see more
  • Target setting control (BLDCMotor or StepperMotor) - commander.target(&motor, cmd) - see more
  • Full motion control (BLDCMotor or StepperMotor) - commander.motion(&motor, cmd) - see more

For example if you are interested in full configuration of one motor your code could look something like this:

BLDCMotor motor = .....
Commander commander = ....

// defined wrapper for generic callback
void onMotor(char* cmd){commander.motor(&motor, cmd);}

void setup(){
  ...
  commander.add('m',onMotor,"my motor");
  ...
}
void loop(){
  ...
  commander.run();
}

Or maybe you wish to tune the velocity PID and you and change the target value of the motor and you wish to remove unnecessary memory overhead due to the other functionalities you do nto necessarily need, then your code could look something like:

BLDCMotor motor = .....
Commander commander = ....

// defined wrappers for generic callbacks
void onPid(char* cmd){commander.pid(&motor.PID_velocity, cmd);}
void onLpf(char* cmd){commander.lpf(&motor.LPF_velocity, cmd);}
void onTarget(char* cmd){commander.target(&motor, cmd);}

void setup(){
  ...
  commander.add('C',onPid,"PID vel");
  commander.add('L',onLpf,"LPF vel");
  commander.add('T',onTarget,"target vel (+ torque limit)");
  ...
}
void loop(){
  ...
  commander.run();
}

This simple interface provides the user a simple way to make communicate and configure multiple motors, PID controllers, low pass filters, scalar variables and custom commands in the same time if necessary. It also makes the tuning of the custom control loops much easier since you can close the loop with a pid controller PIDController very easily and just add it to the commander to tune it in real time.

You can find more examples in library examples examples/utils/communication_test/commander folder.

Commander built-in commands

When using the Commander in your program the user will have three built-in default commands he can use:

  • ? - list all the commands available
  • # - get/set decimal point number
    • Examples:
      • get decimal places #
      • set 5 decimal places: #5
  • @ - get/set verbose output mode
    • Examples:
      • get mode: @
      • set user friendly mode : @2
      • set noting mode : @0
      • set on request mode : @1

The list command ? will display all the commands that were added to the Commander and their labels. For example if we have the added commands like these ones:

void setup(){
  ...
  commander.add('M',doSomeMotor,"some motor");
  commander.add('P',doSomePID,"some pid");
  commander.add('R',doSomeOtherMotor,"some other motor");
  ...
}

Here is the example of the output of the list ? command in user-friendly mode:

$ ?
M: some motor
P: some pid
R: some other motor

Configuration commands

Commander class has two configuration parameters:

  • verbose - Serial output mode
  • decimal_places - Number of decimal places for floating point numbers

Number of decimal places for floating point numbers can be changed easily by setting the parameter decimal_places:

commander.decimal_places = 4; // default 3

Serial output mode can be easily changed by setting the parameter verbose

// VerboseMode::nothing           - display nothing - good for monitoring
// VerboseMode::on_request        - display only on user request
// VerboseMode::user_friendly     - display textual messages to the user (default)
// VerboseMode::machine_readable  - display machine readable messages 
commander.verbose = VerboseMode::user_friendly;

There are four types of output modes:

  • VerboseMode::nothing - this mode does not output anything to the serial terminal - it is very useful when Commander is used in combination with monitoring to avoid unknown values in the Arduino’s Serial Plotter for example
    $ MLU1.2 # set voltage limit to 1.2V for the motor with the command id 'M'
    $        # no response   
    
  • VerboseMode::on_request - this mode outputs only the results of get and set commands and will not output any additional unnecessary (human readable) text.
    $ MLU1.2 # set voltage limit to 1.2V for the motor with the command id 'M'
    $ 1.2    # set value is 1.2  
    
  • VerboseMode::user_friendly - this mode is the default mode and is intended for the cases when it is the user who sends the commands using the serial monitor. This mode will in addition to all the necessary get and set values output additional text for easier comprehension for human user.
    $ MLU1.2                  # set voltage limit to 1.2V for the motor with the command id 'M'
    $ Limits| volt: 1.2000    # human readable return - value is 1.2  
    
  • VerboseMode::machine_readable - this mode is there for easier parsing returned values by software. This mode is essentially the same as the VerboseMode::on_request, however it will repeat the command characters before returning values.
    $ MLU1.2 # set voltage limit to 1.2V for the motor with the command id 'M'
    $ MLU1.2 # machine readable format, command repeated + set value is 1.2  
    

List of available commands

All built-in commands and subcommands are defined in the library source, in file src/communication/commands.h. If you wish to change the character id of a certain command that is the place to do it. 😄

In general we can separate the commands into:

When adding the scalar variable to the commander or the motion control target the only command letter used is the one provided to the commander.add.

Commander provides a very simple way to extend the command list and implement new ones

Commander with Serial Monitor in Arudino IDE

Once the commander interface is added to the code you will be able to communicate to it using the Serial Monitor of the Arduino IDE

Commander paramters in the serial monitor are the same as for every other Arduino code working with the Serial. Make sure to:

  • Set the baudrate number the same as in the ino file : for examle if in the ino file you have Serial.begin(115200), the baud rate should be 115200
  • Make sure to set the termination character to newline

SimpleFOCStudio by @JorgeMaker

SimpleFOCStudio is an awesome application built by @JorgeMaker which we will try to keep up to date with out library. It is a python application that uses commander interface for tunning and configuring the motor.

For more info how to install and use this application visit the studio docs .