PyMSP is a Python library for handling MSP (MultiWii Serial Protocol) messages. It supports both MSP v1 and MSP v2 protocols used in flight controllers like INAV and Betaflight. The library features a unified MSPFrame type for handling both MSP v1 and v2 messages, plus streaming MSP data processing with MSPStreamProcessor.
- Pack MSP v1 and v2 messages
- Unpack MSP v1 and v2 messages with the unified MSPFrame type
- Support for payloads and flags
- Checksum validation
- Comprehensive error handling
- Unified MSPFrame class for consistent handling of both MSP versions
- MSPStreamProcessor for handling streaming MSP data with partial frame support
- Garbage data recovery and synchronization
- Support for both MSP v1 and v2 protocols in streaming mode
- Header format:
$M<(to FC) and$M>(from FC) - Size field: 1 byte (max 255 bytes payload)
- Message ID: 1 byte (0-255)
- Checksum: XOR of all data bytes
- Proper error handling for oversized payloads
- Header format:
$X<(to FC) and$X>(from FC) - Size field: 2 bytes (little endian, max 65535 bytes payload)
- Message ID: 2 bytes (little endian, 0-65535)
- Flags: 1 byte
- Checksum: XOR of all data bytes
- New
MSPFramedataclass that represents both MSP v1 and v2 messages - Provides uniform interface for handling different MSP versions
- Includes all frame components: header, size, flags, message_id, payload, checksum, and protocol_version
- Handles streaming MSP data with partial frame delivery support
- Recovers from garbage data in the stream
- Processes both MSP v1 and v2 frames in the same stream
- Implements the
push_bytesmethod that accepts new data and yields complete frames as they become available
pip install pymspfrom pymsp import MSPv1
msp_v1 = MSPv1()
# Pack a message with ID 101 and payload
packed_message = msp_v1.pack(101, b'\x01\x02\x03\x04')
print(packed_message)from pymsp import MSPv1, MSPFrame
msp_v1 = MSPv1()
# Unpack a message - now returns an MSPFrame object
frame: MSPFrame = msp_v1.unpack(b'$M>\x03\x65\x01\x02\x03\x8c')
print(f"Message ID: {frame.message_id}")
print(f"Payload: {frame.payload}")
print(f"Protocol: v{frame.protocol_version}")
print(f"Size: {frame.size}")
print(f"Header: {frame.header}")from pymsp import MSPv2
msp_v2 = MSPv2()
# Pack a message with ID 200 and payload
packed_message = msp_v2.pack(200, b'\x01\x02\x03\x04')
print(packed_message)from pymsp import MSPv2, MSPFrame
msp_v2 = MSPv2()
# Unpack a message - now returns an MSPFrame object
frame: MSPFrame = msp_v2.unpack(b'$X>\x03\x00\xc8\x00\x01\x02\x03\x8c')
print(f"Message ID: {frame.message_id}")
print(f"Payload: {frame.payload}")
print(f"Flags: {frame.flags}")
print(f"Protocol: v{frame.protocol_version}")
print(f"Size: {frame.size}")Both MSP v1 and v2 frames can be converted back to bytes:
from pymsp import MSPv1
msp_v1 = MSPv1()
# Pack and unpack a message
packed_message = msp_v1.pack(101, b'\x01\x02\x03\x04')
frame = msp_v1.unpack(b'$M>' + packed_message[3:])
# Convert frame back to bytes
bytes_again = frame.to_bytes()
print(bytes_again)The MSPStreamProcessor handles streaming MSP data, including:
- Partial frame delivery (frames split across multiple reads)
- Multiple frames in a single data chunk
- Garbage data recovery
- Both MSP v1 and v2 protocol support
from pymsp import MSPStreamProcessor, MSPFrame
# Create a stream processor
processor = MSPStreamProcessor()
# Process incoming data in chunks
data_chunk1 = b"GARBAGE$data" # May include non-MSP data
data_chunk2 = b"$M>\x04\x65\x01\x02\x03\x04\x65" # Complete MSP v1 frame
data_chunk3 = b"$X>\x04\x00\x01\x00\x0B\x01\x02\x03\x04\x1B" # Complete MSP v2 frame
# Process each chunk and collect frames
all_frames = []
for chunk in [data_chunk1, data_chunk2, data_chunk3]:
frames = list(processor.push_bytes(chunk))
all_frames.extend(frames)
# Process the collected frames
for frame in all_frames:
print(f"MSP {frame.protocol_version} Frame - ID: {frame.message_id}, Payload: {frame.payload.hex()}")- Implemented following TDD principles
- Started with API specification and test cases
- Developed implementation to meet test requirements
- All tests pass (34/34 including streaming processor tests)
- Comprehensive coverage of functionality and edge cases
- Comprehensive pack/unpack functionality for both protocols
- Strict validation of message format and integrity
- Proper header recognition and generation
- Accurate checksum calculation and verification
- Detailed error handling with MSPException
- Unified MSPFrame for consistent handling of both MSP versions
- MSPStreamProcessor for streaming data handling with partial frame support
- Garbage data recovery and synchronization capability
pymsp/__init__.py- Package initializationpymsp/msp.py- Main MSP implementation with MSPFrame and MSPStreamProcessortests/test_msp.py- Complete API specification and unit teststests/test_comprehensive.py- Edge case and integration testsexample_usage.py- Demonstration script with MSPFrame usage
Handles MSP v1 protocol messages.
pack(message_id, payload=b''): Packs a message with the given ID and payload.unpack(raw_message): Unpacks a raw MSP message, returning an MSPFrame object.
Handles MSP v2 protocol messages.
pack(message_id, payload=b'', flags=b'\\x00'): Packs a message with the given ID, payload, and flags.unpack(raw_message): Unpacks a raw MSP message, returning an MSPFrame object.
Unified class representing both MSP v1 and v2 messages.
header: Message header bytessize: Size of the payloadflags: Flags (for MSP v2), empty for MSP v1message_id: Message identifierpayload: Message payload byteschecksum: Checksum valueprotocol_version: 1 for MSP v1, 2 for MSP v2
to_bytes(): Converts the frame back to raw bytes format
Handles streaming MSP data with support for partial frames.
__init__(): Creates a new stream processor with internal bufferpush_bytes(new_bytes): Processes new bytes and yields complete MSPFrame objects as they become available
Pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change.
MIT