CRSF (Crossfire) Protocol Specification

repository·main·Indexed 41 days ago

https://github.com/tbs-fpv/tbs-crsf-spec

The authoritative documentation for the CRSFv3 protocol developed by Team BlackSheep. This binary frame-based protocol is designed for low-latency, high-update-rate bidirectional communication between RC devices, supporting telemetry sharing and remote configuration of Flight Controllers (FC) and OSDs. It covers hardware interface requirements (UART and I2C), frame construction, CRC8 implementation, routing logic, and detailed parameter data types.

Tokens
11.3K
Snippets
32
Records
41
Agent score
1%

What's inside CRSF

  1. Overview of the CRSF Protocol

    main

    CRSF (Crossfire) is a binary frame-based protocol designed for low-latency, high-update-rate communication between radio-controlled (RC) devices. It supports bidirectional communication, allowing for telemetry sharing from a flying platform to an RC, and remote configuration of devices like Flight Controllers (FC) or OSDs via the RC.

    Key Features:

    • Low latency for RC signals (RC-TX and RX-FC).
    • Bidirectional telemetry.
    • Remote configuration of connected devices.
    • Receiver serial number sharing for model matching.
  2. Use 0x88 Rotorflight Telemetry Envelope

    main

    The 0x88 frame is used to carry Rotorflight telemetry data from the Flight Controller to the Transmitter.

    Because the exact format depends on the specific Rotorflight version, the frame encoder (in the flight controller) and the Lua decoder (in the transmitter) are released as a matched pair. This allows different models on the same transmitter to utilize different protocol versions simultaneously.

  3. Understand 0x2B Parameter Settings (Entry) frames

    main

    The 0x2B frame type is used by a device (node) to share parameter definitions with a host. This includes the parameter's name, type, constraints (min/max/default), and hierarchy.

    Key Concepts

    • Root Folder: Parameter_number 0 is a special entry that defines the device's root folder structure. Its data_type must be 0x0B FOLDER. A suggested name is "ROOT" (0x52 0x4F 0x4F 0x54 0x00).
    • Fallback Behavior: If a device does not respond to a request for Parameter_number 0, it does not support a root folder structure. The host should then request parameters sequentially starting from number 1 up to the Parameters_total value found in the 0x29 Parameter Device Information frame.
    • Payload Chunking: If the Data_type_payload_chunk is greater than 56 bytes, the payload will be split into multiple frames.
    • Hidden Parameters: The data_type is an 8-bit value where bit 7 is the visibility flag (1 = hidden, 0 = visible) and bits 6-0 represent the actual data_type enum.
        uint8_t         Sync_byte;                  // 0xc8
        uint8_t         Frame_length;
        uint8_t         Frame_type;                 // 0x2b = Parameter settings (entry)
        uint8_t         Destination_address;        // 0xea = RC
        uint8_t         Origin_address;             // 0xee = TX
        uint8_t         Parameter_number;           // starting from 0
        uint8_t         Parameter_chunks_remaining; // Chunks remaining count
        {
            Data_type_payload_chunk;                // see payload for each type below
        }
        uint8_t         CRC_8;                      // Frame CRC
  4. How to read large parameters using Chunks

    main

    The maximum CRSF frame size is 64 bytes (including sync byte and CRC). If a parameter requested via 0x2B Parameter settings (entry) or 0x2C Parameter settings (read) is larger than the maximum size, the device will split it into multiple chunks.

    Workflow:

    1. Request: The host requests a parameter chunk (starting with chunk 0) using 0x2C.
    2. Response: The device replies with 0x2B. If the parameter is split, the reply includes Parameter_chunks_remaining.
    3. Iteration: If chunks_remaining > 0, the host must increment the chunk count and send a new 0x2C request for the next chunk.
    4. Completion: When chunks_remaining == 0, the host has received the full parameter.

    Chunk Payload Structure:

    uint8_t Parameter_number;                           // starting from 0
    uint8_t Parameter_chunks_remaining;                 // Chunks remaining count
            Part of Parameter settings (entry) payload. // up to 56 bytes
  5. CRSF Frame Construction and Structure

    main

    CRSF frames are limited to a maximum of 64 bytes (including Sync and CRC bytes). The protocol uses Big Endian (MSB) endianness.

    Frame Types

    1. Broadcast Frame: Used for general broadcasting. [Sync byte] [Frame Length] [Type] [Payload] [CRC]
    2. Extended Header Frame: Used for point-to-point routing. [Sync byte] [Frame Length] [Type] [Destination Address] [Origin Address] [Payload] [CRC]

    Field Details

    • Sync byte: The receiver should expect one of the following:
      • 0xC8 (Serial sync byte)
      • 0x00 (Broadcast device address)
      • A specific Device Address
    • Frame length: The number of bytes in the frame excluding the Sync byte and the Frame Length byte itself (i.e., Total Frame Size - 2).
      • Valid range: 2 to 62.
      • If the value is out of range, the frame must be discarded.
    • Payload Handling:
      • If a frame is larger than the expected size for its type, the receiver should ignore the extra fields (this allows for protocol version extensions).
      • If optional fields are present, they may be represented as empty fields or be entirely absent. Do not attempt to read optional fields beyond the defined payload length.
  6. CRSF Routing Logic

    main

    CRSF operates as a star network with fixed address tables on each node.

    Routing Rule: If a device has multiple CRSF ports, it is required to forward all received frames to the other ports.

    Warning: Loop connections are strictly forbidden, as they would cause messages to be forwarded endlessly.

  7. Parse Voltage Group telemetry (0x0E)

    main

    The 0x0E frame transmits voltage telemetry. The interpretation of the Voltage_source_id determines how the Voltage_values[] array is processed:

    • 0..127: Interpreted as cell voltages of a single battery (up to 29S). Multiple batteries can be reported using different source_ids (e.g., 0 for battery 1, 1 for battery 2).
    • 128..255: Interpreted as general voltages from a single source (e.g., an ESC reporting incoming voltage, BEC output, and MCU voltage). Use 128 for ESC 1, 129 for ESC 2, etc.
        uint8_t     Voltage_source_id;  // source of the voltages
        uint16_t    Voltage_values[];   // Up to 29 voltages in millivolts (e.g. 3.850V = 3850)
  8. Wrap MSP requests and responses using 0x7A and 0x7B

    main

    Betaflight devices support wrapping MSP (MultiWii Serial Protocol) frames within CRSF frames.

    • 0x7A (Request): Wraps MSP requests ($M< or $X<).
    • 0x7B (Response): Wraps MSP responses ($M>, $X>, $M!, $X!).

    Implementation Details:

    1. Stripping: The MSP header ($ + M/X + [/]/!) and the original MSP CRC are stripped before packing into the CRSF payload.
    2. Chunking: If the MSP body exceeds the 64-byte CRSF limit, it is divided into chunks. The maximum chunk size is 57 bytes.
    3. Status Byte: Each CRSF frame contains a status byte before the MSP body:
      • Bits 0-3: Cyclic sequence number.
      • Bit 4: 1 if this is the start of a new frame; 0 otherwise.
      • Bits 5-6: MSP protocol version (1 or 2).
      • Bit 7: Error flag (for responses only).
    4. Routing: MSP responses must be sent to the original requester by swapping the destination and origin bytes in the CRSF header.
  9. Hardware Interface Requirements

    main

    CRSF can be implemented using several hardware interfaces depending on the connection type:

    Single-Wire Half-Duplex UART

    Used typically between the RC and TX.

    • Master/Slave: The RC acts as the master. It sends a frame at a pre-configured frequency, switches the line to high-impedance mode, and waits for the TX to respond.
    • Default Settings: 400 kbaud, 8N1, 3.3V level.
    • Supported Speeds: 115.2 kbaud, 1Mbaud, 2Mbaud (hardware dependent).

    Dual-Wire Full-Duplex UART

    Used on the flying platform side (e.g., between RX and FC).

    • Connection: Standard regular UART connection.
    • Default Settings: 416666 baud, 8N1, 3.0 to 3.3V level.
    • Note: Only non-inverted (regular) UART is supported.

    Multi-master I2C (BST)

    (Note: Marked as EOL)

    • Settings: 100kHz, 3.3V level, 7-bit addresses.
    • Addressing: The device's read address is Device Address + 1.
    • Requirement: Must support general call frames (broadcast frames) and release the SDA line to avoid blocking the bus.
  10. Parse Barometric Altitude and Vertical Speed (0x09)

    main

    The 0x09 frame provides bit-efficient altitude and vertical speed data.

    Altitude Packing

    Altitude is packed into a uint16_t. The Most Significant Bit (MSB, bit 15) determines the precision:

    • MSB = 0: Altitude is in decimeters with a -10000dm offset (0 represents -1000m; 10000 represents 0m; 0x7fff represents 2276.7m).
    • MSB = 1: Altitude is in meters with no offset.

    Vertical Speed Packing

    Vertical speed is represented in cm/s using a logarithmic scale. This provides $\pm$2500cm/s range with 3cm/s precision at low speeds, and 70cm/s precision at speeds around 25m/s.

    // Altitude unpack example
    int32_t get_altitude_dm(uint16_t packed){
        return (packed & 0x8000) ? (packed & 0x7fff) * 10 : (packed - 10000);
    }
    
    // Altitude pack example
    uint16_t get_altitude_packed (int32_t altitude_dm){
        enum {
            ALT_MIN_DM = 10000,
            ALT_THRESHOLD_DM = 0x8000 - ALT_MIN_DM,
            ALT_MAX_DM = 0x7ffe * 10 - 5,
        }
    
        if(altitude_dm < -ALT_MIN_DM) return 0;
        if(altitude_dm > ALT_MAX_DM) return 0xfffe;
        if(altitude_dm < ALT_THRESHOLD_DM) return altitude_dm + ALT_MIN_DM;
        return ((altitude_dm + 5) / 10) | 0x8000;
    }
    
    // Vertical speed pack/unpack examples
    const int   Kl = 100;
    const float Kr = .026;
    
    int8_t  get_vertical_speed_packed (int16_t vertical_speed_cm_s){
        return (int8_t)(log(abs(vertical_speed_cm_s)/Kl + 1)/Kr) * sign(vertical_speed_cm_s);
    }
    
    int16_t get_vertical_speed_cm_s (int8_t vertical_speed_packed){
        return (exp(abs(vertical_speed_packed * Kr)) - 1) * Kl * sign(vertical_speed_packed);
    }
  11. Use 0x81 and 0x82 mLRS Reserved frames for mBridge communication

    main

    The 0x81 and 0x82 broadcast frames are used to establish communication between mLRS TX modules and mLRS Configuration Lua scripts (e.g., on EdgeTx or OpenTx). These frames encapsulate the mLRS 'mBridge' protocol within CRSF frames to enable parameter setting, metadata communication, and version control.

    • 0x81 (Lua/Remote Controller $\rightarrow$ TX Module): Used by the script to send commands to the module. It includes the mBridge sync bytes 0x4F, 0x57.
    • 0x82 (TX Module $\rightarrow$ Lua/Remote Controller): Used by the module to send data back to the script.

    Note: The mBridge protocol is complex and serves multiple purposes for the mLRS link system beyond simple Lua configuration.

    flowchart LR
        0xEE ~~~ id0[Frame Length] ~~~ 0x81 ~~~ 0x4F ~~~ 0x57 ~~~ id1[Command Byte] ~~~ Payload ~~~ CRC