Skip to content

Framing Details

This page covers the technical details of the framing system.

Frame Components

Frames consist of two parts:

  1. Header: Determines sync bytes and addressing
  2. Payload: Determines length encoding and checksum

Header Types

Basic Header (2 bytes)

[0x90] [0x70 + PayloadType]

Two-byte sync for reliable synchronization. Recommended for most applications.

Tiny Header (1 byte)

[0x70 + PayloadType]

Single-byte sync for bandwidth-limited scenarios.

None Header (0 bytes)

(no header)

For trusted links where synchronization isn’t needed.

Payload Types

Minimal

[MSG_ID] [PAYLOAD...]

1 byte overhead. Requires message length lookup. No checksum or length field.

Default

[LENGTH] [MSG_ID] [PAYLOAD...] [CRC_HI] [CRC_LO]

4 bytes overhead. Includes length (1 byte, max 255) and Fletcher-16 checksum.

Extended

[LENGTH_HI] [LENGTH_LO] [MSG_ID] [PAYLOAD...] [CRC_HI] [CRC_LO]

5 bytes overhead. 16-bit length field (max 65535 bytes).

ExtendedMultiSystemStream

[LENGTH_HI] [LENGTH_LO] [SRC] [DST] [SEQ] [MSG_ID] [PAYLOAD...] [CRC_HI] [CRC_LO]

8 bytes overhead. Adds source/destination addresses and sequence numbers for multi-node networks.

Frame Profiles

Profiles combine header + payload types:

ProfileHeaderPayloadTotal Overhead
StandardBasicDefault6 bytes
SensorTinyMinimal2 bytes
IPCNoneMinimal1 byte
BulkBasicExtended8 bytes
NetworkBasicExtendedMultiSystemStream11 bytes

Checksum (Fletcher-16)

The Default and Extended payload types use Fletcher-16 checksum.

Magic Numbers:

  • Basic frame start: 0x90 followed by 0x70 + PayloadType
  • Tiny frame start: 0x70 + PayloadType
  • Payload type base value: 0x70

The checksum algorithm:

For each byte in [LENGTH, MSG_ID, PAYLOAD]:
sum1 = (sum1 + byte) mod 255
sum2 = (sum2 + sum1) mod 255
checksum = (sum2 << 8) | sum1

Parser State Machine

The parser implements a state machine:

IDLE → wait for start byte(s)
HEADER → read header bytes
PAYLOAD → read length, msg_id, data
CHECKSUM → verify CRC
COMPLETE → return message

For Minimal payloads, the parser requires a message length callback to determine payload size.

Usage in C++

#include "FrameProfiles.hpp"
using namespace FrameParsers;
// Using profiles
uint8_t buffer[1024];
ProfileStandardWriter writer(buffer, sizeof(buffer));
ProfileStandardAccumulatingReader reader;
// Encode
writer.write(msg);
send_data(writer.buffer(), writer.size());
// Decode (streaming)
while (receiving) {
if (auto result = reader.push_byte(read_byte())) {
handle_message(result.msg_id, result.msg_data, result.msg_len);
}
}
// Decode (buffer)
reader.add_data(buffer, buffer_size);
while (auto result = reader.next()) {
handle_message(result.msg_id, result.msg_data, result.msg_len);
}

Usage in Python

from struct_frame_parser import Parser, HeaderType, PayloadType
# Create parser with specific frame types
parser = Parser(
enabled_headers=[HeaderType.BASIC, HeaderType.TINY],
enabled_payloads=[PayloadType.DEFAULT, PayloadType.EXTENDED]
)
# Encode
frame = parser.encode(
msg_id=42,
msg=b"payload data",
header_type=HeaderType.BASIC,
payload_type=PayloadType.DEFAULT
)
# Decode (byte-by-byte)
for byte in incoming_data:
result = parser.parse_byte(byte)
if result.valid:
handle_message(result)

Custom Profiles

Create custom frame formats by combining header and payload types:

// Custom: Tiny header with Extended payload
using CustomConfig = FrameConfig<
HeaderTiny,
PayloadExtended
>;
FrameEncoderWithCrc<CustomConfig> encoder;
BufferParserWithCrc<CustomConfig> parser;
from struct_frame_parser import create_custom_profile, HeaderType, PayloadType
custom = create_custom_profile(
"TinyExtended",
HeaderType.TINY,
PayloadType.EXTENDED
)