6 The ``pw_rpc`` module provides a system for defining and invoking remote
7 procedure calls (RPCs) on a device.
9 .. admonition:: Try it out!
11 For a quick intro to ``pw_rpc``, see the
12 :ref:`module-pw_hdlc-rpc-example` in the :ref:`module-pw_hdlc` module.
16 This documentation is under construction.
21 1. RPC service declaration
22 --------------------------
23 Pigweed RPCs are declared in a protocol buffer service definition.
25 * `Protocol Buffer service documentation
26 <https://developers.google.com/protocol-buffers/docs/proto3#services>`_
27 * `gRPC service definition documentation
28 <https://grpc.io/docs/what-is-grpc/core-concepts/#service-definition>`_
30 .. code-block:: protobuf
43 rpc MethodOne(Request) returns (Response) {}
44 rpc MethodTwo(Request) returns (stream Response) {}
47 This protocol buffer is declared in a ``BUILD.gn`` file as follows:
49 .. code-block:: python
51 import("//build_overrides/pigweed.gni")
52 import("$dir_pw_protobuf_compiler/proto.gni")
54 pw_proto_library("the_service_proto") {
55 sources = [ "foo_bar/the_service.proto" ]
58 2. RPC code generation
59 ----------------------
60 ``pw_rpc`` generates a C++ header file for each ``.proto`` file. This header is
61 generated in the build output directory. Its exact location varies by build
62 system and toolchain, but the C++ include path always matches the sources
63 declaration in the ``pw_proto_library``. The ``.proto`` extension is replaced
64 with an extension corresponding to the protobuf library in use.
66 ================== =============== =============== =============
67 Protobuf libraries Build subtarget Protobuf header pw_rpc header
68 ================== =============== =============== =============
69 Raw only .raw_rpc (none) .raw_rpc.pb.h
70 Nanopb or raw .nanopb_rpc .pb.h .rpc.pb.h
71 pw_protobuf or raw .pwpb_rpc .pwpb.h .rpc.pwpb.h
72 ================== =============== =============== =============
74 For example, the generated RPC header for ``"foo_bar/the_service.proto"`` is
75 ``"foo_bar/the_service.rpc.pb.h"`` for Nanopb or
76 ``"foo_bar/the_service.raw_rpc.pb.h"`` for raw RPCs.
78 The generated header defines a base class for each RPC service declared in the
79 ``.proto`` file. A service named ``TheService`` in package ``foo.bar`` would
80 generate the following base class:
82 .. cpp:class:: template <typename Implementation> foo::bar::generated::TheService
84 3. RPC service definition
85 -------------------------
86 The serivce class is implemented by inheriting from the generated RPC service
87 base class and defining a method for each RPC. The methods must match the name
88 and function signature for one of the supported protobuf implementations.
89 Services may mix and match protobuf implementations within one service.
93 The generated code includes RPC service implementation stubs. You can
94 reference or copy and paste these to get started with implementing a service.
95 These stub classes are generated at the bottom of the pw_rpc proto header.
97 A Nanopb implementation of this service would be as follows:
101 #include "foo_bar/the_service.rpc.pb.h"
105 class TheService : public generated::TheService<TheService> {
107 pw::Status MethodOne(ServerContext& ctx,
108 const foo_bar_Request& request,
109 foo_bar_Response& response) {
111 return pw::OkStatus();
114 void MethodTwo(ServerContext& ctx,
115 const foo_bar_Request& request,
116 ServerWriter<foo_bar_Response>& response) {
118 response.Write(foo_bar_Response{.number = 123});
122 } // namespace foo::bar
124 The Nanopb implementation would be declared in a ``BUILD.gn``:
126 .. code-block:: python
128 import("//build_overrides/pigweed.gni")
130 import("$dir_pw_build/target_types.gni")
132 pw_source_set("the_service") {
133 public_configs = [ ":public" ]
134 public = [ "public/foo_bar/service.h" ]
135 public_deps = [ ":the_service_proto.nanopb_rpc" ]
140 pw_rpc's generated classes will support using ``pw_protobuf`` or raw buffers
141 (no protobuf library) in the future.
143 4. Register the service with a server
144 -------------------------------------
145 This example code sets up an RPC server with an :ref:`HDLC<module-pw_hdlc>`
146 channel output and the example service.
150 // Set up the output channel for the pw_rpc server to use. This configures the
151 // pw_rpc server to use HDLC over UART; projects not using UART and HDLC must
152 // adapt this as necessary.
153 pw::stream::SysIoWriter writer;
154 pw::rpc::RpcChannelOutput<kMaxTransmissionUnit> hdlc_channel_output(
155 writer, pw::hdlc::kDefaultRpcAddress, "HDLC output");
157 pw::rpc::Channel channels[] = {
158 pw::rpc::Channel::Create<1>(&hdlc_channel_output)};
160 // Declare the pw_rpc server with the HDLC channel.
161 pw::rpc::Server server(channels);
163 pw::rpc::TheService the_service;
165 void RegisterServices() {
166 // Register the foo.bar.TheService example service.
167 server.Register(the_service);
169 // Register other services
173 // Set up the server.
176 // Declare a buffer for decoding incoming HDLC frames.
177 std::array<std::byte, kMaxTransmissionUnit> input_buffer;
179 PW_LOG_INFO("Starting pw_rpc server");
180 pw::hdlc::ReadAndProcessPackets(
181 server, hdlc_channel_output, input_buffer);
186 A service is a logical grouping of RPCs defined within a .proto file. ``pw_rpc``
187 uses these .proto definitions to generate code for a base service, from which
188 user-defined RPCs are implemented.
190 ``pw_rpc`` supports multiple protobuf libraries, and the generated code API
191 depends on which is used.
193 .. _module-pw_rpc-protobuf-library-apis:
195 Protobuf library APIs
196 =====================
203 Testing a pw_rpc integration
204 ============================
205 After setting up a ``pw_rpc`` server in your project, you can test that it is
206 working as intended by registering the provided ``EchoService``, defined in
207 ``pw_rpc_protos/echo.proto``, which echoes back a message that it receives.
209 .. literalinclude:: pw_rpc_protos/echo.proto
213 For example, in C++ with nanopb:
217 #include "pw_rpc/server.h"
219 // Include the apporpriate header for your protobuf library.
220 #include "pw_rpc/echo_service_nanopb.h"
222 constexpr pw::rpc::Channel kChannels[] = { /* ... */ };
223 static pw::rpc::Server server(kChannels);
225 static pw::rpc::EchoService echo_service;
228 server.RegisterService(&echo_service);
233 Pigweed RPC servers and clients communicate using ``pw_rpc`` packets. These
234 packets are used to send requests and responses, control streams, cancel ongoing
235 RPCs, and report errors.
239 Pigweed RPC packets consist of a type and a set of fields. The packets are
240 encoded as protocol buffers. The full packet format is described in
241 ``pw_rpc/pw_rpc_protos/internal/packet.proto``.
243 .. literalinclude:: pw_rpc_protos/internal/packet.proto
247 The packet type and RPC type determine which fields are present in a Pigweed RPC
248 packet. Each packet type is only sent by either the client or the server.
249 These tables describe the meaning of and fields included with each packet type.
251 Client-to-server packets
252 ^^^^^^^^^^^^^^^^^^^^^^^^
253 +---------------------------+----------------------------------+
254 | packet type | description |
255 +===========================+==================================+
256 | REQUEST | RPC request |
258 | | .. code-block:: text |
264 | | (unless first client stream) |
266 +---------------------------+----------------------------------+
267 | CLIENT_STREAM_END | Client stream finished |
269 | | .. code-block:: text |
276 +---------------------------+----------------------------------+
277 | CLIENT_ERROR | Received unexpected packet |
279 | | .. code-block:: text |
285 +---------------------------+----------------------------------+
286 | CANCEL_SERVER_STREAM | Cancel a server stream |
288 | | .. code-block:: text |
294 +---------------------------+----------------------------------+
298 The client sends ``CLIENT_ERROR`` packets to a server when it receives a packet
299 it did not request. If the RPC is a streaming RPC, the server should abort it.
301 The status code indicates the type of error. If the client does not distinguish
302 between the error types, it can send whichever status is most relevant. The
303 status code is logged, but all status codes result in the same action by the
304 server: aborting the RPC.
306 * ``NOT_FOUND`` -- Received a packet for a service method the client does not
308 * ``FAILED_PRECONDITION`` -- Received a packet for a service method that the
309 client did not invoke.
311 Server-to-client packets
312 ^^^^^^^^^^^^^^^^^^^^^^^^
313 +-------------------+--------------------------------+
314 | packet type | description |
315 +===================+================================+
316 | RESPONSE | RPC response |
318 | | .. code-block:: text |
325 | | (unless in server stream) |
326 +-------------------+--------------------------------+
327 | SERVER_STREAM_END | Server stream and RPC finished |
329 | | .. code-block:: text |
335 +-------------------+--------------------------------+
336 | SERVER_ERROR | Received unexpected packet |
338 | | .. code-block:: text |
341 | | - service_id (if relevant) |
342 | | - method_id (if relevant) |
344 +-------------------+--------------------------------+
348 The server sends ``SERVER_ERROR`` packets when it receives a packet it cannot
349 process. The client should abort any RPC for which it receives an error. The
350 status field indicates the type of error.
352 * ``NOT_FOUND`` -- The requested service or method does not exist.
353 * ``FAILED_PRECONDITION`` -- Attempted to cancel an RPC that is not pending.
354 * ``RESOURCE_EXHAUSTED`` -- The request came on a new channel, but a channel
355 could not be allocated for it.
356 * ``INTERNAL`` -- The server was unable to respond to an RPC due to an
357 unrecoverable internal error.
359 Inovking a service method
360 -------------------------
361 Calling an RPC requires a specific sequence of packets. This section describes
362 the protocol for calling service methods of each type: unary, server streaming,
363 client streaming, and bidirectional streaming.
367 In a unary RPC, the client sends a single request and the server sends a single
374 default_note_color = aliceblue;
378 leftnote = "PacketType.REQUEST\nchannel ID\nservice ID\nmethod ID\npayload"
383 rightnote = "PacketType.RESPONSE\nchannel ID\nservice ID\nmethod ID\npayload\nstatus"
389 In a server streaming RPC, the client sends a single request and the server
390 sends any number of responses followed by a ``SERVER_STREAM_END`` packet.
396 default_note_color = aliceblue;
400 leftnote = "PacketType.REQUEST\nchannel ID\nservice ID\nmethod ID\npayload"
405 label = "responses (zero or more)",
406 rightnote = "PacketType.RESPONSE\nchannel ID\nservice ID\nmethod ID\npayload"
411 rightnote = "PacketType.SERVER_STREAM_END\nchannel ID\nservice ID\nmethod ID\nstatus"
415 Server streaming RPCs may be cancelled by the client. The client sends a
416 ``CANCEL_SERVER_STREAM`` packet to terminate the RPC.
422 default_note_color = aliceblue;
426 leftnote = "PacketType.REQUEST\nchannel ID\nservice ID\nmethod ID\npayload"
431 label = "responses (zero or more)",
432 rightnote = "PacketType.RESPONSE\nchannel ID\nservice ID\nmethod ID\npayload"
438 leftnote = "PacketType.CANCEL_SERVER_STREAM\nchannel ID\nservice ID\nmethod ID"
443 rightnote = "PacketType.SERVER_STREAM_END\nchannel ID\nservice ID\nmethod ID\nstatus"
449 In a client streaming RPC, the client sends any number of RPC requests followed
450 by a ``CLIENT_STREAM_END`` packet. The server then sends a single response.
452 The first client-to-server RPC packet does not include a payload.
456 ``pw_rpc`` does not yet support client streaming RPCs.
462 default_note_color = aliceblue;
466 leftnote = "PacketType.REQUEST\nchannel ID\nservice ID\nmethod ID"
471 label = "requests (zero or more)",
472 leftnote = "PacketType.REQUEST\nchannel ID\nservice ID\nmethod ID\npayload"
478 leftnote = "PacketType.CLIENT_STREAM_END\nchannel ID\nservice ID\nmethod ID"
483 rightnote = "PacketType.RESPONSE\nchannel ID\nservice ID\nmethod ID\npayload\nstatus"
487 The server may terminate a client streaming RPC at any time by sending its
494 default_note_color = aliceblue;
498 leftnote = "PacketType.REQUEST\nchannel ID\nservice ID\nmethod ID"
503 label = "requests (zero or more)",
504 leftnote = "PacketType.REQUEST\nchannel ID\nservice ID\nmethod ID\npayload"
509 rightnote = "PacketType.RESPONSE\nchannel ID\nservice ID\nmethod ID\npayload\nstatus"
513 Bidirectional streaming RPC
514 ^^^^^^^^^^^^^^^^^^^^^^^^^^^
515 In a bidirectional streaming RPC, the client sends any number of requests and
516 the server sends any number of responses. The client sends a
517 ``CLIENT_STREAM_END`` packet when it has finished sending requests. The server
518 sends a ``SERVER_STREAM_END`` packet after it receives the client's
519 ``CLIENT_STREAM_END`` and finished sending its responses.
521 The first client-to-server RPC packet does not include a payload.
525 ``pw_rpc`` does not yet support bidirectional streaming RPCs.
531 default_note_color = aliceblue;
535 leftnote = "PacketType.REQUEST\nchannel ID\nservice ID\nmethod ID"
540 label = "requests (zero or more)",
541 leftnote = "PacketType.REQUEST\nchannel ID\nservice ID\nmethod ID\npayload"
544 ... (messages in any order) ...
548 label = "responses (zero or more)",
549 rightnote = "PacketType.RESPONSE\nchannel ID\nservice ID\nmethod ID\npayload"
555 leftnote = "PacketType.CLIENT_STREAM_END\nchannel ID\nservice ID\nmethod ID"
560 label = "responses (zero or more)",
561 rightnote = "PacketType.RPC\nchannel ID\nservice ID\nmethod ID\npayload"
566 rightnote = "PacketType.SERVER_STREAM_END\nchannel ID\nservice ID\nmethod ID\nstatus"
570 The server may terminate the RPC at any time by sending a ``SERVER_STREAM_END``
571 packet with the status, even if the client has not sent its ``STREAM_END``. The
572 client may cancel the RPC at any time by sending a ``CANCEL_SERVER_STREAM``
579 default_note_color = aliceblue;
583 leftnote = "PacketType.RPC\nchannel ID\nservice ID\nmethod ID"
588 label = "requests (zero or more)",
589 leftnote = "PacketType.RPC\nchannel ID\nservice ID\nmethod ID\npayload"
594 label = "responses (zero or more)",
595 rightnote = "PacketType.RPC\nchannel ID\nservice ID\nmethod ID\npayload"
601 leftnote = "PacketType.CANCEL_SERVER_STREAM\nchannel ID\nservice ID\nmethod ID"
606 rightnote = "PacketType.STREAM_END\nchannel ID\nservice ID\nmethod ID\nstatus"
612 Declare an instance of ``rpc::Server`` and register services with it.
616 Document the public interface
620 The following size report showcases the memory usage of the core RPC server. It
621 is configured with a single channel using a basic transport interface that
622 directly reads from and writes to ``pw_sys_io``. The transport has a 128-byte
623 packet buffer, which comprises the plurality of the example's RAM usage. This is
624 not a suitable transport for an actual product; a real implementation would have
625 additional overhead proportional to the complexity of the transport.
627 .. include:: server_size
629 RPC server implementation
630 -------------------------
634 The RPC Server depends on the ``pw::rpc::internal::Method`` class. ``Method``
635 serves as the bridge between the ``pw_rpc`` server library and the user-defined
636 RPC functions. Each supported protobuf implementation extends ``Method`` to
637 implement its request and response proto handling. The ``pw_rpc`` server
638 calls into the ``Method`` implementation through the base class's ``Invoke``
641 ``Method`` implementations store metadata about each method, including a
642 function pointer to the user-defined method implementation. They also provide
643 ``static constexpr`` functions for creating each type of method. ``Method``
644 implementations must satisfy the ``MethodImplTester`` test class in
645 ``pw_rpc_private/method_impl_tester.h``.
647 See ``pw_rpc/internal/method.h`` for more details about ``Method``.
658 packets [shape = beginpoint];
661 label = "pw_rpc library";
663 server [label = "Server"];
664 service [label = "Service"];
665 method [label = "internal::Method"];
668 stubs [label = "generated services", shape = ellipse];
669 user [label = "user-defined RPCs", shape = roundedbox];
671 packets -> server -> service -> method -> stubs -> user;
672 packets -> server [folded];
673 method -> stubs [folded];
682 user -> stubs [folded];
685 label = "pw_rpc library";
687 server [label = "Server"];
688 method [label = "internal::Method"];
689 channel [label = "Channel"];
692 stubs [label = "generated services", shape = ellipse];
693 user [label = "user-defined RPCs", shape = roundedbox];
694 packets [shape = beginpoint];
696 user -> stubs -> method [folded];
697 method -> server -> channel;
698 channel -> packets [folded];
703 The RPC client is used to send requests to a server and manages the contexts of
708 The ``pw::rpc::Client`` class is instantiated with a list of channels that it
709 uses to communicate. These channels can be shared with a server, but multiple
710 clients cannot use the same channels.
712 To send incoming RPC packets from the transport layer to be processed by a
713 client, the client's ``ProcessPacket`` function is called with the packet data.
717 #include "pw_rpc/client.h"
721 pw::rpc::Channel my_channels[] = {
722 pw::rpc::Channel::Create<1>(&my_channel_output)};
723 pw::rpc::Client my_client(my_channels);
727 // Called when the transport layer receives an RPC packet.
728 void ProcessRpcPacket(ConstByteSpan packet) {
729 my_client.ProcessPacket(packet);
732 .. _module-pw_rpc-making-calls:
736 RPC calls are not made directly through the client, but using one of its
737 registered channels instead. A service client class is generated from a .proto
738 file for each selected protobuf library, which is then used to send RPC requests
739 through a given channel. The API for this depends on the protobuf library;
741 :ref:`appropriate documentation<module-pw_rpc-protobuf-library-apis>`. Multiple
742 service client implementations can exist simulatenously and share the same
745 When a call is made, a ``pw::rpc::ClientCall`` object is returned to the caller.
746 This object tracks the ongoing RPC call, and can be used to manage it. An RPC
747 call is only active as long as its ``ClientCall`` object is alive.
750 Use ``std::move`` when passing around ``ClientCall`` objects to keep RPCs
753 Client implementation details
754 -----------------------------
758 ``ClientCall`` stores the context of an active RPC, and serves as the user's
759 interface to the RPC client. The core RPC library provides a base ``ClientCall``
760 class with common functionality, which is then extended for RPC client
761 implementations tied to different protobuf libraries to provide convenient
762 interfaces for working with RPCs.
764 The RPC server stores a list of all of active ``ClientCall`` objects. When an
765 incoming packet is recieved, it dispatches to one of its active calls, which
766 then decodes the payload and presents it to the user.