I have spent a lot of time on the command part of LibreServo and I think it is one of the most important parts of the project, it is how LibreServo is presented to the user. It offers a flexibility and possibilities that I have never seen in any manufacturer.
The documentation of the commands will be divided into two articles, this article is more focused on examples and explanation of execution and the other article is focused on the description of LibreServo commands.
LibreServo has a task manager for the high-priority engine management part and a separate low-priority task manager for sending data to the user only (commands GX). This must be taken into account since the order and timing will be independent between the commands to receive data and the rest of the commands.
All commands start with the character 'S' and at least one servomotor number and end with ';'. Commands can be chained with the character '|' so that all commands are treated together as a block.
This article will be divided into the following topics:
- Servomotor Selection
- Command chaining
- Immediate commands [ ! ]
- CRC check
- Task managers and command timing
- Recovery mode
Servomotor Selection
All commands start with the character 'S' followed by at least one servomotor ID. The ID of a servo motor ranges from 1 to 255 (default is 1). LibreServo allows sending the same command to different servomotors at the same time. The '-' character indicates a range and the ',' character indicates independent servomotor IDs. For example, the commandS1,3,4,10-20M1000; means that servomotors with ID 1, 3, 4 and all with ID between 10 and 20 (both included) should move to position 1000. In addition, ID 0 is reserved (it is for broadcast) and any command sent to ID 0 will be like sending the command to all servomotors regardless of their actual ID.
Example:
S5MW500|S0M12000:1000; | All servomotors will move to position 12000 in 1000 milliseconds, but servomotor 5 after a 500ms delay. |
---|---|
S2,5,10-14,6M2000:500; | Servomotors 2, 5, 6, 10, 11, 12, 13 and 14 will move to position 2000 in 500 milliseconds |
Command chaining
As we have seen, all commands start with the character 'S' and at least one servomotor number and end with ';'. You can chain commands with the '|' character so that all commands are treated together as a block. This is very useful for many reasons, one of the main ones being that until LibreServo sees the ';' character it doesn't start executing, so if you want different servomotors to execute different commands but want to make sure they all start moving at the same time, sending all the commands in one command is the way to make sure they start at the same time. Also, it is useful to send the variable reading commands along with the rest of the commands.
Example:
S1L255:0:0|S1T1567:300:100|S1T1567:10:0|S1T1567:300:100; | Chain a series of Tone and LED commands, it can be the start of a song |
---|---|
S2M1000:500|S3M2000:1000|S2M500:500; | Servomotor 2 will move to 1000 in 500ms and then to 500 in 500ms while servomotor 3 moves to 2000 in 1000ms |
S2M1000:500|S2Gs13,11:500:1; | Servomotor 2 will move at 1000 in 500ms and will report every millisecond its position and current consumption for viewing on the Serial Plotter of the Arduino, for example |
Immediate commands [ ! ]
Another unique feature of LibreServo is immediate or priority commands. A command can be sent as immediate with the '!' character before ';' or '|'. To take effect, it must be the first command for that servo motor that is sent, if it is sent with more chained commands, the one that is marked as immediate. An immediate command causes the execution of the command being executed to be aborted and clears all other commands in the task manager. An immediate command from the variable reading group (GX) does not affect the other commands as it is in a separate task manager, the same as the other immediate commands does not affect the variable reading commands.
Example:
S3M2000:200!; | Servomotor 3 stops the command it was doing and clears all commands from its task manager, then moves to position 2000 in 200ms |
---|---|
S2M1000:500|S3M2000:1000!|S2M500:500!|S3M500:1000; | Servomotor 2 will NOT receive the commands as immediate because '!' is in its second command, while servomotor 3 will, since it is its first command in the command chain |
S2M1000:500!|S2Gs13,11:100:5; | Servomotor 2 will receive an immediate command, but it does not affect the task manager of the Gs command, so if it was already executing a GX command, it will not execute the new Gs command at the same time as the new immediate motion command |
S2M1000:500!|S2Gs13,11:100:5!; | This would be the proper way to make sure that the servo motor stops doing whatever it was doing in both task managers and executes our commands as a priority |
Comprobación CRC
The CRC check is used to make sure that the command received by LibreServo is correct and that no characters have changed during the serial (RS485) transmission. By default CRC checking is enabled in LibreServo but optionally (uso_crc = 2), this means that if you send a command with CRC checking LibreServo will accept it (and if the CRC checking is wrong, it will not execute the command), but if you send commands without CRC checking LibreServo will also accept them (if the command is correct, of course). Of course, you can modify the variable 117 (uso_crc) to make the use of CRC mandatory or to not accept any command with CRC.
By default the CRC check that is done is called CRC-16/AUG-CCITT. The following online crc calculator is recommended for making checks.
The CRC code is sent at the end of the command in hexadecimal format, just before the ';' character and after the '!' character if any. It is preceded by the '#' character and takes into account the entire command up to just the character before the '#' character.
Example:
S1G5#E0CF; | The CRC code is sent as hexadecimal. In this case it would be E0CF |
---|---|
S1M1000:1000|S1GW20|S1Gs16,21,9:1100:1|S3MF#DD7B; | Of course in chained commands the CRC code works in the same way and takes into account the complete command, even if parts are for different servomotors |
Task managers and command timing
In LibreServo there are two task managers. One is the main one for all commands and the other one is exclusive only for variable reading commands. The variable writing and saving commands also go in the main task manager.
Both task managers are like FIFO queues, the first command that comes in is the first to be executed. The task manager just executes one command after another waiting for one command to finish to start executing the next command. Both task managers run at 1KHz, so the minimum execution time for any command is 1ms.
There are two major differences between task managers. The main has a capacity of 100 commands and runs at high priority, while the variable reading task manager has a capacity of 15 commands and runs at low priority. Low priority indicates that its execution is not guaranteed to be exactly every millisecond. It is possible that there may be small variations depending on other interrupts within LibreServo. When we talk about small variations, we always talk about variations of less than a millisecond, so it should not really affect us in the least.
Having these concepts clear, let's see some examples to see what is the order and timing of execution:
Example:
S1MT200000:300:2000|S1MT150000:1000|S1GS9:300:1|S3MS80000:200:3000|S1GW1700|S1GS9:150:1; |
---|
In this example we see a command composed of several subcommands. Although the commands of servo motor 3 is interleaved, each servo motor takes only its commands. We also see how the variable reading commands work in another queue |
S1MT200000:300:1500|S1MT100000:300:1500|S1GS9:1500:1|S3M5000:1000; [WAIT 0,5 seg.] S3M0:750|S3M10000:750!|S1M50000:1000!|S1GS9:1000:1; [WAIT 0,5 seg.] S1M600000:1000|S1GS6:1000:1; |
In this example we see how '!' works. We see how it only affects each specific queue of each servomotor. The commands with preference '!' must be the first command for that servomotor of its queue, as we see with servomotor 3, in the second set of commands does not apply '!' because it is not the first command |
Recovery mode
There are several reasons why we could lose communication with LibreServo. If we do not know the servo motor ID, we just have to send a command to the servo with ID 0 (remember that all servo motors respond to that ID) and pick up or change the servo motor ID. But, if we do not know the serial port speed, or we have saved a speed that is higher than what we can use, for example, arduino only reaches 115200bps but LibreServo is able to communicate at 9Mbps, in that case there is the possibility of forcing LibreServo to reconfigure with the default configuration.
To get LibreServo into recovery mode, we must start LibreServo with pin A of the RS485 port connected to ground and B directly to VCC. WARNING! VCC should never be higher than 13V in this operation because the RS485 chip does not support higher voltages on its input pins. If pins A and B are as mentioned above when turning on LibreServo, LibreServo starts flashing the white LED faster and faster until after 6 seconds, if A and B have not changed value at any time, it reconfigures and saves the default configuration in flash. Now we can turn LibreServo off or continue using it since it will have the default configuration loaded.