Skip to main content

MSP Protocol Documentation

Welcome to the comprehensive documentation for the MultiWii Serial Protocol (MSP), the standard communication protocol used by Betaflight and other flight controller firmware.

What is MSP?​

MSP (MultiWii Serial Protocol) is a lightweight, binary communication protocol designed for bidirectional communication between flight controllers and external devices such as:

  • Ground Control Stations (GCS) - Mission planning and telemetry
  • Configurators - Betaflight Configurator, INAV Configurator
  • Companion Computers - Raspberry Pi, Jetson for autonomous flight
  • Telemetry Systems - Real-time flight data transmission
  • Custom Applications - Data logging, research platforms

History​

MSP originated with the MultiWii project, an early open-source flight controller firmware for Arduino-based boards. The protocol was designed to be:

  • Simple to implement
  • Low overhead for embedded systems
  • Extensible for new features

As flight controller firmware evolved, MSP was adopted and extended by:

  1. Baseflight - First ARM-based fork
  2. Cleanflight - Major refactoring and modernization
  3. Betaflight - Performance-focused fork (most popular today)
  4. INAV - Navigation-focused fork

Protocol Versions​

VersionHeaderDescription
MSPv1$MOriginal protocol, XOR checksum, 255-byte payload limit
MSPv1 JUMBO$MExtended v1 with 16-bit payload length
MSPv2$XModern protocol, CRC8 checksum, 16-bit command codes
MSPv2 over V1$MV2 wrapped in V1 for backwards compatibility

Current API Version: 1.48

Quick Start​

Here's a minimal example to query the flight controller's API version:

use std::io::{Read, Write};

/// MSP command codes
const MSP_API_VERSION: u8 = 1;

/// Build an MSPv1 request frame
fn build_msp_request(command: u8) -> Vec<u8> {
let mut frame = Vec::with_capacity(6);

// Header
frame.push(b'$');
frame.push(b'M');
frame.push(b'<'); // Request direction

// Payload size (0 for query)
frame.push(0);

// Command
frame.push(command);

// Checksum: XOR of size and command
let checksum = 0 ^ command;
frame.push(checksum);

frame
}

/// Parse an MSPv1 response
fn parse_msp_response(data: &[u8]) -> Option<(u8, Vec<u8>)> {
if data.len() < 6 {
return None;
}

// Verify header
if &data[0..3] != b"$M>" {
return None;
}

let size = data[3] as usize;
let command = data[4];

if data.len() < 5 + size + 1 {
return None;
}

let payload = data[5..5 + size].to_vec();

// Verify checksum
let mut checksum = size as u8 ^ command;
for byte in &payload {
checksum ^= byte;
}

if checksum != data[5 + size] {
return None;
}

Some((command, payload))
}

fn main() -> std::io::Result<()> {
// Open serial port (adjust path for your system)
let mut port = std::fs::OpenOptions::new()
.read(true)
.write(true)
.open("/dev/ttyUSB0")?;

// Send API version request
let request = build_msp_request(MSP_API_VERSION);
port.write_all(&request)?;

// Read response
let mut buffer = [0u8; 64];
let n = port.read(&mut buffer)?;

if let Some((cmd, payload)) = parse_msp_response(&buffer[..n]) {
println!("Command: {}", cmd);
println!("Protocol Version: {}", payload[0]);
println!("API Major: {}", payload[1]);
println!("API Minor: {}", payload[2]);
}

Ok(())
}

Documentation Structure​

This documentation is organized into the following sections:

Protocol​

Commands​

Payloads​

Examples​

Data Types​

Throughout this documentation, the following data type conventions are used:

TypeSizeDescription
U81 byteUnsigned 8-bit integer (0-255)
U162 bytesUnsigned 16-bit integer, little-endian
U324 bytesUnsigned 32-bit integer, little-endian
I81 byteSigned 8-bit integer (-128 to 127)
I162 bytesSigned 16-bit integer, little-endian
I324 bytesSigned 32-bit integer, little-endian

Byte Order: All multi-byte values are transmitted in little-endian format.

Flight Controller Identifiers​

Different flight controller firmware can be identified by their 4-character variant code:

CodeFirmware
BTFLBetaflight
INAVINAV
CLFLCleanflight
BAFLBaseflight
MWIIMultiWii
RCFLRaceflight

Next Steps​