Need Advice On BolderFlight Sbus Sketch

readysteadi

Active member
Hey Everybody, I've been delving into Sbus control for a brushless gimbal using the Bolderflight Sbus library. There were not a lot of examples to follow but I thought I had a handle on the code. I made a simple sketch to control one channel (channel 1) using a potentiometer to control the value. I tested the gimbal using sbus output before hand just to make sure it was reading sbus properly. The sketch compiles and uploads fine and I can see my pot values respond but I get no movement from the gimbal. Gimbal is connected to Pin1 (tx serial1) and ground. Any advice would be much appreciated. I know Brian who created the library has been pretty active on this forum.

Code:
#include "sbus.h"
#define NUM_OF_CHANNEL 8
/* SBUS object, writing SBUS */
bfs::SbusTx sbus_tx(&Serial1);
/* SBUS data */
bfs::SbusData data;

const int potentiometerPin = A0;  // Analog pin for the potentiometer

void setup() {
  Serial.begin(115200);
  while (!Serial) {}
  sbus_tx.Begin();
}

void loop() {
  // Read the value of the potentiometer
  int potValue = analogRead(potentiometerPin);
  
  // Map the potentiometer value to the SBUS range
  int sbusValue = map(potValue, 0, 1023, 0, 2047);

  // Initialize SBUS data
  data = bfs::SbusData();

  // Set the SBUS value to the mapped potentiometer value
  data.ch[1] = sbusValue;  // Assuming control on channel 0

  // Display the potentiometer value (for debugging)
  Serial.println(potValue);
  Serial.println(sbusValue);

  // Write the SBUS data to the servos
  sbus_tx.data(data);
  sbus_tx.Write();
  
  delay(20);  // Adjust delay based on your requirements
}
 
I know that when using an arduino board for Sbus output the signal has to be inverted to be understood by RC hardware, but from my understanding the Teensy and the Bolderflight library already outputs an inverted signal.
 
I would try setting the command for all 16 SBUS channels in case there is an issue with the channel numbering. Obviously the library is 0 based, but some of the programmers are 1 based, so there could be some confusion about what channel you're trying to control. I would also try keeping the commanded value in the range 172 - 1811, the SBUS can theoretically work with 0 - 2047, but I'm not sure if all of the servos / gimbals can use the full range.
 
Thanks Brian, and thanks for the library! I mapped the pot values as you suggested and set the command to all channels but I'm still not seeing any action in the gimbal. You don't see any other glaring errors or omission in the code? Also signal should already be inverted coming out of tx pin correct? Thanks for taking a look

Matt
 
Hi Matt,

Nothing is standing out to me and correct, the signal should already be inverted.

If I were in this position, I would probably use the SBUS library in a passthrough configuration:
SBUS Receiver --> Teensy --> Gimbal

I'm assuming that this will work given that you've already tested SBUS Receiver --> Gimbal

The code would look like the SBUS example online:
https://github.com/bolderflight/sbus/blob/main/examples/arduino/sbus_example/sbus_example.ino

This way you could see the the signals that the SBUS receiver is sending to the gimbal and you could check the timing that the receiver is sending sbus packets by declaring a couple of ints and adding this snippet below line 45:

Code:
int t1, t2;

void setup() {...}

void loop() {
  if (sbus_rx.Read()) {
    t1 = micros();
    Serial.println(t1 - t2);
    t2 = t1;
  }
...
}

Confirming that the gimbal moves and knowing both the SBUS commands and the timing, I think it will help figure out why your code isn't working.
 
Couple other things to check is: 1. that the servo has some high current power source, 2. the servo power ground is shared with the Teensy ground.
 
Hey Thanks again Brian. I was able to connect the board as you suggested and uploaded the modified code. The receiver was taking the signal into the RX serial2 and outputting TX Serial2 and I had full control of the gimbal using the remote. the value for the millis were around 8988 though the last value fluctuated constantly. range for the channels also tended to be around 192-1809 What would you suggest doing from here?
 
1. Leaving the Teensy set up as in the passthrough test, I would comment out the part of the code that sets the data struct to the read data struct and replace it with your potentiometer values. You're still using the receiver for timing, but the pot for everything else. Verify that you can now control the gimbals. I would probably start with a range of around 200 - 1800 and then, assuming that works, try the full 0 - 2047 range.
2. Now I would try having the Teensy time the frames, looks like your receiver is sending at about 100 Hz, so I would try that.
 
Thanks I'll give this a try. And sorry if you have to explain it to me like I'm 5 but I'm a very hobbyist level programmer so how would I go about setting the timing? Would that have to be done with a modification to the library?
 
Here is my clumsy attempt that did not work;

Code:
#include "sbus.h"
#define NUM_OF_CHANNEL 8

/* SBUS object, reading SBUS */
bfs::SbusRx sbus_rx(&Serial2);
/* SBUS object, writing SBUS */
bfs::SbusTx sbus_tx(&Serial2);
/* SBUS data */
bfs::SbusData data;

const int potentiometerPin = A0;  // Analog pin for the potentiometer

void setup() {
  /* Serial to display data */
  Serial.begin(115200);
  while (!Serial) {}
  /* Begin the SBUS communication */
  sbus_rx.Begin();
  sbus_tx.Begin();
}

void loop () {
int potValue = analogRead(potentiometerPin);
  
  // Map the potentiometer value to the SBUS range
  int sbusValue = map(potValue, 0, 1023, 200, 1809);

  // Initialize SBUS data
  

  if (sbus_rx.Read()) {
    /* Grab the received data */
    //data = sbus_rx.data();
    data = bfs::SbusData();
    data.ch[1] = sbusValue;
    Serial.print(sbusValue);
    /* Display the received data */
    //for (int8_t i = 0; i < data.NUM_CH; i++) {
    //  Serial.print(data.ch[i]);
    //  Serial.print("\t");
    }
    /* Display lost frames and failsafe data */
    Serial.print(data.lost_frame);
    Serial.print("\t");
    Serial.println(data.failsafe);
    /* Set the SBUS TX data to the received data */
    sbus_tx.data(data);
    /* Write the data to the servos */
    sbus_tx.Write();
  }
 
This is what I was thinking for step 1. It's the same setup as the passthrough, which worked, just overwriting the value with the POT value. I avoid using the map function, since I'm not sure how well that works, but mapping a 10 bit read, which IIRC is what is default without using analogReadResolution, to an 11 bit value is the same as just multiplying by 2:

Code:
#include "sbus.h"

/* SBUS object, reading SBUS */
bfs::SbusRx sbus_rx(&Serial2);
/* SBUS object, writing SBUS */
bfs::SbusTx sbus_tx(&Serial2);
/* SBUS data */
bfs::SbusData data;
/* POT pin */
const int8_t pot_pin = A0;
int16_t ain_val;

void setup() {
  /* Serial to display data */
  Serial.begin(115200);
  while (!Serial) {}
  /* Begin the SBUS communication */
  sbus_rx.Begin();
  sbus_tx.Begin();
}

void loop () {
  /* Still using the SBUS receiver for timing */
  if (sbus_rx.Read()) {
    /* Grab the received data */
    data = sbus_rx.data();
    /* Read POT value */
    ain_val = analogRead(pot_pin);
    Serial.println(ain_val);
    for (int8_t i = 0; i < bfs::SbusData::NUM_CH; i++) {
      data.ch[i] = ain_val * 2; // mapping 0 - 1023 value to 0 - 2047
    }
    /* Set the SBUS TX data to the received data */
    sbus_tx.data(data);
    /* Write the data to the servos */
    sbus_tx.Write();
  }
}

This is what I was thinking for step 2, where you move the timing to the Teensy assuming that step 1 works. This uses elapsedMillis instead of delay to control the timing.

Code:
#include "sbus.h"

/* SBUS object, reading SBUS */
bfs::SbusRx sbus_rx(&Serial2);
/* SBUS object, writing SBUS */
bfs::SbusTx sbus_tx(&Serial2);
/* SBUS data */
bfs::SbusData data;
/* POT pin */
const int8_t pot_pin = A0;
int16_t ain_val;
/* Timing */
elapsedMillis t_ms;
const int32_t period_ms = 10;

void setup() {
  /* Serial to display data */
  Serial.begin(115200);
  while (!Serial) {}
  /* Begin the SBUS communication */
  sbus_rx.Begin();
  sbus_tx.Begin();
}

void loop () {
  /* 100 Hz frame */
  if (t_ms > period_ms) {
    t_ms = 0;
    /* Read POT value */
    ain_val = analogRead(pot_pin);
    Serial.println(ain_val);
    for (int8_t i = 0; i < bfs::SbusData::NUM_CH; i++) {
      data.ch[i] = ain_val * 2; // mapping 0 - 1023 value to 0 - 2047
    }
    /* Set the SBUS TX data to the received data */
    sbus_tx.data(data);
    /* Write the data to the servos */
    sbus_tx.Write();
  }
}

I've compiled the code, but haven't tested it. Let me know if it works or if you're still having issues and I can dig out some parts for testing.
 
Huzzah! Both things work! Thanks Brian! I've got all three axis turning in unison when I rotate the pot. After playing around with it for a while I started to modify the code again and ran into that will hopefully be my last hurdle in getting this working. In the end of course I will want to have all three channels separate so I wrote the code to hopefully just control channel 1 which according to my tests should be pan. I uploaded and get no movement. I tried different numbers for the channels just to make sure something was not off there and still not getting anything. Would you mind taking one more look and see where I've gone wrong:
Code:
#include "sbus.h"

/* SBUS object, writing SBUS */
bfs::SbusTx sbus_tx(&Serial2);
/* SBUS data */
bfs::SbusData data;
/* POT pin */
const int8_t pot_pin = A0;
int16_t ain_val;
/* Timing */
elapsedMillis t_ms;
const int32_t period_ms = 10;

void setup() {
  /* Serial to display data */
  Serial.begin(115200);
  while (!Serial) {}
  /* Begin the SBUS communication */
  sbus_tx.Begin();
}

void loop () {
  /* 100 Hz frame */
  if (t_ms > period_ms) {
    t_ms = 0;
    /* Read POT value */
    ain_val = analogRead(pot_pin);
    Serial.println(ain_val);
    /* Initialize SBUS data */
    data = bfs::SbusData();
    /* Set the value to Channel 1 directly */
    data.ch[1] = ain_val * 2;
    /* Write the SBUS data to the servos */
    sbus_tx.data(data);
    sbus_tx.Write();
  }
}
 
I don't think I had tried ch[0] but I just plugged it in and no dice. yesterday I tried channels 1-8 and 16 just to check. I also tried just plugging a number in for i and also a sketch just assigning i a value and still not working. I revert back between builds to the working code just to make sure nothing has changed between tests
 
Nothing is standing out to me - maybe it's something as silly as not having the serial port open so it's sitting in the while loop waiting for that.
 
Was having some trouble logging into forum this morning. So I've been experimenting and trying a few things.
I tried another way to isolate a single channel by decreasing the range of channels affected by modifying the line
Code:
for (int8_t i = 0; i < bfs::SbusData::NUM_CH2; i++) {
to be
to make it
Code:
for (int8_t i = 0; i < 2; i++) {
this did not work but I gradually increased this number and when I reached 5 for the value the pot finally had an affect again but still would control all three channels at once.

I also tried declaring #define NUM_OF_CHANNEL 1 at the top of the sketch but again this didn't seem to matter and all channels were affected.
 
I also tried just giving i an assigned channel value of 1 and leaving data.ch = ain_val * 2; hoping that would work but also to no avail
 
Disabled the Serial Port communication just to check, no change. I'm messing around with different values and seeing if anything clicks. Also interesting when I declare #define NUM_OF_CHANNEL 8 it does not seem to affect Num_ch later in the sketch as I can print it out
Code:
Serial.println(data.NUM_CH);
it still shows a vlaue of 16 instead of 8
 
Also found a post for an Sbus Arduino Library which is written to control this same gimbal. They are using 7 ms value for their timing, Don't know if that is actually correct or what it would affect but it was interesting to see..
// Sbus delay value
const int sbusWAIT = 7; //frame timing delay in msecs
 
Still Plodding my way through trying to diagnose the issue. Loaded the passthrough sketch again from this link https://github.com/bolderflight/sbus/blob/main/examples/arduino/sbus_example/sbus_example.ino and took a pic of the output from the Serial monitor. The first 4 columns represent channels 1-4 and each changes value individually as the stick axis moves.
image0.jpg

For contrast I also set the sketch using pot values to print Serial.print(data.ch); I got this output on the serial monitor. There values do not change with the a change in pot value though the gimbal does still respond moving in all three axis simultaneously.
image2.jpg

I would assume that the data.ch printout from the second sketch should be the similar to that of the passthrough sketch.
 
Disabled the Serial Port communication just to check, no change. I'm messing around with different values and seeing if anything clicks. Also interesting when I declare #define NUM_OF_CHANNEL 8 it does not seem to affect Num_ch later in the sketch as I can print it out
Code:
Serial.println(data.NUM_CH);
it still shows a vlaue of 16 instead of 8

You can't change the number of channels, that is defined as a constant:
https://github.com/bolderflight/sbus/blob/main/src/sbus.h#L43

It's needed to be constant, an SBUS packet is consists of a header and then all of the channel data, so every component receiving an SBUS packet receives all of the channel data and knows which channel to respond to based on their configuration.

Also found a post for an Sbus Arduino Library which is written to control this same gimbal. They are using 7 ms value for their timing, Don't know if that is actually correct or what it would affect but it was interesting to see..
// Sbus delay value
const int sbusWAIT = 7; //frame timing delay in msecs

You could try reducing the period to 7 ms instead of 10 ms; however, I doubt that's your issue since in Test #2 that I sent, the frame timing was set to 10 ms.

Still Plodding my way through trying to diagnose the issue. Loaded the passthrough sketch again from this link https://github.com/bolderflight/sbus/blob/main/examples/arduino/sbus_example/sbus_example.ino and took a pic of the output from the Serial monitor. The first 4 columns represent channels 1-4 and each changes value individually as the stick axis moves.
View attachment 32138

For contrast I also set the sketch using pot values to print Serial.print(data.ch); I got this output on the serial monitor. There values do not change with the a change in pot value though the gimbal does still respond moving in all three axis simultaneously.
View attachment 32139

I would assume that the data.ch printout from the second sketch should be the similar to that of the passthrough sketch.


I would add in a println after the for loop on line 36 so that you get a newline every time that you're going to send an SBUS packet, then the columns will match the channel number.
 
Ok That worked adding the Println and Serial.print("\t");

I can now see the data,ch value on all channels in separate columns. This should be a big help.

Interestingly if I change value of ms to 7 vs 10 it seem to have no effect. Pot still changes value and seem to affect gimbal in the same way.

Now just need to figure out why I can't get values to individual channels
 
Back
Top