MSP_STATUS (101)
Returns flight controller status including cycle time, sensor availability, active flight modes, and system load.
Request
Empty payload (0 bytes).
Response Structure
| Offset | Type | Field | Description |
|---|---|---|---|
| 0-1 | U16 | cycle_time | PID loop time in microseconds |
| 2-3 | U16 | i2c_error_count | Accumulated I2C errors |
| 4-5 | U16 | sensor_flags | Active sensor bitmask |
| 6-9 | U32 | flight_mode_flags | Active flight mode bitmask |
| 10 | U8 | current_pid_profile | Active PID profile (0-indexed) |
| 11-12 | U16 | system_load | CPU load × 10 (0-1000 = 0-100%) |
Total size: 13 bytes (minimum)
Sensor Flags
| Bit | Mask | Sensor | Description |
|---|---|---|---|
| 0 | 0x0001 | ACC | Accelerometer present |
| 1 | 0x0002 | BARO | Barometer present |
| 2 | 0x0004 | MAG | Magnetometer present |
| 3 | 0x0008 | GPS | GPS module present |
| 4 | 0x0010 | RANGEFINDER | Rangefinder/sonar present |
| 5 | 0x0020 | GYRO | Gyroscope present |
| 6 | 0x0040 | OPTICAL_FLOW | Optical flow sensor present |
Rust Implementation
use core::fmt;
/// Sensor availability flags
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct SensorFlags(u16);
impl SensorFlags {
pub const ACC: u16 = 1 << 0;
pub const BARO: u16 = 1 << 1;
pub const MAG: u16 = 1 << 2;
pub const GPS: u16 = 1 << 3;
pub const RANGEFINDER: u16 = 1 << 4;
pub const GYRO: u16 = 1 << 5;
pub const OPTICAL_FLOW: u16 = 1 << 6;
pub fn new(bits: u16) -> Self {
SensorFlags(bits)
}
pub fn has_acc(&self) -> bool { self.0 & Self::ACC != 0 }
pub fn has_baro(&self) -> bool { self.0 & Self::BARO != 0 }
pub fn has_mag(&self) -> bool { self.0 & Self::MAG != 0 }
pub fn has_gps(&self) -> bool { self.0 & Self::GPS != 0 }
pub fn has_rangefinder(&self) -> bool { self.0 & Self::RANGEFINDER != 0 }
pub fn has_gyro(&self) -> bool { self.0 & Self::GYRO != 0 }
pub fn has_optical_flow(&self) -> bool { self.0 & Self::OPTICAL_FLOW != 0 }
}
impl fmt::Display for SensorFlags {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut sensors = Vec::new();
if self.has_gyro() { sensors.push("GYRO"); }
if self.has_acc() { sensors.push("ACC"); }
if self.has_baro() { sensors.push("BARO"); }
if self.has_mag() { sensors.push("MAG"); }
if self.has_gps() { sensors.push("GPS"); }
if self.has_rangefinder() { sensors.push("RANGE"); }
if self.has_optical_flow() { sensors.push("OFLOW"); }
write!(f, "[{}]", sensors.join(", "))
}
}
/// MSP_STATUS response payload
#[derive(Debug, Clone, Copy)]
pub struct MspStatus {
/// PID loop cycle time in microseconds
pub cycle_time_us: u16,
/// I2C bus error count
pub i2c_error_count: u16,
/// Active sensors bitmask
pub sensors: SensorFlags,
/// Active flight modes bitmask
pub flight_mode_flags: u32,
/// Current PID profile index
pub current_pid_profile: u8,
/// System CPU load (0-1000 = 0-100%)
pub system_load: u16,
}
impl MspStatus {
pub const COMMAND: u8 = 101;
pub const MIN_SIZE: usize = 13;
/// Parse from MSP response payload
pub fn from_payload(data: &[u8]) -> Option<Self> {
if data.len() < Self::MIN_SIZE {
return None;
}
Some(MspStatus {
cycle_time_us: u16::from_le_bytes([data[0], data[1]]),
i2c_error_count: u16::from_le_bytes([data[2], data[3]]),
sensors: SensorFlags::new(u16::from_le_bytes([data[4], data[5]])),
flight_mode_flags: u32::from_le_bytes([data[6], data[7], data[8], data[9]]),
current_pid_profile: data[10],
system_load: u16::from_le_bytes([data[11], data[12]]),
})
}
/// Get PID loop frequency in Hz
pub fn pid_frequency_hz(&self) -> u32 {
if self.cycle_time_us > 0 {
1_000_000 / self.cycle_time_us as u32
} else {
0
}
}
/// Get CPU load as percentage (0.0-100.0)
pub fn cpu_load_percent(&self) -> f32 {
self.system_load as f32 / 10.0
}
/// Check if a specific flight mode is active
pub fn is_mode_active(&self, mode_bit: u8) -> bool {
self.flight_mode_flags & (1 << mode_bit) != 0
}
}
Raw Hex Example
Request frame:
24 4D 3C 00 65 65
│ │ │ │ │ └─ Checksum (0 ^ 101 = 101)
│ │ │ │ └──── Command (101 = 0x65)
│ │ │ └─────── Size (0)
│ │ └────────── Direction (<)
│ └───────────── Header M
└──────────────── Header $
Response frame:
24 4D 3E 0D 65 D0 07 00 00 21 00 05 00 00 00 00 E8 03 XX
│ │ │ │ │ └───────────────────────────────────────┴─ Payload (13 bytes)
│ │ │ │ └── Command (101)
│ │ │ └───── Size (13 bytes)
│ │ └──────── Direction (>)
│ └─────────── Header M
└────────────── Header $
Payload breakdown:
D0 07 → cycle_time = 0x07D0 = 2000 µs (500 Hz PID loop)
00 00 → i2c_error_count = 0
21 00 → sensor_flags = 0x0021 = ACC + GYRO
05 00 00 00 → flight_mode_flags = 0x00000005
00 → current_pid_profile = 0
E8 03 → system_load = 0x03E8 = 1000 = 100.0%
Usage Example
use std::io::{Read, Write};
fn read_status<T: Read + Write>(port: &mut T) -> Option<MspStatus> {
// Send request
let request = [0x24, 0x4D, 0x3C, 0x00, 0x65, 0x65];
port.write_all(&request).ok()?;
// Read response
let mut buf = [0u8; 32];
let n = port.read(&mut buf).ok()?;
// Validate header
if n < 6 || &buf[0..3] != b"$M>" {
return None;
}
let size = buf[3] as usize;
let payload = &buf[5..5 + size];
MspStatus::from_payload(payload)
}
fn main() {
// Example usage
let payload = [
0xD0, 0x07, // cycle_time = 2000
0x00, 0x00, // i2c_errors = 0
0x21, 0x00, // sensors = ACC + GYRO
0x05, 0x00, 0x00, 0x00, // flight_modes
0x00, // pid_profile = 0
0xE8, 0x03, // system_load = 1000
];
if let Some(status) = MspStatus::from_payload(&payload) {
println!("PID Frequency: {} Hz", status.pid_frequency_hz());
println!("CPU Load: {:.1}%", status.cpu_load_percent());
println!("Sensors: {}", status.sensors);
println!("I2C Errors: {}", status.i2c_error_count);
println!("PID Profile: {}", status.current_pid_profile);
}
}
Output:
PID Frequency: 500 Hz
CPU Load: 100.0%
Sensors: [GYRO, ACC]
I2C Errors: 0
PID Profile: 0
Extended Status (MSP_STATUS_EX)
MSP_STATUS_EX (150) provides additional fields:
| Offset | Type | Field | Description |
|---|---|---|---|
| 0-12 | ... | (same as MSP_STATUS) | Base status fields |
| 13 | U8 | pid_profile_count | Total PID profiles |
| 14 | U8 | rate_profile_index | Current rate profile |
| 15 | U8 | rate_profile_count | Total rate profiles |
| 16+ | ... | Additional fields | Varies by API version |
Use MSP_STATUS_EX for more detailed status information in newer firmware versions.