Shop OBEX P1 Docs P2 Docs Learn Events
Getting Started With FlySky I-BUS — Parallax Forums

Getting Started With FlySky I-BUS

JonnyMacJonnyMac Posts: 8,926
edited 2023-04-06 23:37 in Propeller 2

FlySky products support Futaba S.BUS output, as well as their own I-BUS protocol. Here's the skinny on FlySky I-BUS:

  • Baud rate is 115.2K
  • True mode, 8N2
  • 2-byte header ($20, then $40)
  • 14 channels sent as 28 bytes (channel values are 1000 - 2000)
  • 2-byte checksum

The checksum is $FFFF minus the sum of frame bytes 0..29. I converted one of my S.BUS demos to work with I-BUS -- the conversion took about 10 minutes and it worked right away.

I get the feeling that I-BUS is intended to feed flight-controller boards, as I cannot find I-BUS servos. The upshot of I-BUS is that there is a telemetry port on the receiver and we might be able to send data from a remote P2 to the transmitter. I am going to look into that.

This is just a quick test program. Over the weekend I will create a proper object that will be as easy to use as the S.BUS object.

Program output:

Note that I have a 10-channel radio so the last four channels in the frame default to 1500 (midpoint). There is no flags byte, but the failsafe outputs do kick in when the signal is lost.

14 MAY: I'm happy enough with my I-BUS object that I can release it.

05 APR 23: Dusted off the demo and added simply LED control from FS-i6X switches (per suggestion from Terry in thread).

«1

Comments

  • Thanks, Mike! -- that will helpful. I'm probably going to order a single sensor, anyway, and spy on the transactions.

  • I'm happy enough to with my I-BUS receiver object to release it (attached to first post in thread).

    This is the output from that demo:

  • pik33pik33 Posts: 2,350
    edited 2021-05-14 18:14

    A very good news. I have a project at the university - the robot controller with remote control/sbus receiver. I have also several P2 Edge boards which I want to use in this project

  • JonnyMacJonnyMac Posts: 8,926
    edited 2021-05-14 18:36

    You can find my S.BUS receiver code in the project files from this week's P2 session:
    -- https://forums.parallax.com/discussion/173386/spin-2-for-beginners-futaba-s-bus

    Now we have choices. For those with FlySky products, there is no need to switch to S.BUS as they're pre-configured for I-BUS. I suppose the advantage of S.BUS is the flags byte that alerts us to a dropped frame, or the receiver being in Failsafe mode. Anyway, both objects work nicely and have compatible method calls, so you move between them with no issues (other than checking for Failsafe mode).

  • Cluso99Cluso99 Posts: 18,069
    edited 2021-05-14 19:08

    Jon,
    If you haven’t already done so, i think this would make an extremely interesting Quick Byte which could then be submitted to hackaday.
    This could be one of those killer apps for P2 :)

  • I already committed to Ken that I'd write a Quick Byte about S.BUS since I just came off a presentation about it. I will follow up with I-BUS later, especially if I can sort out the telemetry feedback.

  • My wife bought me a FlySky FS-i6X for my birthday. I have been poking around with it this evening. I am a complete novice with the RC stuff, but have renewed my interest since my 11yo son has really taken to flight sims and flying RC planes.

    Thanks for your work on this Jon! I am interested in sending telemetry back to the transmitter to be displayed via IBUS. Looks like it's going to be a fun challenge figuring out how to do that :)

    --Terry

  • @JonnyMac said:
    The upshot of I-BUS is that there is a telemetry port on the receiver and we might be able to send data from a remote P2 to the transmitter. I am going to look into that.

    How is that telemetry data treated on the transmitter side? Is it simple text data that is displayed without being interpreted? Or does a possibility exist to forward it to an external device? (serial interface)

    It would be totally cool to implement some sort of primary flight display with artificial horizon, altimeter, airspeed, skid angle etc. Drones already have the required IMU built in but because pitch and roll are automatically controlled anyway the artificial horizon would not be of much use. But for fixed wing aircrafts bank and skid angle would be a good feedback to monitor the correct aileron and rudder input of the pilot. And the artificial horizon could be used for upset recovery or avoidance in the case visibility is poor, for example when looking into the sun.

  • More good stuff found here: https://github.com/cleanflight/cleanflight/blob/master/src/main/telemetry/ibus_shared.h

    @ManAtWork I don't think you can display arbitrary data. In what little research I have done, it appears to be structured data.

    With the 3rd part firmware that is available for their transmitters, who knows what is possible though!

  • I was on the set of a low-budget sci-fi series this weekend where the main character (played by actress Easton Alexeyev) is a wolf with lighted eyes and mechanical ears. The eyes are controlled with a pot but can't change in the scene because it requires a costumer. The ears can be moved with a cable mechanism, but that requires a puppeteer on the set and hidden in the shot, and the cables can restrict Easton's movements.

    I told my producer friend about the SkyFly (as it matches their budge constraints). Since we're not shooting the next episode for a month or so, I'm going to see if I can get the iBus telemetry working on the P2, then back-port it to the P1 for the sci-fi project. Let's compare notes later. @"Ken Gracey" has a robot boat that he puts out on Lake Tahoe, it would be fun if he could control it with the SkyFly and get custom and standard telemetry values back.

  • I was able to get all 10 channels working as well. Even though the receiver is only 6 channels, I can get the other 4 from the ibus on the receiver. Without your code Jon, I would be SOL. Thank you!

    I did have to pull the packaged jm_fullduplexserial.spin2 out of the directory, as it was the one with that used "field" as a var, which is now a reserved word. Deleting the file took care of the problem. Getting the data to actually display took a moment, because I didn't realize it was expecting you to press a key to start it up. I am not sure if that is needed.

    I wish there was better documentation for the telemetry packet format. I can see the codeword and number of bytes sent for things such a temperature, altitude, speed, etc, but what I don't know is if a checksum is sent and if there is a magic codeword sent at the beginning of the packet. Trying to unroll Arduino code makes my head hurt. Guess I need to pick one up just to reverse engineer this protocol.

  • JonnyMacJonnyMac Posts: 8,926
    edited 2023-04-04 04:12

    Yeah, changes that Chip made to the Spin2 interpreter stomped on jm_fullduplexserial.spin2 -- TWICE!

    I'm thinking about plugging everything into a breadboard sand putting the servo and sensor ports on separate LA channels to get an idea of what's happening.

    This link seems to pop up a lot in my searches for information.
    -- https://github.com/betaflight/betaflight

    Came across this image; I will compare with LA when I get a chance (though I only have two sensors [temp and voltage])
    -- https://static.rcgroups.net/forums/attachments/1/1/0/6/2/5/a14850329-56-iBus-telemetry.png

  • ke4pjwke4pjw Posts: 1,079
    edited 2023-04-04 19:10

    Ah! that's some good insight there Jon! So it interrogates the sensors on a single wire type of connection. I see the 0x04 0x81 0x7A 0xFF being transmitted on the sensor bus. I assume the 0x81 means it is calling out to the sensor on address 1, based on the linked image in rcgroups.net

    After looking at the image for awhile, this is what I suspect the Interrogatory and Response packets should look like.

    First Octet
    All Interrogatory packets start with $04
    All Response packets start with $06, except for sensor exists detection echo.

    Second Octet
    Interrogatory & Response: $80+x Sensor Exists at address X
    Interrogatory & Response: $90+x Sensor Type at address X
    Interrogatory & Response: $A0+x Sensor Data at address X

    Third Octet
    Interrogatory & Response Sensor Exists: 2nd to last Octet
    Interrogatory Sensor Type: 2nd to last Octet
    Interrogatory Sensor Data: 2nd to last Octet
    Response Sensor Type: Sensor Type code. See #defines below.
    Response Sensor Data: MSByte of Sensor Data

    Fourth Octet
    Interrogatory & Response Sensor Exists: Last Octet
    Interrogatory Sensor Type: Last Octet
    Interrogatory Sensor Data: Last Octet
    Response Sensor Type: Number of bytes returned for Sensor Data
    Response Sensor Data: LSByte of Sensor Data

    2nd to last Octet
    Interrogatory & Response: Chucksum padding to make mod 256 $FF

    Last Octet
    Interrogatory & Response: Mod 256 Checksum: $FF

    #define IBUS_MEAS_TYPE_TEM            0x01 // Temperature
    #define IBUS_MEAS_TYPE_EXTV           0x03 // External voltage
    #define IBUS_MEAS_TYPE_CELL           0x04 // Avg cell voltage
    #define IBUS_MEAS_TYPE_BAT_CURR       0x05 // Battery current
    #define IBUS_MEAS_TYPE_FUEL           0x06 // Remaining battery percentage
    #define IBUS_MEAS_TYPE_RPM            0x07 // Throttle value / battery capacity
    #define IBUS_MEAS_TYPE_CMP_HEAD       0x08 // Heading
    #define IBUS_MEAS_TYPE_CLIMB_RATE     0x09 // Climb rate
    #define IBUS_MEAS_TYPE_COG            0x0a // Course over ground
    #define IBUS_MEAS_TYPE_GPS_STATUS     0x0b // GPS status (2 values)
    #define IBUS_MEAS_TYPE_ACC_X          0x0c // Acc X
    #define IBUS_MEAS_TYPE_ACC_Y          0x0d // Acc Y
    #define IBUS_MEAS_TYPE_ACC_Z          0x0e // Acc Z
    #define IBUS_MEAS_TYPE_ROLL           0x0f // Roll
    #define IBUS_MEAS_TYPE_PITCH          0x10 // Pitch
    #define IBUS_MEAS_TYPE_YAW            0x11 // Yaw
    #define IBUS_MEAS_TYPE_VERTICAL_SPEED 0x12 // Vertical speed
    #define IBUS_MEAS_TYPE_GROUND_SPEED   0x13 // Speed m/s
    #define IBUS_MEAS_TYPE_GPS_DIST       0x14 // Distance from home
    #define IBUS_MEAS_TYPE_ARMED          0x15 // Armed / unarmed
    #define IBUS_MEAS_TYPE_FLIGHT_MODE    0x16 // Flight mode
    #define IBUS_MEAS_TYPE_PRES           0x41 // Pressure
    #define IBUS_MEAS_TYPE_SPE            0x7e // Speed km/h
    

  • Progress. I can send the FS-i6x telemetry from the P2, but as soon as it sends interrogatories that I haven't programmed to expect, it goes away. That's OK, because this is just a proof of concept. Used jm_fullduplexserial in mode %0100. This allowed me to tie TX and RX pins together. I don't know if the data on this port is 8,n,1 or 8,n,2. That may be problematic down the road. Looks like it was in the above picture where they used 8,n,1, as some of the checksums are $FE and not $FF

  • ke4pjwke4pjw Posts: 1,079
    edited 2023-04-05 03:25

    **More Updates

    OK, found that the last octet is different for Response Sensor Data. It ends in $FE. A 1ms wait is needed after the Interrogatory. Also, LSByte goes first.

    So, this is the packet structure as I understand it:

    First Octet
    All Interrogatory packets start with $04
    All Response packets start with $06, except for sensor exists detection echo.

    Second Octet
    Interrogatory & Response: $80+x Sensor Exists at address X
    Interrogatory & Response: $90+x Sensor Type at address X
    Interrogatory & Response: $A0+x Sensor Data at address X

    Third Octet
    Interrogatory & Response Sensor Exists: 2nd to last Octet
    Interrogatory Sensor Type: 2nd to last Octet
    Interrogatory Sensor Data: 2nd to last Octet
    Response Sensor Type: Sensor Type code. See #defines below.
    Response Sensor Data: LSByte of Sensor Data

    Fourth Octet
    Interrogatory & Response Sensor Exists: Last Octet
    Interrogatory Sensor Type: Last Octet
    Interrogatory Sensor Data: Last Octet
    Response Sensor Type: Number of bytes returned for Sensor Data
    Response Sensor Data: MSByte of Sensor Data

    2nd to last Octet
    Interrogatory & Response: Chucksum padding to make mod 256 $FF

    Last Octet
    All Interrogatory & Response except Response Sensor Data: Mod 256 Checksum: $FF
    Response Sensor Data: Mod 256 Checksum: $FF - 1 ($FE)

    {Proof of Concept FlySky Telemetry}
    
    
    CON
      CLK_FREQ = 200_000_000                                        ' system freq as a constant
      RX1      = 2  { I }                                          ' programming / debug
      TX1      = 3  { O }
      _clkfreq = CLK_FREQ                                           ' set system clock
    
    
    OBJ
        telem : "jm_fullduplexserial"                                  ' * serial IO for terminal
    
    
    PUB main()
          telem.start(RX1, TX1, %0100, 115_200)
          repeat until telem.rx() == $04
    
          debug(uhex($04))
          debug(uhex(telem.rx()))
          debug(uhex(telem.rx()))
          debug(uhex(telem.rx()))
    
          waitms(1)
          telem.tx($04)
          telem.tx($81)
          telem.tx($7A)
          telem.tx($FF)
    
          repeat until telem.rx() == $04
    
          debug(uhex($04))
          debug(uhex(telem.rx()))
          debug(uhex(telem.rx()))
          debug(uhex(telem.rx()))
    
          repeat until telem.rx() == $04
    
          debug(uhex($04))
          debug(uhex(telem.rx()))
          debug(uhex(telem.rx()))
          debug(uhex(telem.rx()))
          waitms(1) 
          telem.tx($06)
          telem.tx($91)
          telem.tx($01)
          telem.tx($02)
          telem.tx($65)
          telem.tx($FF)
          repeat 6
            debug(uhex(telem.rx()))
          repeat
            repeat until telem.rx() == $04
            repeat until telem.rx() == $A1
            repeat until telem.rx() == $5A
            repeat until telem.rx() == $FF
            waitms(1) 
            telem.tx($06)
            telem.tx($A1)
            telem.tx($9A)
            telem.tx($01)
            telem.tx($BD)
            telem.tx($FE)
            repeat 6
             debug(uhex(telem.rx()))
    
    
    
  • A little further this evening. Made a packet parser and created a crude structure in memory for sensors. I can only get the "Tem2" sensor to work. Any additional sensors show up as "3" with a value of "0". I am missing something.

    {Proof of Concept FlySky Telemetry}
    
    
    CON
      CLK_FREQ = 200_000_000                                        ' system freq as a constant
      RX1      = 2  { I }                                          ' programming / debug
      TX1      = 3  { O }
      _clkfreq = CLK_FREQ
      Temp = $01
     IBUS_MEAS_TYPE_TEM =            $01 ' Temperature
     IBUS_MEAS_TYPE_EXTV =           $03 ' External voltage
     IBUS_MEAS_TYPE_CELL =           $04 ' Avg cell voltage
     IBUS_MEAS_TYPE_BAT_CURR =       $05 ' Battery current
     IBUS_MEAS_TYPE_FUEL =           $06 ' Remaining battery percentage
     IBUS_MEAS_TYPE_RPM =            $07 ' Throttle value / battery capacity
     IBUS_MEAS_TYPE_CMP_HEAD =       $08 ' Heading
     IBUS_MEAS_TYPE_CLIMB_RATE =     $09 ' Climb rate
     IBUS_MEAS_TYPE_COG =            $0a ' Course over ground
     IBUS_MEAS_TYPE_GPS_STATUS =     $0b ' GPS status (2 values)
     IBUS_MEAS_TYPE_ACC_X =          $0c ' Acc X
     IBUS_MEAS_TYPE_ACC_Y =          $0d ' Acc Y
     IBUS_MEAS_TYPE_ACC_Z =          $0e ' Acc Z
     IBUS_MEAS_TYPE_ROLL =           $0f ' Roll
     IBUS_MEAS_TYPE_PITCH =          $10 ' Pitch
     IBUS_MEAS_TYPE_YAW =            $11 ' Yaw
     IBUS_MEAS_TYPE_VERTICAL_SPEED = $12 ' Vertical speed
     IBUS_MEAS_TYPE_GROUND_SPEED =   $13 ' Speed m/s
     IBUS_MEAS_TYPE_GPS_DIST =       $14 ' Distance from home
     IBUS_MEAS_TYPE_ARMED =          $15 ' Armed / unarmed
     IBUS_MEAS_TYPE_FLIGHT_MODE =    $16 ' Flight mode
     IBUS_MEAS_TYPE_PRES =           $41 ' Pressure
     IBUS_MEAS_TYPE_SPE =            $7e ' Speed km/h
    
    
    OBJ
        telem : "jm_fullduplexserial"                                  ' * serial IO for terminal
    
    var   byte packet[256]
    '      byte txpacket[256]
          byte sensor[60]
    {
    Sensor Memory Map
    Address,Type,Value1,Value2
    
    }
    PUB main()
          telem.start(RX1, TX1, %0100, 115_200)
    
          sensor[0] := 1    ' Address
          sensor[1] := IBUS_MEAS_TYPE_TEM    ' Type
          sensor[2] := $9A  ' Telemetry LSByte
          sensor[3] := $01  ' Telemetry MSByte
    
          sensor[4] := 2    ' Address
          sensor[5] := IBUS_MEAS_TYPE_BAT_CURR    ' Type
          sensor[6] := $5  ' Telemetry LSByte
          sensor[7] := $0  ' Telemetry MSByte
    
    
          repeat
            startpacket()
    PUB startpacket()
      packet[0] := 0
      repeat until packet[0] == $04 ||  packet[0] == $06
        packet[0] := telem.rx()
      packet[1] := telem.rx()
      packet[2] := telem.rx()
      packet[3] := telem.rx()
      if packet[1] >= $A0 && packet[1] <= $B0
        sensortelemetry()
      if packet[1] >= $80 && packet[1] <= $90
        sensorexists()
      if packet[1] >= $90 && packet[1] <= $A0
        sensortype()
      return
    PUB sensorexists() | i
      repeat i from 0 to 14
        if sensor[i*4] == packet[1]-$80 ' Do we have a sensor at requested address ?
          waitms(1)                     ' Repeat it back if we do
          telem.tx(packet[0])
          telem.tx(packet[1])
          telem.tx(packet[2])
          telem.tx(packet[3])
          repeat 4                       ' Update the receive buffer to remove our echo
            telem.rx()
          debug("Sensor found at address ", udec_(packet[1]-$80))
          return
        else
          debug("Sensor not found at address ", uhex_(packet[1]))
      return
    PUB sensortype() | i
      repeat i from 0 to 14
        if sensor[i*4] == packet[1]-$90 ' Do we have a sensor at requested address ?
          waitms(1)
          packet[0] := $06                     ' Construct the sensor type packet and send it
          packet[2] := sensor[i*4+1]    ' Set Sensor Type
          packet[3] := $02              ' Set number of bytes in telemetry. All sensors are two bytes?
          packet[4] := checksum(4)
          packet[5] := $FF
    
          repeat i from 0 to 5
    '        debug("Sensor Type", uhex_(packet[i]))
            telem.tx(packet[i])
    '      abort
          repeat 6
            telem.rx()                   ' Update the receive buffer to remove our echo
          debug("Sensor found of type ", udec_(packet[2]))
    
      return
    PUB sensortelemetry() | i
      repeat i from 0 to 14
        if sensor[i*4] == packet[1]-$A0 ' Do we have a sensor at requested address ?
          waitms(1)
          packet[0] := $06              ' Construct the sensor type packet and send it
          packet[2] := sensor[i*4+2]    ' Set Sensor LSByte
          packet[3] := sensor[i*4+3]    ' Set Sensor MSByte
          packet[4] := checksum(4)
          packet[5] := $FE
    
          repeat i from 0 to 5
    '        debug("Sensor Data", uhex_(packet[i]))
            telem.tx(packet[i])
    
          repeat 6
            telem.rx()                   ' Update the receive buffer to remove our echo
          debug("Sensor Data sent for Address ", udec_(packet[1]-$A0))
    
      return
    PUB checksum(bytecount) : r | i
        r := 0
        repeat i from 0 to bytecount-1
          r := r + packet[i]
        r := r & $FF
        r := $FF - r
        return r
    
    
  • ke4pjwke4pjw Posts: 1,079
    edited 2023-04-06 05:49

    Scratch that. I have a few sensors working now.

    {Proof of Concept FlySky Telemetry}
    
    
    CON
      CLK_FREQ = 200_000_000                                        ' system freq as a constant
      RX1      = 2  { I }                                          ' programming / debug
      TX1      = 3  { O }
      _clkfreq = CLK_FREQ
      Temp = $01
     IBUS_MEAS_TYPE_TEM =            $01 ' Temperature
     IBUS_TYPE_MotorRPM =            $02 ' Motor RPM
     IBUS_MEAS_TYPE_EXTV =           $03 ' External voltage
     IBUS_MEAS_TYPE_CELL =           $04 ' Avg cell voltage
     IBUS_MEAS_TYPE_BAT_CURR =       $05 ' Battery current
     IBUS_MEAS_TYPE_FUEL =           $06 ' Remaining battery percentage
     IBUS_MEAS_TYPE_RPM =            $07 ' Throttle value / battery capacity
     IBUS_MEAS_TYPE_CMP_HEAD =       $08 ' Heading
     IBUS_MEAS_TYPE_CLIMB_RATE =     $09 ' Climb rate
     IBUS_MEAS_TYPE_COG =            $0a ' Course over ground
     IBUS_MEAS_TYPE_GPS_STATUS =     $0b ' GPS status (2 values)
     IBUS_MEAS_TYPE_ACC_X =          $0c ' Acc X
     IBUS_MEAS_TYPE_ACC_Y =          $0d ' Acc Y
     IBUS_MEAS_TYPE_ACC_Z =          $0e ' Acc Z
     IBUS_MEAS_TYPE_ROLL =           $0f ' Roll
     IBUS_MEAS_TYPE_PITCH =          $10 ' Pitch
     IBUS_MEAS_TYPE_YAW =            $11 ' Yaw
     IBUS_MEAS_TYPE_VERTICAL_SPEED = $12 ' Vertical speed
     IBUS_MEAS_TYPE_GROUND_SPEED =   $13 ' Speed m/s
     IBUS_MEAS_TYPE_GPS_DIST =       $14 ' Distance from home
     IBUS_MEAS_TYPE_ARMED =          $15 ' Armed / unarmed
     IBUS_MEAS_TYPE_FLIGHT_MODE =    $16 ' Flight mode
     IBUS_MEAS_TYPE_PRES =           $41 ' Pressure
     IBUS_MEAS_TYPE_SPE =            $7e ' Speed km/h
    
    
    OBJ
        telem : "jm_fullduplexserial"                                  ' * serial IO for terminal
    
    var   byte packet[256]
    '      byte txpacket[256]
          byte sensor[60]
    {
    Sensor Memory Map
    Address,Type,Value1,Value2
    
    }
    PUB main() | i
          telem.start(RX1, TX1, %0100, 115_200)
    
          sensor[0] := 1    ' Address
          sensor[1] := IBUS_MEAS_TYPE_TEM    ' Type
          sensor[2] := $9A  ' Telemetry LSByte
          sensor[3] := $02  ' Telemetry MSByte
    
          sensor[4] := 2    ' Address
          sensor[5] := IBUS_MEAS_TYPE_EXTV    ' Type
          sensor[6] := $9A  ' Telemetry LSByte
          sensor[7] := $04  ' Telemetry MSByte
    
          sensor[8] := 3    ' Address
          sensor[9] := IBUS_TYPE_MotorRPM    ' Type
          sensor[10] := $9A  ' Telemetry LSByte
          sensor[11] := $04  ' Telemetry MSByte
    
          repeat
            startpacket()
    PUB startpacket()
      packet[0] := 0
      repeat until packet[0] == $04 ||  packet[0] == $06
        if packet[0] <> $00
          debug("**** Unsyncronized Packet ****")
        packet[0] := telem.rx()
    
      packet[1] := telem.rx()
      packet[2] := telem.rx()
      packet[3] := telem.rx()
      if packet[1] >= $A0 && packet[1] <= $B0
        sensortelemetry()
        return
      if packet[1] >= $80 && packet[1] <= $90
        sensorexists()
        return
      if packet[1] >= $90 && packet[1] <= $A0
        sensortype()
        return
      debug("**** Unknown Packet ****")
      return
    PUB sensorexists() | i
      repeat i from 0 to 14
        if sensor[i*4] == packet[1]-$80 ' Do we have a sensor at requested address ?
          waitms(1)                     ' Repeat it back if we do
          telem.tx(packet[0])
          telem.tx(packet[1])
          telem.tx(packet[2])
          telem.tx(packet[3])
          repeat 4                       ' Update the receive buffer to remove our echo
            telem.rx()
          debug("Sensor found at address ", udec_(packet[1]-$80))
          return
    
      debug("Sensor not found at address ", uhex_(packet[1]))
      return
    PUB sensortype() | i
      repeat i from 0 to 14
        if sensor[i*4] == packet[1]-$90 ' Do we have a sensor at requested address ?
          waitms(1)
          packet[0] := $06                     ' Construct the sensor type packet and send it
          packet[2] := sensor[i*4+1]    ' Set Sensor Type
          packet[3] := $02              ' Set number of bytes in telemetry. All sensors are two bytes?
          packet[4] := checksum(4)
          packet[5] := $FF
    
          repeat i from 0 to 5
    '        debug("Sensor Type", uhex_(packet[i]))
            telem.tx(packet[i])
    '      abort
          repeat 6
            telem.rx()                   ' Update the receive buffer to remove our echo
          debug("Sensor found of type ", udec_(packet[2]))
    
      return
    PUB sensortelemetry() | i
      repeat i from 0 to 14
        if sensor[i*4] == packet[1]-$A0 ' Do we have a sensor at requested address ?
          waitms(1)
          packet[0] := $06              ' Construct the sensor type packet and send it
          packet[2] := sensor[i*4+2]    ' Set Sensor LSByte
          packet[3] := sensor[i*4+3]    ' Set Sensor MSByte
          packet[4] := checksum(4)
          packet[5] := $FE
    
          repeat i from 0 to 5
    '        debug("Sensor Data", uhex_(packet[i]))
            telem.tx(packet[i])
    
          repeat 6
            telem.rx()                   ' Update the receive buffer to remove our echo
          debug("Sensor Data sent for Address ", udec_(packet[1]-$A0))
    
      return
    PUB checksum(bytecount) : r | i
        r := 0
        repeat i from 0 to bytecount-1
          r := r + packet[i]
        r := r & $FF
        r := $FF - r
        return r
    
  • Neat stuff. Are you connecting the TX and RX pins together? This makes sense of using open-drain TX mode and dealing with one wire.

    I'm going to liberate some of you ideas and see if I can create an object with a PASM2 cog that lets one specify the number of sensors in use, their type, and the ability to load a value from the parent. The background cog would simply listen for and process the requests (I may be trying to over-simplify). I started scribbling some inline PASM while having coffee.

    con
    
      RX_MODE = P_ASYNC_RX
      TX_MODE = P_ASYNC_TX | P_OE | P_HIGH_FLOAT
    
    
    pub get_query(sio) : packet 
    
      org
                            wrpin     #0, sio                       ' tristate mode
    
    wquiet                  mov       pr0, #140                     ' wait for idle ~1.5 byte periods
    .wq1                    waitx     #US_001                    
                            testp     sio                   wc      ' check state of rx pin
            if_nc           jmp       #wquiet                       ' if activity, restart wait
                            djnz      pr0, #.wq1                 
    
                            dirl      sio                           ' reset smart pin
                            wrpin     ##RX_MODE, sio                ' set rx uart
                            dirh      sio                           ' activate
    
    pkt_0                   call      #rx_byte
                            cmp       pr0, #$04             wcz     ' verify 1st byte is $04
            if_ne           jmp       #pkt_0
                            or        packet, pr0
    
    pkt_1                   call      #rx_byte
                            shl       pr0, #8
                            or        packet, pr0 
    
    pkt_2                   call      #rx_byte
                            shl       pr0, #16
                            or        packet, pr0 
    
    pkt_3                   call      #rx_byte
                            shl       pr0, #24
                            or        packet, pr0 
    
                            jmp       #done
    
    
    rx_byte                 testp     sio                   wc      ' anything waiting?
            if_nc           jmp       #rx_byte
                            rdpin     pr0, sio                      ' read new byte
            _ret_           shr       pr0, #24                      ' align lsb
    
    done
      end
    
    
    pub ack_sensor(sio, packet) 
    
      org
                            dirl      sio                           ' reset smart pin
                            wrpin     ##TX_MODE, sio                ' set tx uart
                            dirh      sio                           ' activate
    
                            mov       pr0, #4
    get_byte                call      #tx_byte
                            shr       packet, #8
                            djnz      pr0, #get_byte
    
                            jmp       #done
    
    
    tx_byte                 rdpin     pr7, sio              wc      ' check busy flag
            if_c            jmp       #tx_byte                      '  wait if busy
                            wypin     packet, sio                   ' load into sp uart
                            ret  
    done
      end
    
  • ke4pjwke4pjw Posts: 1,079
    edited 2023-04-06 18:33

    Thanks Jon. Yes, I am connecting both pins together. Something to keep in mind, this plugs into the sensor port, not the servo port which your monitor code above plugs into. So a total of three pins would be needed, unless we can get a 1 wire type serial object going. Not sure if that can be easily done.

    I will try and build a better document for describing the behavior that I see from the receiver. Something of note, any response from the P2 must be less than 8ms, otherwise the receiver spits out another interrogatory packet. Also, I found that I needed to add a delay in responding, because when I turned off debugging, the receiver didn't understand what the P2 was sending. I set a 1ms wait for turnaround.

    Many of the sensors listed above do not display properly in the receiver. I suspect that many of these sensors do display properly with the aftermarket firmware loaded on the transmitter. I will try and flash the firmware this evening to see if that is true.

    BTW, I can't tell you how tickled I was to use your object to turn the lights on and off on the P2 Edge using the switches on the transmitter.

            if i == 5
              if ibus.read(i) == 2000
                PINL(LED1)
              else
                PINH(LED1)
            if i == 6
              if ibus.read(i) == 2000
                PINL(LED2)
              else
                PINH(LED2)
    
    

    I think I am going to have a P2 Edge take flight to control some WS2811 LEDs for my plane. I can even strobe them once as second to full white to make my Super Cub look like the real thing!

  • JonnyMacJonnyMac Posts: 8,926
    edited 2023-04-06 23:39

    I connected my LA to the servo and sensor ports (silly me, didn't do a screen cap); the transactions on the sensor line take place between the servo packets. This suggests that a single cog could handle servo and sensor data with two smart pins: one to RX the servo data, the other that is bi-directional for the sensor port.

    Not sure if that can be easily done.

    It is. I do it with in my Dynamixel object. Assuming the smart pin has been setup with baud and bit count data, all you have to do is turn off the dir bit to reset it, use wrpin to set the mode (TX or RX), and then drive the pin to activate it. Easy-peasy

    BTW, I can't tell you how tickled I was to use your object to turn the lights on and off on the P2 Edge using the switches on the transmitter.

    That makes me grin! -- so much so that I added controlling P56 and P57 LEDs from the demo using SwC and SwD on the FS-i6X transmitter.

    My friend, Rick, has been using RC components to control props in the movie business for years. A long time ago I wrote a P1 object that would measure the servo pulses out of a standard RC receiver; he would convert those values into prop control commands. This image is of snowboarders covered with about 2K WS2812s that are controlled from RC transmitters (one per snowboarder). The snowboarders wore very heavy backpack with the batteries, RC receiver, and a P1 board Rick made.


    This was for a Sony TV commercial.

  • It appears there are 2 and 4 byte sensors. https://github.com/qba667/FlySkyI6/wiki/Telemetry
    I think I want to try this firmware on my i6. It has everything I want regarding GPS data being displayed.

  • I'm going to take a crack at a telemetry cog in the morning. I was hoping I could get an input pin to echo to an output pin (w/o software) which would have been helpful watching things on a logic analyzer, but I'll manage with a breadboard. Should be fun.

  • I spun up a cog with my code from your object. They both worked together, LOL. But I think we are using 4 cogs at that point.

    obj
    ' main                                                          ' * master Spin cog
      ibus : "jm_ibus_rx"                                           ' * ibus receiver
      term : "jm_fullduplexserial"                                  ' * serial IO for terminal
      ansi : "jm_ansi"                                              '   ANSI terminal control sequences
      telemetry : "telemtest"
    ' * uses cog when loaded
    
    {etc}
    
    pub main() | t1, t2, i
    
      setup()
      COGSPIN(NEWCOG,telemetry.main(), @telemetrystack)
      wait_for_terminal(true)
      waitms(100)
    {etc}
    
    

    I flashed my transmitter with the most recent factory firmware and wasn't too happy with the sensor displays. The FlyPlus firmware looks like it has everything I want n regards to telemetry. I will try it this evening and report back how well it works.

    https://github.com/qba667/FlySkyI6/releases/tag/1.7.6

  • FlyPlus is a no-go on the FS-i6X. It will work on the original FS-i6, but not the FS-i6X. Different MCU. The other alternative is OpenTX, but I don't like the UI. https://www.rcgroups.com/forums/showthread.php?3916435-FlySky-I6X-port-of-OpenTX

    I will just keep piddling with the latest stock firmware. OpenTX looks like it has oddball problems. (Latency, things not working after updates, etc)

  • I just received my FS-CAT01 altitude sensor. After popping it on the analyzer, I have determined that the 1st octet actually contains the number of bytes that will be in the packet.

    I will post more information about that sensor when I determine how that data is encoded. My most recent working code is below, with commented warts and all.

    First Octet
    Number of bytes within the packet.

    Second Octet
    Interrogatory & Response: $80+x Sensor Exists at address X
    Interrogatory & Response: $90+x Sensor Type at address X
    Interrogatory & Response: $A0+x Sensor Data at address X

    Third Octet
    Interrogatory & Response Sensor Exists: 2nd to last Octet
    Interrogatory Sensor Type: 2nd to last Octet
    Interrogatory Sensor Data: 2nd to last Octet
    Response Sensor Type: Sensor Type code. See #defines below.
    Response Sensor Data: LSByte of Sensor Data

    Fourth Octet
    Interrogatory & Response Sensor Exists: Last Octet
    Interrogatory Sensor Type: Last Octet
    Interrogatory Sensor Data: Last Octet
    Response Sensor Type: Number of bytes returned for Sensor Data
    Response Sensor Data: MSByte of Sensor Data

    2nd to last Octet
    Interrogatory & Response: Chucksum padding to make mod 256 $FF

    Last Octet
    All Interrogatory & Response except Response Sensor Data: Mod 256 Checksum: $FF
    Response Sensor Data: Mod 256 Checksum: $FF - 1 ($FE)

    {Proof of Concept FlySky Telemetry}
    
    
    CON
      CLK_FREQ = 200_000_000                                        ' system freq as a constant
      RX1      = 2  { I }                                          ' programming / debug
      TX1      = 3  { O }
      _clkfreq = CLK_FREQ
      Temp = $01
     IBUS_MEAS_TYPE_TEM =            $01 ' Temperature
     IBUS_TYPE_MotorRPM =            $02 ' Motor RPM
     IBUS_MEAS_TYPE_EXTV =           $03 ' External voltage
     IBUS_MEAS_TYPE_CELL =           $04 ' Avg cell voltage
     IBUS_MEAS_TYPE_BAT_CURR =       $05 ' Battery current
     IBUS_MEAS_TYPE_FUEL =           $06 ' Remaining battery percentage
     IBUS_MEAS_TYPE_RPM =            $07 ' Throttle value / battery capacity
     IBUS_MEAS_TYPE_CMP_HEAD =       $08 ' Heading
     IBUS_MEAS_TYPE_CLIMB_RATE =     $09 ' Climb rate
     IBUS_MEAS_TYPE_COG =            $0a ' Course over ground
     IBUS_MEAS_TYPE_GPS_STATUS =     $0b ' GPS status (2 values)
     IBUS_MEAS_TYPE_ACC_X =          $0c ' Acc X
     IBUS_MEAS_TYPE_ACC_Y =          $0d ' Acc Y
     IBUS_MEAS_TYPE_ACC_Z =          $0e ' Acc Z
     IBUS_MEAS_TYPE_ROLL =           $0f ' Roll
     IBUS_MEAS_TYPE_PITCH =          $10 ' Pitch
     IBUS_MEAS_TYPE_YAW =            $11 ' Yaw
     IBUS_MEAS_TYPE_VERTICAL_SPEED = $12 ' Vertical speed
     IBUS_MEAS_TYPE_GROUND_SPEED =   $13 ' Speed m/s
     IBUS_MEAS_TYPE_GPS_DIST =       $14 ' Distance from home
     IBUS_MEAS_TYPE_ARMED =          $15 ' Armed / unarmed
     IBUS_MEAS_TYPE_FLIGHT_MODE =    $16 ' Flight mode
     IBUS_MEAS_TYPE_PRES =           $41 ' Pressure
     IBUS_MEAS_TYPE_SPE =            $7e ' Speed km/h
     IBUS_MEAS_TYPE_ALT_FLYSKY =     $f9
    
    OBJ
        telem : "jm_fullduplexserial"                                  ' * serial IO for terminal
    
    var   byte packet[256]
    '      byte txpacket[256]
          byte sensor[60]
    {
    Sensor Memory Map
    Address,Type,Value1,Value2
    
    }
    PUB main() | i
          telem.start(RX1, TX1, %0100, 115_200)
    
          sensor[0] := 1    ' Address
          sensor[1] := IBUS_MEAS_TYPE_PRES    ' Type
          sensor[2] := $04 'number of bytes
          sensor[3] := $76'%01010111'%01010111 '%01010111' 176'$b1 ' Telemetry LSByte
          sensor[4] := $85'%11111111 ' $0 '%11111111 '166'$a5  ' Telemetry MSByte
          sensor[5] := $41
          sensor[6] := $14
    
    
     {
    
          sensor[0] := 3    ' Address
          sensor[1] := IBUS_MEAS_TYPE_TEM    ' Type
          sensor[2] := $02 'number of bytes
          sensor[3] := $9A  ' Telemetry LSByte
          sensor[4] := $01  ' Telemetry MSByte
          sensor[5] := $00
          sensor[6] := $00
    
          sensor[7] := 2    ' Address
          sensor[8] := IBUS_MEAS_TYPE_EXTV    ' Type
          sensor[9] := $02 'number of bytes
          sensor[10] := $9A  ' Telemetry LSByte
          sensor[11] := $04  ' Telemetry MSByte
          sensor[12] := $00
          sensor[13] := $00
    
          sensor[14] := 1    ' Address
          sensor[15] := IBUS_MEAS_TYPE_PRES    ' Type
          sensor[16] := $06 'number of bytes
          sensor[17] := $76'%01010111'%01010111 '%01010111' 176'$b1 ' Telemetry LSByte
          sensor[18] := $85'%11111111 ' $0 '%11111111 '166'$a5  ' Telemetry MSByte
          sensor[19] := $41
          sensor[20] := $14
     }{
          sensor[21] := 4    ' Address
          sensor[22] := IBUS_MEAS_TYPE_ALT_FLYSKY  ' Type    (Sats?
          sensor[23] := $02 'number of bytes
          sensor[24] := $12  ' Telemetry LSByte
          sensor[25] := $01  ' Telemetry MSByte
          sensor[26] := $00
          sensor[27] := $00
     }
          repeat
            startpacket()
    PUB startpacket()
      packet[0] := 0
      repeat until packet[0] == $04 ||  packet[0] == $06
        if packet[0] <> $00
          debug("**** Unsyncronized Packet ****")
        packet[0] := telem.rx()
    
      packet[1] := telem.rx()
      packet[2] := telem.rx()
      packet[3] := telem.rx()
      if packet[1] >= $A0 && packet[1] <= $B0
        sensortelemetry()
        return
      if packet[1] >= $80 && packet[1] <= $90
        sensorexists()
        return
      if packet[1] >= $90 && packet[1] <= $A0
        sensortype()
        return
      debug("**** Unknown Packet ****")
      return
    PUB sensorexists() | i
      repeat i from 0 to 14
        if sensor[i*7] == packet[1]-$80 ' Do we have a sensor at requested address ?
          waitms(1)                     ' Repeat it back if we do
          telem.tx(packet[0])
          telem.tx(packet[1])
          telem.tx(packet[2])
          telem.tx(packet[3])
          repeat 4                       ' Update the receive buffer to remove our echo
            telem.rx()
          debug("Sensor found at address ", udec_(packet[1]-$80))
          return
    
      debug("Sensor not found at address ", uhex_(packet[1]))
      return
    PUB sensortype() | i
      repeat i from 0 to 14
        if sensor[i*7] == packet[1]-$90 ' Do we have a sensor at requested address ?
          waitms(1)
          packet[0] := $06                      ' Construct the sensor type packet and send it
          packet[2] := sensor[i*7+1]    ' Set Sensor Type
          packet[3] := sensor[i*7+2]              ' Set number of bytes in telemetry. All sensors are two bytes?
          packet[4] := checksum(4)
          packet[5] := $FF
    
          repeat i from 0 to 5
    '        debug("Sensor Type", uhex_(packet[i]))
            telem.tx(packet[i])
    '      abort
          repeat 6
            telem.rx()                   ' Update the receive buffer to remove our echo
          debug("Sensor found of type ", uhex_(packet[2]))
    
      return
    PUB sensortelemetry() | i
      repeat i from 0 to 14
        if sensor[i*7] == packet[1]-$A0 ' Do we have a sensor at requested address ?
          waitms(1)
          packet[0] := $04 + sensor[i*7+2]              ' Construct the sensor type packet and send it
          packet[2] := sensor[i*7+3]    ' Set Sensor LSByte
          packet[3] := sensor[i*7+4]    ' Set Sensor MSByte
          if sensor[i*7+2] == $02
            packet[4] := checksum(4)
            packet[5] := $FE
          else
            packet[4] := sensor[i*7+5]    ' Set Sensor LSByte
            packet[5] := sensor[i*7+6]    ' Set Sensor MSByte
            packet[6] := checksum(6)
            packet[7] := $FE
    
          repeat i from 0 to packet[0]-1
    '        debug("Sensor Data", uhex_(packet[i]))
            telem.tx(packet[i])
    
          repeat 8
            telem.rx()                   ' Update the receive buffer to remove our echo
     '     debug("Sensor Data sent for Address ", udec_(packet[1]-$A0))
    
      return
    PUB checksum(bytecount) : r | i
        r := 0
        repeat i from 0 to bytecount-1
          r := r + packet[i]
        r := r & $FF
        r := $FF - r
        return r
    
    
  • Quick update on progress. I can't figure out the encoding scheme used for the FS-CAT01 pressure sensor. This evening, I am going to open the sensor to determine which device they are using, and crossing my fingers that they are just passing the data through. If it were passing temprature data too, that would a bonus.

    Also, I determined that my serial output was a bit off. It was then that I saw the thread about the 19.2Mhz Edges. Sure enough, I have one of those. Setting _xtlfreq = 19_200_000 resolved the problem.

  • I had a few moments this evening to look at the FS-CAT01 data. I determined that the pressure value is a 20bit number that represents Pa. I have not determined what the 12 most significant bits represent. In the example above, if you change the values to...

          sensor[3] := $76
          sensor[4] := $85
          sensor[5] := $01
          sensor[6] := $00
    

    The display should read 997.0 hPa, as the value is 99,702 decimal.

    The actual pressure sensor device in the FS-CAT01 is a Bosh BMP180. There is an IC next to it with no markings. There is a NXP LVC245A on the other side, which I think is used as a buffer for the serial data.

  • JonnyMacJonnyMac Posts: 8,926
    edited 2023-04-16 21:32

    I'm still not caught up to you on this, but I did have a little time today, so I thought I'd create a tool to look at things. First, I made a splitter cable so that I could tap the serial data between the receiver and the first sensor and feed it into the P2. That said, I also wanted to see it on a logic analyzer at the same time, so I reached out and got some help in this thread.

    https://forums.parallax.com/discussion/175285/make-pin-output-and-follow-input-of-neighbor

    Note that for this to work, the serial inputs from the receiver have to be on even pins, and the outputs to the LA are the next odd pin. This is what enables the echo(es):

      ' echo serial inputs for LA
    
      if (USE_ANALYZER == YES)
        wrpin(IBUS_TX1,  P_LOGIC_B_FB)                              ' echo servo stream
        pinl(IBUS_TX1)
        if (USE_SENSORS == YES)
          wrpin(IBUS_TX2,  P_LOGIC_B_FB)                            ' echo sensor stream 
          pinl(IBUS_TX2)
    

    The attached program is a modification of my original demo that lets me look at the servo position stream (same as before), and the sensor stream. Looking at the LA, it seems like doing a single-cog ibus servos + sensors object would be fairly straightforward. The sensor queries/responses always fall between servo packets, and there's only ever one query/response per servo packet.

    Anyway, I have to do work-work for the rest of the day, but maybe this test program will help in some way. I'll get back to it next weekend.

Sign In or Register to comment.