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 youraddress
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());
}
}
The encode module supports encoding HDLC frames.
- pw_hdlc.encode.ui_frame(address: int, data: bytes) bytes
Encodes an HDLC UI-frame with a CRC-32 frame check sequence.
Example:
# Read bytes from serial and encode HDLC frames
import serial
from pw_hdlc import encode
ser = serial.Serial()
address = 123
ser.write(encode.ui_frame(address, b'your data here!'))
The Encoder class provides a way to build complete, escaped HDLC UI frames.
- Encoder.uiFrame(address, data)
- Arguments:
address (
number()
) – frame address.data (
Uint8Array()
) – frame data.
- Returns:
Uint8Array containing a complete HDLC frame.
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. TheResult
contains theFrame
, which is invalidated by the nextProcess()
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.
-
Result<Frame> Process(std::byte new_byte)#
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
}
}
}
- class pw_hdlc.decode.FrameDecoder
Decodes one or more HDLC frames from a stream of data.
- __init__() None
- process(data: bytes) Iterable[Frame]
Decodes and yields HDLC frames, including corrupt frames.
The ok() method on Frame indicates whether it is valid or represents a frame parsing error.
- Yields:
Frames, which may be valid (frame.ok()) or corrupt (!frame.ok())
- process_byte(byte: int) Optional[Frame]
Processes a single byte and returns a frame if one was completed.
- process_valid_frames(data: bytes) Iterable[Frame]
Decodes and yields valid HDLC frames, logging any errors.
Example:
# Decode data read from serial
import serial
from pw_hdlc import decode
ser = serial.Serial()
decoder = decode.FrameDecoder()
while True:
for frame in decoder.process_valid_frames(ser.read()):
# Handle the decoded frame
It is possible to decode HDLC frames from a stream using different protocols or unstructured data. This is not recommended, but may be necessary when introducing HDLC to an existing system.
The FrameAndNonFrameDecoder
Python class supports working with raw data and
HDLC frames in the same stream.
- class pw_hdlc.decode.FrameAndNonFrameDecoder(non_frame_data_handler: Callable[[bytes], Any], *, mtu: Optional[int] = None, timeout_s: Optional[float] = None, handle_shared_flags: bool = True)
Processes both HDLC frames and non-frame data in a stream.
- __init__(non_frame_data_handler: Callable[[bytes], Any], *, mtu: Optional[int] = None, timeout_s: Optional[float] = None, handle_shared_flags: bool = True) None
Yields valid HDLC frames and passes non-frame data to callback.
- Parameters:
mtu – Maximum bytes to receive before flushing raw data. If a valid HDLC frame contains more than MTU bytes, the valid frame will be emitted, but part of the frame will be included in the raw data.
timeout_s – How long to wait before automatically flushing raw data. If a timeout occurs partway through a valid frame, the frame will be emitted, but part of the frame will be included in the raw data.
handle_shared_flags – Whether to permit HDLC frames to share a single flag byte between frames. If False, partial HDLC frames may be emitted as raw data when HDLC frames share a flag byte, but raw data won’t have to wait for a timeout or full MTU to be flushed.
- flush_non_frame_data() None
Flushes any data in the buffer as non-frame data.
If a valid HDLC frame was flushed partway, the data for the first part of the frame will be included both in the raw data and in the frame.
- process(data: bytes) Iterable[Frame]
Processes a stream of mixed HDLC and unstructured data.
Yields OK frames and calls non_frame_data_handler with non-HDLC data.
The decoder class unescapes received bytes and adds them to a buffer. Complete, valid HDLC frames are yielded as they are received.
- Decoder.process(data)
- Arguments:
data (
Uint8Array()
) – bytes to be decoded.
- Yields:
HDLC frames, including corrupt frames. The Frame.ok() method whether the frame is valid.
- processValidFrames(data)
- Arguments:
data (
Uint8Array()
) – bytes to be decoded.
- Yields:
Valid HDLC frames, logging any errors.
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.