Frsky S.Port Interface

Introduction

As part of our PoC we are reviewing the Frsky S.Port interface for connection to our IRCF360° sensor to provide telemetry data. 


FRSKY S.PORT Dashboard

A Frsky S.PORT dashboard has been created using processing.org Java script (see image below). The java sketch merely "sniffs" the current sensors which are being polled and displays the byte values on a dashboard. The last 2 columns display the package size & occurrence of the polled sensor. 

For sensor 161 you can see the sensor is polled more than others. This is because a battery sensor has been plugged into the s.port. 

The reading of the S.PORT was merely done by plugging the S.PORT signal into a FTDI FT232RL USB to TTL Serial Converter Adapter Module. This basically converts RS-232 or TTL serial transmissions to USB signals, in order to allow support for legacy devices (these just cost a few € on ebay). You can download a configuration tools from the FTDI site which allows you to configure  the TX and RX pins as "inverted". The Baud in the processing Sketch is set to 57600, N, 8, 1. 

The draft processing sketch is included below and will be uploaded to our GitHub sites soon. 

FrSky S.PORT Dashboard



We are adding more details as we get them, so this page is just a placeholder and will be updated as we are working on the project. 


The S.Port protocol is quite well documented on the following websites:

Summary of the S.Port characteristics:

  • 3.3V inverted single wire serial protocol running at 57.600 baud serial protocol, so it can be used with an UART. 
  • Half duplexed / Single wire, meaning that both transmit and receive are using the same wire
  • Receiver (e.g. FrSky X8R) polls sensors by sending:
    • 0x7E (8bit start-stop)
    • 8_bit_sensor_id
  • If a sensor is present it answers by sending:
    • 0x10 (8bit data frame header)
    • value_type (16 bit, e.g. voltage / speed)
    • value (32 bit, may be signed or unsigned depending on value type)
    • checksum (8 bit)
  • If no sensor is present no answer gets sent and the receiver goes on to the next sensor-id
  • Not all  sensor ids (0x00-0xFF) are polled. known to work are: 0x00, 0xA1
  • A value type indicates what kind of value will be transmitted, e.g. voltage / current / speed etc.
  • One sensor can support multiple value types: Each time it receives a poll it may send a different value type. So if a sensor supports output of voltage and current it may output voltage on the first poll, current on the second, voltage on the third, current at the fourth and so on.
  • Value types are described here:   https://code.google.com/p/opentx/source/browse/trunk/src/telemetry/frsky_sport.cpp

See our other pages related to this project:
OpenXsensor is the evolution of OpenXvario and extends it to multi-sensing applications.

Based on an Arduino platform, it connects to different sensors and transmits the measurements via RC industry telemetry protocols.

References / Credits:




List of Sensor ID's:

Draft version of the processing sketch:

S-PORT Real Time Graphical Display of FrSky S.PORT

// Processing Sketch - This Processing Sketch is used for graphical representation of the FrSky S.PORT protocol in real-time
// In this examples we used an Frysky X8R and X4R receiver which was bound to a Frysky Taranis Plus
// Graphical represetentation of Futuba SBUS
// Example by Colin Bacon
// ROBOTmaker
// www.robotmaker.eu


//To get this to work you'll need to have S.PORT conneted receiver that is connected via  FTDI USB thingy to your PC
//As the S.PORT is an inverted signal your need to configure the RX and TTX pins of the FTDI to inverted (using the configurarion tool that can be downloaded from the FTDI website)
//Further detail of how to do this are on our website
//thanks to all contributors who have published  this protcol. Details on our website. 

//This is still work in progress !!!!


import processing.serial.*;
int FRSKY_ALT_ZERO =0x0000;

//**
//** * info | comment
//** * ---- | -------
//** * sensor ID(s)   | FRSKY_SP_ALT ~ FRSKY_SP_ALT+15 (0x0100 ~ 0x010f)
//** * physical ID(s) | 0 - Altimeter high precision / 3 - Altimeter normal precision
//** * value          | (int) float * 100 [m]
//** * 
//** * N.B. OpenTX use the first non-zero value and set it as offset reference.
//** * 
//** * \brief altimeter (barometric altitude)
//** * \warning normal precision altimeter conflicts with GPS physical ID 3
//**

int  FRSKY_SP_ALT = 0x0100;
/**
 * info | comment
 * ---- | -------
 * sensor ID(s)   | FRSKY_SP_VARIO ~ FRSKY_SP_VARIO+15 (0x0110 ~ 0x011f)
 * physical ID(s) | 0 - Altimeter high precision
 * value          | ?
 * 
 * \todo what difference with ALT ?
 **/
int  FRSKY_SP_VARIO =  0x0110;

/**
 * info  | comment
 * ---- | -------
 * sensor ID(s)   | FRSKY_SP_CURR ~ FRSKY_SP_CURR+15 (0x0200 ~ 0x020f)
 * physical ID(s) | 2 - FSC current sensor
 * value          | (int) float * 10 [A]
 * 
 * \brief VFAS/FSC current
 */
int  FRSKY_SP_CURR  =  0x0200;

/**
 * info | comment
 * ---- | -------
 * sensor ID(s)   | FRSKY_SP_VFAS ~ FRSKY_SP_VFAS+15 (0x0210 ~ 0x021f)
 * physical ID(s) | 2 - FSC current sensor
 * value          | (int) float * 100 [V]
 * 
 * \brief VFAS/FSC voltage
 */
int  FRSKY_SP_VFAS = 0x0210;

/**
 * info | comment
 * ---- | -------
 * sensor ID(s)   | FRSKY_SP_CELLS ~ FRSKY_SP_CELLS+15 (0x0300 ~ 0x030f)
 * physical ID(s) | ?
 * value          | see FrskySP::lipoCell(uint8_t id, float val1, float val2) for data format
 * 
 * \brief FLVSS Lipo cell voltage
 */
int FRSKY_SP_CELLS  = 0x0300;

/**
 * info | comment
 * ---- | -------
 * sensor ID(s)   | FRSKY_SP_T1 ~ FRSKY_SP_T1+15 (0x0400 ~ 0x040f)
 * physical ID(s) | 1 - FLVSS Lipo sensor
 * value          | int [°C]
 * 
 * \brief Temperature
 */
int  FRSKY_SP_T1 =  0x0400;

/**
 * info | comment
 * ---- | -------
 * sensor ID(s)   | FRSKY_SP_T2 ~ FRSKY_SP_T2+15 (0x0410 ~ 0x041f)
 * physical ID(s) | ?
 * value          | int [°C]
 * 
 * \brief Temperature
 */;
int  FRSKY_SP_T2 =  0x0410;

/**
 * info | comment
 * ---- | -------
 * sensor ID(s)   | FRSKY_SP_RPM ~ FRSKY_SP_RPM+15 (0x0500 ~ 0x050f)
 * physical ID(s) | 4
 * value          | int [rpm]
 * 
 * \brief RPM
 */
int  FRSKY_SP_RPM =  0x0500;

/**
 * info | comment
 * ---- | -------
 * sensor ID(s)   | FRSKY_SP_FUEL ~ FRSKY_SP_FUEL+15 (0x0600 ~ 0x060f)
 * physical ID(s) | ?
 * value          | int 0~100 [%]
 * 
 * \brief Fuel level
 */
int  FRSKY_SP_FUEL =  0x0600;

/**
 * info | comment
 * ---- | -------
 * sensor ID(s)   | FRSKY_SP_ACCX ~ FRSKY_SP_ACCX+15 (0x0700 ~ 0x071f)
 * physical ID(s) | ?
 * value          | (int) float * 100 [g]
 * 
 * \brief Accelerometer (X)
 */
int  FRSKY_SP_ACCX = 0x0700;

/**
 * info | comment
 * ---- | -------
 * sensor ID(s)   | FRSKY_SP_ACCY ~ FRSKY_SP_ACCY+15 (0x0710 ~ 0x071f)
 * physical ID(s) | ?
 * value          | (int) float * 100 [g]
 * 
 * \brief Accelerometer (Y)
 */

int  FRSKY_SP_ACCY = 0x0710;

/**
 * info | comment
 * ---- | -------
 * sensor ID(s)   | FRSKY_SP_ACCZ ~ FRSKY_SP_ACCZ+15 (0x0720 ~ 0x072f)
 * physical ID(s) | ?
 * value          | (int) float * 100 [g]
 * 
 * \brief Accelerometer (Z)
 */
int FRSKY_SP_ACCZ = 0x0720;

/**
 * info | comment
 * ---- | -------
 * sensor ID(s)   | FRSKY_SP_GPS_LONG_LATI ~ FRSKY_SP_GPS_LONG_LATI+15 (0x0800 ~ 0x080f)
 * physical ID(s) | 3 - GPS
 * value          | ?
 * 
 * \todo
 */
int  FRSKY_SP_GPS_LONG_LATI  = 0x0800;

/**
 * info | comment
 * ---- | -------
 * sensor ID(s)   | FRSKY_SP_GPS_ALT ~ FRSKY_SP_GPS_ALT+15 (0x0820 ~ 0x082f)
 * physical ID(s) | 3 - GPS
 * value          | (int) float * 100 [m]
 * 
 * N.B. OpenTX:
 * * must have a GpsFix (FRSKY_SP_GPS_LAT_B or FRSKY_SP_GPS_LONG_B must be non null)
 * * use the first non-zero value and set it as offset reference
 * 
 * \brief GPS altitude
 */
int  FRSKY_SP_GPS_ALT = 0x0820;

/**
 * info | comment
 * ---- | -------
 * sensor ID(s)   | FRSKY_SP_GPS_SPEED ~ FRSKY_SP_GPS_SPEED+15 (0x0830 ~ 0x083f)
 * physical ID(s) | 3 - GPS
 * value          | (int) float * 1000 [knots]
 * 
 * \brief GPS speed
 * \warning The speed shown on OpenTX has a little drift, because the knots to shown value conversion is simplified.
 * Allthough, raw knots will be recorded in the logs, and the conversion will be correctly in Companion.
 * This was discussed in this issue: https://github.com/opentx/opentx/issues/1422
 */
int RSKY_SP_GPS_SPEED = 0x0830;

/**
 * info | comment
 * ---- | -------
 * sensor ID(s)   | FRSKY_SP_GPS_COURSE ~ FRSKY_SP_GPS_COURSE+15 (0x0840 ~ 0x084f)
 * physical ID(s) | 3 - GPS
 * value          | (int) float * 100 [°]
 * limits         | 0~359.99°
 * 
 * \brief GPS course (heading)
 */
int FRSKY_SP_GPS_COURSE = 0x0840;

/**
 * info | comment
 * ---- | -------
 * sensor ID(s)   | FRSKY_SP_GPS_TIME_DATE ~ FRSKY_SP_GPS_TIME_DATE+15 (0x0850 ~ 0x085f)
 * physical ID(s) | 3 - GPS
 * value          | ?
 * 
 * \brief GPS time and date
 * \todo
 */
int  FRSKY_SP_GPS_TIME_DATE = 0x0850;

/**
 * info | comment
 * ---- | -------
 * sensor ID(s)   | FRSKY_SP_A3 ~ FRSKY_SP_A3+15 (0x0900 ~ 0x090f)
 * physical ID(s) | 5- SP2UART(Host)
 * value          | ?
 * 
 * \brief A3 ADC sensor
 * \todo scale and calibration
 */
int  FRSKY_SP_A3 = 0x0900;

/**
 * info | comment
 * ---- | -------
 * sensor ID(s)   | FRSKY_SP_A4 ~ FRSKY_SP_A4+15 (0x0910 ~ 0x091f)
 * physical ID(s) | 5 - SP2UART(Host)
 * value          | ?
 * 
 * \brief A4 ADC sensor
 * \todo scale and calibration
 */
int  FRSKY_SP_A4 = 0x0910;

/**
 * info | comment
 * ---- | -------
 * sensor ID(s)   | FRSKY_SP_AIR_SPEED ~ FRSKY_SP_AIR_SPEED+15 (0x0a00 ~ 0x0a0f)
 * physical ID(s) | ?
 * value          | knots * 10
 * 
 * \brief Air speed sensor
 * \warning The speed shown on OpenTX has a little drift, because the knots to shown value conversion is simplified.
 * Allthough, raw knots will be recorded in the logs, and the conversion will be correctly in Companion.
 * This was discussed in this issue: https://github.com/opentx/opentx/issues/1422
 */
int FRSKY_SP_AIR_SPEED = 0x0a00;

/**
 * Not used - already handled by X8R receiver
 *
 * info | comment
 * ---- | -------
 * sensor ID(s)   | FRSKY_SP_RSSI_ID (0xf101)
 * physical ID(s) | ?
 * 
 * \brief RX RSSI (N/U)
 */
int  FRSKY_SP_RSSI_ID = 0xf101;

/**
 * Not used - already handled by X8R receiver
 *
 * info | comment
 * ---- | -------
 * sensor ID(s)   | FRSKY_SP_ADC1_ID (0xf102)
 * physical ID(s) | ?
 *
 * \brief A1 ADC sensor (N/U)
 */
int FRSKY_SP_ADC1_ID = 0xf102;

/**
 * info | comment
 * ---- | -------
 * sensor ID(s)   | FRSKY_SP_ADC2_ID (0xf103)
 * physical ID(s) | ?
 * value          | ?
 * 
 * \brief A2 ADC sensor
 * \todo scale and calibration
 */
int  FRSKY_SP_ADC2_ID = 0xf103;

/**
 * info | comment
 * ---- | -------
 * sensor ID(s)   | FRSKY_SP_BATT_ID (0xf104)
 * physical ID(s) | ?
 * value          | ?
 * 
 * \brief unknown
 * \todo what is this sensor ?
 */
int  FRSKY_SP_BATT_ID =  0xf104;

// Declare 2D array

Serial myPort;  // The serial port
float     x, y;
int       sensorID;
String    myString = null;
int[]    myBuffer = new int [250];
byte[]    myBytes = new byte [20];
byte[]    SizeofmyBytes = new byte [20];
int []    sensorArrayID = new int [255];

int       errors = 0;
boolean   failsafe = false;
int       idx, inByte, lost, packetErrors, pollCount, SensorID;
long      last_refresh = 0;
float     ymag = 0;
float     newYmag = 0;
float     xmag = 0;
float     newXmag = 0; 

int yoffSet   =100; 
int yStartPos = 100;
int xoffSet   = 150;
// Declare 2D array
int[][] telemetryArray = new int[254][254];
int[][] myArray = new int[254][254];
int cols = width;
int rows = height;
int i=0, j=0, c=0;
int CurrentSensorValue=0;
int currentPosition;  
Boolean startByteFound  = false; //set the startup of reading Bytes

// ********************************************************************************
void setup() 
{
    size(1200, 800, FX2D); //give good dashboard resolutions
    //size(1200, 800, P3D); //used for drawing cubes
    //fullScreen(); //This sets it to full screen, but comment out the size commmand above
    pixelDensity(1); //Sharpen the text
    
    // List all the available serial ports
    printArray(Serial.list());
    // Open the port you are using at the rate you want:
   // myPort = new Serial(this, Serial.list()[0], 100000,'E',8,2);
   myPort = new Serial(this, Serial.list()[0], 57600);
    //myString = myPort.readStringUntil(15); //clean the first buffer
    //println(myPort); //Debug to show the port opened 
    //background(255);
   // fill(255);  //Set text colour
    //textSize(12); //Reset text size
    //stroke(255);
    currentPosition = -1; //this is to signify a start-up situation
}

// ********************************************************************************
void draw() 
{
  
  
 while (myPort.available() > 0) {
     idx=0; //reset the counter
     inByte = myPort.read(); //Read the Byte that just came in
    //if (myPort.read () == 0x7E) {
      if (inByte == 126) 
          { //Start Byte Found
            startByteFound = true;
            drawCanvas(); //We now know that this is new packet,  so close the previous packet and draw the results on the canvas. 
            currentPosition = 1; //Set the position in the array as the first
            yoffSet = 100; //Rest the canvas start positions
            xoffSet = 100; //Rest the canvas start positions
          }
          else //it's either a subsequent byte or a lost packet
          {
            if (currentPosition >= 0) //Check if it's not a startup package with value -1 then continue
            { 
               if (startByteFound)  //If the start byte has already been found then this is a subseqent byte, else its a lost packet and this can be ignored
               {
                 currentPosition ++; //We know this is a second or subsequent byte of a packet, so increase the Byte position in the packet
                 //Check if this is the second byte position as this would then be the sensorID which needs to be stored
                 if (currentPosition ==2)
                     {
                      sensorID = inByte; 
                      countSensorOccurrances(); //store the number of times the sensors is scanned 
                     }
                  stuffArray(); //Stuff the array with the bytes that come into to the packet 
                 }         
          } //if StartByteFound 
        }  
      }
    } 
   
  
void stuffArray() {
 
  //add the current byte value to the current sensorID. Do this until a new startByte is received
  myArray [sensorID] [currentPosition] = inByte;
  myArray [sensorID] [14] = currentPosition; //store the size of the packet
  
}

void countSensorOccurrances() {
  
  CurrentSensorValue = myArray[sensorID][15]; //Get current sensor 'hit' count
  myArray[sensorID][15]= CurrentSensorValue + 1;//increment the value for the correspoing sensorID position and put it back. if (CurrentSensorValue >= 200) CurrentSensorValue=0;
  

 }

void drawCanvas() {

  //Draw a table of values
  background(100); //Set the table background to grey
  fill(255);  //Set text colour to black
  
  
  
  //Display currernt packet size
  noStroke(); //turn off the board lines
  textSize(14); //set text size
  text("Size of Packet", 100,50);
  rect(110, 60, currentPosition , 5); 
  
   
  //Display the current sensorID         
  text("SensorID", 250,50);
  text(sensorID, 250,70);
  
   //Create the table title
  textSize(26); //set larger text for the title
  text("FrySky S.PORT - Status Dashboard", width/2-150, 30); //Place the title in the centre of the dashboard
  
  //Create the table grid lines
  stroke(0,255,0); //set the colour of the grid lines
  line(xoffSet, yoffSet, 1000, yoffSet);
  textSize(14); //set text size
        
  //create a list of all sensors found
    for( i=0;i<250;i++)
       {   
          if (myArray[i][15]!= 0) //ignore empty sensor ID's
                {  
                  yoffSet = 20 + yoffSet; //start postiion from top 
                  text("Sensor " + i + " = " , 150,yoffSet);
                  xoffSet = 190;
                  for (j=0;j<11;j++)
                     {
                       xoffSet = 60 + xoffSet; //start postiion from left
                       text( myArray[i][j], xoffSet,yoffSet);
                       line(xoffSet -5, yStartPos, xoffSet-5, yoffSet + 5);
                     }
                  
                  text( myArray[i][14], xoffSet+60,yoffSet ); // Print the packetsize values
                  line(150, yoffSet+5, 1000, yoffSet + 5);
                  line(xoffSet -5 + 60, yStartPos, xoffSet-5+60, yoffSet + 5);      
                  
                  text( myArray[i][15], xoffSet+120,yoffSet ); // Print the occurance values
                  line(150, yoffSet+5, 1000, yoffSet + 5);
                  line(xoffSet -5 + 120, yStartPos, xoffSet-5+120, yoffSet + 5);      
                  
                    //Draw a little packet size indicator bar

                  
                }    
                
        }
  
}



void echoResponse(){
  
    //Draw the list of results
   
   
   //send a response
       //if (sensorID == 0xE4) 
       //{  // dec228 - Physical ID 5 - RPM
       //   sendData(FRSKY_SP_RPM, 11111);
          
          //myPort.write(0x10);
          //myPort.write(sensorID);
          //myPort.write(sensorID); //sensorID
          
          //myPort.write(sensorID); //Value
          //myPort.write(0xFF); //False CRC
        //} 
        //myPort.write(0x10);
        //myPort.write(sensorID);
        //myPort.write(sensorID); //sensorID
          
         //myPort.write(sensorID); //Value
         //myPort.write(0xFF); //False CRC
}

// ********************************************************************************
//void   sendData(int id, int val) {
//  myPort.write(id);
//  myPort.write(val);
//}

Comments