1 .. _module-pw_hdlc_lite:
6 `High-Level Data Link Control (HDLC)
7 <https://en.wikipedia.org/wiki/High-Level_Data_Link_Control>`_ is a data link
8 layer protocol intended for serial communication between devices. HDLC is
9 standardized as `ISO/IEC 13239:2002 <https://www.iso.org/standard/37010.html>`_.
11 The ``pw_hdlc_lite`` module provides a simple, robust frame-oriented
12 transport that uses a subset of the HDLC protocol. ``pw_hdlc_lite`` supports
13 sending between embedded devices or the host. It can be used with
14 :ref:`module-pw_rpc` to enable remote procedure calls (RPCs) on embedded on
17 **Why use the pw_hdlc_lite module?**
19 * Enables the transmission of RPCs and other data between devices over serial.
20 * Detects corruption and data loss.
21 * Light-weight, simple, and easy to use.
22 * Supports streaming to transport without buffering, since the length is not
25 .. admonition:: Try it out!
27 For an example of how to use HDLC with :ref:`module-pw_rpc`, see the
28 :ref:`module-pw_hdlc_lite-rpc-example`.
41 The HDLC implementation in ``pw_hdlc_lite`` supports only HDLC information
42 frames. These frames are encoded as follows:
46 _________________________________________
48 | | | | | | |... [More frames]
49 |_|_|_|__________________________|____|_|...
52 F = flag byte (0x7e, the ~ character)
55 FCS = frame check sequence (CRC-32)
58 Encoding and sending data
59 -------------------------
60 This module first writes an initial frame delimiter byte (0x7E) to indicate the
61 beginning of the frame. Before sending any of the payload data through serial,
62 the special bytes are escaped:
64 +-------------------------+-----------------------+
65 | Unescaped Special Bytes | Escaped Special Bytes |
66 +=========================+=======================+
68 +-------------------------+-----------------------+
70 +-------------------------+-----------------------+
72 The bytes of the payload are escaped and written in a single pass. The
73 frame check sequence is calculated, escaped, and written after. After this, a
74 final frame delimiter byte (0x7E) is written to mark the end of the frame.
76 Decoding received bytes
77 -----------------------
78 Frames may be received in multiple parts, so we need to store the received data
79 in a buffer until the ending frame delimiter (0x7E) is read. When the
80 ``pw_hdlc_lite`` decoder receives data, it unescapes it and adds it to a buffer.
81 When the frame is complete, it calculates and verifies the frame check sequence
82 and does the following:
84 * If correctly verified, the decoder returns the decoded frame.
85 * If the checksum verification fails, the frame is discarded and an error is
90 There are two primary functions of the ``pw_hdlc_lite`` module:
92 * **Encoding** data by constructing a frame with the escaped payload bytes and
94 * **Decoding** data by unescaping the received bytes, verifying the frame
95 check sequence, and returning successfully decoded frames.
99 The Encoder API provides a single function that encodes data as an HDLC
104 .. cpp:namespace:: pw
106 .. cpp:function:: Status hdlc_lite::WriteInformationFrame(uint8_t address, ConstByteSpan data, stream::Writer& writer)
108 Writes a span of data to a :ref:`pw::stream::Writer <module-pw_stream>` and
109 returns the status. This implementation uses the :ref:`module-pw_checksum`
110 module to compute the CRC-32 frame check sequence.
114 #include "pw_hdlc_lite/encoder.h"
115 #include "pw_hdlc_lite/sys_io_stream.h"
118 pw::stream::SysIoWriter serial_writer;
119 Status status = WriteInformationFrame(123 /* address */,
123 PW_LOG_INFO("Writing frame failed! %s", status.str());
129 .. automodule:: pw_hdlc_lite.encode
132 .. code-block:: python
135 from pw_hdlc_lite import encode
137 ser = serial.Serial()
138 ser.write(encode.information_frame(b'your data here!'))
142 The decoder class unescapes received bytes and adds them to a buffer. Complete,
143 valid HDLC frames are yielded as they are received.
147 .. cpp:class:: pw::hdlc_lite::Decoder
149 .. cpp:function:: pw::Result<Frame> Process(std::byte b)
151 Parses a single byte of an HDLC stream. Returns a Result with the complete
152 frame if the byte completes a frame. The status is the following:
154 - OK - A frame was successfully decoded. The Result contains the Frame,
155 which is invalidated by the next Process call.
156 - UNAVAILABLE - No frame is available.
157 - RESOURCE_EXHAUSTED - A frame completed, but it was too large to fit in
158 the decoder's buffer.
159 - DATA_LOSS - A frame completed, but it was invalid. The frame was
160 incomplete or the frame check sequence verification failed.
162 .. cpp:function:: void Process(pw::ConstByteSpan data, F&& callback, Args&&... args)
164 Processes a span of data and calls the provided callback with each frame or
167 This example demonstrates reading individual bytes from ``pw::sys_io`` and
168 decoding HDLC frames:
172 #include "pw_hdlc_lite/decoder.h"
173 #include "pw_sys_io/sys_io.h"
178 if (!pw::sys_io::ReadByte(&data).ok()) {
179 // Log serial reading error
181 Result<Frame> decoded_frame = decoder.Process(data);
183 if (decoded_frame.ok()) {
184 // Handle the decoded frame
191 .. autoclass:: pw_hdlc_lite.decode.FrameDecoder
194 Below is an example using the decoder class to decode data read from serial:
196 .. code-block:: python
199 from pw_hdlc_lite import decode
201 ser = serial.Serial()
202 decoder = decode.FrameDecoder()
205 for frame in decoder.process_valid_frames(ser.read()):
206 # Handle the decoded frame
211 pw::stream::SysIoWriter
212 ------------------------
213 The ``SysIoWriter`` C++ class implements the ``Writer`` interface with
214 ``pw::sys_io``. This Writer may be used by the C++ encoder to send HDLC frames
219 .. autoclass:: pw_hdlc_lite.rpc.HdlcRpcClient
224 - **Expanded protocol support** - ``pw_hdlc_lite`` currently only supports
225 information frames with a single address byte and control byte. Support for
226 different frame types and extended address or control fields may be added in
229 - **Higher performance** - We plan to improve the overall performance of the
230 decoder and encoder implementations by using SIMD/NEON.