Protocol Overview
The MSP protocol supports multiple versions designed for different use cases and hardware capabilities. This page covers the fundamentals of MSP communication.
Protocol Versions Comparison
| Feature | MSPv1 | MSPv1 JUMBO | MSPv2 Native | MSPv2 over V1 |
|---|---|---|---|---|
| Header | $M | $M | $X | $M |
| Command ID Size | 8-bit | 8-bit | 16-bit | 16-bit |
| Max Payload | 255 bytes | 65535 bytes | 65535 bytes | 65535 bytes |
| Checksum | XOR | XOR | CRC8-DVB-S2 | Dual (CRC8 + XOR) |
| Backward Compatible | N/A | Yes | No | Yes |
Direction Indicators
MSP uses direction bytes to distinguish between requests and responses:
| Direction | Character | Hex Value | Description |
|---|---|---|---|
| Request | < | 0x3C | Message FROM host TO flight controller |
| Response | > | 0x3E | Message FROM flight controller TO host |
| Error | ! | 0x21 | Error response (command failed or unknown) |
/// MSP direction indicators
#[repr(u8)]
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum MspDirection {
Request = b'<', // 0x3C - Host to FC
Response = b'>', // 0x3E - FC to Host
Error = b'!', // 0x21 - Error response
}
impl MspDirection {
pub fn from_byte(b: u8) -> Option<Self> {
match b {
b'<' => Some(MspDirection::Request),
b'>' => Some(MspDirection::Response),
b'!' => Some(MspDirection::Error),
_ => None,
}
}
}
Magic Bytes
The protocol version is identified by the first two bytes (magic header):
| Version | Byte 0 | Byte 1 | Pattern |
|---|---|---|---|
| MSPv1 | $ (0x24) | M (0x4D) | $M |
| MSPv2 | $ (0x24) | X (0x58) | $X |
/// MSP protocol version
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum MspVersion {
V1,
V2,
}
impl MspVersion {
pub fn from_header(header: &[u8; 2]) -> Option<Self> {
match header {
[b'$', b'M'] => Some(MspVersion::V1),
[b'$', b'X'] => Some(MspVersion::V2),
_ => None,
}
}
pub fn header_bytes(&self) -> [u8; 2] {
match self {
MspVersion::V1 => [b'$', b'M'],
MspVersion::V2 => [b'$', b'X'],
}
}
}
API Version
The current Betaflight API version is 1.48. The API version can be queried using MSP_API_VERSION (command 1).
/// API version information
#[derive(Debug, Clone, Copy)]
pub struct ApiVersion {
pub protocol_version: u8, // MSP protocol version (0 = v1, 2 = v2)
pub api_major: u8, // API major version
pub api_minor: u8, // API minor version
}
impl ApiVersion {
pub fn from_payload(data: &[u8]) -> Option<Self> {
if data.len() < 3 {
return None;
}
Some(ApiVersion {
protocol_version: data[0],
api_major: data[1],
api_minor: data[2],
})
}
}
Message Flow
Request-Response Pattern
MSP follows a simple request-response pattern:
Host Flight Controller
| |
| ---- MSP Request ($M<) ----> |
| |
| <--- MSP Response ($M>) ---- |
| |
Unsolicited Messages
Some MSP implementations support push-based telemetry, but the standard pattern is poll-based (host initiates all communication).
Command Categories
Commands are organized into functional groups:
| Range | Category | Description |
|---|---|---|
| 0-10 | System Info | Protocol/API version, board info |
| 32-62 | Configuration | Settings (battery, modes, features) |
| 64-99 | Data & Storage | EEPROM, dataflash, blackbox |
| 101-150 | Telemetry | Status, sensors, RC inputs |
| 150-199 | Extended Status | Additional status and OSD |
| 200-229 | Control/Write | SET commands for configuration |
| 230-255 | Utility | Debug, EEPROM write, reboot |
GET vs SET Commands
Most configuration items have paired commands:
- GET (read): Query current value
- SET (write): Modify value
By convention:
- SET commands are typically GET command + 1
- GET commands have empty request payloads
- SET commands include the new values in the request payload
// Example: PID commands
const MSP_PID: u8 = 112; // GET - read PID values
const MSP_SET_PID: u8 = 202; // SET - write PID values
Error Handling
When a command fails or is unknown, the flight controller responds with:
- Direction byte:
!(0x21) instead of> - Empty payload or error code
/// Check if response indicates an error
fn is_error_response(direction: u8) -> bool {
direction == b'!'
}
Result Codes
Internal result codes used by MSP handlers:
| Code | Name | Description |
|---|---|---|
| 1 | MSP_RESULT_ACK | Command executed successfully |
| 0 | MSP_RESULT_NO_REPLY | No response generated |
| -1 | MSP_RESULT_ERROR | Command failed |
| -2 | MSP_RESULT_CMD_UNKNOWN | Command not recognized |
Timing Considerations
- Response Time: Typically 1-10ms for simple queries
- Serial Baud Rate: Common rates are 115200, 230400, 420000, 921600
- Polling Rate: 10-100 Hz is typical for telemetry
- Timeout: Recommended 100-500ms timeout for responses
/// Recommended MSP timing constants
pub const MSP_DEFAULT_TIMEOUT_MS: u64 = 500;
pub const MSP_TELEMETRY_RATE_HZ: u32 = 50;
pub const MSP_SERIAL_BAUD: u32 = 115200;
Next Steps
- Frame Structure - Detailed byte-level format
- Checksums - Checksum algorithms
- Version Detection - Protocol negotiation