Decoding FrSKY telemetry data with an Arduino – Part three

So now we want to add our own types of data. We’ll still keep the standard FrSky telemetry data for all the standard sensors, but perhaps we want to send additional messages – these might be strings of characters, floating point values, integers, even possibly an image from a camera (though an image would take a long time to transmit due to the low bandwidth available).

To begin with I’ll concentrate on the modifications to the base station – remember that’s what I call the Arduino on the ground connected to the transmitter you’re holding. Once these modifications are done, the program will still work exactly the same as was described in part two, – but once we start injecting suitable data using an Arduino in our aircraft, this modified ‘base station’ program will be ready to also receive and display the new data packet types.

Recall that telemetry hub data frames were framed with the byte 0x5E and that we therefore had to do the byte stuffing technique to encode/decode 0x5E as 0x5D:0x3E and 0x5D as 0x5D:0x3D.

In the same way we’ll now use 0x6E to frame our own data frames so we’ll need to do a similar byte stuffing trick to encode/decode 0x6E as 0x6D:0x6C and 0x6D as 0x6D:0x6B. The bytes 0x5E and 0x5D might also occur inside our own data frames, and to avoid these being detected as ordinary NBDPs (see part two) within our own data frames we shall also encode 0x5E as 0x6D:0x6A and 0x5D as 0x6D:0x69.

This all sounds very complicated, but it doesn’t affect the existing code as the additional layer of byte stuffing only has to occur INSIDE our own data frames.

To keep it as simple as possible I’ll make all our own data frames the same length as each other: our frames will start and end with 0x6E. The first byte of our frame will be an identifier that tells us what variety of our data is being received, but regardless of that identifier, there will always be twenty bytes of data following before the ‘end-of-frame’ marking 0x6E. There’s no particular reason for choosing twenty, and no reason while all our data frames need be the same length – once you have the code working it will be easy to modify it to use different and/or variable lengths.

We’ll store the identifier inside our data packet in FrSkyTelemetryDataID, then receive 20 bytes and store them in myDataPacket, receive the terminating 0x6E, process whatever data is inside myDataPacket and then return to the normal processing loop described in part two. To start with, the processing of our own data packets will just consist of printing the identifier type, followed by the twenty bytes displayed as hex codes.

So here are the changes to the part two code (changes in orange) to do that. I’ve omitted lots of the existing code but included one or more lines of original code (in black) either side of the changes, so you can locate where the changes are:

void handleDataByte(uint8_t data) {
static uint8_t myDataPacket[21];
static uint8_t myDataIndex;
static uint8_t dataPacket[4];

switch (FrSkyUserDataMode) {
case 0: // idle
if (data == 0x5E) { // telemetry hub frames begin with ^ (0x5E)
FrSkyUserDataMode = 1; // expect telemetry hub DataID next
}
else if (data == 0x6E) { // myData frames begin with 0x6E
FrSkyUserDataMode = 3; // expect packet type next
}
break;
case 1: // expecting telemetry hub DataID

case 0x26: // z acceleration
Serial.print("Z acceleration: ");
Serial.print(format(FrSkyUserDataLow, data));
Serial.print("\n");
break;
default:
break;
}
FrSkyUserDataMode = 0; // received and stored both data bytes,
// so back to idle mode
} // else
break;
case 3: // expecting <packet type>
FrSkyUserDataStuffing = false;
FrSkyTelemetryDataID = data;
FrSkyUserDataMode = 4; // expect twenty bytes of data next
myDataIndex = 0;
break;
case 4: // expecting twenty bytes of data
if (FrSkyUserDataStuffing) {
FrSkyUserDataStuffing = false;
if (data == 0x6C)
data = 0x6E;
else if (data == 0x6B)
data = 0x6D;
else if (data == 0x6A)
data = 0x5E;
else if (data == 0x69)
data = 0x5D;
else { // byte stuffing is only valid for (unstuffed) bytes:
// 0x6E, 0x6D, 0x5E or 0x5D

FrSkyUserDataMode = 0; // back to idle mode
break;
}
}
else if (data == 0x6D) { // following byte should be the code
// for a stuffed byte

FrSkyUserDataStuffing = true;
break;
}
myDataPacket[myDataIndex++] = data;
if (myDataIndex >= 20) {
FrSkyUserDataMode = 5; // expect terminating 0x6E next
}
break;
case 5: // expecting terminating 0x6E of a myData data packet
if (data == 0x6E) {
switch (FrSkyTelemetryDataID) {
default:
Serial.print("myDataPacket: ");
if (FrSkyTelemetryDataID < 10) { // pad leading space
// for better formatting

Serial.print(" ");
}
Serial.print(FrSkyTelemetryDataID);
Serial.print(": ");
for (i = 0; i < 20; i++) {
if (myDataPacket[i] < 16) { // format all bytes
// as two hex digits

Serial.print("0");
}
Serial.print(myDataPacket[i], HEX);
Serial.print(" ");
}
Serial.print("\n");
break;
}
}
FrSkyUserDataMode = 0; // back to idle mode
break;
default: // should never happen
FrSkyUserDataMode = 0; // back to idle mode
break;
}
}

Here’s an example of what our modified program will display.

RSSI: 74 AD1: 71 AD2: 62
myDataPacket: 19: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
myDataPacket: 1: E6 0A D6 41 6A 60 EC BE 15 8D 39 42 00 00 00 00 00 00 00 00
myDataPacket: 2: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 C8 41 01 00 01 00
RSSI: 77 AD1: 71 AD2: 62
myDataPacket: 3: 00 00 00 00 18 FC 00 00 00 00 00 00 07 00 78 00 64 03 00 00
myDataPacket: 4: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Barometric Altitude: 122.86
RSSI: 80 AD1: 71 AD2: 62
GPS C.O.G.: 00.00
GPS speed: 00.16
GPS Altitude: 79.06
Date: 2019-03-17
Time: 17:41:21
X acceleration: 08
Y acceleration: 455
Z acceleration: 901
Latitude: 53 deg 17.4902 min N
RSSI: 77 AD1: 71 AD2: 63
Longitude: 03 deg 33.1422 min W
Temperature 1: 27
Temperature 2: 28
myDataPacket: 17: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
RSSI: 78 AD1: 71 AD2: 62
myDataPacket: 18: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
myDataPacket: 19: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
myDataPacket: 1: 03 0F D6 41 FD CC EA BE AA E8 39 42 00 00 00 00 00 00 00 00
RSSI: 75 AD1: 71 AD2: 62

Note the new ‘myDataPacket’ lines interleaved with the previous data from part two.

Next we need to look at the code running on the aircraft that injects this data to the receiver, but this post is already getting long, so I’ll continue in part four

Join the conversation

1 Comment

  1. Nice, keep up the good work! I am looking forward to part four! Definitely will be using this on my DHT and D4R-II receiver.

Leave a comment

Your e-mail address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.