pw_hdlc: API reference#

This page describes the Encoder, Decoder, and RPC APIs of pw_hdlc.

Encoder#

The Encoder API provides a single function that encodes data as an HDLC unnumbered information frame.

Status pw::hdlc::WriteUIFrame(uint64_t address, ConstByteSpan payload, stream::Writer &writer)#

Writes an HDLC unnumbered information frame (UI frame) to the provided pw::stream writer.

return:

A pw::Status instance describing the result of the operation:

  • pw::Status::Ok(): The write finished successfully.

  • pw::Status::ResourceExhausted(): The write failed because the size of the frame would be larger than the writer’s conservative limit.

  • pw::Status::InvalidArgument(): The start of the write failed. Check for problems in your address argument’s value.

Parameters:
  • address – The frame address.

  • payload – The frame data to encode.

  • writer – The pw::stream to write the frame to. The frame contains the following bytes. See pw_hdlc: Design for more information.

    • HDLC flag byte (0x7e)

    • Address (variable length, up to 10 bytes)

    • UI-frame control (metadata) byte

    • Payload (0 or more bytes)

    • Frame check sequence (CRC-32, 4 bytes)

    • HDLC flag byte (0x7e)

Example:

// Writes a span of data to a pw::stream::Writer and returns the status. This
// implementation uses the pw_checksum module to compute the CRC-32 frame check
// sequence.

#include "pw_hdlc/encoder.h"
#include "pw_hdlc/sys_io_stream.h"

int main() {
  pw::stream::SysIoWriter serial_writer;
  Status status = WriteUIFrame(123 /* address */, data, serial_writer);
  if (!status.ok()) {
    PW_LOG_INFO("Writing frame failed! %s", status.str());
  }
}

Decoder#

class Decoder#

Subclassed by pw::hdlc::DecoderBuffer< kSizeBytes >

Public Functions

Result<Frame> Process(std::byte new_byte)#

Parses a single byte of an HDLC stream.

Returns:

A pw::Result with the complete frame if the byte completes a frame. The status can be one of the following:

  • OK - A frame was successfully decoded. The Result contains the Frame, which is invalidated by the next Process() call.

  • UNAVAILABLE - No frame is available.

  • RESOURCE_EXHAUSTED - A frame completed, but it was too large to fit in the decoder’s buffer.

  • DATA_LOSS - A frame completed, but it was invalid. The frame was incomplete or the frame check sequence verification failed.

template<typename F, typename ...Args>
inline void Process(ConstByteSpan data, F &&callback, Args&&... args)#

Processes a span of data and calls the provided callback with each frame or error.

Example:

// Read individual bytes from pw::sys_io and decode HDLC frames.

#include "pw_hdlc/decoder.h"
#include "pw_sys_io/sys_io.h"

int main() {
  std::byte data;
  while (true) {
    if (!pw::sys_io::ReadByte(&data).ok()) {
      // Log serial reading error
    }
    Result<Frame> decoded_frame = decoder.Process(data);

    if (decoded_frame.ok()) {
      // Handle the decoded frame
    }
  }
}

RPC#

class pw_hdlc.rpc.HdlcRpcClient(read: ~typing.Callable[[], bytes], paths_or_modules: ~typing.Union[~typing.Iterable[~typing.Union[str, ~pathlib.Path, module]], ~pw_protobuf_compiler.python_protos.Library], channels: ~typing.Iterable[~pw_rpc.descriptors.Channel], output: ~typing.Callable[[bytes], ~typing.Any] = <function write_to_file>, client_impl: ~typing.Optional[~pw_rpc.client.ClientImpl] = None, *, _incoming_packet_filter_for_testing: ~typing.Optional[~pw_rpc.descriptors.ChannelManipulator] = None, rpc_frames_address: int = 82, log_frames_address: int = 1, extra_frame_handlers: ~typing.Optional[~typing.Dict[int, ~typing.Callable[[~pw_hdlc.decode.Frame], ~typing.Any]]] = None)

An RPC client configured to run over HDLC.

Expects HDLC frames to have addresses that dictate how to parse the HDLC payloads.

__init__(read: ~typing.Callable[[], bytes], paths_or_modules: ~typing.Union[~typing.Iterable[~typing.Union[str, ~pathlib.Path, module]], ~pw_protobuf_compiler.python_protos.Library], channels: ~typing.Iterable[~pw_rpc.descriptors.Channel], output: ~typing.Callable[[bytes], ~typing.Any] = <function write_to_file>, client_impl: ~typing.Optional[~pw_rpc.client.ClientImpl] = None, *, _incoming_packet_filter_for_testing: ~typing.Optional[~pw_rpc.descriptors.ChannelManipulator] = None, rpc_frames_address: int = 82, log_frames_address: int = 1, extra_frame_handlers: ~typing.Optional[~typing.Dict[int, ~typing.Callable[[~pw_hdlc.decode.Frame], ~typing.Any]]] = None)

Creates an RPC client configured to communicate using HDLC.

Parameters:
  • read – Function that reads bytes; e.g serial_device.read.

  • paths_or_modules – paths to .proto files or proto modules.

  • channels – RPC channels to use for output.

  • output – where to write “stdout” output from the device.

  • client_impl – The RPC Client implementation. Defaults to the callback client implementation if not provided.

  • rpc_frames_address – the address used in the HDLC frames for RPC packets. This can be the channel ID, or any custom address.

  • log_frames_address – the address used in the HDLC frames for “stdout” output from the device.

  • extra_fram_handlers – Optional mapping of HDLC frame addresses to their callbacks.

class pw_hdlc.rpc.HdlcRpcLocalServerAndClient(server_command: Sequence, port: int, protos: Union[Iterable[Union[str, Path, module]], Library], *, incoming_processor: Optional[ChannelManipulator] = None, outgoing_processor: Optional[ChannelManipulator] = None)

Runs an RPC server in a subprocess and connects to it over a socket.

This can be used to run a local RPC server in an integration test.

__init__(server_command: Sequence, port: int, protos: Union[Iterable[Union[str, Path, module]], Library], *, incoming_processor: Optional[ChannelManipulator] = None, outgoing_processor: Optional[ChannelManipulator] = None) None

Creates a new HdlcRpcLocalServerAndClient.

The RpcChannelOutput implements pw_rpc’s pw::rpc::ChannelOutput interface, simplifying the process of creating an RPC channel over HDLC. A pw::stream::Writer must be provided as the underlying transport implementation.

If your HDLC routing path has a Maximum Transmission Unit (MTU) limitation, using the FixedMtuChannelOutput is strongly recommended to verify that the currently configured max RPC payload size (dictated by pw_rpc’s static encode buffer) will always fit safely within the limits of the fixed HDLC MTU after HDLC encoding.