10. Serial I/O - Part 3
Let's think about what data types. Remember the SimVar
structure we created in Part 1 of the series, we created a struct
with two floats
that represent the Altitude and Barometric Setting of an altimeter.
private struct SimVars
{
public float Altitude; // INDICATED ALTITUDE (feet)
public float KohlsmanSettingHg; // KOHLSMAN SETTING HG (inHG)
}
In memory, Arduino's float
and double
are both 4-bytes objects, but our PC is 64-bit and C#'s double
is a 8-bytes object. Fortunately, float
is 32-bits so we can register our SimVars as SIMCONNECT_DATATYPE.FLOAT32
type to get the values as floats
and send them directly to Arduino.
Changing the C# Code to send the data in bytes
Let's change the private void Simconnect_OnRecvSimobjectData(SimConnect sender, SIMCONNECT_RECV_SIMOBJECT_DATA data)
method. Instead of calling serialPort.WriteLine(...)
we will be converting the values to byte arrays and sending them using serialPort.Write(byte[] buffer, int offset, int count)
.
// comment out so we don't send it as a string anymore
//serialPort.WriteLine($"{simvars.Altitude},{simvars.KohlsmanSettingHg}");
// first, create a buffer to put the data
List<byte> buffer = new();
// then lets add the two simvars. We will use BitConverter to get the floats as a byte array
buffer.AddRange(BitConverter.GetBytes(simvars.Altitude));
buffer.AddRange(BitConverter.GetBytes(simvars.KohlsmanSettingHg));
// now, lets add our newline to specify the end of our message
buffer.Add((byte)'\n');
// finally, lets write the message to the serial port
serialPort.Write(buffer.ToArray(), 0, buffer.Count);
That's it. We created a byte buffer, we converted both SimVars
to byte arrays using the utility class BitConverter
, while adding them to our buffer. We added our newline
to specify the end of our message and we wrote the bytes to the serial port. Our message size should always be 9 bytes, that is:
- 4 bytes - for the Altitude
- 4 bytes - for the Barometer Setting
- 1 byte - for the Newline Character ('\n')
Changing the Arduino Sketch to read and convert Serial bytes
To read bytes from the serial port, we will be using the Serial.readBytes(char* buffer, int length)
. We will then use memcpy(float* value, char* buffer, int size)
to literally copy the 4 bytes we have in the buffer into the object's memory. This will reconstruct the float
value we sent from the C# app. Finally we will send a message back to see if all of this worked:
void setup() {
// initialize the port and set the baud rate to 115200, change to 9600 if you are having communication issues, but make sure it's the same rate as in the C# code
Serial.begin(115200);
}
float altimeter;
float barometer;
void loop() {
// check if we have at least 8 bytes. our full message should be 9 bytes,
if (Serial.available() > 8) {
altimeter = readFloat();
barometer = readFloat();
Serial.readStringUntil('\n');
// send the data back through the serial connection
Serial.print("Altitude: ");
Serial.print(altimeter);
Serial.print(", Barometer: ");
Serial.println(barometer);
}
}
// util method to read 4 bytes from the serial port and return them as a float
float readFloat() {
char buffer[4];
Serial.readBytes(buffer, 4);
float value;
memcpy(&value, buffer, sizeof(float));
return value;
}
Lets upload the Sketch to the Arduino, and press on the Connect button in the Hello MSFS
app. We can see that the data is being sent correctly and the new message format is being displayed with the correct Altimeter and Barometer Setting values:
Wrapping things up
In Part 1 we learned about the Serial Port
, how to send and receive data from and to the PC using C# code and an Arduino Sketch in C++. We learned how to identify our Arduino devices using PID/VID, which can be used to maintain a list of connected devices and send the SimVar
data to them, but for sake of simplicity, just hardcoded my COM port in the examples to give the reader the option of adding that logic with the WMI query snippet provided.
In Part 2 we extended the code we wrote that uses SimConnect SDK to subscribe to SimVars
and send them to an Arduino device using the Serial Port
object as a string, and we verified that the data was received correctly by sending the same string back and printing it in the Debug output window.
In Part 3 we modified the code again to send the data in their own native type by using BitConverter
to convert the floats
to byte arrays, build a byte buffer with both Altitude and Barometer Setting SimVars
byte arrays and our newline
terminator, and modified the Arduino Sketch to read the data from the Serial
connection as bytes and convert them back to float
types and rebuilt our message and send it back to the Hello MSFS
app to visually verify that the data was received and converted correctly.
Now you have the building blocks on how to build your own instruments based on the SimVar
data from Microsoft Flight Simulator 2020, and hopefully you can start building some kickass instruments and build up your sim cockpit!