Generate S.Bus with Arduino in a simple way

Did you noticed that lately I write about radios quite often? Well, I do and it's not a coincidence. Proper introduction for what I'm working on will happen in a next few days, but now I will only write that this will be a mid-range, cheap, DIY radio link for UAVs. By mid-range I mean up to 5km. So, it will be positioned somewhere between 2.4GHz systems and full sale LRSes like DragonLink or TBS Crossfire.

Back to business. I've discovered, that there is very little in The Internet how to generate S.Bus with Arduino. OK, there are few libraries for reading Futaba S.Bus protocol like mikeshub/FUTABA_SBUS or zendes/SBUS but the only library made simple I've found is bolderflight/SBUS. Too bad it works only with Teensy devices. So, after a few hours of hard work, reading code of OpenTX, MultiWii, INAV, reading RcGroups and final help of Konstantin Sharlaimov (Digital Entity of INAV), I give you:

Generate S.Bus packets with Arduino in a simple way

But first, few simple facts:

  • S.Bus protocol is INVERTED on a hardware level. That mean, Arduino can not talk directly with other S.Bus devices without additional inverter. Like this one
  • When talking to most modern flight controllers, inverter might not be required and long as flight controller can disable onboard inverters. F1 and F4 do not have build in inverters at all, so any UART can be used directly and in case of F3 and F7, INAV/Betaflight can disable inversion in software
  • Futaba S.Bus in "FASSTest 18CH" protocols encodes 16 RC channels and 2 digital channels (ON/OFF)
  • S.Bus encodes each RC channel with 11 bits
  • Internally, channel values are mapped as:
    • -100% = 173 (equivalent of 1000 in PWM servo signal)
    • 0% = 992 (equivalent of 1500 in PWM servo signal)
    • 100% = 1811 (equivalent of 2000 in PMW servo signal)
  • Serial port has to be configured as 100000bps, SERIAL_8E2 (8 bits, even, 2 stop bits)
  • Failsafe state is transfered via single is flags byte
  • One S.Bus packet takes 25 bytes
  • One frame is transmitted every 15ms (FASSTest 18CH mode)
#define RC_CHANNEL_MIN 990
#define RC_CHANNEL_MAX 2010

#define SBUS_MIN_OFFSET 173
#define SBUS_MID_OFFSET 992
#define SBUS_MAX_OFFSET 1811
#define SBUS_CHANNEL_NUMBER 16
#define SBUS_PACKET_LENGTH 25
#define SBUS_FRAME_HEADER 0x0f
#define SBUS_FRAME_FOOTER 0x00
#define SBUS_FRAME_FOOTER_V2 0x04
#define SBUS_STATE_FAILSAFE 0x08
#define SBUS_STATE_SIGNALLOSS 0x04
#define SBUS_UPDATE_RATE 15 //ms

void sbusPreparePacket(uint8_t packet[], int channels[], bool isSignalLoss, bool isFailsafe){

    static int output[SBUS_CHANNEL_NUMBER] = {0};

    /*
     * Map 1000-2000 with middle at 1500 chanel values to
     * 173-1811 with middle at 992 S.BUS protocol requires
     */
    for (uint8_t i = 0; i < SBUS_CHANNEL_NUMBER; i++) {
        output[i] = map(channels[i], RC_CHANNEL_MIN, RC_CHANNEL_MAX, SBUS_MIN_OFFSET, SBUS_MAX_OFFSET);
    }

    uint8_t stateByte = 0x00;
    if (isSignalLoss) {
        stateByte |= SBUS_STATE_SIGNALLOSS;
    }
    if (isFailsafe) {
        stateByte |= SBUS_STATE_FAILSAFE;
    }
    packet[0] = SBUS_FRAME_HEADER; //Header

    packet[1] = (uint8_t) (output[0] & 0x07FF);
    packet[2] = (uint8_t) ((output[0] & 0x07FF)>>8 | (output[1] & 0x07FF)<<3);
    packet[3] = (uint8_t) ((output[1] & 0x07FF)>>5 | (output[2] & 0x07FF)<<6);
    packet[4] = (uint8_t) ((output[2] & 0x07FF)>>2);
    packet[5] = (uint8_t) ((output[2] & 0x07FF)>>10 | (output[3] & 0x07FF)<<1);
    packet[6] = (uint8_t) ((output[3] & 0x07FF)>>7 | (output[4] & 0x07FF)<<4);
    packet[7] = (uint8_t) ((output[4] & 0x07FF)>>4 | (output[5] & 0x07FF)<<7);
    packet[8] = (uint8_t) ((output[5] & 0x07FF)>>1);
    packet[9] = (uint8_t) ((output[5] & 0x07FF)>>9 | (output[6] & 0x07FF)<<2);
    packet[10] = (uint8_t) ((output[6] & 0x07FF)>>6 | (output[7] & 0x07FF)<<5);
    packet[11] = (uint8_t) ((output[7] & 0x07FF)>>3);
    packet[12] = (uint8_t) ((output[8] & 0x07FF));
    packet[13] = (uint8_t) ((output[8] & 0x07FF)>>8 | (output[9] & 0x07FF)<<3);
    packet[14] = (uint8_t) ((output[9] & 0x07FF)>>5 | (output[10] & 0x07FF)<<6);  
    packet[15] = (uint8_t) ((output[10] & 0x07FF)>>2);
    packet[16] = (uint8_t) ((output[10] & 0x07FF)>>10 | (output[11] & 0x07FF)<<1);
    packet[17] = (uint8_t) ((output[11] & 0x07FF)>>7 | (output[12] & 0x07FF)<<4);
    packet[18] = (uint8_t) ((output[12] & 0x07FF)>>4 | (output[13] & 0x07FF)<<7);
    packet[19] = (uint8_t) ((output[13] & 0x07FF)>>1);
    packet[20] = (uint8_t) ((output[13] & 0x07FF)>>9 | (output[14] & 0x07FF)<<2);
    packet[21] = (uint8_t) ((output[14] & 0x07FF)>>6 | (output[15] & 0x07FF)<<5);
    packet[22] = (uint8_t) ((output[15] & 0x07FF)>>3);

    packet[23] = stateByte; //Flags byte
    packet[24] = SBUS_FRAME_FOOTER; //Footer
}

uint8_t sbusPacket[SBUS_PACKET_LENGTH];
int rcChannels[SBUS_CHANNEL_NUMBER];
uint32_t sbusTime = 0;

void setup() {
    for (uint8_t i = 0; i < SBUS_CHANNEL_NUMBER; i++) {
        rcChannels[i] = 1500;
    }
    Serial.begin(100000, SERIAL_8E2);
}

void loop() {
    uint32_t currentMillis = millis();

    /*
     * Here you can modify values of rcChannels while keeping it in 1000:2000 range
     */

    if (currentMillis > sbusTime) {
        sbusPreparePacket(sbusPacket, rcChannels, false, false);
        Serial.write(sbusPacket, SBUS_PACKET_LENGTH);

        sbusTime = currentMillis + SBUS_UPDATE_RATE;
    }
}

I hope code above is simple enough. setup method initializes serial port in correct mode Serial.begin(100000, SERIAL_8E2); and loop encodes and send S.Bus frame every 15ms. Packet encoding can be done in a better way, but hey, at least it is working!

23 thoughts on “Generate S.Bus with Arduino in a simple way”

        1. From Futaba R7018SB S.Bus/2 18Ch FASSTest Telemetry Receiver specifications:

          Speed (Frame Rate): 6.3ms / 15ms FASSTest (12ch/18ch)
          7ms / 14ms FASST (High Speed/Normal)

  1. this sounds like i’d like to give it a try. look forward to your next install!
    Russ from Coral Springs, Florida, USA

  2. Witam, dzięki za ten wpis, bo właśnie też szukałem w sieci czegoś na temat SBUS-a. Chcę to wykorzystać w moim projekcie zdalnego sterowania na nRF24. Potrzebuje mieć wyjście w odbiorniku typu PPM lub SBUS i wydaje mi się, że SBUS jest bardziej precyzyjny (bo cyfrowy…) – wyjście szeregowe do kontrolera lotu.
    Widzę, że działasz w projekcie INAV. To dobrze, że mamy tam Polaka ;-)), bo zawsze można się chyba o coś zapytać rodaka….
    Pozdrawiam

  3. Hey i am building diy 16ch radio with nrf24 and Arduino nano and i want to convert the received channel to sbus out so that it can talk with pixhwak flight controller i am not very good at program so can u pls tell me that can i use this codes to convert signal that i recieve from nrf tx

      1. and i have mapped channel in void sbusPreparePacket and used int for each channel and mappend according to what u wrote in comment but what should i write in void loop where u wrote modify rc value to 1000:2000 and i am directly tramsmitting 173 to 1811 from tx so @ sbusPreparePacket i mapped it to 1000-2000

        1. this is how i mapped each channel
          output[0] = map(output[0], 173, 1811, 1000, 2000);
          is it correct ????

  4. Really cool job. I’m trying to use it in my project with a quad and a esp32. My flight controller is spracing F3 and I’m not making something right. Do I have to build a hardware inverter since my flight controller have one build in, or the code doesnt work on esp 32 Rx Tx?

    1. This was never tested on a ESP32. You should get an oscilloscope and logic state analyzer to see what is outputted over there

  5. Thank you! I have built quad with Arduino for autonomous flight. Sending SBUS using your code to FC – its a Matec F4 board with the Inverter you show and it is “working” in that the channels are coming in and showing when I have Betaflight connected to the FC. However, it won’t arm and throttle does not respond. I have tested it with a radio and receiver and it works just fine. What could be stopping it from working? Does the FC not respond if it thinks the sbus data stream isn’t reliable? Is it timing or something like that? Every second the FC won’t get a packet for about 25ms due to some other processing I need to do. Could that be the problem? Not sure how to test it.

    1. Hello,
      I am working on getting a PC to control a Betaflight FC, too. Did you get your problems solved by now?
      Thanks beforehand!

  6. Hello Pawel,
    Thanks – you seem to be blazing a trail in this technology.

    can you set me on the right path to add to this code.
    I want to generate an sbus code from 1 to 2 analogue inputs and 1 to 2 digital inputs.
    I will transmit the sbus signal to the quad and decode it there.

    Its a bit like a cut down transmitter just using a ps2 joystick and couple buttons.

    So that the receiver can read pot positions on the transmitter 1,3,3 or 1,0,3, or 1,66. I can remotely point the camera attached to the servo motor. i will use either arduino or sbus to pwm or ppm at the receiver end.

    best regards, mark

  7. Any Ideas how i might build in the first part of arduino joystick.

    I want to encode the Switch / X value & Y value into the sbus

    regards, mark

    // Arduino pin numbers
    const int SW_pin = 2; // digital pin connected to switch output
    const int X_pin = 0; // analog pin connected to X output
    const int Y_pin = 1; // analog pin connected to Y output

    void setup() {
    pinMode(SW_pin, INPUT);
    digitalWrite(SW_pin, HIGH);
    Serial.begin(115200);
    }

    void loop() {
    Serial.print(“Switch: “);
    Serial.print(digitalRead(SW_pin));
    Serial.print(“\n”);
    Serial.print(“X-axis: “);
    Serial.print(analogRead(X_pin));
    Serial.print(“\n”);
    Serial.print(“Y-axis: “);
    Serial.println(analogRead(Y_pin));
    Serial.print(“\n\n”);
    delay(500);
    }

  8. Hello, thanks for this article, it’s hard to find anything usable on SBUS and Arduino. I have one, hopefully simple, question. I have uploaded your code verbatim to an Arduino Pro Mini and I have wired it to an F4 flight controller running Betaflight. I did add the following 2 lines of code to create some movement:

    /*
    * Here you can modify values of rcChannels while keeping it in 1000:2000 range
    */
    rcChannels[5]+=2;
    if (rcChannels[5] > 2000) rcChannels[5] = 1000;

    Currently I have wired the board to the FC as follows:
    Arduino GND -> FC GND
    Arduino VCC -> FC 5V
    Arduino TX0 -> FC SBUS

    My question is whether or not this wiring is correct as I am getting nothin on the radio tab of Betaflight?

    Thank you so much for your help.

    1. Hello, i have the same issue described by Miguel de la Pena.
      I’m using a HobbyWiring XROTOR FlightController F4 G2 that have an integrated inverter on SBUS pin. Using BetaFlight firmware
      I’m doing some tests using this code on Serial1 of an Arduino Mega 2560. I’ve powered the arduino board thourght usb cable, and connected the PIN 18 ( Mega TX1 ) to the SBUS pin of FligtController.

      I’m trying to use this code
      /*
      * Here you can modify values of rcChannels while keeping it in 1000:2000 range
      */

      rcChannels[6] += 100;
      if(rcChannels[6] > 2000 ) rcChannels[6] = 1500;

      But nothing seems to happen.

      If I connect the FUTABA receiver directly to the SBUS PIN using the remote controller it works correctly, so it’s not a board problem.

      Strange thing: If it connect the SBUS PIN to the Serial0 ( TX0 PIN 1 of MEGA ) the signal on beta flight get crazy for an instant when the arduino restart after code upload.

      Can someone help me ?

      1. Hi Jassy, I was finally able to figure out my problem. In my case it was fixed with an SBUS hardware inverter. You may want to try different pins to see if it helps, just my two cents.

        1. Hi Miguel,
          maybe stupid question…. But the hardware inverter does nothing but invert the signal a.k.a. flipping zeros to ones and vice versa, correct?
          So you could easy handle this inverting problem by implementing this bit flipping, don’t you?

          I am using an Arduino MKR Zero and Betaflight and have the same problem: Using a normal SBUS Receiver works just fine, Arduino doesn’t work

Leave a Reply

Your email address will not be published.

This site uses Akismet to reduce spam. Learn how your comment data is processed.