2 stable releases

Uses new Rust 2024

new 1.2.1 Feb 8, 2026
1.1.0 Jun 15, 2025
1.0.0 Jun 15, 2025

#1260 in Embedded development

MIT license

140KB
1.5K SLoC

PK Command

Crates.io Version

[中文]

This repository contains the specification and an implementation of the PK Command, a communication protocol designed for embedded devices.

Library usage

This Rust library is universal on both host and device. The caller needs to construct a PkCommand instance on the host or device and keep calling the instance's poll method.

Variable and method management is provided by types that implement the PkVariableAccessor and PkMethodAccessor traits, where the non-blocking mechanism of methods is based on the Pollable trait. This library also provides predefined PkVHashmapWrapper (for variables), PkMHashmapWrapper (for methods), and PkPollable.

Example

See the test case.

The Protocol


lib.rs:

PK Command

A lightweight, reliable data transfer protocol designed for constrained channels (e.g., HID, Serial) between a Host and an Embedded Device.

Key Features

  • Reliability: Built-in ACK/Retransmission mechanism.
  • Efficiency: Fixed-length headers and data slicing for small MTUs.
  • Flexibility: Supports variable access (GET/SET) and remote method invocation (INVOK).
  • no_std Support: Core logic is compatible with embedded systems without an OS. Note that alloc is still used in no_std environments.
  • Wait Mechanism: Keep-alive AWAIT packets for long-running operations.

Architecture

The protocol operates using Transaction Chains. A chain starts with a START command and ends with ENDTR. Large payloads are automatically sliced into SDATA packets.

Command Format

Every command follows the fixed layout:

[MSG ID][OPERATION NAME] [OBJECT] [DATA].

  • MSG ID: A base-94 encoded unsigned integer (using ASCII characters from '!' to '~') that uniquely identifies the command within a transaction.
  • OPERATION NAME: A 5-character ASCII string representing the command type. All the operations defined in the specification are defined in this library. (See types::Operation for details.)
  • OBJECT: An optional 5-character ASCII string that provides additional context for the operation (e.g., variable name, method name).
  • DATA: An optional binary payload that carries parameters or return values. It can be of arbitrary length and may contain any byte values.

See Command for a structured representation of commands and utilities for parsing and serialization.

Note that:

  • OBJECT is either omitted, or exactly 5 ASCII characters.
  • DATA (payload) is arbitrary binary data.
  • The total length of a command is limited by the transportation layer's MTU (e.g., 64 bytes for HID). (See PkCommandConfig.) But the protocol handles slicing and reassembly of large payloads automatically, so you can work with large data without worrying about the underlying transport constraints.

Example

use pk_command::{PkCommand, PkCommandConfig, PkHashmapMethod, PkHashmapVariable};

// 1. Setup configuration and accessors
let config = PkCommandConfig::default(64);
let vars = PkHashmapVariable::new(vec![]);
let methods = PkHashmapMethod::new(vec![]);

// 2. Initialize the state machine
let pk = PkCommand::<_, _, std::time::Instant>::new(config, vars, methods);

// 3. Basic loop driving the protocol
loop {
    // Handle received bytes from your transport (HID/Serial/etc.)
    if let Some(bytes) = transport.recv() {
        pk.incoming_command(bytes);
    }

    // Process and get commands to send back
    if let Some(cmd) = pk.poll() {
        transport.send(cmd.to_bytes());
    }

    if pk.is_complete() {
        break;
    }
}

Feature flags

  • std: Enables features that require the Rust standard library. (Mainly the convenient wrappers like PkPromise, PkHashmapVariable, PkHashmapMethod) Enabled by default.
  • embassy: Enables integration with the Embassy async framework. Flags below are also enabled when this is active:
    • embassy-time: Enables the support for embassy-time crate, which provides timekeeping utilities for embedded environments.
    • embassy-runtime: Enables the support for embassy-executor crate, which provides integration between the main state machine and Embassy async tasks.
  • tokio-runtime: Enables integration with the Tokio async runtime. Provides tokio_adapter for running async operations within method implementations. Requires std feature.
  • smol-runtime: Enables integration with the Smol async executor. Provides smol_adapter for running async operations within method implementations. Requires std feature.

Dependencies

~0–16MB
~154K SLoC