crsfparser_web_logo

CrsfParser C++ library

v1.0.0

Table of contents

Overview

CrsfParser is C++ library designed for decoding and encoding packets of CRSF FPV drone control protocol. The library used for CRSF packets translation and extracts only CRSF_FRAMETYPE_RC_CHANNELS_PACKED packets. For other packets types the library just detects them in data buffer. The library doesn’t have third-party dependencies, but test application depends on SerialPort library (provides methods to work with serial ports, source code included, Apache 2.0 license).

Versions

Table 1 - Library versions.

Version Release date What’s new
1.0.0 08.08.2024 - First version.

Library files

The library is supplied only by source code. The user is given a set of files in the form of a CMake project (repository). The repository structure is shown below:

CMakeLists.txt -------------- Main CMake file of the library.
src  ------------------------ Folder with library source code.
    CMakeLists.txt ---------- CMake file of the library.
    CrsfParser.h ------------ Main library header file.
    CrsfParserVersion.h ----- Header file with library version.
    CrsfParserVersion.h.in -- Service CMake file to generate version header.
    CrsfParser.cpp ---------- C++ implementation file.
test ------------------------ Folder for test application files.
    3rdparty ---------------- Folder with third-party libraries.
        CMakeLists.txt ------ CMake file to include third-party libraries.
        SerialPort ---------- Folder with SerialPort library source code.
    CMakeLists.txt ---------- CMake file for test application.
    main.cpp ---------------- Source C++ file of test application.

CrsfParser class description

CrsfParser class declaration

namespace cr
{
namespace clib
{
/// CRSF protocol parser.
class CrsfParser
{
public:

    /// Get library version.
    static std::string getVersion();

    /// Detect CRSF packet.
    CrsfPacket detect(uint8_t nextByte, uint8_t* packet, int& size);

    /// Get decoded RC channels.
    void getRcChannels(uint32_t channels[16]);

    /// Encode RC channels packet.
    bool encodeRcChannels(uint32_t channels[16], uint8_t* packet, int& size);
};
}
}

getVersion method

The getVersion() method return string of current CrsfParser class version. Method declaration:

static std::string getVersion();

Method can be used without CrsfParser class instance:

cout << "CrsfParser version: " << cr::clib::CrsfParser::getVersion();

Console output:

CrsfParser version: 1.0.0

detect method

The detect(…) method detects CRSF packets in data buffer and return packet type. The method checks only CRSF packets presence and returns detected packet and packet size. In additional the method specially detects CRSF_FRAMETYPE_RC_CHANNELS_PACKED packets and extracts RC channels values from them. Once the data from the serial port has been read the user must pass it to the method byte-by-byte (each byte separately in sequence). The library has an internal buffer. When the user passes another byte to the method, the library shifts the buffer by one byte and copies the new byte to the end of the buffer. If the library detects a valid CRSF frame in internal buffer, it returns general packet type or RC_CHANNELS packet type. Method declaration:

CrsfPacket detect(uint8_t nextByte, uint8_t* packet, int& size);
Parameter Value
nextByte Next read byte from serial port.
packet Pointer to packet buffer. If any CRSF packet detected the method will copy packet data to buffer. Size of buffer must be >= 64.
size Size of detected packet. If no packets detected size will 0.

Returns: Packet type according to CrsfPacket enum declared in CrsfParser.h file. Enum declaration:

namespace cr
{
namespace clib
{
/// CRSF packet type.
enum class CrsfPacket
{
    /// Not packet.
    NONE = 1,
    /// RC channels packet.
    RC_CHANNELS,
    /// Other.
    OTHER
};
}
}

Table 2 - Packets types.

Type Description
NONE No valid CRSF packet detected.
RC_CHANNELS CRSF_FRAMETYPE_RC_CHANNELS_PACKED detected. If this packet detected the detect(…) method will parser RC channels values and will store them into internal variables. User can obtain RC channels values with getRcChannels method.
OTHER Any other CRSF packet detected.

Bellow is an example of using detect(…) method:

// CRSF packet with additional data
uint8_t rfChannelsPacket[] = { 
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xc8, 0x18, 0x16,
    0xe0, 0x03, 0x1f, 0xf8, 0xc0, 0x07, 0x3e, 0xf0, 0x81, 0x0f, 0x7c,
    0xe0, 0x03, 0x1f, 0xf8, 0xc0, 0x07, 0x3e, 0xf0, 0x81, 0x0f, 0x7c, 0xad,
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
};

// Detect byte-by-byte.
CrsfParser parser;
uint8_t packet[64];
int packetSize = 0;
for (int i = 0; i < sizeof(rfChannelsPacket); ++i)
{
    CrsfPacket type = parser.detect(rfChannelsPacket[i], packet, packetSize);
    if (type == CrsfPacket::RC_CHANNELS)
    {
        uint32_t channels[16];
        parser.getRcChannels(channels);
        cout << "RC channels: ";
        for (int i = 0; i < 16; ++i)
            cout << channels[i] << " ";
        cout << endl;
    }
    else if (type == CrsfPacket::OTHER)
    {
        cout << "Other packet" << endl;
    }
    else if (type == CrsfPacket::NONE)
    {
        cout << "None packet" << endl;
    }
    else 
    {
        cout << "Unknown packet" << endl;
    }
}

getRcChannels method

The getRcChannels(…) method returns RC channels values after decoding CRSF_FRAMETYPE_RC_CHANNELS_PACKED packet by detect(…) method. Method declaration:

void getRcChannels(uint32_t channels[16]);
Parameter Value
channels Pointer to array of RC channels values.

Bellow is an example of using getRcChannels(…) method:

// CRSF packet with additional data
uint8_t rfChannelPacket[] = { 
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xc8, 0x18, 0x16,
    0xe0, 0x03, 0x1f, 0xf8, 0xc0, 0x07, 0x3e, 0xf0, 0x81, 0x0f, 0x7c,
    0xe0, 0x03, 0x1f, 0xf8, 0xc0, 0x07, 0x3e, 0xf0, 0x81, 0x0f, 0x7c, 0xad,
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
};

// Detect byte-by-byte.
CrsfParser parser;
uint8_t packet[64];
int packetSize = 0;
for (int i = 0; i < sizeof(rfChannelPacket); ++i)
{
    CrsfPacket type = parser.detect(rfChannelPacket[i], packet, packetSize);
    if (type == CrsfPacket::RC_CHANNELS)
    {
        uint32_t channels[16];
        parser.getRcChannels(channels);
        cout << "RC channels: ";
        for (int i = 0; i < 16; ++i)
            cout << channels[i] << " ";
        cout << endl;
    }
}

encodeRcChannels method

The encodeRcChannels(…) method encodes CRSF_FRAMETYPE_RC_CHANNELS_PACKED packet. It used to generate CRSF_FRAMETYPE_RC_CHANNELS_PACKED to control FPV drone. Method declaration:

bool encodeRcChannels(uint32_t channels[16], uint8_t* packet, int& size);
Parameter Value
channels Pointer to array of RC channels values. Each channel can have value from 0 to 1984. The method checks if values are valid.
packet Pointer to encoded packet data. Size of buffer must be >= 64.
size Size of packet. Will be 26 bytes.

Returns: TRUE if CRSF_FRAMETYPE_RC_CHANNELS_PACKED packet is encoded or FALSE if any any input RC channel value out of range [0:1984].

Bellow is an example of using encodeRcChannels(…) method:

// Fill channels.
uint32_t channels[16];
for (int i = 0; i < 16; ++i)
    channels[i] = 992;

// Encoder RC channels.
uint8_t encodedPacket[64];
int encodedSize = 0;
parser.encodeRcChannels(channels, encodedPacket, encodedSize);

// Show data.
cout << "Encoded packet: ";
for (int i = 0; i < encodedSize; ++i)
    cout << hex << (int)encodedPacket[i] << " ";
cout << dec << endl;

Test application

Folder CrsfParser/test contains the test application files. The test application opens two serial ports. The application receives data from first serial ports, detects CRSF packets and transmit the to secod serial port. When the application detects any CRSF packet it shows packet data in HEX format. In case detection CRSF_FRAMETYPE_RC_CHANNELS_PACKED packet the application shows RC channels values, encodes them and transmit via second serial port. After start user should set first (to receive data) and second (to transmit data) serial ports names. In Linux user should set full name of serial port file, but in Windows juts serial port number (COM port number). Application’s console output:

CrsfParser v1.0.0 test
Set COM port num (1,2,3,...) to receive data: 1
Set COM port num (1,2,3,...) to transmit data: 2
No input data
No input data
Other packet detected: c8 18 16 e0 3 1f f8 c0 7 3e f0 81 f 7c e0 3 1f f8 c0 7 3e f0 81 f 7c ad
No input data
RC channels: 992 992 992 992 992 992 992 992 992 992 992 992 992 992 992 992

Build and connect to your project

Typical commands to build CrsfParser library:

cd CrsfParser
mkdir build
cd build
cmake ..
make

If you want to connect CrsfParser library to your CMake project as source code you can make the following. For example, if your repository has structure:

CMakeLists.txt
src
    CMakeList.txt
    yourLib.h
    yourLib.cpp

Create folder 3rdparty and copy folder of CrsfParser repository there. New structure of your repository:

CMakeLists.txt
src
    CMakeList.txt
    yourLib.h
    yourLib.cpp
3rdparty
    CrsfParser

Create CMakeLists.txt file in 3rdparty folder. CMakeLists.txt should contain:

cmake_minimum_required(VERSION 3.13)

################################################################################
## 3RD-PARTY
## dependencies for the project
################################################################################
project(3rdparty LANGUAGES CXX)

################################################################################
## SETTINGS
## basic 3rd-party settings before use
################################################################################
# To inherit the top-level architecture when the project is used as a submodule.
SET(PARENT ${PARENT}_YOUR_PROJECT_3RDPARTY)
# Disable self-overwriting of parameters inside included subdirectories.
SET(${PARENT}_SUBMODULE_CACHE_OVERWRITE OFF CACHE BOOL "" FORCE)

################################################################################
## CONFIGURATION
## 3rd-party submodules configuration
################################################################################
SET(${PARENT}_SUBMODULE_CRSF_PARSER                     ON  CACHE BOOL "" FORCE)
if (${PARENT}_SUBMODULE_CRSF_PARSER)
    SET(${PARENT}_CRSF_PARSER                           ON  CACHE BOOL "" FORCE)
    SET(${PARENT}_CRSF_PARSER_TEST                      OFF CACHE BOOL "" FORCE)
endif()

################################################################################
## INCLUDING SUBDIRECTORIES
## Adding subdirectories according to the 3rd-party configuration
################################################################################
if (${PARENT}_SUBMODULE_CRSF_PARSER)
    add_subdirectory(CrsfParser)
endif()

File 3rdparty/CMakeLists.txt adds folder CrsfParser to your project and excludes test application from compiling (by default test application excluded from compiling if CrsfParser included as sub-repository). Your repository new structure will be:

CMakeLists.txt
src
    CMakeList.txt
    yourLib.h
    yourLib.cpp
3rdparty
    CMakeLists.txt
    CrsfParser

Next you need include folder 3rdparty in main CMakeLists.txt file of your repository. Add string at the end of your main CMakeLists.txt:

add_subdirectory(3rdparty)

Next you have to include CrsfParser library in your src/CMakeLists.txt file:

target_link_libraries(${PROJECT_NAME} CrsfParser)

Done!