SBUS Library

Status
Not open for further replies.
Hi Brian,

how many SBUS objects can I create? I need to use Serial1 for serial communication, and serial 2 and 3 for SBUS communication. Thing is I create

"
SBUS x8r_gimbal(Serial2);
SBUS x8r_camera(Serial3);

void setup()
{
pinMode(led_pin, OUTPUT);
pinMode(reset_pin, OUTPUT);
Serial.begin(baud); // USB, communication to PC or Mac
HWSERIAL.begin(baud); // communication to hardware serial
x8r_gimbal.begin();
x8r_camera.begin();
}

void loop()

if(x8r_gimbal.read(&gimbal[0], &failSafe, &lostFrame))
x8r_gimbal.write(&gimbal[0]);
"
If I do x8r_camera.begin(), tx2 won't transmit data. rx2 will still receive SBUS data though. And tx3 also works fine.

Any clue why only one tx works?
Ps: I'm using serial1 for serial communication and it works fine
 
Which Teensy are you compiling and testing on? For Teensy 3.0 and 3.1/3.2, this would be expected behavior - I have to emulate two stop bits and define some timers to help with that, which would only allow for 1 Teensy object to send data. For other Teensy devices (i.e. LC, 3.5, and 3.6), this would not be expected behavior.
 
It's not flexible, which is why it's not implemented, but yes, you can work around it.

In SBUS.cpp, you would change this (lines 26 - 33):
Code:
#if defined(__MK20DX128__) || defined(__MK20DX256__)
	// globals needed for emulating two stop bytes on Teensy 3.0 and 3.1/3.2
	IntervalTimer serialTimer;
	HardwareSerial* SERIALPORT;
	uint8_t PACKET[25];
	volatile int SENDINDEX;
	void sendByte();
#endif

To the following, basically creating two of everything:
Code:
#if defined(__MK20DX128__) || defined(__MK20DX256__)
	// globals needed for emulating two stop bytes on Teensy 3.0 and 3.1/3.2
	IntervalTimer serialTimer1, serialTimer2;
	HardwareSerial *SERIALPORT1, *SERIALPORT2;
	uint8_t PACKET1[25], PACKET2[25];
	volatile int SENDINDEX1, SENDINDEX2;
	void sendByte1(), sendByte2();
#endif

Then in the rest of the code, we need to update the variables and functions used. This starts with the begin method and assigning the serial port pointers:
Code:
		_bus->begin(_sbusBaud,SERIAL_8E1_RXINV_TXINV);
		if (_bus == &Serial2) {
			SERIALPORT1 = _bus;
		} else if (_bus == &Serial3) {
			SERIALPORT2 = _bus;
		}

You'll notice right away, that we need to check the Serial port value before assignment since the object could have been created for either, but we're assigning to a global variable, so we're already reducing flexibility and assigning the port numbers specific for your setup. This would need to change if your serial port numbers ever change.

Then in the write method would get modified to:
Code:
		if (_bus == &Serial2) {
			noInterrupts();
			memcpy(&PACKET1,&packet,sizeof(packet));
			interrupts();
			serialTimer1.priority(255);
			serialTimer1.begin(sendByte1,130);
		} else if (_bus == &Serial3) {
			noInterrupts();
			memcpy(&PACKET2,&packet,sizeof(packet));
			interrupts();
			serialTimer2.priority(255);
			serialTimer2.begin(sendByte2,130);
		}

Note that I had to hard code a check to see whether it's Serial2 or Serial3. This is specific to your setup and would need to be modified if it changes. Finally, the logic of the interrupt routine needs to be updated
Code:
	void sendByte1()
	{
		if (SENDINDEX1 < 25) {
			SERIALPORT1->write(PACKET1[SENDINDEX1]);
			SENDINDEX1++;
		} else {
			serialTimer1.end();
			SENDINDEX1 = 0;
		}
	}
	void sendByte2()
	{
		if (SENDINDEX2 < 25) {
			SERIALPORT2->write(PACKET2[SENDINDEX2]);
			SENDINDEX2++;
		} else {
			serialTimer2.end();
			SENDINDEX2 = 0;
		}
	}
 
Thanks, it works! However delay is noticeable and data gets corrupted more easily. Is it supposed to happen? If yes, I guess I can work out a solution the way it is right now
 
Thanks, it works! However delay is noticeable and data gets corrupted more easily. Is it supposed to happen? If yes, I guess I can work out a solution the way it is right now

I'm not sure, hasn't been tested other than what you've been doing.

If you're fine with the two write methods being called at the same time, an alternative approach is to use one timer and write out the two packets back to back. In that case you still need two serial port pointers and two buffers, but that's it:

Code:
	IntervalTimer serialTimer;
	HardwareSerial *SERIALPORT1, *SERIALPORT2;
	uint8_t PACKET1[25], PACKET2[25];
	volatile int SENDINDEX;
	void sendByte();

The begin method would still be:
Code:
		_bus->begin(_sbusBaud,SERIAL_8E1_RXINV_TXINV);
		if (_bus == &Serial2) {
			SERIALPORT1 = _bus;
		} else if (_bus == &Serial3) {
			SERIALPORT2 = _bus;
		}

The write method would then be:
Code:
		if (_bus == &Serial2) {
			noInterrupts();
			memcpy(&PACKET1,&packet,sizeof(packet));
			interrupts();
		} else if (_bus == &Serial3) {
			noInterrupts();
			memcpy(&PACKET2,&packet,sizeof(packet));
			interrupts();
			serialTimer.priority(255);
			serialTimer.begin(sendByte,130);
		}

Notice that actually sending the commands out is now tied to calling the write method on Serial3. Finally, the send function would be:
Code:
	void sendByte()
	{
		if (SENDINDEX < 25) {
			SERIALPORT1->write(PACKET1[SENDINDEX]);
			SENDINDEX++;
		} else if (SENDINDEX < 50) {
			SERIALPORT2->write(PACKET2[SENDINDEX - 25]);
			SENDINDEX++;
		} else {
			serialTimer.end();
			SENDINDEX = 0;
		}
	}

I haven't tested it, but it should work a little better using just one timer and sending both packets back to back.

Brian
 
Not sure if you are losing data, and have some freedom in your choosing of Serial objects are used...
On Teensy 3.2, Serial1 and Serial2 have larger hardware buffers than does Serial3, so depending on what you are using Serial1 for now, might help to swap 1 with 3?

But I have no knowledge of SBus and the like.
 
Hi Brian;

I have been trying on the following code modified from your bolderflight github for reading SBUS input from a frsky receiver on teensy4:
#include "Wire.h"
#include "SBUS.h"

// a SBUS object, which is on hardware
// serial port 1
SBUS x8r(Serial1);

// channel, fail safe, and lost frames data
uint16_t channels[16];
bool failSafe;
bool lostFrame;

void setup() {
Wire.begin();
Serial.begin(115200);
// begin the SBUS communication
x8r.begin();
}

void loop() {
int i = 1;
// look for a good SBUS packet from the receiver

Serial.println("h");
if(x8r.read(&channels[0], &failSafe, &lostFrame)){
Serial.println("h234 ");
// write the SBUS packet to an SBUS compatible servo
for (i=0; i < 8; i++) {
Serial.print(i);
Serial.print(" :");
Serial.print(channels);
Serial.print(" "); };
Serial.println();

x8r.write(&channels[0]);
}
}

I am using Pin0 (RX1) (and also tried RX5) on Teensy4 as input and the SBUS signal from the receiver passes through an inverter circuit. This code and setup works on an Arduino Mega 2560 board, but not on the teensy4. I have tried using 3.3V or 5V, lowering the CPU speed to 24MHz, using #include <I2C_t3.h>. All not working on teensy4. Not sure why it works on Mega 2560 but not on a teensy4. Please help.
 
Hi Funwey,

Have you downloaded the library recently? I only recently edited it to enable the Teensy 4.
https://github.com/bolderflight/SBUS

Also:
1. Don't use the inverter circuit. The Teensy 3.x, LC, and 4.0 read the inverted signal directly.
2. I2C isn't used for SBUS, it's strictly a Serial library.
 
Hi Bryan,
After relocating and the usual hassles getting back up to speed, I've got my Teensy 3.1 based flight data recorder running. It probably doesn't make much sense to get into the code given it's complexity (which may be my problem), but for a simple question.
In your notes you say that S-Bus runs at 100,000 bps. Yet we are setting our serial ports to 115,200. can this cause missing data? I ask because I read a number of sensors and then the S-bus in a sequence and find that I get no data on occasional passes. I have port set to Serial2. Might that also be a problem?
best, john
 
Hi Bryan,
After relocating and the usual hassles getting back up to speed, I've got my Teensy 3.1 based flight data recorder running. It probably doesn't make much sense to get into the code given it's complexity (which may be my problem), but for a simple question.
In your notes you say that S-Bus runs at 100,000 bps. Yet we are setting our serial ports to 115,200. can this cause missing data? I ask because I read a number of sensors and then the S-bus in a sequence and find that I get no data on occasional passes. I have port set to Serial2. Might that also be a problem?
best, john

Hey John,

In the SBUS library, you'll see that I set the baud rate to 100,000 in the begin method:
https://github.com/bolderflight/SBUS/blob/master/src/SBUS.cpp#L47

Where _sbusBaud is defined as 100000 in the SBUS.h file. In this example I set the Serial object baud to 115200, but this object is just the USB serial to visualize the data, not the hardware serial used by the SBUS library:
https://github.com/bolderflight/SBUS/blob/master/examples/AIN_SBUS_example/AIN_SBUS_example.ino

Hope that makes sense.

Brian
 
Hey John,

In the SBUS library, you'll see that I set the baud rate to 100,000 in the begin method:
https://github.com/bolderflight/SBUS/blob/master/src/SBUS.cpp#L47

Where _sbusBaud is defined as 100000 in the SBUS.h file. In this example I set the Serial object baud to 115200, but this object is just the USB serial to visualize the data, not the hardware serial used by the SBUS library:
https://github.com/bolderflight/SBUS/blob/master/examples/AIN_SBUS_example/AIN_SBUS_example.ino

Hope that makes sense.

Brian

Hi Brian, let me munch on this. I think I've figured out how to send the least amount of code which will show what I'm doing along with the results which show intermittant outrages on the S-Bus data recording.
best,
john
 
Ok,
the S-Bus reader is running in a sub-routine of my flight-datar- ecorder sketch. The GPS is a UBLOX M8N running at 10 Hz but not with your code Brian, I had other code which worked and doing the migration will wait fixing the S-Bus module. The recorder is installed in an R/C Airplane and records output from gps, sensors, accelerometers, compass, and the S-Bus. I need the sbus-data which reports control input into throttle and servos to divine better rates of input for an autopilot which I will eventually get to. The data are written to an SD using typical SD code. The microcontroller and SD module are Teensy products.

Subroutine code looks like this: /*
altimu simple_minimu sd implementation 2-21-16 jaf
add hand monitor printout 2-21-16 jaf
add SBUS copy to SD 12-3-2019 jaf
changed to save SBUS raw signals to SD - appears to work 5-8-2019 jaf
clean up notes,commas and eliminate unneeded data 12-7-2019 jaf
Code:
*/

void printdata(void)
{
  float pressure = ps.readPressureInchesHg();
  float altitude = ps.pressureToAltitudeFeet(pressure);
  float Height = altitude - basealtitude;
  uint16_t channels[16]; bool failSafe; bool lostFrame;
  Serial3.print("hAcc:"); Serial3.println(posllh.hAcc / 1000.0f);
  dataFile.print(hour());
  dataFileprintDigits(minute());
  dataFileprintDigits(second());
  dataFile.print(",");
  dataFile.print(" MHd:");
  dataFile.print(MAG_Heading);
  dataFile.print(",");
  dataFile.print(" lat/lon: "); dataFile.print(","); 
  dataFile.print(posllh.lat / 10000000.0f, 6); dataFile.print(","); dataFile.print(posllh.lon / 10000000.0f, 6);
  dataFile.print(",");
  dataFile.print(" hMSL: ");    
  dataFile.print(posllh.hMSL / 1000.0f);
  dataFile.print(" bar: ");  
  dataFile.print(pressure);
  dataFile.print(" hAG ");  dataFile.print(Height);
  dataFile.print(" hAcc: ");    dataFile.print(posllh.hAcc / 1000.0f);
  dataFile.print(" vAcc: ");    dataFile.print(posllh.vAcc / 1000.0f);
  dataFile.print(", ANG:");
  dataFile.print(ToDeg(roll));
  dataFile.print(",");
  dataFile.print(ToDeg(pitch));
  dataFile.print(",");
  dataFile.print(ToDeg(yaw));
  dataFile.print(",");
  dataFile.print("AS:");
  dataFile.print(",");
  Wire.beginTransmission(0x75);  
  Wire.write(0x07); 
  Wire.endTransmission();
  Wire.beginTransmission(0x75);  
  Wire.requestFrom(0x75, 2);  // request 6 bytes from slave device #8
  
  while(Wire.available()) {  // slave may send less than requested
  int airspeed=Wire.read();
  dataFile.print(airspeed / 10);
//  Serial.print("Airspeed:"); Serial.println(airspeed);
    Wire.endTransmission(); 
  }  
   
  dataFile.print(", ");
  dataFile.print("S-Bus");
  dataFile.print(",");
  if (x8r.read(&channels[0], &failSafe, &lostFrame)) {
    for (unsigned int i = 0; i < 4; i++) {
      dataFile.print(channels[i]);
      dataFile.print(",");
    }
  }
  dataFile.println("endline");
}
long convert_to_dec(float x)
{
  return x * 10000000;
}
void dataFileprintDigits(int digits) {
  // utility function for digital clock display: prints preceding colon and leading 0
  dataFile.print(":");
  if (digits < 10)
    dataFile.print('0');
  dataFile.print(digits);
}

output recorded to MicroSD chip is this:
This is a sample of output. S-Bus output is intermittent and when present located just before "endline"

other data are
time, Magnetyic Heading, gps lat/lon, Height above MSL, barometric pressutre, height above ground (set at initialization of flight, gps horizontal accuracy, gps vertical accuracy, Euler attitude angles, airspeed, and S-bus output showing commands received from transceiver,and "endline"




9:44:44, MHd:341.38, lat/lon: ,0.000000,0.000000, hMSL: -17.00 bar: 30.28 hAG -0.42 hAcc: 4294967.50 vAcc: 3750003.50, ANG:20.35,32.70,-132.81,AS:,50, S-Bus,15,1660,1148,1045,endline
9:44:44, MHd:341.38, lat/lon: ,0.000000,0.000000, hMSL: -17.00 bar: 30.28 hAG -0.42 hAcc: 4294967.50 vAcc: 3750003.50, ANG:20.41,32.72,-133.92,AS:,50, S-Bus,endline
9:44:44, MHd:341.38, lat/lon: ,0.000000,0.000000, hMSL: -17.00 bar: 30.28 hAG -0.42 hAcc: 4294967.50 vAcc: 3750003.50, ANG:20.54,32.76,-135.02,AS:,50, S-Bus,endline
9:44:44, MHd:341.38, lat/lon: ,0.000000,0.000000, hMSL: -17.00 bar: 30.28 hAG -0.41 hAcc: 4294967.50 vAcc: 3750003.50, ANG:20.60,32.80,-136.15,AS:,50, S-Bus,endline
9:44:44, MHd:341.38, lat/lon: ,0.000000,0.000000, hMSL: -17.00 bar: 30.28 hAG -0.41 hAcc: 4294967.50 vAcc: 3750003.50, ANG:20.61,32.85,-137.29,AS:,50, S-Bus,endline
9:44:45, MHd:341.38, lat/lon: ,0.000000,0.000000, hMSL: -17.00 bar: 30.28 hAG -0.41 hAcc: 4294967.50 vAcc: 3750003.50, ANG:20.53,32.89,-138.46,AS:,50, S-Bus,endline
9:44:45, MHd:342.27, lat/lon: ,0.000000,0.000000, hMSL: -17.00 bar: 30.28 hAG -0.41 hAcc: 4294967.50 vAcc: 3750003.50, ANG:20.49,32.85,-139.60,AS:,50, S-Bus,endline
9:44:45, MHd:342.27, lat/lon: ,0.000000,0.000000, hMSL: -17.00 bar: 30.28 hAG -0.60 hAcc: 4294967.50 vAcc: 3750003.50, ANG:20.51,32.90,-140.48,AS:,50, S-Bus,endline
9:44:45, MHd:342.27, lat/lon: ,0.000000,0.000000, hMSL: -17.00 bar: 30.28 hAG -0.60 hAcc: 4294967.50 vAcc: 3750003.50, ANG:20.50,32.99,-141.33,AS:,50, S-Bus,endline
9:44:45, MHd:342.27, lat/lon: ,0.000000,0.000000, hMSL: -17.00 bar: 30.28 hAG -0.60 hAcc: 4294967.50 vAcc: 3750003.50, ANG:20.44,33.05,-142.18,AS:,50, S-Bus,endline
9:44:45, MHd:342.27, lat/lon: ,0.000000,0.000000, hMSL: -17.00 bar: 30.28 hAG -0.60 hAcc: 4294967.50 vAcc: 3750003.50, ANG:20.48,33.08,-142.98,AS:,50, S-Bus,endline
9:44:45, MHd:342.27, lat/lon: ,0.000000,0.000000, hMSL: -17.00 bar: 30.27 hAG 1.19 hAcc: 4294967.50 vAcc: 3750003.50, ANG:20.50,33.10,-143.77,AS:,50, S-Bus,992,998,172,1068,endline
9:44:45, MHd:342.44, lat/lon: ,0.000000,0.000000, hMSL: -17.00 bar: 30.27 hAG 1.19 hAcc: 4294967.50 vAcc: 3750003.50, ANG:20.65,33.14,-144.49,AS:,50, S-Bus,992,999,172,1068,endline
9:44:45, MHd:342.44, lat/lon: ,0.000000,0.000000, hMSL: -17.00 bar: 30.27 hAG 1.19 hAcc: 4294967.50 vAcc: 3750003.50, ANG:20.75,33.14,-145.09,AS:,50, S-Bus,992,998,1196,7,endline
9:44:45, MHd:342.44, lat/lon: ,0.000000,0.000000, hMSL: -17.00 bar: 30.27 hAG 1.19 hAcc: 4294967.50 vAcc: 3750003.50, ANG:20.85,33.11,-145.69,AS:,50, S-Bus,endline
9:44:45, MHd:342.44, lat/lon: ,0.000000,0.000000, hMSL: -17.00 bar: 30.28 hAG -0.39 hAcc: 4294967.50 vAcc: 3750003.50, ANG:20.89,33.09,-146.32,AS:,50, S-Bus,992,999,172,1068,endline
9:44:45, MHd:342.44, lat/lon: ,0.000000,0.000000, hMSL: -17.00 bar: 30.28 hAG -0.39 hAcc: 4294967.50 vAcc: 3750003.50, ANG:20.98,33.07,-146.92,AS:,60, S-Bus,992,999,172,1068,endline
9:44:45, M

this is looping a bit faster than 10 Hz.

I'm mystified by why the S-BUS recording either works or doesn't. It could be that the output is there and the problem is with the SDcard recording, but still I would have thought it would read partial packets.
It may be a timing issue with the S-BUS reader. For example, if the S-Bus is not emitting channels[0] when this section is run, then it won't save anything. I need to modify this so that the section will wait for channels[0] to come around and then run, but I don't know how to do this. Maybe this needs to be on it's own loop within the larger subroutine. ??

I'd love to hear from anyone who may have some suggestions on what else I should look at.

If you 'd like to see all of the code, I can attach a file.
 
Last edited:
I think I've figured it out. it's a synchronization problem. I need to make the sbus-read subroutine read the sbus until it gets a channels[0], then read that. should be simple.
 
Does this look like a good way to read S-bus until packet starts with channels[0] and then print it?

"print_SBus" is a subroutine called from main output section.

Code:
void print_SBus(){
  uint16_t channels[16]; bool failSafe; bool lostFrame;
   while (x8r.read(&channels[0], &failSafe, &lostFrame)) {
    for (unsigned int i = 0; i < 4; i++) {
      dataFile.print(channels[i]);
      dataFile.print(",");
     }

Later: Just tried it and it works fine and my Flight Data recorder is now firing on all 7 cylinders. Yippee!
 
Well, it doesn't quite work fine. Most of the time I get double output. I'm reading four channels and it looks like the "While" is open long enough sometimes to read two passes of the S-Bus Data.
I could clear this out in post-processing, but it would be nice to get it right initially. To summarize, if I use "IF" statement, i miss about a third of the packets, if I use "WHILE" I get 2/3s double copies.

What else can I try?
 
HI Brian,
This is what I posted on the Arduino forum. The first response was to ask for all of the code - which is a lot. Since you devised the S-Bus library, maybe you can recognize the problem, I tend to suspect it is with the While routine, not your code, but ....


Here is the code:

Code:
void print_SBus(){
  uint16_t channels[16]; bool failSafe; bool lostFrame;
   while (x8r.read(&channels[0], &failSafe, &lostFrame)) {
    for (unsigned int i = 0; i < 4; i++) {
      dataFile.print(channels[i]);
      dataFile.print(",");
     }

This is a subroutine intended to store the first four channels of a 16 channel S-Bus packet to a micro-SD file. It runs at the end of an output routine in the larger application which writes gps, and myriad sensor data to the microSD.

There is a synchronization issue here. The overall code runs at about 10Hz, that is it reads every sensor at approximately this frequency affected somewhat by variation in data size from some sensors.

The S-Bus data stream runs at a different frequency - much faster.

The problem with the snippet above is that while it always reads the first four channels of each packet, it often reads the next packet at well and so I get two instances instead of the one I need.

I'm assuming the problem is the "WHILE" loop sometime doesn't shut off quickly enough after reading the first packet, and if the next one it sees is a channels[0] packet, it reads that one as well.

I tried using an "IF" statement instead, but it often missed the channels[0] flag and accordingly failed to record that packet - a miss in other words. I have to have a read every time this snippet runs.

So my question is can I add another line to this to write only one packet of the four channels? Alternatively, is there another way to run this little loop which will assure saving only a single instance of the data?
 
Writing S-Bus output to a microSD card is very twitchy. I think I'm going to quit messing with kit and just remove the repeated channels from the output file.
 
Which Teensy are you compiling and testing on? For Teensy 3.0 and 3.1/3.2, this would be expected behavior - I have to emulate two stop bits and define some timers to help with that, which would only allow for 1 Teensy object to send data. For other Teensy devices (i.e. LC, 3.5, and 3.6), this would not be expected behavior.

Hey Brian,

is Teensy 4.0 okay to go with 2 SBUS objects?
Thanks

Vasco
 
Status
Not open for further replies.
Back
Top