Futaba SBUS reverse engineered to work with Arduino

The library has been updated to no longer rely on the SerialPort.h library

Nice project. FYI, you can get cheap sbus receivers from Hobby King now. I have been looking for a similar solution. I want to convert the sbus protocol into a ppm signal. Does your project have that ability? If so, any idea of how much lag there would be?

Thanks

Sir
I am very interested by this project.
I use the Arduino 1.0 version.
I'm not an Arduino specialist and so, I doesn't compile This like the reply 4.
I have an error like "expected constructor,destructor,or type conversion before '<' token and the line "SerialPort<0,64,64> port0;" is in Yellow.
I have had file "SerialPort.h" from "SerialPortBeta20120106.zip"
In fact, I have a lot of error :
sketch_jan07b.cpp:1:24: error: SerialPort.h: No such file or directory
sketch_jan07b:4: error: expected constructor, destructor, or type conversion before '<' token
sketch_jan07b.cpp: In function 'void setup()':
sketch_jan07b:22: error: 'port0' was not declared in this scope
sketch_jan07b:22: error: 'SP_2_STOP_BIT' was not declared in this scope
sketch_jan07b:22: error: 'SP_EVEN_PARITY' was not declared in this scope
sketch_jan07b:22: error: 'SP_8_BIT_CHAR' was not declared in this scope
sketch_jan07b.cpp: In function 'void update_servos()':
sketch_jan07b:86: error: 'sbus_data' was not declared in this scope
sketch_jan07b:96: error: 'sbus_data' was not declared in this scope
sketch_jan07b:111: error: 'sbus_data' was not declared in this scope
sketch_jan07b:115: error: 'sbus_data' was not declared in this scope
sketch_jan07b:119: error: 'sbus_data' was not declared in this scope
sketch_jan07b:121: error: 'sbus_data' was not declared in this scope
sketch_jan07b:128: error: 'port0' was not declared in this scope
sketch_jan07b:128: error: 'sbus_data' was not declared in this scope
sketch_jan07b.cpp: In function 'void update_channels()':
sketch_jan07b:145: error: 'sbus_data' was not declared in this scope
sketch_jan07b:160: error: 'sbus_data' was not declared in this scope
sketch_jan07b:167: error: 'sbus_data' was not declared in this scope
sketch_jan07b:175: error: 'sbus_data' was not declared in this scope
sketch_jan07b:178: error: 'sbus_data' was not declared in this scope
sketch_jan07b.cpp: In function 'void feedLine()':
sketch_jan07b:183: error: 'port0' was not declared in this scope
sketch_jan07b:210: error: 'sbus_data' was not declared in this scope
Could you hepl me ?
Best regards
Marc

Fantastic work on this Mike 8)

I have a Futaba 14 channel transmitter (T8FG) with the R6208SB S-BUS receiver.

I would like to use the S-BUS channel to control 4 PPM outputs via the Arduino. Just probably simple switched outputs not servos at all.

One appplication is two create a different output depending on the PMM input range from the transmitter.

Is it possible to use your code for this 4 channel application, or is it designed for just one channel.

Many thanks and great work again.

Steve

Sorry for the slow response guys, I thought I was subscribed to this thread. In the future if someone needs some help with this library PM me if I don't respond fairly quickly.

m_marc0 - I have updated the library to no longer require SerialPort.h. It now uses the standard arduino conventions. The library is currently configured to run off of Serial1 on a mega. To change the edit the FUTABA_SBUS.h line 12

#define SBUS_SIGNAL_OK          0x00
#define SBUS_SIGNAL_LOST        0x01
#define SBUS_SIGNAL_FAILSAFE    0x03
#define BAUDRATE 100000
#define port Serial1  <---- this is the line to change

Tissy -I'm not quite clear on what you are trying to accomplish. You can create a PPM signal based off the different channels, but to what end? If you could describe your project in a little more detial hopefully I can give you a better answer. Also, are you sure you aren't confusing PPM with PWM?

Thanks Mike.

Sorry, you're right, I wasn't very clear.

Basically I have a Futaba R6208SB S-BUS receiver which I am using on a multi-rotor.

I have one of the outputs from this receiver going to an Arduino MiniPro which is programmed to detect the PWM signal and use an IR LED to control a Sony NEX5 camera. This works very will and I can switch the camera modes between video and stills and take manual or automatic interval pictures.

As this is connected to one of the main outputs of the Rx and I have a Futaba 14 channel Tx (T8FG), I would like to move auxiliary functions such as this to the S-Bus.

So the code on the MiniPro will read the S-BUS and perform a function (either servo based or logic based) on say 4 channels.

I know this device (FUTM4191 SBD-1 S.Bus Decoder) is available to decode the S-Bus signal to PWM, however I would still need the MiniPro to perform certain functions, so I was looking at cutting out the middle man if possible.

http://www.futaba-rc.com/sbus/

I would need to define the channel on the MiniPro which corresponds to the channel on the Tx, for example channels 9 - 12.

Does that make sense or have I made it worse :smiley:

Steve

Got it. What you need to do is have a way to turn SBUS into PWM.

Let's talk at little bit more about SBUS and DSM2/DSMx serial. They are simply transmitting unsigned integers in raw binary over a UART. This means once you have these values you can do whatever you want with them. DSMx and SBUS have a 2048 resolution (2^11) whereas DSM2 has a 1024 (2^10) resolution. OK so the question now is how do we do something with this information.

First you map those values to a range you want to use:

void MapVar (float *x, float *y, float in_min, float in_max, float out_min, float out_max){
  *y = (*x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}
void MapVar (int16_t *x, float *y, float in_min, float in_max, float out_min, float out_max){
  *y = (*x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}

void MapVar (uint16_t *x, float *y, float in_min, float in_max, float out_min, float out_max){
  *y = (*x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}

In the above code I am using pointers. In addition to using pointers you could simple pass the variables or make the variables global. For the servo control you want to generate a PWM signal. Here is a great article about the difference between the two:

http://www.endurance-rc.com/ppmtut.php

Both PPM and PWM use square waves of varying width to convey information. Let's look at servos and ESC (electronic speed controllers). They are controlled by PWM signal with a frequency between 50 - 500Hz and a pulse width of around 1000 - 2000us. For this signal we are concerned with the pulse width and not the PWM's duty cycle. The center is 1500us, but many servos center at 1520. If you run a servo at too high a frequency it will break the servo. This is also for ESCs.

There are a number of ways that you can generate these signals with the arduino. The easiest is to use servo.h. It will allow you to make a lot of these signals on any pin you want. It also doesn't run all that fast. The maximum refresh rate is ~125Hz. A good flight controller runs the control loops very fast and updates the ESCs at ~400Hz. You want to use AVR C. I know that can be a big turn off for a lot of people here since it is considered difficult. And it is, but it is not too bad. If you do it right you only have to figure it out once. Here is another code sample to illustrate how to generate higher frequency ESC PWM signals. This code was written for the mega, but if you look at the datasheet for the 128 it will become clear to you as to how to do it. If it not don't be afraid to ask for assistance with a concept you are struggling with.

//motor defines
#define FREQ 400
#define PRESCALE 8
#define PERIOD ((F_CPU/PRESCALE/FREQ) - 1)

#define Motor1WriteMicros(x) OCR3B = x * 2//motor 1 is attached to pin2
#define Motor2WriteMicros(x) OCR3C = x * 2//motor 2 is attached to pin3
#define Motor3WriteMicros(x) OCR3A = x * 2//motor 3 is attached to pin5
#define Motor4WriteMicros(x) OCR4A = x * 2//motor 4 is attached to pin6

void setup(){
  MotorInit();
  //do other stuff
}

void loop(){
  Motor1WriteMicros(1250);
  Motor2WriteMicros(someVar);
  //do other stuff
}

void MotorInit(){
  DDRE |= B00111000;//set the ports as outputs
  DDRH |= B00001000;

  
  // Init PWM Timer 3                                       
  // WGMn1 WGMn2 WGMn3  = Mode 14 Fast PWM, TOP = ICRn ,Update of OCRnx at BOTOM
  TCCR3A = (1<<WGM31)|(1<<COM3A1)|(1<<COM3B1)|(1<<COM3C1);  // Clear OCnA/OCnB/OCnC on compare match, set OCnA/OCnB/OCnC at BOTTOM (non-inverting mode)
  TCCR3B = (1<<WGM33)|(1<<WGM32)|(1<<CS31);                 // Prescaler set to 8, that gives us a resolution of 0.5us
  ICR3 = PERIOD;                                // Clock_speed / ( Prescaler * desired_PWM_Frequency) #defined above.  

  TCCR4A = (1<<WGM41)|(1<<COM4A1);
  TCCR4B = (1<<WGM43)|(1<<WGM42)|(1<<CS41);
  ICR4 = PERIOD;
  
  Motor1WriteMicros(1000);//set the output compare value
  Motor2WriteMicros(1000);
  Motor3WriteMicros(1000);
  Motor4WriteMicros(1000);
  
}

This code will not run on the miniPro. This is meant to be an example as to how to set this all up. Furthermore, the 128 only has 1 16bit timer. The above code gives a 0.5us resolution. To use this method with the 128 it will give you two channels with 16bit resolution and two channels with 8 bit of resolution.

Hope that helps. Let me know if there is anything further I can clear up for you.

Michael,

Thank you so much for your detailed response, its really appreciated.

I always seem to jump straight into the deep end.

Is there a reason the code will not run on a ATMega328 Pro Mini?

I have posted my code below to hopefully give you an idea of what I am trying to achieve. Instead of using the normal output from my RC receiver, I would like to adapt the code to use the S-BUS instead and thereby freeing up at least one of the normal Rx channels.

You will see from the code below that if the PWM input is detected between certain frequencies, then it outputs accordingly, in this case utilising an IR Led.

In addition, I would like to use another S-Bus channel for the Arduino to switch on landing lights for example if again a switch on the Tx is 'on'. Chances are I won't need it to control servos, just some auxiliary devices.

So my need is not to generate a PWM signal from the Arduino, but to interpret the S-BUS signal and have set routines depending on the channel input.

Hope that makes sense and thank you again for your assistance.

Steve

#include <multiCameraIrControl.h>

int x=50, y=300, z=100; 
int ledPin = 13;
int IRPin = 2;
int RxInput = A0;
int pic_pause = 5000;
Sony Nex5(IRPin);

void setup()
{
Serial.begin(9600);      // 
  pinMode (IRPin, OUTPUT);  // Output pin for IR LED
  pinMode (RxInput, INPUT);    // Input pin for PWM from Rx
}

void loop () {
z = pulseIn(RxInput, HIGH, 20000);
if (z<1400) {
  digitalWrite(IRPin, 0);
  Serial.println(z);
  delay(y);  
  }
//if (z>1401 && z<1599) {

  if (z>1155 && z<1170) {
    digitalWrite(IRPin, 1);
    Nex5.shutterNow();
  //  Nex5.shutterDelayed();
  //  delay(500);
    digitalWrite(IRPin, 0);
    Serial.println(z);
  //  delay(y);
    }
  
  if (z>1780 && z<1820) {
    digitalWrite(IRPin, 1);
    Nex5.shutterNow();
  //  delay(500);
    digitalWrite(IRPin, 0);
    Serial.println(z);
    delay(pic_pause);
    }

  if (z>1430 && z<1500) {
    digitalWrite(IRPin, 1);
    Nex5.toggleVideo();
    delay(500);
    digitalWrite(IRPin, 0);
    Serial.println(z);
  //  delay(y);
    }
  
  if (z>1600)   {  
    digitalWrite(IRPin, 1); delay (x); digitalWrite(IRPin, 0); delay (x);
    digitalWrite(IRPin, 1); delay (x); digitalWrite(IRPin, 0); delay (x);
    delay(y);
    Serial.println(z);
    }
  }

You should be able to do that no problem. Just assign the variable Z to one of the channels off the SBUS. Use that map function to map max and min of the SBUS channel to whatever you want or modify the code to work with different ranges.

Thanks Mike.

So should the code you have posted above still use the FUTABA_SBUS.h library?

I haven't used the map function before, so should the variables in_min & in_max be the expected PWM frequencies such as 1023 for example from the Rx?

I think I can work my code, I'm just trying to understand how the S_Bus channels are defined etc.

Could you expand a little more please :slight_smile:

Thanks again Mike,

Steve

Hi Mike,

Great stuff you have done, have you had a chance to look at the SBUS2 protocol? It seems to be frames appended after the channel data is sent. I just received an FX32 so should be able to do some tests shortly. Would be interested in building a "base" sensor PCB around Arduino which people could extend to build their own sensors.

One thing I did read is that increasing the telemetry data rate has a direct impact on servo smoothness, which I guess makes perfect sense. Price to pay for sharing bandwidth I guess however not clear if there is any difference depending on amount of telemetry slots being used.

Best,
Serge

I am attempting to test compile the example code and am having an issue.
Arduino IDE 1.5.2 Board is Mega
This is the line that fails
Serial<<sBus.channels[0]<<","<<sBus.channels[1]<<","<<sBus.channels[2]<<"\r\n";

With this error:
invalid operands of types "int" and 'const char[2]' to binary 'operator<<'
sbus_example.ino: In function 'void loop()':
sbus_example:19: error: invalid operands of types 'int' and 'const char [2]' to binary 'operator<<'

I am sorry to bother you, but I am at a loss. While I have done some Arduino projects, I must confess that I am having a difficult time following even the example However, If I can get it to compile, then I will continue to plug away a figure out how this works.
Thank you for your time and efforts.
Rick Harms

In regards to my previous reply, please ignore my question on the compile error. I realized that for my project I do not need that ability. For now, I replaced it with a series of print statements to verify that I can indeed read the sbus. IT WORKED. I am ecstatic. This will become the basis for an on board mixer that I am working on. Thank you.

Hi,

is there a way to make sbus signal with your library?

Any plans to extend the library to SBUS2?

I am using the following code and I am not getting any output

#include <FUTABA_SBUS.h>
#include <Streaming.h>


FUTABA_SBUS sBus;
#include <Servo.h> 
 
Servo myservo;  // create servo object to control a servo 
                // a maximum of eight servo objects can be created 
 
int pos = 0;    // variable to store the servo position 



void setup(){
  sBus.begin();
  Serial.begin(115200);
   myservo.attach(9);  // attaches the servo on pin 9 to the servo object 
}

void loop(){
  sBus.FeedLine();

  if (sBus.toChannels == 1){
    sBus.UpdateServos();
    sBus.UpdateChannels();
    sBus.toChannels = 0;
   Serial<<sBus.channels[0]<<","<<sBus.channels[1]<<","<<sBus.channels[2]<<"\r\n";

  }
  pos = sBus.channels[2];
  myservo.write(map(pos, 1000, 2000, 1, 180)); 

}

From what i see the if condition sBus.toChannels == 1 is never reached.

I have tried 2 different Hex investors and on a Nano and Mega, the receiver I am using is the FR Sky TFR8SB and I have the Signal line from the S.Bus port connected to the input to the Hex inverter, output of that pin to RX on Arduino. The receiver is bound to the transmitter.

If I use Serial<<sBus.failsafe_status<<" Fail safe status \r\n"; I always receive a 0 from the status.

if someone can post some working code I would appreciate it, Thanks

Please see the attached schematic that I have used for this.

That all looks fine. Do you have access to an oscilloscope or logic analyzer? Are you getting anything on the serial port? Try echoing each byte received in hex. Is your receiver 3.3v logic levels?

Hi All,

I got this working now, looks like i had a poor connection on my breadboard that was causing the issues.

Thanks for the help.

One issues i am having is detecting the s.bus signal loss or failsafe, it seams to detect for one pulse and stops, should i have this within the " if (toChannels == 1){" or out side this?