Open embedded specification

Portable Frame Protocol

Carry complete messages across constrained links and routed device networks with one small, predictable frame format.

Status
Stable
Wire version
1
Specification
1.0.0
Licence
MIT

64 bytes

A fixed frame size for constrained links and implementations with known resource limits.

Any payload

Application messages remain opaque, so PFP does not dictate a serialisation format.

Any link

Link profiles can carry complete frames over USB, CAN, Bluetooth, IP, or another transport.

Routed

Session addressing, gateways, discovery, and hop limits support bounded device topologies.

PFP moves complete messages across small links and through gateways while keeping resource use bounded. It defines how data is framed and routed. It does not define what the application puts inside each message.

This separation lets a product keep its existing application protocol, choose links that suit the hardware, and implement PFP in its existing language and architecture.

PFP carries opaque application messages over separately defined link profiles.

A small, predictable frame

Every PFP wire-version 1 frame is exactly 64 bytes. Twenty bytes describe the session, route, message, fragment, and priority. Forty bytes carry payload. The final four bytes contain a CRC-32/ISO-HDLC integrity check over everything that precedes them.

Every PFP v1 frame is exactly 64 bytes. Segment widths are proportional.

A fixed frame makes capacity and buffering easier to plan. Receivers can validate a complete frame before allocating reassembly state. Gateways can forward valid frames without decoding the application payload.

One upper-layer message may contain from 1 to 10,200 bytes. PFP splits it into as many as 255 frames, numbers every fragment, and carries the exact original message length. A receiver can accept fragments out of order, ignore identical duplicates, reject conflicting data, and publish only a completely reassembled message.

An 86-byte message becomes three numbered frames and is published only after complete reassembly.

The following is illustrative pseudocode, not a PFP or Corelib API:

message = application.encode(value)
frames = pfp.fragment(message, destination)

for frame in frames:
    link.send(frame)
frame = link.receive()

if pfp.validate(frame):
    complete_message = pfp.accept(frame)
    if complete_message is ready:
        application.handle(complete_message)

PFP does not acknowledge or retransmit missing frames. A link profile, an upper-layer protocol, or the surrounding system supplies reliability when it is required.

One session, from a direct device to a routed topology

A PFP session has one root. The root creates the session and assigns temporary addresses. A directly attached node can also act as a gateway. It discovers downstream links and forwards frames to them within defined limits. Persistent UUIDs identify devices independently of their session address.

The root assigns session-scoped addresses and maintains the authoritative graph through gateways.

Hop limits keep forwarding bounded. Session identifiers isolate stale traffic, while discovery and control messages allow the root to maintain the authoritative topology. A simple two-node connection uses the same framing and session rules without needing a larger routed network.

A focused contract

PFP definesAnother layer defines
Fixed 64-byte frame encodingApplication request and response semantics
Fragmentation and exact reassemblyPayload serialisation
Session-scoped node addressesPersistent product identity beyond UUIDs
Gateway discovery and bounded routingLink-specific bootstrap and transport details
Per-frame CRC integrity checkingDelivery acknowledgements and retransmission
Validation and resource-bounded behaviourAuthentication, confidentiality, and hostile-link protection

This boundary keeps PFP small enough for constrained devices. Other layers can then handle the concerns that sit outside framing and routing.

Corelib is one implementation

Corelib implements PFP wire version 1 for portable C11 projects and includes an optional fixed-storage C++14 facade. It provides a practical way to build a PFP-compatible device for Programmor. Corelib’s APIs, storage model, and upper-layer transaction protocol are not part of PFP.

Other implementations can conform to the same specification without using Corelib.

The repository is the specification

This page introduces PFP but does not replace the normative protocol text. PFP releases use semantic versioning, and tagged specification releases are frozen. Read the PFP v1.0.0 specification for complete requirements, field definitions, validation rules, and topology behaviour. Proposed changes and later releases are managed in the PFP repository.