RPC over HDLC example#

pw_hdlc: Simple, robust, and efficient serial communication

The pw_hdlc module includes an example of bringing up a pw_rpc server that can be used to invoke RPCs. The example code is located at //pw_hdlc/rpc_example/. This tutorial walks through invoking RPCs interactively and with a script using the RPC over HDLC example.

The example implementation of the system_server facade from pw_rpc sends HDLC-encoded RPC packets via pw_sys_io. It has blocking sends and reads so it is not suitable for performance-sensitive applications. This mostly serves as a simplistic example for quickly bringing up RPC over HDLC on bare-metal targets.

Note

This tutorial assumes that you’ve got a stm32f429i-disc1 board, but the instructions work with any target that has implemented pw_sys_io.

Get started#

1. Set up your board#

Connect the board you’ll be communicating with. For the stm32f429i-disc1 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.

. ./activate.sh
pw package install nanopb
gn gen out --args='dir_pw_third_party_nanopb="//environment/packages/nanopb"'
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/rpc_example/bin/rpc_example.elf.

Flash this image to your board. If you are using the stm32f429i-disc1 board you can flash the image with OpenOCD.

openocd -f \
  targets/stm32f429i_disc1/py/stm32f429i_disc1_utils/openocd_stm32f4xx.cfg \
  -c "program \
  out/stm32f429i_disc1_debug/obj/pw_hdlc/rpc_example/bin/rpc_example.elf \
  verify reset exit"

4. Invoke RPCs from an interactive console#

The RPC console uses pw_console 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.

pw-system-console --no-rpc-logging --proto-globs pw_rpc/echo.proto \
  --device /dev/ttyACM0

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. 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():

>>> device.rpcs.pw.rpc.EchoService.Echo(msg='Hello, world!')
(Status.OK, pw.rpc.EchoMessage(msg='Hello, world!'))

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.

python pw_hdlc/rpc_example/example_script.py --device /dev/ttyACM0

You should see this output:

The status was Status.OK
The payload was msg: "Hello"

The device says: Goodbye!

Local RPC example project#

This example is similar to the above example, except it uses a socket to connect a server to a client, both running on the host.

1. Build Pigweed#

Activate the Pigweed environment and build the code.

. ./activate.sh
pw package install nanopb
gn gen out --args='dir_pw_third_party_nanopb="//environment/packages/nanopb"'
ninja -C out

2. Start the server#

Run a pw_rpc server in one terminal window.

./out/pw_strict_host_clang_debug/obj/pw_hdlc/rpc_example/bin/rpc_example

3. Start the client#

In a separate Pigweed-activated terminal, run the pw-system-console RPC client with --proto-globs set to pw_rpc/echo.proto. Additional protos can be added if needed.

pw-system-console --no-rpc-logging --proto-globs pw_rpc/echo.proto \
  --socket-addr default

Tip

The --socket-addr value may be replaced with an IP and port separated by a colon, e.g. 127.0.0.1:33000. If using a Unix socket, the path to the file follows file:, e.g. file:/path/to/unix/socket. Unix socket Python support is pending, see python/cpython#77589.

Tip

The default RPC channel ID (1) can be overriden with --channel-id.

4. Invoke RPCs from the client#

Invoke RPCs from the interactive console on the client side.

>>> device.rpcs.pw.rpc.EchoService.Echo(msg='Hello, world!')
(Status.OK, pw.rpc.EchoMessage(msg='Hello, world!'))

See also

More pw_hdlc docs#

Get started & guides

How to set up and use pw_hdlc

API reference

Reference details about the pw_hdlc API

Design

Design details about pw_hdlc

Code size analysis

The code size impact of pw_hdlc

RPC over HDLC example

A step-by-step example of sending RPCs over HDLC

Experimental async router

An experimental asynchronous HDLC router using pw_channel