Get started & guides#
pw_hdlc: Simple, robust, and efficient serial communication
pw_hdlc
implements a subset of the High-Level Data Link Control (HDLC) protocol.
HDLC is a data link layer protocol intended for serial communication between
devices and is standardized as ISO/IEC 13239:2002.
Get started with pw_hdlc#
Depend on the library:
Add @pigweed//pw_hdlc
to the deps
list in your Bazel target:
cc_library("...") {
# ...
deps = [
# ...
"@pigweed//pw_hdlc",
# ...
]
}
This assumes that your Bazel WORKSPACE
has a repository named @pigweed
that points to the upstream Pigweed repository.
Add $dir_pw_hdlc
to the deps
list in your pw_executable()
build target:
pw_executable("...") {
# ...
deps = [
# ...
"$dir_pw_hdlc",
# ...
]
}
Add pw_hdlc
to your pw_add_library
or similar CMake target:
pw_add_library(my_library STATIC
HEADERS
# ...
PRIVATE_DEPS
# ...
pw_hdlc
# ...
)
There are two ways to use pw_hdlc
from a Zephyr project:
(Recommended) Depend on
pw_hdlc
in your CMake target. See the CMake tab. The Pigweed team recommends this approach because it enables precise CMake dependency analysis.Add
CONFIG_PIGWEED_HDLC=y
to your Zephyr project’s configuration. Also addCONFIG_PIGWEED_HDLC_RPC=y
if using RPC. Although this is the typical Zephyr solution, the Pigweed team doesn’t recommend this approach because it makespw_hdlc
a global dependency and exposes its includes to all targets.
And then encode and decode some data!
Set up RPC over HDLC#
Encoding#
// 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 = pw::hdlc::WriteUIFrame(123 /* address */, data, serial_writer);
if (!status.ok()) {
PW_LOG_INFO("Writing frame failed! %s", status.str());
}
}
# 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!'))
Allocating buffers when encoding#
HDLC encoding overhead changes depending on the payload size and the
nature of the data being encoded. pw_hdlc
provides helper functions
for determining the size of buffers. The helper functions provide
worst-case sizes of frames given a certain payload size and vice-versa.
#include "pw_assert/check.h"
#include "pw_bytes/span.h"
#include "pw_hdlc/encoder"
#include "pw_hdlc/encoded_size.h"
#include "pw_status/status.h"
// The max on-the-wire size in bytes of a single HDLC frame after encoding.
constexpr size_t kMtu = 512;
constexpr size_t kRpcEncodeBufferSize = pw::hdlc::MaxSafePayloadSize(kMtu);
std::array<std::byte, kRpcEncodeBufferSize> rpc_encode_buffer;
// Any data encoded to this buffer is guaranteed to fit in the MTU after
// HDLC encoding.
pw::ConstByteSpan GetRpcEncodeBuffer() {
return rpc_encode_buffer;
}
Decoding#
// 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;
std::array<std::byte, 128> decode_buffer;
pw::hdlc::Decoder decoder(decode_buffer);
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
}
}
}
# 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
Allocating buffers when decoding#
pw::hdlc::Decoder
is a helper for allocating a buffer. It has slightly
lower overhead because it doesn’t need to decode the entire escaped
frame in-memory.
#include "pw_hdlc/decoder.h"
// The max on-the-wire size in bytes of a single HDLC frame after encoding.
constexpr size_t kMtu = 512;
// Create a decoder given the MTU constraint.
constexpr size_t kDecoderBufferSize =
pw::hdlc::Decoder::RequiredBufferSizeForFrameSize(kMtu);
pw::hdlc::DecoderBuffer<kDecoderBufferSize> decoder;