From d43bc711cce096424befed89c0f4b748c432942a Mon Sep 17 00:00:00 2001 From: Witty-Wizard Date: Mon, 1 Jul 2024 00:17:18 +0530 Subject: [PATCH] Added F.Port Channel Decoding --- Docs/fport.md | 23 ++++++++++++ Docs/index.md | 87 +++++++++++++++++++++++---------------------- Docs/sbus.md | 8 ++--- src/SerialIO.h | 19 +++++----- src/crsf/crsf.h | 6 ++++ src/fport/fport.cpp | 44 +++++++++++++++++++++++ src/fport/fport.h | 48 +++++++++++++++++++++++++ src/ibus/ibus.cpp | 63 ++++++++++++++------------------ src/ibus/ibus.h | 3 +- src/sbus/sbus.cpp | 40 +++++++++------------ src/sbus/sbus.h | 12 ++++--- 11 files changed, 231 insertions(+), 122 deletions(-) create mode 100644 Docs/fport.md create mode 100644 src/fport/fport.cpp create mode 100644 src/fport/fport.h diff --git a/Docs/fport.md b/Docs/fport.md new file mode 100644 index 0000000..ce53ea3 --- /dev/null +++ b/Docs/fport.md @@ -0,0 +1,23 @@ +# FrSky F.Port {#frskyfport} + +FrSky F.Port is a protocol developed by FrSky Electronic Co., Ltd., designed for communication between receivers and connected devices like servos or sensors. It is a one-line bus system that supports both control and data transmission, operating at a higher speed (115200 baud per second) compared to its predecessor, S.Port. + +## Specifications + +- [Physical Layer](#physical_fport) +- [Message Format](#format_fport) + +### Physical Layer {#physical_fport} + +The physical layer of the FrSky F.Port protocol utilizes UART (Universal Asynchronous Receiver-Transmitter) communication at a baud rate of 115200. + +#### Uart Configuration + +When it comes to the configuration of the UART communication for SBus, it uses the following format: + +- **8 bits of data**: Each data frame consists of 8 bits, representing the information being transmitted. +- **No parity**: It does not use parity bit instead it uses a checksum at the end of the whole packet. +- **1 stop bits**: Following the data bits and the parity bit, there are 1 stop bits. Stop bits indicate the end of a data frame and provide timing for the receiving device to process the data. + +### Message Format {#format_fport} +For the data format and more details go to [this link](https://github.com/betaflight/betaflight/files/1491056/F.Port.protocol.betaFlight.V2.1.2017.11.21.pdf) diff --git a/Docs/index.md b/Docs/index.md index 02c12d7..d32f16c 100644 --- a/Docs/index.md +++ b/Docs/index.md @@ -3,11 +3,13 @@ SerialIO is a common library designed to simplify the implementation of RC proto # Supported Protocol -- [Futaba SBus protocol](#sbus) +- [Futaba SBus protocol](#futabasbus) - [Crossfire RC protocol](https://github.com/crsf-wg/crsf/wiki) - [Flysky IBus protocol](https://basejunction.wordpress.com/2015/08/23/en-flysky-i6-14-channels-part1/) +- [FrSky F.Port](#frskyfport) # Getting Started + - [Installation](#installation) - [Tutorial](#tutorial) - [Examples](#example) @@ -36,64 +38,64 @@ lib_deps = Witty-Wizard/SerialIO To use the library for decoding RC protocols in your Arduino project, follow these steps: -1. **Include Necessary Libraries**: - Include the required libraries at the beginning of your sketch: - ```cpp - #include - ``` -2. **Define Channel Data Structure**: - Define a structure to hold the decoded RC channel data. - ```cpp - crsf_channels_t channelData; - ``` -3. **Instantiate SerialIO Object**: - Create an instance of the SerialIO class, specifying the serial port, RX pin, and TX pin: - ```cpp - SerialIO *receiver = new crsf(&Serial1, pinRX, pinTX); - ``` - To instantiate a SerialIO object for receiving data only, you can create an instance of the crsf class specifying the serial port, RX pin: - ```cpp - SerialIO *receiver = new crsf(&Serial1, pinRX); - ``` -4. **Initialize Communication**: - Call the begin() method to initialize communication with the specified serial port: - ```cpp - void setup() { - receiver->begin(); - } - ``` - -5. **Process Incoming Data**: -In the loop() function, call the processIncoming() method to process incoming bytes: - +1. **Include Necessary Libraries**: + Include the required libraries at the beginning of your sketch: + ```cpp + #include + ``` +2. **Define Channel Data Structure**: + Define a structure to hold the decoded RC channel data. + ```cpp + crsf_channels_t channelData; + ``` +3. **Instantiate SerialIO Object**: + Create an instance of the SerialIO class, specifying the serial port, RX pin, and TX pin: + ```cpp + SerialIO *receiver = new crsf(&Serial1, pinRX, pinTX); + ``` + To instantiate a SerialIO object for receiving data only, you can create an instance of the crsf class specifying the serial port, RX pin: ```cpp - receiver->processIncoming(); + SerialIO *receiver = new crsf(&Serial1, pinRX); ``` -6. **Retrieve Channel Data**: -To retrieve the decoded RC channel data, call the getChannel() method, passing a pointer to the channelData structure: +4. **Initialize Communication**: + Call the begin() method to initialize communication with the specified serial port: ```cpp - receiver->getChannel(&channelData); + void setup() { + receiver->begin(); + } ``` -**see also**: - - @ref SerialIO - - @ref sbus - - @ref crsf - - @ref ibus + +5. **Process Incoming Data**: + In the loop() function, call the processIncoming() method to process incoming bytes: + + ```cpp + receiver->processIncoming(); + ``` + +6. **Retrieve Channel Data**: + To retrieve the decoded RC channel data, call the getChannel() method, passing a pointer to the channelData structure: + + ```cpp + receiver->getChannel(&channelData); + ``` + + **see also**: - @ref SerialIO - @ref sbus - @ref crsf - @ref ibus ## Examples {#example} ### CRSF Basic Example + @include "./examples/espresiff/crsf_basic/crsf_basic.ino" ### SBUS Basic Example -@include "./examples/espresiff/sbus_basic/sbus_basic.ino" - +@include "./examples/espresiff/sbus_basic/sbus_basic.ino" # Guide to Adding More Protocols ## Steps + To add more protocols to your project, follow these steps: 1. **Create a New Protocol File** : Start by creating a new header file for each additional protocol you want to add. For example, if you want to add a protocol named "XYZ", create a file named `xyz_protocol.h`. @@ -151,4 +153,5 @@ private: ``` # License + This library is distributed under the GNU [General Public License version 3.0](https://www.gnu.org/licenses/gpl-3.0.html). diff --git a/Docs/sbus.md b/Docs/sbus.md index b13d475..d6872b4 100644 --- a/Docs/sbus.md +++ b/Docs/sbus.md @@ -1,14 +1,14 @@ -# Futaba SBus {#sbus} +# Futaba SBus {#futabasbus} The Futaba S.Bus protocol was introduced around 2009. It was developed by Futaba, a leading manufacturer of RC systems, to address the limitations of traditional PWM (Pulse Width Modulation) systems used for RC control.Unlike PWM, which requires separate wires for each channel, S.Bus uses a single serial connection to transmit data for multiple channels, reducing the complexity of wiring in RC models. \image html sbus.png width=700cm ## Specifications -- [Physical Layer](#physical) +- [Physical Layer](#physical_sbus) - [Message Format](#format) -### Physical Layer {#physical} +### Physical Layer {#physical_sbus} The physical layer of the Futaba SBus protocol utilizes UART (Universal Asynchronous Receiver-Transmitter) communication at a baud rate of 100000. @@ -25,7 +25,7 @@ When it comes to the configuration of the UART communication for SBus, it uses t For using SBus with a microcontroller, an inverter is typically required for the inverted UART logic level. This inversion is necessary because traditional UART operates with an active high level, while SBus uses inverted UART with an active low level. \image html inverter.png width=600cm -### Message Format {#format} +### Message Format {#format_sbus} \image html formatsbus.png The SBus protocol uses a specific message format for transmitting control data from the transmitter to the receiver. diff --git a/src/SerialIO.h b/src/SerialIO.h index cf83e8a..78c36d4 100644 --- a/src/SerialIO.h +++ b/src/SerialIO.h @@ -5,9 +5,10 @@ #pragma once #ifndef SerialIO_H #define SerialIO_H -#include "crsf/crsf_protocol.h" #include +#define PACKED __attribute__((packed)) + /**************************************************************************/ /*! @brief Class that stores state and functions for initialising and decoding @@ -52,18 +53,14 @@ class SerialIO { protected: Stream - *_rxPort; // Pointer to the hardware serial port used for communication. - bool _headerDetected; // Flag indicating whether a header has been detected - // in the incoming data. - bool _inverted; // Indicates whether the serial signal is inverted (true) or - // not (false). - uint8_t _rxIndex; // Index for the receive_buffer. - int _rxPin; // The RX pin number. - int _txPin; // The TX pin number. - uint8_t _buffer; - uint8_t _prevBuffer; + *_rxPort; // Pointer to the hardware serial port used for communication. + bool _inverted; // Indicates whether the serial signal is inverted (true) or + // not (false). + int _rxPin; // The RX pin number. + int _txPin; // The TX pin number. }; #include "crsf/crsf.h" +#include "fport/fport.h" #include "ibus/ibus.h" #include "sbus/sbus.h" diff --git a/src/crsf/crsf.h b/src/crsf/crsf.h index 1d6d32a..370c019 100644 --- a/src/crsf/crsf.h +++ b/src/crsf/crsf.h @@ -1,6 +1,7 @@ /*! * @file crsf.h * @brief Header file for the CRSF protocol implementation. + * @author Witty-Wizard */ #pragma once @@ -8,6 +9,7 @@ #define CRSF_H #include "../SerialIO.h" // Include header file for the serial IO class +#include "crsf_protocol.h" #define CRC8_POLY_D5 0xD5 @@ -18,6 +20,10 @@ class crsf : public SerialIO { private: crsf_channels_t channelData; uint8_t _rxData[CRSF_MAX_PACKET_SIZE]; + bool _headerDetected; // Flag indicating whether a header has been detected in + // the incoming data. + uint8_t _rxIndex; // Index for the receive_buffer. + uint8_t _buffer; public: /** diff --git a/src/fport/fport.cpp b/src/fport/fport.cpp new file mode 100644 index 0000000..c79ec3a --- /dev/null +++ b/src/fport/fport.cpp @@ -0,0 +1,44 @@ +/*! + * @file fport.cpp + * @brief Source file for the F.Port implementations + * @author Witty-Wizard + */ + +#include "fport.h" + +fport::fport(Stream *rxPort, int rxPin, int txPin, bool inverted) + : SerialIO(rxPort, rxPin, txPin, inverted) {} + +void fport::begin() { + +// Initialize the serial port +#if defined(ARDUINO_ARCH_ESP32) + HardwareSerial *serialPort = (HardwareSerial *)_rxPort; + serialPort->begin(FPORT_BAUDRATE, SERIAL_8N1, _rxPin, _txPin, _inverted); +#elif defined(ARDUINO_ARCH_RP2040) + SerialUART *serialPort = (SerialUART *)_rxPort; + serialPort->setPinout(_txPin, _rxPin); + serialPort->begin(FPORT_BAUDRATE, SERIAL_8N1); +#else +#warning "Unsupported hardware platform." +#endif +} + +void fport::processIncoming() { + while (_rxPort->available()) { + _rx_buffer[FPORT_MAX_PACKET_SIZE - 1] = _rxPort->read(); + if (_rx_buffer[0] == 0x7E && + _rx_buffer[FPORT_MAX_PACKET_SIZE - 1] == 0x7E) { + memcpy(&_channelData, &_rx_buffer[4], sizeof(_channelData)); + } else { + leftShift(_rx_buffer, sizeof(_rx_buffer)); + } + } +} + +void fport::getChannel(void *channelData) {} + +void fport::leftShift(uint8_t arr[], size_t size) { + memmove(arr, arr + 1, (size - 1)); + arr[size - 1] = 0xFF; +} \ No newline at end of file diff --git a/src/fport/fport.h b/src/fport/fport.h new file mode 100644 index 0000000..012e684 --- /dev/null +++ b/src/fport/fport.h @@ -0,0 +1,48 @@ +/*! + * @file fport.h + * @brief Header file for the FPort Protocol definations + * @author Witty-Wizard + */ + +#pragma once +#ifndef FPORT_H +#define FPORT_H + +#include "../SerialIO.h" + +#define FPORT_BAUDRATE 115200 ///< F.Port baudrate +#define FPORT_MAX_PACKET_SIZE 29 ///< F.Port maximum packet length + +typedef struct fport_channels_s { + unsigned channel1 : 11; + unsigned channel2 : 11; + unsigned channel3 : 11; + unsigned channel4 : 11; + unsigned channel5 : 11; + unsigned channel6 : 11; + unsigned channel7 : 11; + unsigned channel8 : 11; + unsigned channel9 : 11; + unsigned channel10 : 11; + unsigned channel11 : 11; + unsigned channel12 : 11; + unsigned channel13 : 11; + unsigned channel14 : 11; + unsigned channel15 : 11; + unsigned channel16 : 11; +} PACKED fport_channels_t; + +class fport : public SerialIO { +private: + uint8_t _rx_buffer[FPORT_MAX_PACKET_SIZE]; + fport_channels_t _channelData; + +public: +explicit fport(Stream *rxPort, int rxPin = -1, int txPin = -1, + bool inverted = true); + void begin() override; + void processIncoming() override; + void getChannel(void *channelData) override; + void leftShift(uint8_t arr[],size_t size); +}; +#endif \ No newline at end of file diff --git a/src/ibus/ibus.cpp b/src/ibus/ibus.cpp index 16ba45c..dec60c4 100644 --- a/src/ibus/ibus.cpp +++ b/src/ibus/ibus.cpp @@ -23,48 +23,34 @@ void ibus::begin() { void ibus::processIncoming() { while (_rxPort->available()) { - _prevBuffer = _buffer; - _buffer = _rxPort->read(); - if (_headerDetected) { - _rxData[_rxIndex++] = _buffer; - - } else { - if (_prevBuffer == IBUS_HEADER1 && _buffer == IBUS_HEADER2) { - _headerDetected = true; - _rxIndex = 0; - _rxData[_rxIndex++] = IBUS_HEADER1; - _rxData[_rxIndex++] = IBUS_HEADER2; + _rxData[IBUS_MAX_PACKET_SIZE - 1] = _rxPort->read(); + if (_rxData[0] == IBUS_HEADER1 && _rxData[1] == IBUS_HEADER2) { + if (checkSum()) { + _channelData.header = (_rxData[2] << 8) | _rxData[0]; + _channelData.channel1 = (_rxData[3] << 8) | _rxData[2]; + _channelData.channel2 = (_rxData[5] << 8) | _rxData[4]; + _channelData.channel3 = (_rxData[7] << 8) | _rxData[6]; + _channelData.channel4 = (_rxData[9] << 8) | _rxData[8]; + _channelData.channel5 = (_rxData[11] << 8) | _rxData[10]; + _channelData.channel6 = (_rxData[13] << 8) | _rxData[12]; + _channelData.channel7 = (_rxData[15] << 8) | _rxData[14]; + _channelData.channel8 = (_rxData[17] << 8) | _rxData[16]; + _channelData.channel9 = (_rxData[19] << 8) | _rxData[18]; + _channelData.channel10 = (_rxData[21] << 8) | _rxData[20]; + _channelData.channel11 = (_rxData[23] << 8) | _rxData[22]; + _channelData.channel12 = (_rxData[25] << 8) | _rxData[24]; + _channelData.channel13 = (_rxData[27] << 8) | _rxData[26]; + _channelData.channel14 = (_rxData[29] << 8) | _rxData[28]; + _channelData.checksum = (_rxData[31] << 8) | _rxData[30]; } - } - - if (_rxIndex == sizeof(_rxData) / sizeof(_rxData[0])) { - _rxIndex = 0; - _headerDetected = false; + } else { + leftShift(_rxData, sizeof(_rxData)); } } - - if (checkSum()) { - channelData.header = (_rxData[2] << 8) | _rxData[0]; - channelData.channel1 = (_rxData[3] << 8) | _rxData[2]; - channelData.channel2 = (_rxData[5] << 8) | _rxData[4]; - channelData.channel3 = (_rxData[7] << 8) | _rxData[6]; - channelData.channel4 = (_rxData[9] << 8) | _rxData[8]; - channelData.channel5 = (_rxData[11] << 8) | _rxData[10]; - channelData.channel6 = (_rxData[13] << 8) | _rxData[12]; - channelData.channel7 = (_rxData[15] << 8) | _rxData[14]; - channelData.channel8 = (_rxData[17] << 8) | _rxData[16]; - channelData.channel9 = (_rxData[19] << 8) | _rxData[18]; - channelData.channel10 = (_rxData[21] << 8) | _rxData[20]; - channelData.channel11 = (_rxData[23] << 8) | _rxData[22]; - channelData.channel12 = (_rxData[25] << 8) | _rxData[24]; - channelData.channel13 = (_rxData[27] << 8) | _rxData[26]; - channelData.channel14 = (_rxData[29] << 8) | _rxData[28]; - channelData.checksum = (_rxData[31] << 8) | _rxData[30]; - } } void ibus::getChannel(void *channelData) { - *static_castchannelData) *>(channelData) = this->channelData; + *static_cast(channelData) = _channelData; } bool ibus::checkSum() { @@ -84,3 +70,8 @@ bool ibus::checkSum() { // Check if the sum matches the expected CRC return (sum == 0xFFFF); // Assuming IBUS CRC is 0xFFFF when correct } + +void ibus::leftShift(uint8_t arr[], size_t size) { + memmove(arr, arr + 1, (size - 1)); + arr[size - 1] = 0xFF; +} diff --git a/src/ibus/ibus.h b/src/ibus/ibus.h index 53a5bf8..36676f6 100644 --- a/src/ibus/ibus.h +++ b/src/ibus/ibus.h @@ -38,7 +38,7 @@ typedef struct ibus_channels_s { */ class ibus : public SerialIO { private: - ibus_channels_t channelData; + ibus_channels_t _channelData; uint8_t _rxData[IBUS_MAX_PACKET_SIZE]; ///< Buffer to store received IBUS data bool checkSum(); @@ -70,6 +70,7 @@ class ibus : public SerialIO { * channel data will be stored. */ void getChannel(void *channelData) override; + void leftShift(uint8_t arr[], size_t size); }; #endif // IBUS_H diff --git a/src/sbus/sbus.cpp b/src/sbus/sbus.cpp index 362ee01..596eea7 100644 --- a/src/sbus/sbus.cpp +++ b/src/sbus/sbus.cpp @@ -1,3 +1,8 @@ +/*! + * @file sbus.cpp + * @brief Source file for the SBus implementations + * @author Witty-Wizard + */ #include "sbus.h" sbus::sbus(Stream *rxPort, int rxPin, int txPin, bool inverted) @@ -14,38 +19,27 @@ void sbus::begin() { serialPort->setPinout(_txPin, _rxPin); serialPort->begin(SBUS_BAUDRATE, SERIAL_8E2); #else -#warning #warning "Unsupported hardware platform." +#warning "Unsupported hardware platform." #endif } void sbus::processIncoming() { while (_rxPort->available()) { - _prevBuffer = _buffer; - _buffer = _rxPort->read(); - - if (_headerDetected == true) { - _rxData[_rxIndex] = _buffer; - _rxIndex++; - if (_rxIndex > 23) { - _headerDetected = false; - } + _rxData[SBUS_MAX_PACKET_SIZE - 1] = _rxPort->read(); + if (_rxData[0] == HEADER_SBUS && + _rxData[SBUS_MAX_PACKET_SIZE - 1] == FOOTER_SBUS) { + memcpy(&_channelData, _rxData, sizeof(_channelData)); } else { - if (_prevBuffer == FOOTER_SBUS && _buffer == HEADER_SBUS) { - _headerDetected = true; - _rxData[0] = 0x0F; - _rxData[24] = 0x00; - _rxIndex = 1; - } - } - - if (_rxIndex == sizeof(_rxData) / sizeof(_rxData[0])) { - _rxIndex = 0; - _headerDetected = false; + leftShift(_rxData, sizeof(_rxData)); } } - memcpy(&channelData, _rxData, sizeof(channelData)); } void sbus::getChannel(void *channelData) { - *static_castchannelData) *>(channelData) = this->channelData; + *static_cast(channelData) = _channelData; +} + +void sbus::leftShift(uint8_t arr[], size_t size) { + memmove(arr, arr + 1, (size - 1)); + arr[size - 1] = 0xFF; } \ No newline at end of file diff --git a/src/sbus/sbus.h b/src/sbus/sbus.h index 82fda71..2ad901d 100644 --- a/src/sbus/sbus.h +++ b/src/sbus/sbus.h @@ -9,10 +9,10 @@ #include "../SerialIO.h" // Include header file for the serial IO class -#define HEADER_SBUS 0x0F ///< SBUS Header Byte -#define FOOTER_SBUS 0x00 ///< SBUS Footer Byte -#define SBUS_BAUDRATE 100000 ///< SBUS baudrate -#define SBUS_MAX_PACKET_SIZE 25 ///< SBUS packet length +#define HEADER_SBUS 0x0F ///< SBus Header Byte +#define FOOTER_SBUS 0x00 ///< SBus Footer Byte +#define SBUS_BAUDRATE 100000 ///< SBus baudrate +#define SBUS_MAX_PACKET_SIZE 25 ///< SBus packet length typedef struct sbus_channels_s { unsigned header : 8; @@ -45,7 +45,7 @@ typedef struct sbus_channels_s { */ class sbus : public SerialIO { private: - sbus_channels_t channelData; + sbus_channels_t _channelData; uint8_t _rxData[SBUS_MAX_PACKET_SIZE]; public: @@ -76,6 +76,8 @@ class sbus : public SerialIO { * channel data will be stored. */ void getChannel(void *channelData) override; + + void leftShift(uint8_t arr[], size_t size); }; #endif // SBUS_H