{"id":436,"date":"2019-03-17T17:50:49","date_gmt":"2019-03-17T17:50:49","guid":{"rendered":"http:\/\/ceptimus.co.uk\/?p=436"},"modified":"2019-03-17T17:50:49","modified_gmt":"2019-03-17T17:50:49","slug":"decoding-frsky-telemetry-data-with-an-arduino-part-three","status":"publish","type":"post","link":"https:\/\/ceptimus.co.uk\/index.php\/2019\/03\/17\/decoding-frsky-telemetry-data-with-an-arduino-part-three\/","title":{"rendered":"Decoding FrSKY telemetry data with an Arduino \u2013 Part three"},"content":{"rendered":"\n<p>So now we want to add our own types of data.  We&#8217;ll still keep the standard FrSky telemetry data for all the standard sensors, but perhaps we want to send additional messages &#8211; 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).<\/p>\n\n\n\n<p>To begin with I&#8217;ll concentrate on the modifications to the base station &#8211; remember that&#8217;s what I call the Arduino on the ground connected to the transmitter you&#8217;re holding.  Once these modifications are done, the program will still work exactly the same as was described in <a href=\"https:\/\/ceptimus.co.uk\/?p=307\">part two<\/a>, &#8211; but once we start injecting suitable data using an Arduino in our aircraft, this modified &#8216;base station&#8217; program will be ready to also receive and display the new data packet types.<\/p>\n\n\n\n<p>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.<\/p>\n\n\n\n<p>In the same way we&#8217;ll now use 0x6E to frame our own data frames so we&#8217;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.<\/p>\n\n\n\n<p>This all sounds very complicated, but it doesn&#8217;t affect the existing code as the additional layer of byte stuffing only has to occur INSIDE our own data frames.<\/p>\n\n\n\n<p>To keep it as simple as possible I&#8217;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 &#8216;end-of-frame&#8217; marking 0x6E.  There&#8217;s no particular reason for choosing twenty, and no reason while all our data frames need be the same length &#8211; once you have the code working it will be easy to modify it to use different and\/or variable lengths.<\/p>\n\n\n\n<p>We&#8217;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.<\/p>\n\n\n\n<p>So here are the changes to the part two code (changes in orange) to do that.  I&#8217;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:<\/p>\n\n\n<pre>void handleDataByte(uint8_t data) {<br><span style=\"color: #ff6600;\">  static uint8_t myDataPacket[21];<\/span><br><span style=\"color: #ff6600;\">  static uint8_t myDataIndex;<\/span><br>  static uint8_t dataPacket[4];<br><br>  switch (FrSkyUserDataMode) {<br>    case 0: \/\/ idle<br>      if (data == 0x5E) { \/\/ telemetry hub frames begin with ^ (0x5E)<br>        FrSkyUserDataMode = 1; \/\/ expect telemetry hub DataID next<br>      }<br>      <span style=\"color: #ff6600;\">else if (data == 0x6E) { \/\/ myData frames begin with 0x6E<\/span><br><span style=\"color: #ff6600;\">        FrSkyUserDataMode = 3; \/\/ expect packet type next<\/span><br><span style=\"color: #ff6600;\">      }<\/span><br>      break;<br>    case 1: \/\/ expecting telemetry hub DataID<br><br>         case 0x26: \/\/ z acceleration<br>            Serial.print(\"Z acceleration: \");<br>            Serial.print(format(FrSkyUserDataLow, data));<br>            Serial.print(\"\\n\");<br>            break;<br>          default:<br>            break;<br>        }<br>        FrSkyUserDataMode = 0; \/\/ received and stored both data bytes,<br>                               \/\/ so back to idle mode<br>      } \/\/ else<br>      break;<br><span style=\"color: #ff6600;\">    case 3: \/\/ expecting &lt;packet type&gt; <\/span><br><span style=\"color: #ff6600;\">      FrSkyUserDataStuffing = false;<\/span><br><span style=\"color: #ff6600;\">      FrSkyTelemetryDataID = data;<\/span><br><span style=\"color: #ff6600;\">      FrSkyUserDataMode = 4; \/\/ expect twenty bytes of data next<\/span><br><span style=\"color: #ff6600;\">      myDataIndex = 0;<\/span><br><span style=\"color: #ff6600;\">      break;<\/span><br><span style=\"color: #ff6600;\">    case 4: \/\/ expecting twenty bytes of data<\/span><br><span style=\"color: #ff6600;\">      if (FrSkyUserDataStuffing) {<\/span><br><span style=\"color: #ff6600;\">        FrSkyUserDataStuffing = false;<\/span><br><span style=\"color: #ff6600;\">        if (data == 0x6C)<\/span><br><span style=\"color: #ff6600;\">          data = 0x6E;<\/span><br><span style=\"color: #ff6600;\">        else if (data == 0x6B)<\/span><br><span style=\"color: #ff6600;\">          data = 0x6D;<\/span><br><span style=\"color: #ff6600;\">        else if (data == 0x6A)<\/span><br><span style=\"color: #ff6600;\">          data = 0x5E;<\/span><br><span style=\"color: #ff6600;\">        else if (data == 0x69)<\/span><br><span style=\"color: #ff6600;\">          data = 0x5D;<\/span><br><span style=\"color: #ff6600;\">        else { \/\/ byte stuffing is only valid for (unstuffed) bytes:<br>               \/\/ 0x6E, 0x6D, 0x5E or 0x5D<\/span><br><span style=\"color: #ff6600;\">          FrSkyUserDataMode = 0; \/\/ back to idle mode<\/span><br><span style=\"color: #ff6600;\">          break;<\/span><br><span style=\"color: #ff6600;\">        }<\/span><br><span style=\"color: #ff6600;\">      }<\/span><br><span style=\"color: #ff6600;\">      else if (data == 0x6D) { \/\/ following byte should be the code <br>                               \/\/ for a stuffed byte<\/span><br><span style=\"color: #ff6600;\">        FrSkyUserDataStuffing = true;<\/span><br><span style=\"color: #ff6600;\">        break;<\/span><br><span style=\"color: #ff6600;\">      }<\/span><br><span style=\"color: #ff6600;\">      myDataPacket[myDataIndex++] = data;<\/span><br><span style=\"color: #ff6600;\">      if (myDataIndex &gt;= 20) {<\/span><br><span style=\"color: #ff6600;\">        FrSkyUserDataMode = 5; \/\/ expect terminating 0x6E next<\/span><br><span style=\"color: #ff6600;\">      }<\/span><br><span style=\"color: #ff6600;\">      break;<\/span><br><span style=\"color: #ff6600;\">    case 5: \/\/ expecting terminating 0x6E of a myData data packet<\/span><br><span style=\"color: #ff6600;\">      if (data == 0x6E) {<\/span><br><span style=\"color: #ff6600;\">        switch (FrSkyTelemetryDataID) {<\/span><br><span style=\"color: #ff6600;\">          default:<\/span><br><span style=\"color: #ff6600;\">            Serial.print(\"myDataPacket: \");<\/span><br><span style=\"color: #ff6600;\">            if (FrSkyTelemetryDataID &lt; 10) { \/\/ pad leading space <br>                                             \/\/ for better formatting<\/span><br><span style=\"color: #ff6600;\">              Serial.print(\" \");<\/span><br><span style=\"color: #ff6600;\">            }<\/span><br><span style=\"color: #ff6600;\">            Serial.print(FrSkyTelemetryDataID);<\/span><br><span style=\"color: #ff6600;\">            Serial.print(\": \");<\/span><br><span style=\"color: #ff6600;\">            for (i = 0; i &lt; 20; i++) {<\/span><br><span style=\"color: #ff6600;\">              if (myDataPacket[i] &lt; 16) { \/\/ format all bytes <br>                                          \/\/ as two hex digits<\/span><br><span style=\"color: #ff6600;\">                Serial.print(\"0\");<\/span><br><span style=\"color: #ff6600;\">              }<\/span><br><span style=\"color: #ff6600;\">              Serial.print(myDataPacket[i], HEX);<\/span><br><span style=\"color: #ff6600;\">              Serial.print(\" \");<\/span><br><span style=\"color: #ff6600;\">            }<\/span><br><span style=\"color: #ff6600;\">            Serial.print(\"\\n\");<\/span><br><span style=\"color: #ff6600;\">            break;<\/span><br><span style=\"color: #ff6600;\">        }<\/span><br><span style=\"color: #ff6600;\">      }<\/span><br><span style=\"color: #ff6600;\">      FrSkyUserDataMode = 0; \/\/ back to idle mode<\/span><br><span style=\"color: #ff6600;\">      break;<\/span><br>    default: \/\/ should never happen<br>      FrSkyUserDataMode = 0; \/\/ back to idle mode<br>      break;<br>  }<br>}<\/pre>\n\n\n<p>Here&#8217;s an example of what our modified program will display.<\/p>\n\n\n<pre>RSSI: 74 AD1: 71 AD2: 62<br>myDataPacket: 19: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 <br>myDataPacket:  1: E6 0A D6 41 6A 60 EC BE 15 8D 39 42 00 00 00 00 00 00 00 00 <br>myDataPacket:  2: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 C8 41 01 00 01 00 <br>RSSI: 77 AD1: 71 AD2: 62<br>myDataPacket:  3: 00 00 00 00 18 FC 00 00 00 00 00 00 07 00 78 00 64 03 00 00 <br>myDataPacket:  4: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 <br>Barometric Altitude: 122.86<br>RSSI: 80 AD1: 71 AD2: 62<br>GPS C.O.G.: 00.00<br>GPS speed: 00.16<br>GPS Altitude: 79.06<br>Date: 2019-03-17<br>Time: 17:41:21<br>X acceleration: 08<br>Y acceleration: 455<br>Z acceleration: 901<br>Latitude: 53 deg 17.4902 min N<br>RSSI: 77 AD1: 71 AD2: 63<br>Longitude: 03 deg 33.1422 min W<br>Temperature 1: 27<br>Temperature 2: 28<br>myDataPacket: 17: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 <br>RSSI: 78 AD1: 71 AD2: 62<br>myDataPacket: 18: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 <br>myDataPacket: 19: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 <br>myDataPacket:  1: 03 0F D6 41 FD CC EA BE AA E8 39 42 00 00 00 00 00 00 00 00 <br>RSSI: 75 AD1: 71 AD2: 62<\/pre>\n\n\n<p>Note the new &#8216;myDataPacket&#8217; lines interleaved with the previous data from part two.<\/p>\n\n\n\n<p>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&#8217;ll continue in <a href=\"https:\/\/ceptimus.co.uk\/?p=452\">part four<\/a>&#8230;<\/p>\n","protected":false},"excerpt":{"rendered":"<p>So now we want to add our own types of data. We&#8217;ll still keep the standard FrSky telemetry data for all the standard sensors, but perhaps we want to send additional messages &#8211; these might be strings of characters, floating point values, integers, even possibly an image from a camera (though an image would take [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[7],"tags":[],"class_list":["post-436","post","type-post","status-publish","format-standard","hentry","category-uncategorized"],"_links":{"self":[{"href":"https:\/\/ceptimus.co.uk\/index.php\/wp-json\/wp\/v2\/posts\/436","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/ceptimus.co.uk\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/ceptimus.co.uk\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/ceptimus.co.uk\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/ceptimus.co.uk\/index.php\/wp-json\/wp\/v2\/comments?post=436"}],"version-history":[{"count":0,"href":"https:\/\/ceptimus.co.uk\/index.php\/wp-json\/wp\/v2\/posts\/436\/revisions"}],"wp:attachment":[{"href":"https:\/\/ceptimus.co.uk\/index.php\/wp-json\/wp\/v2\/media?parent=436"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/ceptimus.co.uk\/index.php\/wp-json\/wp\/v2\/categories?post=436"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/ceptimus.co.uk\/index.php\/wp-json\/wp\/v2\/tags?post=436"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}