The binary output string
How to define the output string in binary format
Output string in binary format
In the output string you define the sequence of bytes sent through the output.
Here we are going to deal with the binary format.
Usually, the binary format uses less bytes to send data compared to the decimal format, but you need to use some tricks to make sure they are read in the correct order, making it harder to handle. This is specially valid for the serial interface, where the probability to loose data is higher, making it necessary to check the order of the data and to use some bytes as markers.
If you use a shared memory or UDP output, you get a packet and the bytes order is kept intact and easy to analyze. In UDP, just check the size of the packet, if it's wrong, something is lost.
First, let's look at how the string is interpreted in binary format and how it converts to a byte array.
Add an output to test the results of a string. You can debug the generated bytes for the string you type in the lower area of the output.
Binary format string: <123>
Outputs a byte with value 123
Outputs one byte that defines the value 123.
Binary format string: <<1234>
Outputs two bytes that define the value 1234.
They are byte 4 and byte 210. Notice that 4*256+210=1234
The two << define that the numeric value inside that markers, is a two byte value.
<<123> would create two bytes also. The byte 0 and byte 123.
You can have as many < as you want. <<<<123456> would generate 4 bytes:
0, 1, 226 and 64
((0*256+1)*256+226)*256+64=123456
Binary format string: <1234>
Outputs one byte that defines the value 1234.
WAIT!
1234 doesn't fit one byte. The maximum value in one byte is 255.
So you are getting bad values.
You get just the last byte of the needed bytes to define that value.
Like we saw above, 1234 needs byte 4 and byte 210. So here we get just byte 210.
Carefull with this!
Binary format string: AB<123>
You can output chars values. Each char is assigned a byte value.
A as the byte value 65
B as the byte value 66
So the output from above is 3 bytes: 65, 66 and 123.
The chars < and > can't be used, they are reserved for numeric values and actuators keywords.
Bellow, you can see the ASCII chart, that gives you the bits value for each char.
ASCII Chart from
Binary format string: 123<123>
ATTENTION!
123 is not the same as <123> in binary format.
<123> is a byte with value 123, while the 123 is 3 chars where:
1 as the byte value of 49
2 as the byte value of 50
3 as the byte value of 51
Binary format string: A<Axis1a>B<Wind>
In this string, A and B are chars with the values:
A as the byte value 65
B as the byte value 66
Inside the <> we have two keywords. They identify the actuators and they are replaced with the actuators values.
The number of bytes used for each actuator depend on the number of bits used for each, and that value is defined in the rig module or in the directs modules.
So up to 8 bits, we use one byte, from 9 to 16 bits, we use two bytes, and so on.
In the figure we use 10 bits, that's two bytes.
The <Wind> actuator as a value of zero, so the output is two bytes with zero value.
The <Axis1a> is the first actuator of the hexapod and is outputing a value of 511. For two bytes, this translates to bytes 1 and 255: 1*256+255=511
Here is the main difference to the decimal output. It's the way the keywords are converted to the byte array.
Actuators keys and number of bits in a rig module.
Actuator key and number of bits in a direct module.
Binary format string: <<<<Wind>
Unlike the numeric values inside the <>, the actuators are defined for a specific number of bits/bytes, so using multiple < is ignored by Mover.
Here the actuator as a value of zero, and uses 10 bits, so it's outputing two bytes with value zero.
If you use an unknow key, everything inside <> is ignored.
Example 1 - Arduino Uno controlling one fan
We have an Arduino controling a fan to simulate wind.
The Arduino needs to receive the speed from Mover and it accepts an 8 bit / 1 byte value.
That's a value between 0 and 255.
So in Mover, add a direct to generate that wind value and set the bit output to 8 bits.
In the serial output put the string, select the port of the Arduino and set the speed of the serial communication.
So here we are just sending sequential bytes to set the speed. In the Arduino side, we just need to read the byte value. That's basic, the code would be something like:
// Pin that controls the motor controller speed
int analogSpeedPin = 3;
void setup() {
// Start serial communication
Serial.begin(115200, SERIAL_8N1);
// Set pin as output
pinMode(analogSpeedPin, OUTPUT);
}
void loop() {
if (Serial.available())
{
// Get the byte with the speed value
var fanSpeed = Serial.read();
// Set the pin value
analogWrite(analogSpeedPin, fanSpeed);
}
}
We are using speed of veichle to generate the wind and speed comes in m/s.
The way the direct is setup, you get 0 for 0 m/s and 255 for 100 m/s or 360 km/h in the bit output.
We are just mapping the values this way in the example. You can chage this to your preferences or type of vehicle.
Notice that in some sources we have the wind value that takes into account the canopy when availlable.
You can try the slider to see the values changing. All values under 0 or above 100 are cropped. Here the important
Example 2 - Arduino Uno controlling two fans with different speeds
We have an Arduino Uno controling two fans to simulate wind speed.
The Arduino needs to receive the speeds from Mover and it accepts an 8 bit / 1 byte value.
That's a value between 0 and 255.
The idea is to change speeds of the fans depending on vehicle speed and yaw speed.
Yaw speed introduces a difference of speed in the fans.
So in Mover, add two multi directs to generate the wind value and set the bit outputs to 8 bits.
By default speed value is selected. Add the yaw speed value also on the multi direct.
Invert the gain in one of them to work on the opposite way. While one fan increases speed the other one decreases.
Remember to change the keyword of the actuators. They must be different. I used <LWind> and <RWind>.
In the serial output put the string like we have in the figure, select the port of the Arduino and set the speed of the serial communication.
Carefully look at the Arduino code to understand why we use two 0 bytes:
// Pins that control the motors controllers speed
int analogLeftSpeedPin = 3;
int analogRightSpeedPin = 5;
// Buffer that contains the received bytes
// To have valid data, the buffer should be:
// Byte_0 Byte_0 Byte_LeftValue Byte_RightValue
// Think like that:
// If we get two sequential 0, we know the following two
// values are the speed of the fans
// Even if the fans speed is 0, you get 4 times 0
// This ensures that we get the correct bytes for left
// and right fans
// Also 0 is stopped fan, so no mixing whatever we get
byte bytesBuffer[4] = {0};
void setup() {
// Start serial communication
Serial.begin(115200, SERIAL_8N1);
// Set pins as output
pinMode(analogLeftSpeedPin, OUTPUT);
pinMode(analogRightSpeedPin, OUTPUT);
}
void loop() {
if (Serial.available())
{
// Read new byte and put it at the end of the buffer
bytesBuffer[0]=bytesBuffer[1];
bytesBuffer[1]=bytesBuffer[2];
bytesBuffer[2]=bytesBuffer[3];
bytesBuffer[3] = Serial.read();
counter--;
// Verify if we have a valid sequence
if(bytesBuffer[0] == 0 && bytesBuffer[1] == 0)
{
// Set the pins values
analogWrite(analogLeftSpeedPin, bytesBuffer[2]);
analogWrite(analogRightSpeedPin, bytesBuffer[3]);
}
}
}
This type of code exiges some imagination from your part, to be sure the values ar not mixed in any way.
The above code could be optimized and works well for the fans, but might not be good enough for other applications.
Example 3 - ESP32 controlling 3 actuators
We have an ESP32 controlling 3 actuators.
The actuators position uses 15 bit values. That's two bytes.
So we need at least 6 bytes to get the 3 positions.
How could I know the start of those 6 bytes?
Easy, 2 bytes are 16 bits. Since values go to 15 bits maximum, we could use as a marker, the maximum value of 16 bits. That's 2 bytes with 255.
16 bits maximum value is 65535 that corresponds to bytes 255 and 255.
15 bits maximum value is 32767 that corresponds to bytes 127 and 255.
So if we use two 255 bytes in sequence as marker, we know the start of the 6 bytes.
The positions will never use the first byte with value 255.
So resuming, we use just 8 bytes to get the desired positions in 15 bits.
Code is similar to the example 2:
// Buffer that contains the received bytes
byte bytesBuffer[8] = {0};
void setup() {
// Start serial communication
Serial.begin(115200, SERIAL_8N1);
// Other initializations
}
void loop() {
if (Serial.available())
{
// Read new byte and put it at the end of the buffer
bytesBuffer[0]=bytesBuffer[1];
bytesBuffer[1]=bytesBuffer[2];
bytesBuffer[2]=bytesBuffer[3];
bytesBuffer[3]=bytesBuffer[4];
bytesBuffer[4]=bytesBuffer[5];
bytesBuffer[5]=bytesBuffer[6];
bytesBuffer[6]=bytesBuffer[7];
bytesBuffer[7] = Serial.read();
counter--;
// Verify if we have a valid sequence
if(bytesBuffer[0] == 255 && bytesBuffer[1] == 255)
{
// Get the positions
axis1Position = bytesBuffer[2]*256+bytesBuffer[3];
axis2Position = bytesBuffer[4]*256+bytesBuffer[5];
axis3Position = bytesBuffer[6]*256+bytesBuffer[7];
}
}
}
An idea to optimize the code, is to make sure we step over 8 bytes after we got a valid sequence.