Skip to content

Commit

Permalink
Fixed bug in SBUS timing, added options for non-inverted signals, and…
Browse files Browse the repository at this point in the history
… simplified the class interfaces.
  • Loading branch information
flybrianfly committed Sep 28, 2022
1 parent 7e2f061 commit 1c353d1
Show file tree
Hide file tree
Showing 10 changed files with 286 additions and 308 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
# Changelog

## v8.0.0
- Fixed bug in SbusRx timing where the last packet waited for the start of the next packet before returning true
- Added option to specify a non-inverted signal for cases where that is a hardware option
- Removed std::array dependency to facilitate use on Arduino AVR
- Updated ESP32 implementation for greater consistency to other processors
- Implemented data struct to ease reading and setting SBUS values without needing std::array

## v7.0.0
- Using std::array for passing around SBUS data and commands

Expand Down
6 changes: 3 additions & 3 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,14 @@ if (DEFINED MCU)
FetchContent_Declare(
mcu_support
GIT_REPOSITORY https://github.com/bolderflight/mcu-support.git
GIT_TAG v1.0.0
GIT_TAG v1.1.0
)
FetchContent_MakeAvailable(mcu_support)
# Setting up the toolchain
set(CMAKE_TOOLCHAIN_FILE "${mcu_support_SOURCE_DIR}/cmake/cortex.cmake")
# Project information
project(Sbus
VERSION 7.0.0
VERSION 8.0.0
DESCRIPTION "SBUS encoder and decoder"
LANGUAGES C CXX
)
Expand All @@ -23,7 +23,7 @@ if (DEFINED MCU)
FetchContent_Declare(
core
GIT_REPOSITORY https://github.com/bolderflight/core.git
GIT_TAG v3.0.2
GIT_TAG v3.1.0
)
FetchContent_MakeAvailable(core)
# Add the library target
Expand Down
2 changes: 1 addition & 1 deletion LICENSE.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
The MIT License (MIT)

Copyright (c) 2021 Bolder Flight Systems Inc
Copyright (c) 2022 Bolder Flight Systems Inc

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

Expand Down
138 changes: 46 additions & 92 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
[![Pipeline](https://gitlab.com/bolderflight/software/sbus/badges/main/pipeline.svg)](https://gitlab.com/bolderflight/software/sbus/) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)

![Bolder Flight Systems Logo](img/logo-words_75.png)     ![Arduino Logo](img/arduino_logo_75.png)

# Sbus
This library communicates with SBUS receivers and servos and is compatible with Arduino ARM and CMake build systems.
This library communicates with SBUS receivers and servos and is compatible with Arduino and CMake build systems.
* [License](LICENSE.md)
* [Changelog](CHANGELOG.md)
* [Contributing guide](CONTRIBUTING.md)
Expand Down Expand Up @@ -40,7 +40,7 @@ SBUS uses an inverted serial protocol, which is not commonly supported in Arduin
* STM32L432xx
* ESP32

For all other microcontrollers, you **must** use a serial inverter.
For all other microcontrollers, you **must** use a serial inverter. If you've modified this library to work with other microcontrollers, please submit a pull request.

# Installation

Expand All @@ -51,7 +51,7 @@ Simply clone or download and extract the zipped library into your Arduino/librar
#include "sbus.h"
```

An example is located in *examples/arduino/sbus_example/sbus_example.ino*. This library is tested with Teensy 3.x, 4.x, and LC devices and should work with other ARM devices. It is **not** expected to work with AVR due to the use of std::array.
An example is located in *examples/arduino/sbus_example/sbus_example.ino*. This library is tested with Teensy 3.x, 4.x, and LC devices and should work with other Arduino devices.

## CMake
CMake is used to build this library, which is exported as a library target called *sbus*. The header is added as:
Expand All @@ -73,154 +73,108 @@ This will build the library and example executable called *sbus_example*. The ex
* MKL26Z64
* IMXRT1062_T40
* IMXRT1062_T41
* IMXRT1062_MMOD

These are known to work with the same packages used in Teensy products. Also switching packages is known to work well, as long as it's only a package change.

The *sbus_example* target creates an executable for communicating with sbus receivers and servos. This target also has a *_hex* for creating the hex file and an *_upload* for using the [Teensy CLI Uploader](https://www.pjrc.com/teensy/loader_cli.html) to flash the Teensy. Please note that the CMake build tooling is expected to be run under Linux or WSL, instructions for setting up your build environment can be found in our [build-tools repo](https://github.com/bolderflight/build-tools).
The *sbus_example* target creates an executable for communicating with sbus receivers and servos. This target also has a *_hex* for creating the hex file and an *_upload* for using the [Teensy CLI Uploader](https://www.pjrc.com/teensy/loader_cli.html) to flash the Teensy. Instructions for setting up your build environment can be found in our [build-tools repo](https://github.com/bolderflight/build-tools).

# Namespace
This library is within the namespace *bfs*.

# SbusRx
This class is used for receiving SBUS data from an SBUS capable receiver.

**SbusRx(HardwareSerial *bus)** Creates an SbusRx object. A pointer to the Serial object corresponding to the serial port used is passed. The RX pin of the serial port will receive SBUS packets.

```C++
bfs::SbusRx sbus(&Serial1);
```
**void Begin()** Initializes SBUS communication.
```C++
sbus.Begin();
```

**(ESP-32 ONLY) void Begin(const int8_t rxpin, const int8_t txpin)** Initialized SBUS communication, given the Serial RX and TX pins.

**bool Read()** Parses SBUS packets, returns true on successfully receiving an SBUS packet.
# SbusData
This struct defines SBUS data that can be read and returned by the *SbusRx* object or set and sent by the *SbusTx* object.

```C++
if (sbus.Read()) {
// Do something with the received data
}
```
## Data Members

**static constexpr int8_t NUM_CH()** A constant defining the number of SBUS channels (i.e. 16), useful for defining arrays to read the data into.
**bool lost_frame** Whether a frame has been lost.

**std::array<int16_t, NUM_SBUS_CH_> ch()** Returns an array of received channel data.
**bool failsafe** Whether the receiver has entered failsafe mode or to command servos to enter failsafe mode.

```C++
std::array<int16_t, bfs::SbusRx::NUM_CH()> rx_ch = sbus.ch();
```
**bool ch17, ch18** State of channel 17 and channel 18.

**bool ch17()** Returns the value of channel 17.

```C++
bool ch17 = sbus.ch17();
```
**static constexpr int8_t NUM_CH = 16** The number of SBUS channels.

**bool ch18()** Returns the value of channel 18.
**int16_t ch[NUM_CH]** An array of SBUS channel data.

```C++
bool ch18 = sbus.ch18();
```
# SbusRx
This class is used for receiving SBUS data from an SBUS capable receiver.

**bool lost_frame()** Returns true if a frame has been lost.
**SbusRx(HardwareSerial &ast;bus)** Creates an *SbusRx* object. A pointer to the *Serial* object corresponding to the serial port used is passed. The RX pin of the serial port will receive SBUS packets.

```C++
bool lost_frame = sbus.lost_frame();
bfs::SbusRx sbus(&Serial1);
```
**bool failsafe()** Returns true if the receiver has entered failsafe mode.
**SbusRx(HardwareSerial &ast;bus, const bool inv)** Creates an *SbusRx* object. A pointer to the *Serial* object corresponding to the serial port used is passed along with a second parameter, *inv*, which sets whether inverted serial is used. If *inv* is true, the signal is the standard inverted SBUS, otherwise it is non-inverted SBUS.
```C++
bool failsafe = sbus.failsafe();
bfs::SbusRx sbus(&Serial1, false);
```

# SbusTx
This class is used for transmitting SBUS data to SBUS capable servos.

**SbusTx(HardwareSerial &ast;bus)** Creates an SbusTx object. A pointer to the Serial object corresponding to the serial port used is passed. The TX pin of the serial port will transmit SBUS packets.

```C++
bfs::SbusTx sbus(&Serial1);
```
**(ESP32 ONLY) SbusRx(HardwareSerial &ast;bus, const int8_t rxpin, const int8_t txpin, const bool inv)** Creates an *SbusRx* object. A pointer to the *Serial* object corresponding to the serial port used is passed along with the RX pin number (*rxpin*), TX pin number (*txpin*), and whether inverted serial is used (*inv*). If *inv* is true, the signal is the standard inverted SBUS, otherwise it is non-inverted SBUS.

**void Begin()** Initializes SBUS communication.

```C++
sbus.Begin();
```

**(ESP-32 ONLY) void Begin(const int8_t rxpin, const int8_t txpin)** Initialized SBUS communication, given the Serial RX and TX pins.

**void Write()** Writes an SBUS packet. The packet is written immediately, you should regulate timing of sending packets to servos to maintain a frequency of approximately 100 Hz or 50 Hz, depending on the setup of the SBUS system.

```C++
sbus.Write();
```

**static constexpr int8_t NUM_CH()** A constant defining the number of SBUS channels (i.e. 16), useful for defining arrays to write the data from.

**void ch17(bool val)** Sets the value of channel 17 to be transmitted.
**bool Read()** Parses SBUS packets, returns true on successfully receiving an SBUS packet.

```C++
sbus.ch17(true);
if (sbus.Read()) {
// Do something with the received data
}
```

**void ch18(bool val)** Sets the value of channel 18 to be transmitted.
**SbusData data()** Returns the *SbusData* structure, populated with data from the last received packet.

```C++
sbus.ch18(true);
if (sbus.Read()) {
bfs::SbusData data = sbus.data();
}
```

**void lost_frame(bool val)** Sets whether to transmit the lost frame flag.

```C++
sbus.lost_frame(true);
```
# SbusTx
This class is used for transmitting SBUS data to SBUS capable servos.

**void failsafe(bool val)** Sets whether to transmit the failsafe flag.
**SbusTx(HardwareSerial &ast;bus)** Creates an *SbusTx* object. A pointer to the *Serial* object corresponding to the serial port used is passed. The TX pin of the serial port will receive SBUS packets.

```C++
sbus.failsafe(true);
bfs::SbusTx sbus(&Serial1);
```
**void ch(const std::array<int16_t, NUM_SBUS_CH_> &cmd)** Sets the channel data to be transmitted given an array of SBUS commands.
**SbusTx(HardwareSerial &ast;bus, const bool inv)** Creates an *SbusTx* object. A pointer to the *Serial* object corresponding to the serial port used is passed along with a second parameter, *inv*, which sets whether inverted serial is used. If *inv* is true, the signal is the standard inverted SBUS, otherwise it is non-inverted SBUS.
```C++
/* Set SBUS commands */
std::array<int16_t, bfs::SbusTx::NUM_CH()> cmd;
sbus.ch(cmd);
bfs::SbusTx sbus(&Serial1, false);
```

**bool ch17()** Returns the CH17 data to be transmitted.
**(ESP32 ONLY) SbusTx(HardwareSerial &ast;bus, const int8_t rxpin, const int8_t txpin, const bool inv)** Creates an *SbusTx* object. A pointer to the *Serial* object corresponding to the serial port used is passed along with the RX pin number (*rxpin*), TX pin number (*txpin*), and whether inverted serial is used (*inv*). If *inv* is true, the signal is the standard inverted SBUS, otherwise it is non-inverted SBUS.

```C++
bool ch17 = sbus.ch17();
```

**bool ch18()** Returns the CH18 data to be transmitted.
**void Begin()** Initializes SBUS communication.

```C++
bool ch18 = sbus.ch18();
sbus.Begin();
```

**bool lost_frame()** Returns the lost frame flag to be transmitted.
**void Write()** Writes an SBUS packet. The packet is written immediately, you should regulate timing of sending packets to servos to maintain a frequency of approximately 100 Hz or 50 Hz, depending on the setup of the SBUS system.

```C++
bool lost_frame = sbus.lost_frame();
sbus.Write();
```

**bool failsafe()** Returns the failsafe flag to be transmitted.
**void data(const SbusData &data)** Sets the SBUS data, which will be transmitted on the next *Write* method.

```C++
bool failsafe = sbus.failsafe();
bfs::SbusData data;
data.ch[0] = 900;
sbus.data(data);
```

**std::array<int16_t, NUM_SBUS_CH_> ch()** Returns the channel data to be transmitted.
**SbusData data()** Returns the SBUS data buffered in the *SbusTx* object, which will be transmitted on the next *Write* method.

```C++
std::array<int16_t, bfs::SbusRx::NUM_CH()> rx_ch = sbus.ch();
bfs::SbusData data = sbus.data();
```
41 changes: 15 additions & 26 deletions examples/arduino/sbus_example/sbus_example.ino
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
* Brian R Taylor
* [email protected]
*
* Copyright (c) 2021 Bolder Flight Systems Inc
* Copyright (c) 2022 Bolder Flight Systems Inc
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the “Software”), to
Expand All @@ -23,51 +23,40 @@
* IN THE SOFTWARE.
*/

/*
* This example reads an SBUS packet from an SBUS receiver and writes it to an
* SBUS compatible servo. The SBUS out capability (i.e. writing a command to
* the servo) could be generated independently; however, the packet timing
* would need to be controlled by the programmer, the write function simply
* generates an SBUS packet and writes it to the servos. In this case the
* packet timing is handled by the SBUS receiver and waiting for a good packet
* read.
*/

#include "sbus.h"

/* SbusRx object on Serial1 */
bfs::SbusRx sbus_rx(&Serial1);
/* SbusTx object on Serial1 */
bfs::SbusTx sbus_tx(&Serial1);
/* Array for storing SBUS data */
std::array<int16_t, bfs::SbusRx::NUM_CH()> sbus_data;
/* SBUS object, reading SBUS */
bfs::SbusRx sbus_rx(&Serial2);
/* SBUS object, writing SBUS */
bfs::SbusTx sbus_tx(&Serial2);
/* SBUS data */
bfs::SbusData data;

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

void loop() {
void loop () {
if (sbus_rx.Read()) {
/* Grab the received data */
sbus_data = sbus_rx.ch();
data = sbus_rx.data();
/* Display the received data */
for (int8_t i = 0; i < bfs::SbusRx::NUM_CH(); i++) {
Serial.print(sbus_data[i]);
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(sbus_rx.lost_frame());
Serial.print(data.lost_frame);
Serial.print("\t");
Serial.println(sbus_rx.failsafe());
Serial.println(data.failsafe);
/* Set the SBUS TX data to the received data */
sbus_tx.ch(sbus_data);
sbus_tx.data(data);
/* Write the data to the servos */
sbus_tx.Write();
}
}

22 changes: 11 additions & 11 deletions examples/cmake/sbus_example.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
* Brian R Taylor
* [email protected]
*
* Copyright (c) 2021 Bolder Flight Systems Inc
* Copyright (c) 2022 Bolder Flight Systems Inc
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the “Software”), to
Expand All @@ -26,11 +26,11 @@
#include "sbus.h"

/* SBUS object, reading SBUS */
bfs::SbusRx sbus_rx(&Serial1);
bfs::SbusRx sbus_rx(&Serial2);
/* SBUS object, writing SBUS */
bfs::SbusTx sbus_tx(&Serial1);
/* Array for storing SBUS data */
std::array<int16_t, bfs::SbusRx::NUM_CH()> sbus_data;
bfs::SbusTx sbus_tx(&Serial2);
/* SBUS data */
bfs::SbusData data;

int main() {
/* Serial to display data */
Expand All @@ -42,18 +42,18 @@ int main() {
while (1) {
if (sbus_rx.Read()) {
/* Grab the received data */
sbus_data = sbus_rx.ch();
data = sbus_rx.data();
/* Display the received data */
for (int8_t i = 0; i < bfs::SbusRx::NUM_CH(); i++) {
Serial.print(sbus_data[i]);
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(sbus_rx.lost_frame());
Serial.print(data.lost_frame);
Serial.print("\t");
Serial.println(sbus_rx.failsafe());
Serial.println(data.failsafe);
/* Set the SBUS TX data to the received data */
sbus_tx.ch(sbus_data);
sbus_tx.data(data);
/* Write the data to the servos */
sbus_tx.Write();
}
Expand Down
Loading

0 comments on commit 1c353d1

Please sign in to comment.