SBUS Library

Status
Not open for further replies.

brtaylor

Well-known member
Hello everyone, I have an introduction of me in a recent post.

I'd like to announce availability of an SBUS library. SBUS is a protocol for RC receivers to send commands to servos. Unlike PWM, SBUS uses a bus architecture where a single signal line can be connected up to 16 servos with each receiving a unique command. SBUS capable servos are required; each can be programmed with a unique address (Channel 0 - 15) using an SBUS servo programmer. Advantages of SBUS include the reduction of wiring clutter and ease of parsing commands from RC receivers.

I'd like to give big shout outs to Uwe Gartmann on ARMmbed and Paul Stroffregen on this PJRC thread. I learned a lot about SBUS parsing and sending a serial 8E2 SBUS packet based on those resources.

This library enables Teensy devices to read and parse SBUS packets from an SBUS capable RC receiver. Data can be read as counts or calibrated to +/- 1.0 values. Additionally, SBUS packets can be created and sent to SBUS capable servos given an array of position commands. I tested the code using the X8R and X4R SBUS receivers and the D25MA SBUS servo. I hope the library is useful and I would love feedback, especially on additional receivers and servos!

Brian
 
Hi Brian

nice other people are still working on this SBUS stuff. Fortunately, the problem with T3.2 and and 8E2 will with T3.5/6 be a problem going away.

I had short look at your code. The problem I see is thanks to the performance of the T3.2 not that much of a problem, because there is still plenty of processing power left - but : during the transmission of an SBUS packet the teensy is absorbed by this task and not available for other inbterrupt driven tasks like handling an IMU.

Maybe you have a look at what is already published in the thread you mentionned. The solution with an interrupt-driven approach frees a considerable amount of processor time, takes only little time for each byte to be sent and works on Futaba and Frsky servos.

By the way - do you have information about the SBUS2 protocol ? I know it is around, but its hard to have a hand on it.

regards Andy
 
Hi Andy, thanks! Good idea and I've been working on updating the library to use interrupts for the serial write. Going on a short vacation, but hope to have something working later next week.

My understanding with SBUS2 is that it's used for sending telemetry. I am pretty excited to try this with the FrSky products, especially with OpenTX on my Taranis transmitter. From what I understand, at least for FrSky products, the telemetry downlink is pretty low bandwidth, but I've still been considering it to send health and status bits from the aircraft to the Taranis transmitter to issue a warning to the pilot with most of the uplink / downlink occurring over 900 MHz. I'll be sure to post as I have a chance to dig into it further...

Brian
 
Hi Andi,

Thanks! I did have a look at the code. The issue I was having was that IntervalTimer needs to be a global, as I learned here:
https://forum.pjrc.com/threads/37021-Using-IntervalTimer-in-Arduino-Library

I got it working, cleaned up, and tested this morning. My SBUS library has been updated and is now using interrupts rather than elapsedMicros to send the SBUS packet on the Teensy 3.1/3.2.
https://github.com/bolderflight/SBUS

I'll post a reply to your pressure transducer question in a moment on the AMS5812 / AMS5915 thread:
https://forum.pjrc.com/threads/36102-Introduction-and-AMSYS-AMS-5812-and-AMS-5915-Libraries

Best!
Brian
 
Hi Brian

the arrival of my first T3.6 was a good reason to end a loooong break. I had a look at your latest SBUS-coding and it worked right away. Thanks for the work.

However - if I pull the SBUS signal wire from the X8R and reconnect, the program quite often hangs and is unable to resync.

Well, this affects operation only if the wiring is not 100%, but it also shows some weakness in the sync coding. The code from mbed has the same problem and I had it as well last spring. On T3.2 I found a solution that seemed to work reliable, but did not work on T3.6. I found the reason and resync looks quite reliable and fast.

Please let me know if you can reproduce the problem as feedback If you are interested in my resync-solution let me know.

regards Andi
 
Hi Brian

the arrival of my first T3.6 was a good reason to end a loooong break. I had a look at your latest SBUS-coding and it worked right away. Thanks for the work.

However - if I pull the SBUS signal wire from the X8R and reconnect, the program quite often hangs and is unable to resync.

Well, this affects operation only if the wiring is not 100%, but it also shows some weakness in the sync coding. The code from mbed has the same problem and I had it as well last spring. On T3.2 I found a solution that seemed to work reliable, but did not work on T3.6. I found the reason and resync looks quite reliable and fast.

Please let me know if you can reproduce the problem as feedback If you are interested in my resync-solution let me know.

regards Andi

Hi Andi,

It took a few tries, but I was able to reproduce the problem. Yes, I'd be interested in seeing your resync solution.

Thanks!
Brian
 
Hi Brian

here my sync routine :

//------------------------------------------------------------
int Teensy_SBUS::trySBUSSync(){

int i;

elapsedMillis waiting; // "waiting" starts at zero


#ifdef DEBUG_SBUSTRYSYNC
Serial.println("TrySync");
#endif


trySync++; // for statistics only, member of class, to be stored on SD card for reliability check

for (i=1; i<25; i++) {
sbusData = 0;
}

while(port1.available()) // clear any data in buffer, port1.clear() worked on T3.2, on T3.6 probably not
port1.read();

waiting = 0;

while((port1.available() <25)&&(waiting<50)){ // let buffer fill, but stop after 50ms
delay(1);
}

if (port1.available() <25){ // looks like no packet came in
memcpy(channels,failsafeChannels,36); // Teensy must failsafe now on its own
SBUS_status=SBUS_NO_PACKET; // not SBUS convention, used in this program
}
else{
SBUS_status=readSBUSpacket(); // integrity is checked in this routine as well

}


#ifdef DEBUG_SBUSTRYSYNC
printSBUSBytesHEX();
printChannelData();
#endif


return SBUS_status;
}
//------------------------------------------------------------

Do you have knowledge when the frame lost bit is actually activated on X8R ? Is in this context one frame one packet ? How many lost frames does it take for the failsafe ?

regards

Andi
 
Hi Brian

here my sync routine :

//------------------------------------------------------------
int Teensy_SBUS::trySBUSSync(){

int i;

elapsedMillis waiting; // "waiting" starts at zero


#ifdef DEBUG_SBUSTRYSYNC
Serial.println("TrySync");
#endif


trySync++; // for statistics only, member of class, to be stored on SD card for reliability check

for (i=1; i<25; i++) {
sbusData = 0;
}

while(port1.available()) // clear any data in buffer, port1.clear() worked on T3.2, on T3.6 probably not
port1.read();

waiting = 0;

while((port1.available() <25)&&(waiting<50)){ // let buffer fill, but stop after 50ms
delay(1);
}

if (port1.available() <25){ // looks like no packet came in
memcpy(channels,failsafeChannels,36); // Teensy must failsafe now on its own
SBUS_status=SBUS_NO_PACKET; // not SBUS convention, used in this program
}
else{
SBUS_status=readSBUSpacket(); // integrity is checked in this routine as well

}


#ifdef DEBUG_SBUSTRYSYNC
printSBUSBytesHEX();
printChannelData();
#endif


return SBUS_status;
}
//------------------------------------------------------------

Do you have knowledge when the frame lost bit is actually activated on X8R ? Is in this context one frame one packet ? How many lost frames does it take for the failsafe ?

regards

Andi


Hi Andi,

Can you try this development branch and let me know if it fixes the sync issues that you're having?
https://github.com/bolderflight/SBUS/tree/updatedSync

Thanks much!
Brian
 
Do you have knowledge when the frame lost bit is actually activated on X8R ? Is in this context one frame one packet ? How many lost frames does it take for the failsafe ?

I had a bug in the code for the failsafe and lost frames, which I noticed while looking into making the sync more robust. Fixes have been pushed to the master branch and the development branch I posted previously testing out the more robust sync. Additionally, I did some testing on my X8R and X4R. With both of these receivers, as soon as they lose connection to the transmitter the lost frames starts counting. 102 frames, or a little more than 900 milliseconds later, the failsafe is activated.

Best,
Brian
 
I had a bug in the code for the failsafe and lost frames, which I noticed while looking into making the sync more robust. Fixes have been pushed to the master branch and the development branch I posted previously testing out the more robust sync. Additionally, I did some testing on my X8R and X4R. With both of these receivers,f as soon as they lose connection to the transmitter the lost frames starts counting. 102 frames, or a little more than 900 milliseconds later, the failsafe is activated.

Best,
Brian

Hi Brian

the updated code looks fine. Thanks for the information above.
Frame==packet, right ? To sum it up : receiver looses connection with TX -> packet/frame status changes from SBUS_SIGNAL_OK to SBUS_SIGNAL_LOST for around 100 packets/frames -> then status changes to SBUS_SIGNAL_FAILSAFE. I hope i got it right.

regards Andi
 
Hi Brian

I played around with the SBUS_SIGNAL_LOST a little bit. I started counting lost and received packets and transmit now the ratio via SPort to the Taranis-TX. At RSSI around 20-25 30-40% of the packages are still received. RSSI is fine, but what counts is the amount of packages received. SBUS-servos seem to be quite forgiving. I once made a test and if I remember well they still work at refresh rates below 1Hz.
In the next few days I will make a test and drive a few kilometers. Earlier tests gave around 3000m range, but at that time i could not receive information about % of packets received.

regards Andi
 
Hi Brian

the updated code looks fine. Thanks for the information above.
Frame==packet, right ? To sum it up : receiver looses connection with TX -> packet/frame status changes from SBUS_SIGNAL_OK to SBUS_SIGNAL_LOST for around 100 packets/frames -> then status changes to SBUS_SIGNAL_FAILSAFE. I hope i got it right.

regards Andi

Yes, that's all correct! Logically it makes sense too, even with my RX and TX close to each other I'll occasionally get a lost packet, which is expected by the system. So it makes sense that it waits for a short bit to re-establish the connection before going into failsafe mode.

Brian
 
Hello brian,
thanks for the sbus library. i am having problem in scaling the values. i can see that, when transmitter is sending 982 (minimum value on pixhawk), the code receives it as 172 and when it sends 2006,it receives as 1811. this becomes nonlinear, i dont understand how to recover and map the values for a servo.please help me on this.Thank u
 
Hello brian,
thanks for the sbus library. i am having problem in scaling the values. i can see that, when transmitter is sending 982 (minimum value on pixhawk), the code receives it as 172 and when it sends 2006,it receives as 1811. this becomes nonlinear, i dont understand how to recover and map the values for a servo.please help me on this.Thank u

Are you trying to use this library to receive SBUS signals from a receiver, send SBUS commands to a servo or both?

If receiving SBUS signals from a receiver, which receiver are you using? What is the minimum and maximum values using the read function, rather than the readCal function?

Thanks,
Brian
 
Are you trying to use this library to receive SBUS signals from a receiver, send SBUS commands to a servo or both?

If receiving SBUS signals from a receiver, which receiver are you using? What is the minimum and maximum values using the read function, rather than the readCal function?

Thanks,
Brian

Hi,brian
thanks for your kind reply. immediately after making this post, i was able to solve the issue, by mapping the outputs. receiver outputs was min 982, max 2006. so scaled it to that range from (172 to 1811)..Thank u
 
This library has been very useful for me while setting up a joystick emulator with a Teensy LC and a FrSky XM receiver so I can use my Taranis transmitter as a Windows USB game controller.

I'm mapping channels 1 through 6 to the joystick analog axes, rotations, and sliders successfully.

However, I've run in to a problem. I'm also trying to map the remaining 10 channels (7 through 16) to act like joystick buttons and set a button to be pressed whenever the input from the SBUS channel is over a threshold. I seem to get this to work for three to four buttons at a time, but whenever I try to add code for more buttons/channel readings it seems I no longer get the SBUS library read function to return true any longer. Serial.print statements still produce output in the loop function before the read call, but nothing after ever is executed and the joystick basically never updates as a result.

I'm new to C++ and Arduinos so perhaps I'm just overlooking something obvious in my code?

Code:
#include <SBUS.h>

#define SBUS_MIN 172 // minimum value SBUS input
#define SBUS_MAX 1811 // maximum value SBUS input
#define ANALOG_MIN 0 // minimum value analog joystick output
#define ANALOG_MAX 1023 // maximum value analog joystick output
#define SBUS_ON 1600; // minimum value SBUS input to determine if button should be considered pressed

SBUS sbus(Serial1);

uint16_t channels[16];
uint8_t failSafe;
uint16_t lostFrames = 0;

void setup() {
  sbus.begin();
}

void loop() {
  if (!sbus.read(&channels[0], &failSafe, &lostFrames)) return;
 
  Joystick.X(mapSbusToAnalog(channels[0]));
  Joystick.Y(mapSbusToAnalog(channels[1]));
  Joystick.Z(mapSbusToAnalog(channels[2]));
  Joystick.Zrotate(mapSbusToAnalog(channels[3]));
  Joystick.sliderLeft(mapSbusToAnalog(channels[4]));
  Joystick.sliderRight(mapSbusToAnalog(channels[5]));

  Joystick.button(1, mapSbusToDigital(channels[6]));
  Joystick.button(2, mapSbusToDigital(channels[7]));
  Joystick.button(3, mapSbusToDigital(channels[8]));
//  Joystick.button(4, mapSbusToDigital(channels[9]));
//  Joystick.button(5, mapSbusToDigital(channels[10]));
//  Joystick.button(6, mapSbusToDigital(channels[11]));
//  Joystick.button(7, mapSbusToDigital(channels[12]));
//  Joystick.button(8, mapSbusToDigital(channels[13]));
//  Joystick.button(9, mapSbusToDigital(channels[14]));
//  Joystick.button(10, mapSbusToDigital(channels[15]));
}

long mapSbusToAnalog(uint16_t& value) {
  return (value - SBUS_MIN) * (ANALOG_MAX - ANALOG_MIN) / (SBUS_MAX - SBUS_MIN) + ANALOG_MIN;
}

boolean mapSbusToDigital(uint16_t& value) {
  return value >= SBUS_ON;
}

Whenever I uncomment the lines that set button 4 and/or 5, it seems I never get a SBUS packet read again. Sometimes button 4 seems to not cause this, sometimes it does... I initially tried all of them and that did not work. I slowly enable lines and was testing with Serial.print debug statements to isolate the problem when I found out that I could enable 3 or 4 buttons before the problem appeared again.

Am I overloading the Teensy LC somehow? Did I maybe get a bad Teensy LC? Is this some SBUS nuance I don't understand? Is this the joystick library? Is it my bad coding? I'm not even sure where to start looking further. Any ideas would be very appreciated!
 
Last edited:
Can you post or link the code for the Joystick class? I'm not seeing anything obvious, but I want to try to replicate the issue.

Brian
 
I'm not able to replicate it completely since I'm not familiar with the joystick functionality, but I played some with outputting everything to the screen using Serial.print statements. That return that you have after the if statement is troubling. Can you try the following?

Code:
#include <SBUS.h>

#define SBUS_MIN 172 // minimum value SBUS input
#define SBUS_MAX 1811 // maximum value SBUS input
#define ANALOG_MIN 0 // minimum value analog joystick output
#define ANALOG_MAX 1023 // maximum value analog joystick output
#define SBUS_ON 1600; // minimum value SBUS input to determine if button should be considered pressed

SBUS sbus(Serial1);

uint16_t channels[16];
uint8_t failSafe;
uint16_t lostFrames = 0;

void setup() {
  sbus.begin();
}

void loop() {
  if (sbus.read(&channels[0], &failSafe, &lostFrames)) {
    Joystick.X(mapSbusToAnalog(channels[0]));
    Joystick.Y(mapSbusToAnalog(channels[1]));
    Joystick.Z(mapSbusToAnalog(channels[2]));
    Joystick.Zrotate(mapSbusToAnalog(channels[3]));
    Joystick.sliderLeft(mapSbusToAnalog(channels[4]));
    Joystick.sliderRight(mapSbusToAnalog(channels[5]));

    Joystick.button(1, mapSbusToDigital(channels[6]));
    Joystick.button(2, mapSbusToDigital(channels[7]));
    Joystick.button(3, mapSbusToDigital(channels[8]));
    Joystick.button(4, mapSbusToDigital(channels[9]));
    Joystick.button(5, mapSbusToDigital(channels[10]));
    Joystick.button(6, mapSbusToDigital(channels[11]));
    Joystick.button(7, mapSbusToDigital(channels[12]));
    Joystick.button(8, mapSbusToDigital(channels[13]));
    Joystick.button(9, mapSbusToDigital(channels[14]));
    Joystick.button(10, mapSbusToDigital(channels[15]));
  }
}

long mapSbusToAnalog(uint16_t& value) {
  return (value - SBUS_MIN) * (ANALOG_MAX - ANALOG_MIN) / (SBUS_MAX - SBUS_MIN) + ANALOG_MIN;
}

boolean mapSbusToDigital(uint16_t& value) {
  return value >= SBUS_ON;
}
 
Can you try the following?
Same problem exists still. However, you motivated me to revert to basic isolation efforts again and if I remove all the Joystick calls together and just do Serial.print calls with results from SBUS, I can easily read all the values on all channels no problem. It has to be the Joystick code, right?

I've poked around through a few threads of people doing custom joysticks, but they seem to modify the supporting code heavily. I'm going to have to dig deeper into all this I suppose. Thank you for at least looking at all this with me Brian! I sincerely appreciate your library and your attention.
 
Playing with the Joystick class a bit more, I did the following test which works fine.
Code:
int a;
unsigned long toggle;
boolean state;

void setup() {
  a = 0;
  toggle = millis();
  state = false;
}

void loop() {

  if (++a > 1023) a = 0;
  Joystick.X(a);
  Joystick.Y(a);
  Joystick.Z(a);
  Joystick.Zrotate(a);
  Joystick.sliderLeft(a);
  Joystick.sliderRight(a);

  if (millis() - toggle < 2000) return;
  toggle = millis();
  state = !state;
  Joystick.button(1, state);
  Joystick.button(2, state);
  Joystick.button(3, state);
  Joystick.button(4, state);
  Joystick.button(5, state);
  Joystick.button(6, state);
  Joystick.button(7, state);
  Joystick.button(8, state);
  Joystick.button(9, state);
  Joystick.button(10, state);
}

So, it seems to be something about using the Joystick class with the SBUS library that causes the SBUS read to fail.

Could there be some timing issue somewhere?
 
Is there some way I could give this a try here? I don't have any hardware to give me the SBUS data into Serial1.

Hi Paul, the library contains functionality to write SBUS packets as well, which need to be sent every 10ms. If you loopback Serial1 TX to Serial1 RX, this code will get you a good SBUS packet into Serial1.

Code:
#include "SBUS.h"

SBUS x8r(Serial1);

IntervalTimer myTimer;

void setup() {
  Serial.begin(115200);
  while(!Serial){}
  x8r.begin();
  myTimer.begin(SendSbus, 10000);
}

void loop() {
  uint16_t channels[16];
  uint8_t failSafe;
  uint16_t lostFrames = 0;
  if (x8r.read(&channels[0], &failSafe, &lostFrames)) {
    Serial.println("Good Sbus packet received");
  }
}

void SendSbus() {
  uint16_t channels[16];
  x8r.write(&channels[0]);
}

I'll be traveling until Monday of next week, but happy to help debug after that.
 
Status
Not open for further replies.
Back
Top