pw_rpc, pw_hdlc_lite: Documentation update
Change-Id: Ie4b7121177133e1539ae3dca066a15e9fba689e7
Reviewed-on: https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/19161
Commit-Queue: Wyatt Hepler <hepler@google.com>
Reviewed-by: Keir Mierle <keir@google.com>
Reviewed-by: Ewout van Bekkum <ewout@google.com>
diff --git a/pw_checksum/docs.rst b/pw_checksum/docs.rst
index 6157fe6..8d5f849 100644
--- a/pw_checksum/docs.rst
+++ b/pw_checksum/docs.rst
@@ -2,6 +2,8 @@
.. highlight:: sh
+.. _chapter-pw-checksum:
+
-----------
pw_checksum
-----------
diff --git a/pw_hdlc_lite/BUILD.gn b/pw_hdlc_lite/BUILD.gn
index def4fd0..8165eb8 100644
--- a/pw_hdlc_lite/BUILD.gn
+++ b/pw_hdlc_lite/BUILD.gn
@@ -123,5 +123,12 @@
}
pw_doc_group("docs") {
- sources = [ "docs.rst" ]
+ sources = [
+ "docs.rst",
+ "rpc_example/docs.rst",
+ ]
+ inputs = [
+ "py/pw_hdlc_lite/decode.py",
+ "py/pw_hdlc_lite/encode.py",
+ ]
}
diff --git a/pw_hdlc_lite/docs.rst b/pw_hdlc_lite/docs.rst
index fbb283c..7b76bcf 100644
--- a/pw_hdlc_lite/docs.rst
+++ b/pw_hdlc_lite/docs.rst
@@ -1,130 +1,117 @@
-.. _chapter-pw-hdlc:
-
.. default-domain:: cpp
.. highlight:: sh
+.. _chapter-pw-hdlc-lite:
+
------------
pw_hdlc_lite
------------
-pw_hdlc_lite is a module that enables serial communication between devices
-using the HDLC-Lite protocol.
+`High-Level Data Link Control (HDLC)
+<https://en.wikipedia.org/wiki/High-Level_Data_Link_Control>`_ is a data link
+layer protocol intended for serial communication between devices. HDLC is
+standardized as `ISO/IEC 13239:2002 <https://www.iso.org/standard/37010.html>`_.
-Compatibility
-=============
-C++17
+The ``pw_hdlc_lite`` module provides a simple, robust frame-oriented
+transport that uses a subset of the HDLC protocol. ``pw_hdlc_lite`` supports
+sending between embedded devices or the host. It can be used with
+:ref:`chapter-pw-rpc` to enable remote procedure calls (RPCs) on embedded on
+devices.
-Dependencies
-============
-* ``pw_bytes``
-* ``pw_log``
-* ``pw_preprocessor``
-* ``pw_result``
-* ``pw_rpc``
-* ``pw_status``
-* ``pw_span``
-* ``pw_stream``
-* ``pw_sys_io``
+**Why use the pw_hdlc_lite module?**
-HDLC-Lite Overview
-==================
-High-Level Data Link Control (HDLC) is a data link layer protocol which uses
-synchronous serial transmissions for communication between two devices. Unlike
-the standard HDLC protocol which uses six fields of embedded information, the
-HDLC-Lite protocol is a minimal version that only uses the bare essentials.
+ * Enables the transmission of RPCs and other data between devices over serial.
+ * Detects corruption and data loss.
+ * Light-weight, simple, and easy to use.
+ * Supports streaming to transport without buffering, since the length is not
+ encoded.
-The HDLC-Lite data frame in ``pw_hdlc_lite`` uses a start and end frame
-delimiter (0x7E), the escaped binary payload and the CCITT-CRC16 value.
-It looks like:
+.. admonition:: Try it out!
-.. code-block:: text
+ For an example of how to use HDLC with :ref:`chapter-pw-rpc`, see the
+ :ref:`chapter-pw-hdlc-rpc-example`.
- [More frames]
- _________________________________________ _______
- | | | | | |...| | |
- | | | | | |...| | |
- |_|______________________________|__|_|_|...|___|_|
- F Payload CRC F F CRC F
+.. toctree::
+ :maxdepth: 1
+ :hidden:
-Basic Overview
-==============
-The ``pw_hdlc_lite`` module provides a simple, reliable packet-oriented
-transport that uses the HDLC-Lite protocol to send and receive data to and from
-embedded devices. This is especially needed for making RPC calls on devices
-during testing because the ``pw_rpc`` module does not handle the transmission of
-RPCs. This module enables the transmission of RPCs and other bytes through a
-serial connection.
-
-There are essentially two main functions of the ``pw_hdlc_lite`` module:
-
- * **Encoding** the data by escaping the bytes of the payload, calculating the
- CCITT-CRC16 value, constructing a data frame and sending the
- resulting data packet through serial.
- * **Decoding** the data by unescaping the received bytes, verifying the
- CCITT-CRC16 value and returning the successfully decoded packets.
-
-**Why use the ``pw_hdlc_lite`` module?**
-
- * Enables the transmission of RPCs and other data between devices over serial
- * Resilient to corruption and data loss.
- * Light-weight, simple and easy to use.
- * Supports streaming to transport without buffering - e.g. protocol buffers
- have length-prefix.
+ rpc_example/docs
Protocol Description
====================
+Frames
+------
+The HDLC implementation in ``pw_hdlc_lite`` supports only HDLC information
+frames. These frames are encoded as follows:
+
+.. code-block:: text
+
+ _________________________________________
+ | | | | | | |...
+ | | | | | | |... [More frames]
+ |_|_|_|__________________________|____|_|...
+ F A C Payload FCS F
+
+ F = flag byte (0x7e, the ~ character)
+ A = address field
+ C = control field
+ FCS = frame check sequence (CRC-32)
+
+
Encoding and sending data
-------------------------
This module first writes an initial frame delimiter byte (0x7E) to indicate the
beginning of the frame. Before sending any of the payload data through serial,
-the special bytes are escaped accordingly:
+the special bytes are escaped:
- +-----------------------+----------------------+
- |Unescaped Special Bytes| Escaped Special Bytes|
- +=======================+======================+
- | 0x7E | 0x7D5E |
- +-----------------------+----------------------+
- | 0x7D | 0x7D5D |
- +-----------------------+----------------------+
+ +-------------------------+-----------------------+
+ | Unescaped Special Bytes | Escaped Special Bytes |
+ +=========================+=======================+
+ | 7E | 7D 5E |
+ +-------------------------+-----------------------+
+ | 7D | 7D 5D |
+ +-------------------------+-----------------------+
The bytes of the payload are escaped and written in a single pass. The
-CCITT-CRC16 value is calculated, escaped and written after. After this, a final
-frame delimiter byte (0x7E) is written to mark the end of the frame.
+frame check sequence is calculated, escaped, and written after. After this, a
+final frame delimiter byte (0x7E) is written to mark the end of the frame.
Decoding received bytes
-----------------------
-Packets may be received in multiple parts, so we need to store the received data
+Frames may be received in multiple parts, so we need to store the received data
in a buffer until the ending frame delimiter (0x7E) is read. When the
-pw_hdlc_lite decoder receives data, it unescapes it and adds it to a buffer.
-When the frame is complete, it calculates and verifies the CCITT-CRC16 bytes and
-does the following:
+``pw_hdlc_lite`` decoder receives data, it unescapes it and adds it to a buffer.
+When the frame is complete, it calculates and verifies the frame check sequence
+and does the following:
-* If correctly verified, the decoder returns the decoded packet.
-* If the checksum verification fails, the data packet is discarded.
-
-During the decoding process, the decoder essentially transitions between 3
-states, where each state indicates the method of decoding that particular byte:
-
-NO_PACKET --> PACKET_ACTIVE --> (ESCAPE | NO_PACKET).
+* If correctly verified, the decoder returns the decoded frame.
+* If the checksum verification fails, the frame is discarded and an error is
+ reported.
API Usage
=========
+There are two primary functions of the ``pw_hdlc_lite`` module:
+
+ * **Encoding** data by constructing a frame with the escaped payload bytes and
+ frame check sequence.
+ * **Decoding** data by unescaping the received bytes, verifying the frame
+ check sequence, and returning successfully decoded frames.
Encoder
-------
-The Encoder API invloves a single function that encodes the data using the
-HDLC-Lite protocol and sends the data through the serial.
+The Encoder API provides a single function that encodes data as an HDLC
+information frame.
C++
^^^
-In C++, this function is called ``EncodeAndWritePayload`` and it accepts a
-ConstByteSpan called payload and an object of type Writer& as arguments. It
-returns a Status object that indicates if the write was successful. This
-implementation uses the ``pw_checksum`` module to compute the CRC16 value. Since
-the function writes a starting and ending frame delimiter byte at the beginnning
-and the end of frames, it is safe to encode multiple spans. The usage of this
-function is as follows:
+.. cpp:namespace:: pw
+
+.. cpp:function:: Status hdlc_lite::WriteInformationFrame(uint8_t address, ConstByteSpan data, stream::Writer& writer)
+
+ Writes a span of data to a :ref:`pw::stream::Writer <chapter-pw-stream>` and
+ returns the status. This implementation uses the :ref:`chapter-pw-checksum`
+ module to compute the CRC-32 frame check sequence.
.. code-block:: cpp
@@ -132,70 +119,57 @@
#include "pw_hdlc_lite/sys_io_stream.h"
int main() {
- pw::stream::SerialWriter serial_writer;
- constexpr std::array<byte, 1> test_array = { byte(0x41) };
- auto status = EncodeAndWritePayload(test_array, serial_writer);
+ pw::stream::SysIoWriter serial_writer;
+ Status status = WriteInformationFrame(123 /* address */,
+ data,
+ serial_writer);
+ if (!status.ok()) {
+ PW_LOG_INFO("Writing frame failed! %s", status.str());
+ }
}
-In the example above, we expect the encoder to send the following bytes:
-
-- **0x7E** - Initial Frame Delimiter
-- **0x41** - Payload
-- **0x15** - LSB of the CCITT-CRC16 value
-- **0xB9** - MSB of the CCITT-CRC16 value
-- **0x7E** - End Frame Delimiter
-
Python
^^^^^^
-In Python, the function is called ``encode_and_write_payload`` and it accepts
-the payload as a byte object and a callable to which is used to write the data.
-This function does not return anything, and uses the binascii library function
-called crc_hqx to compute the CRC16 bytes. Like the C++ function, the Python
-function also writes a frame delimiter at the beginnning and the end of frames
-so it is safe to encode multiple spans consecutively. The usage of this function
-is as follows:
+.. automodule:: pw_hdlc_lite.encode
+ :members:
.. code-block:: python
import serial
- from pw_hdlc_lite import encoder
+ from pw_hdlc_lite import encode
ser = serial.Serial()
- encoder.encode_and_write_payload(b'A', ser.write)
-
-We expect this example to give us the same result as the C++ example above since
-it encodes the same payload.
+ ser.write(encode.information_frame(b'your data here!'))
Decoder
-------
-The Decoder API involves a Decoder class whose main functionality is a function
-that unescapes the received bytes, adds them to a buffer and returns the
-successfully decoded packets. A class is used so that the user can call the
-decoder object's adding bytes functionality on the currently received bytes
-instead of waiting on the entire packet to arrive.
+The decoder class unescapes received bytes and adds them to a buffer. Complete,
+valid HDLC frames are yielded as they are received.
C++
^^^
-The main functionality of the C++ ``Decoder`` class is the 'AddByte' function
-which accepts a single byte as an argument, unescapes it and adds it to the
-buffer. If the byte is the ending frame delimiter flag (0x7E) it attempts to
-decode the packet and returns a ``Result`` object indicating the success of the
-operation:
+.. cpp:class:: pw::hdlc_lite::Decoder
- * The returned ``pw::Result`` object will have status ``Status::OK`` and
- value ConstByteSpan containing the most recently decoded packet if it finds
- the end of the data-frame and successfully verifies the CRC.
- * The returned ``pw::Result`` object will have status ``Status::UNAVAILABLE``
- if it doesnt find the end of the data-frame during that function call.
- * The returned ``pw::Result`` object will have status ``Status::DATA_LOSS``
- if it finds the end of the data-frame, but the CRC-verification fails. It
- also returns this status if the packet in question does not have the
- 2-byte CRC in it.
- * The returned ``pw::Result`` object will have status
- ``Status::RESOURCE_EXHAUSTED`` if the decoder buffer runs out of space.
+ .. cpp:function:: pw::Result<Frame> Process(std::byte b)
-Here's a C++ example of reading individual bytes from serial and then using the
-decoder object to decode the received data:
+ Parses a single byte of an HDLC stream. Returns a Result with the complete
+ frame if the byte completes a frame. The status is 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.
+
+ .. cpp:function:: void Process(pw::ConstByteSpan data, F&& callback, Args&&... args)
+
+ Processes a span of data and calls the provided callback with each frame or
+ error.
+
+This example demonstrates reading individual bytes from ``pw::sys_io`` and
+decoding HDLC frames:
.. code-block:: cpp
@@ -203,66 +177,62 @@
#include "pw_sys_io/sys_io.h"
int main() {
- byte data;
+ std::byte data;
while (true) {
if (!pw::sys_io::ReadByte(&data).ok()) {
// Log serial reading error
}
- auto decoded_packet = decoder.AddByte(data);
+ Result<Frame> decoded_frame = decoder.Process(data);
- if (decoded_packet.ok()) {
- // Use decoded_packet to get access to the most recently decoded packet
+ if (decoded_frame.ok()) {
+ // Handle the decoded frame
}
}
}
Python
^^^^^^
-The main functionality of the Python ``Decoder`` class is the ``add_bytes``
-generator which unescapes the bytes object argument and adds them to a buffer
-until it encounters the ending frame delimiter (0x7E) flag. The generator yields
-the decoded packets as byte objects upon successfully verification of the CRC16
-value of the received bytes. If the CRC verification fails it raises a
-CrcMismatchError exception.
+.. autoclass:: pw_hdlc_lite.decode.FrameDecoder
+ :members:
-Below is an example of the usage of the decoder class to decode bytes read from
-serial:
+Below is an example using the decoder class to decode data read from serial:
.. code-block:: python
import serial
- from pw_hdlc_lite import decoder
+ from pw_hdlc_lite import decode
ser = serial.Serial()
- decode = decoder.Decoder()
+ decoder = decode.FrameDecoder()
- while true:
- byte = ser.read(1)
+ while True:
+ for frame in decoder.process_valid_frames(ser.read()):
+ # Handle the decoded frame
- for decoded_packet in decode.add_bytes(byte):
- # Do something with the decoded packet
+Additional features
+===================
-Like the C++ example, this reads individual bytes and adds them to the decoder.
-
-Features
-========
-
-pw::stream::SerialWriter
+pw::stream::SysIoWriter
------------------------
-The ``SerialWriter`` class implements the ``Writer`` interface by using sys_io
-to write data over a serial connection. This Writer object is used by the C++
-encoder to send the encoded bytes to the device.
+The ``SysIoWriter`` C++ class implements the ``Writer`` interface with
+``pw::sys_io``. This Writer may be used by the C++ encoder to send HDLC frames
+over serial.
-Roadmap & Status
-================
+HdlcRpcClient
+-------------
+.. autoclass:: pw_hdlc_lite.rpc.HdlcRpcClient
+ :members:
-- **Additional fields** - As it currently stands, ``pw_hdlc_lite`` uses only
- three fields of control bytes: starting frame delimiter, 2-byte CRC and an
- ending frame delimiter. However, if we decided to send larger, more
- complicated RPCs and if wanted to stream log debug and error messages, we will
- require additional fields of data to ensure the different packets are sent
- correctly. Thus, in the future, we plan to add additional channel and sequence
- byte fields that could enable separate channels for pw_rpc, QoS etc.
+Roadmap
+=======
+- **Expanded protocol support** - ``pw_hdlc_lite`` currently only supports
+ information frames with a single address byte and control byte. Support for
+ different frame types and extended address or control fields may be added in
+ the future.
- **Higher performance** - We plan to improve the overall performance of the
decoder and encoder implementations by using SIMD/NEON.
+
+Compatibility
+=============
+C++17
diff --git a/pw_hdlc_lite/public/pw_hdlc_lite/decoder.h b/pw_hdlc_lite/public/pw_hdlc_lite/decoder.h
index 1e14066..d15cbe7 100644
--- a/pw_hdlc_lite/public/pw_hdlc_lite/decoder.h
+++ b/pw_hdlc_lite/public/pw_hdlc_lite/decoder.h
@@ -92,7 +92,8 @@
//
Result<Frame> Process(std::byte b);
- // Calls the provided callback with each frame or error.
+ // Processes a span of data and calls the provided callback with each frame or
+ // error.
template <typename F, typename... Args>
void Process(ConstByteSpan data, F&& callback, Args&&... args) {
for (std::byte b : data) {
diff --git a/pw_hdlc_lite/rpc_example/docs.rst b/pw_hdlc_lite/rpc_example/docs.rst
new file mode 100644
index 0000000..94bc3f6
--- /dev/null
+++ b/pw_hdlc_lite/rpc_example/docs.rst
@@ -0,0 +1,104 @@
+.. default-domain:: cpp
+
+.. highlight:: sh
+
+.. _chapter-pw-hdlc-rpc-example:
+
+=============================
+RPC over HDLC example project
+=============================
+The :ref:`chapter-pw-hdlc-lite` module includes an example of bringing up a
+:ref:`chapter-pw-rpc` server that can be used to invoke RPCs. The example code
+is located at ``pw_hdlc_lite/rpc_example``. This section walks through invoking
+RPCs interactively and with a script using the RPC over HDLC example.
+
+These instructions assume the STM32F429i Discovery board, but they work with
+any target with :ref:`pw::sys_io <chapter-pw-sys-io>` implemented.
+
+---------------------
+Getting started guide
+---------------------
+
+1. Set up your board
+====================
+Connect the board you'll be communicating with. For the Discovery board, connect
+the mini USB port, and note which serial device it appears as (e.g.
+``/dev/ttyACM0``).
+
+2. Build Pigweed
+================
+Activate the Pigweed environment and run the default build.
+
+.. code-block:: sh
+
+ source activate.sh
+ gn gen out
+ ninja -C out
+
+3. Flash the firmware image
+===========================
+After a successful build, the binary for the example will be located at
+``out/<toolchain>/obj/pw_hdlc_lite/rpc_example/bin/rpc_example.elf``.
+
+Flash this image to your board. If you are using the STM32F429i Discovery Board,
+you can flash the image with `OpenOCD <http://openocd.org>`_.
+
+.. code-block:: sh
+
+ openocd -f targets/stm32f429i-disc1/py/stm32f429i_disc1_utils/openocd_stm32f4xx.cfg \
+ -c "program out/stm32f429i_disc1_debug/obj/pw_hdlc_lite/rpc_example/bin/rpc_example.elf"
+
+4. Invoke RPCs from in an interactive console
+=============================================
+The RPC console uses `IPython <https://ipython.org>`_ to make a rich interactive
+console for working with pw_rpc. Run the RPC console with the following command,
+replacing ``/dev/ttyACM0`` with the correct serial device for your board.
+
+.. code-block:: text
+
+ $ python -m pw_hdlc_lite.rpc_console --device /dev/ttyACM0
+
+ Console for interacting with pw_rpc over HDLC.
+
+ To start the console, provide a serial port as the --device argument and paths
+ or globs for .proto files that define the RPC services to support:
+
+ python -m pw_hdlc_lite.rpc_console --device /dev/ttyUSB0 sample.proto
+
+ This starts an IPython console for communicating with the connected device. A
+ few variables are predefined in the interactive console. These include:
+
+ rpcs - used to invoke RPCs
+ device - the serial device used for communication
+ client - the pw_rpc.Client
+
+ An example echo RPC command:
+
+ rpcs.pw.rpc.EchoService.Echo(msg="hello!")
+
+ In [1]:
+
+RPCs may be accessed through the predefined ``rpcs`` variable. RPCs are
+organized by their protocol buffer package and RPC service, as defined in a
+.proto file. To call the ``Echo`` method is part of the ``EchoService``, which
+is in the ``pw.rpc`` package. To invoke it synchronously, call
+``rpcs.pw.rpc.EchoService.Echo``:
+
+.. code-block:: python
+
+ In [1]: rpcs.pw.rpc.EchoService.Echo(msg="Your message here!")
+ Out[1]: (<Status.OK: 0>, msg: "Your message here!")
+
+5. Invoke RPCs with a script
+============================
+RPCs may also be invoked from Python scripts. Close the RPC console if it is
+running, and execute the example script. Set the --device argument to the
+serial port for your device.
+
+.. code-block:: text
+
+ $ pw_hdlc_lite/rpc_example/example_script.py --device /dev/ttyACM0
+ The status was Status.OK
+ The payload was msg: "Hello"
+
+ The device says: Goodbye!
diff --git a/pw_rpc/docs.rst b/pw_rpc/docs.rst
index 8076639..048627f 100644
--- a/pw_rpc/docs.rst
+++ b/pw_rpc/docs.rst
@@ -10,9 +10,150 @@
The ``pw_rpc`` module provides a system for defining and invoking remote
procedure calls (RPCs) on a device.
+.. admonition:: Try it out!
+
+ For a quick intro to ``pw_rpc``, see the :ref:`chapter-pw-hdlc-rpc-example` in
+ the :ref:`chapter-pw-hdlc-lite` module.
+
.. attention::
- Under construction.
+ This documentation is under construction.
+
+Creating an RPC
+===============
+
+1. RPC service declaration
+--------------------------
+Pigweed RPCs are declared in a protocol buffer service definition.
+
+* `Protocol Buffer service documentation
+ <https://developers.google.com/protocol-buffers/docs/proto3#services>`_
+* `gRPC service definition documentation
+ <https://grpc.io/docs/what-is-grpc/core-concepts/#service-definition>`_
+
+.. code-block:: protobuf
+
+ syntax = "proto3";
+
+ package foo.bar;
+
+ message Request {}
+
+ message Response {
+ int32 number = 1;
+ }
+
+ service TheService {
+ rpc MethodOne(Request) returns (Response) {}
+ rpc MethodTwo(Request) returns (stream Response) {}
+ }
+
+This protocol buffer is declared in a ``BUILD.gn`` file as follows:
+
+.. code-block:: python
+
+ import("//build_overrides/pigweed.gni")
+ import("$dir_pw_protobuf_compiler/proto.gni")
+
+ pw_proto_library("the_service_proto") {
+ sources = [ "foo_bar/the_service.proto" ]
+ }
+
+2. RPC service definition
+-------------------------
+``pw_rpc`` generates a C++ base class for each RPC service declared in a .proto
+file. The serivce class is implemented by inheriting from this generated base
+and defining a method for each RPC.
+
+A service named ``TheService`` in package ``foo.bar`` will generate the
+following class:
+
+.. cpp:class:: template <typename Implementation> foo::bar::generated::TheService
+
+A Nanopb implementation of this service would be as follows:
+
+.. code-block:: cpp
+
+ namespace foo::bar {
+
+ class TheService : public generated::TheService<TheService> {
+ public:
+ pw::Status MethodOne(ServerContext& ctx,
+ const foo_bar_Request& request,
+ foo_bar_Response& response) {
+ // implementation
+ return pw::Status::OK;
+ }
+
+ void MethodTwo(ServerContext& ctx,
+ const foo_bar_Request& request,
+ ServerWriter<foo_bar_Response>& response) {
+ // implementation
+ response.Write(foo_bar_Response{.number = 123});
+ }
+ };
+
+ } // namespace foo::bar
+
+The Nanopb implementation would be declared in a ``BUILD.gn``:
+
+.. code-block:: python
+
+ import("//build_overrides/pigweed.gni")
+
+ import("$dir_pw_build/target_types.gni")
+
+ pw_source_set("the_service") {
+ public_configs = [ ":public" ]
+ public = [ "public/foo_bar/service.h" ]
+ public_deps = [ ":the_service_proto_nanopb_rpc" ]
+ }
+
+.. attention::
+
+ pw_rpc's generated classes will support using ``pw_protobuf`` or raw buffers
+ (no protobuf library) in the future.
+
+3. Register the service with a server
+-------------------------------------
+This example code sets up an RPC server with an
+:ref:`HDLC<chapter-pw-hdlc-lite>` channel output and the example service.
+
+.. code-block:: cpp
+
+ // Set up the output channel for the pw_rpc server to use. This configures the
+ // pw_rpc server to use HDLC over UART; projects not using UART and HDLC must
+ // adapt this as necessary.
+ pw::stream::SysIoWriter writer;
+ pw::rpc::RpcChannelOutput<kMaxTransmissionUnit> hdlc_channel_output(
+ writer, pw::hdlc_lite::kDefaultRpcAddress, "HDLC output");
+
+ pw::rpc::Channel channels[] = {
+ pw::rpc::Channel::Create<1>(&hdlc_channel_output)};
+
+ // Declare the pw_rpc server with the HDLC channel.
+ pw::rpc::Server server(channels);
+
+ pw::rpc::TheService the_service;
+
+ void RegisterServices() {
+ // Register the foo.bar.TheService example service.
+ server.Register(the_service);
+
+ // Register other services
+ }
+
+ int main() {
+ // Set up the server.
+ RegisterServices();
+
+ // Declare a buffer for decoding incoming HDLC frames.
+ std::array<std::byte, kMaxTransmissionUnit> input_buffer;
+
+ PW_LOG_INFO("Starting pw_rpc server");
+ pw::hdlc_lite::ReadAndProcessPackets(
+ server, hdlc_channel_output, input_buffer);
+ }
Services
========
diff --git a/pw_stream/docs.rst b/pw_stream/docs.rst
index 9128070..b1edfa0 100644
--- a/pw_stream/docs.rst
+++ b/pw_stream/docs.rst
@@ -1,9 +1,9 @@
-.. _chapter-stream:
-
.. default-domain:: cpp
.. highlight:: sh
+.. _chapter-pw-stream:
+
---------
pw_stream
---------