# Rover Communications

# Overview

Communication is a subsystem responsible for bridging the two distinct networks present on the rover: the Ethernet Network and the ROS2 Network. Without it, Jonny Boi (the Jetson Orin Nano) would have no way to talk to the hardware microcontrollers or the Basestation, and the internal ROS2 software stack would have no way to act on external inputs or send commands outward.

On the Ethernet side, the Jetson connects to the hardware boards (ArmBoard, DriveBoard, SensorBoard, ContainerBoard) and to the Basestation over WiFi. On the ROS2 side, the Communications node exchanges messages with internal nodes such as the Behavior Node, which handles business logic, and the Controller Node, which manages hardware commands.

<span style="white-space: pre-wrap;">All messages sent over the network, whether towards the Basestation or the hardware microcontrollers, are encoded as Protobuffers, wrapped inside a </span>[PBEnvelope](https://bookstack.roboteamtwente.nl/books/jonny-boi/page/pbenvelope "PBEnvelope"). The PBEnvelope acts as a typed container that lets the receiver identify what kind of message it is receiving before deserializing the payload. These wrapped messages are transmitted as raw bytes inside UDP packets.

The Communications node operates in both directions:

- Inbound (Ethernet -&gt; ROS2): UDP packets received from the Basestation or hardware boards are parsed, the protobuf payload is extracted from the PBEnvelope, and the data is converted into custom ROS2 messages before being published over the appropriate ROS2 topic for other nodes to consume.
- Outbound (ROS2 -&gt; Ethernet): The Communications node subscribes to relevant ROS2 topics, converts the incoming ROS2 messages back into Protobuffers, wraps them in a PBEnvelope, and transmits them as UDP packets to the intended destination, either the Basestation or a hardware board.

<span style="white-space: pre-wrap;">Consider the Rover Architecture diagram </span>[Rover Architecture diagram](https://bookstack.roboteamtwente.nl/books/jonny-boi/page/overview "Overview")<span style="white-space: pre-wrap;"> to get a visual overview of how the Communications node sits at the center of these data flows.</span>

<span style="white-space: pre-wrap;">Before diving into the codebase, it is strongly recommended to read the </span>[ROS2 documentation](https://docs.ros.org/en/humble/Releases.html)<span style="white-space: pre-wrap;">, particularly the entries regarding topics, messages and publishers/subscribers: </span>  
[https://docs.ros.org/en/humble/Concepts/Basic/About-Interfaces.html](https://docs.ros.org/en/humble/Concepts/Basic/About-Interfaces.html)

<span style="white-space: pre-wrap;">You will also need background knowledge in C++ and UDP networking. The following guide is a solid starting point: </span>  
[https://beej.us/guide/bgnet/html/split-wide/](https://beej.us/guide/bgnet/html/split-wide/)

<span style="white-space: pre-wrap;"> </span>

# UDP Forwarder and ROS2 Publisher

## `<span class="editor-theme-code">udp_forwarder_node.cpp</span>`

This is the main file running the communications logic on Jonny Boi's side. It is a ROS2 node that simultaneously acts as a UDP server and a ROS2 publisher/subscriber. Its primary responsibilities are:

- Receiving raw encoded PBEnvelope packets over UDP from the Basestation or hardware microcontrollers
- Deserializing the protobuf payload and converting it into a custom ROS2 message
- Publishing that ROS2 message over the appropriate topic for internal nodes to consume
- In the outbound direction, subscribing to ROS2 topics and converting messages back into protobufs to be sent as UDP packets

## The Handler Pattern

<span style="white-space: pre-wrap;">The most important architectural concept in this file is the </span>**registry-based handler dispatch system**<span style="white-space: pre-wrap;">. Rather than having a large switch statement that handles every possible message type, the node maintains an </span>`<span class="editor-theme-code">unordered_map</span>`<span style="white-space: pre-wrap;"> that maps each </span>`<span class="editor-theme-code">PBEnvelope::PayloadCase</span>`<span style="white-space: pre-wrap;"> enum value to a dedicated handler object.</span>

<span style="white-space: pre-wrap;">When a UDP packet arrives, the node parses it as a </span>`<span class="editor-theme-code">PBEnvelope</span>`<span style="white-space: pre-wrap;"> and calls </span>`<span class="editor-theme-code">payload_case()</span>`<span style="white-space: pre-wrap;"> to identify the message type. It then looks up the corresponding handler in the map and calls </span>`<span class="editor-theme-code">handle(envelope)</span>`<span style="white-space: pre-wrap;"> on it. Each handler is a class that extends the abstract </span>`<span class="editor-theme-code">Handler</span>`<span style="white-space: pre-wrap;"> base class and is solely responsible for one message type — deserializing the protobuf, mapping its fields to a ROS2 message, and publishing it on the correct topic.</span>

Registering a handler looks like this:

```cpp
handlers_.emplace(
    static_cast<int>(PBEnvelope::kImuInfo),
    std::make_unique<ImuHandler>(this, "imu_data", 10)
);
```

<span style="white-space: pre-wrap;">The three constructor arguments are the node pointer (so the handler can create a publisher), the ROS2 topic name to publish on, and the publisher queue size. Adding support for a new message type is as simple as writing a new handler class and adding one </span>`<span class="editor-theme-code">emplace</span>`<span style="white-space: pre-wrap;"> call here — the rest of the dispatch logic requires no changes. See the </span>**Adding a New Message Type**<span style="white-space: pre-wrap;"> page for a step-by-step guide.</span>

## The rx\_loop Background Thread

<span style="white-space: pre-wrap;">UDP receiving does not happen on the ROS2 spin thread. Instead, the node spawns a dedicated background thread that runs </span>`<span class="editor-theme-code">rx_loop()</span>`<span style="white-space: pre-wrap;"> continuously, blocking on </span>`<span class="editor-theme-code">recvfrom()</span>`<span style="white-space: pre-wrap;"> until a datagram arrives. This design ensures that the ROS2 executor is never blocked waiting for network I/O, and that incoming packets are processed as fast as the network delivers them regardless of what the ROS2 stack is doing.</span>

<span style="white-space: pre-wrap;">This is worth keeping in mind when debugging. If you see unexpected behavior related to timing or message ordering, the source may be a race condition between the rx\_loop thread and the ROS2 spin thread. The </span>`<span class="editor-theme-code">running_</span>`<span style="white-space: pre-wrap;"> flag is declared as </span>`<span class="editor-theme-code">std::atomic<bool></span>`<span style="white-space: pre-wrap;"> specifically to ensure safe cross-thread signaling during shutdown.</span>

## UDP Forwarding Behavior

<span style="white-space: pre-wrap;">Before dispatching to a handler, the node forwards every raw UDP datagram to both </span>`<span class="editor-theme-code">dstA_</span>`<span style="white-space: pre-wrap;"> and </span>`<span class="editor-theme-code">dstB_</span>`<span style="white-space: pre-wrap;"> unconditionally. This means every message type, regardless of its intended destination, is forwarded to both addresses. This is a known architectural limitation — see the </span>**Known Limitations**<span style="white-space: pre-wrap;"> page for more context and the rationale behind it.</span>

## Configuration Parameters

The node's network configuration is fully driven by ROS2 parameters declared at startup:

<table class="align-center" id="bkmrk-parameterdefaultdesc"><colgroup><col></col><col></col><col></col></colgroup><tbody><tr><th class="align-center">Parameter

</th><th>Default

</th><th>Description

</th></tr><tr><td class="align-center">`<span class="editor-theme-code">listen_port</span>`

</td><td>5000

</td><td>UDP port the node listens on

</td></tr><tr><td>`<span class="editor-theme-code">dst_a_port</span>`

</td><td>6000

</td><td>First forwarding destination port

</td></tr><tr><td>`<span class="editor-theme-code">dst_b_port</span>`

</td><td>6001

</td><td>Second forwarding destination port

</td></tr><tr><td>`<span class="editor-theme-code">dst_ip</span>`

</td><td>127.0.0.1

</td><td>Destination IP for both forwarding targets

</td></tr></tbody></table>

<span style="white-space: pre-wrap;">These can be overridden at launch time via a ROS2 launch file or </span>`<span class="editor-theme-code">--ros-args</span>`<span style="white-space: pre-wrap;"> flags without recompiling, making it easy to reconfigure the node for different network environments such as the Jetson on the rover versus a local development machine.UDP Forwarding</span>

<span style="white-space: pre-wrap;">Currently, every received UDP packet is forwarded raw to both </span>`<span class="editor-theme-code">dstA_</span>`<span style="white-space: pre-wrap;"> and </span>`<span class="editor-theme-code">dstB_</span>`<span style="white-space: pre-wrap;"> before handler dispatch.</span>

<p class="callout warning">This implementation will CHANGE SOON. to forward ONLY to appropriate destinations.</p>

# Protobuffers

## What are Protobuffers?

<span style="white-space: pre-wrap;">Protocol Buffers (Protobuffers or protobufs) are Google's binary serialization format for structured data. Compared to alternatives like JSON or XML, they are significantly more compact and faster to serialize/deserialize, making them well-suited for real-time communication between the rover's subsystems. They are also strongly typed and language-agnostic, the same </span>`<span class="editor-theme-code">.proto</span>`<span style="white-space: pre-wrap;"> definition can generate code for C++, Python, Rust, and others. For a general introduction, refer to the official documentation: </span>  
[https://protobuf.dev/overview/](https://protobuf.dev/overview/)

## The ERC-Protobufs Repository

<span style="white-space: pre-wrap;">All protobuf definitions used by the rover live in a shared repository called </span>**ERC-Protobufs**<span style="white-space: pre-wrap;">, which is used as a dependency across the rover's software stack. You can find it linked from the </span>[Starting with Protobufs](https://bookstack.roboteamtwente.nl/books/communication-system/page/starting-with-protobufs "Starting with Protobufs")<span style="white-space: pre-wrap;"> page.</span>

<span style="white-space: pre-wrap;">The repository is organized under a </span>`<span class="editor-theme-code">components/</span>`<span style="white-space: pre-wrap;"> folder, where each subfolder groups messages by the hardware board or subsystem they belong to:</span>

```
components/
  arm_board/
  driving_board/
  sensor_board/
  container_board/
  basestation/
  common/
```

<span style="white-space: pre-wrap;">For example, </span>`<span class="editor-theme-code">components/basestation/detected_object.proto</span>`<span style="white-space: pre-wrap;"> contains the message definition for a single YOLO-detected object sent from Jonny Boi to the Basestation. Placing it under </span>`<span class="editor-theme-code">basestation/</span>`<span style="white-space: pre-wrap;"> makes its intended direction and purpose immediately clear.</span>

## Naming Conventions

<span style="white-space: pre-wrap;">Every message name in the repository must be </span>**globally unique**, regardless of which folder it lives in. This is because proto3 imports reference files by path, but message names exist in a flat global namespace, two messages with the same name will cause build failures.

The convention used across the rover is to prefix every message name with its component:

- `<span class="editor-theme-code">ArmBoardControlSignals</span>`
- `<span class="editor-theme-code">BasestationControlMode</span>`
- `<span class="editor-theme-code">SensorBoardIMUInfo</span>`
- `<span class="editor-theme-code">DrivingBoardMotorMessage</span>`

Follow this convention strictly when adding new messages.

## How Protobufs are Compiled

<span style="white-space: pre-wrap;">The </span>`<span class="editor-theme-code">comms</span>`<span style="white-space: pre-wrap;"> package does not manually clone ERC-Protobufs. Instead, CMake fetches it automatically at build time using </span>`<span class="editor-theme-code">FetchContent</span>`<span style="white-space: pre-wrap;">, pinned to a specific commit hash in </span>`<span class="editor-theme-code">CMakeLists.txt</span>`:

```cmake
FetchContent_Declare(
  erc_protobufs
  GIT_REPOSITORY https://github.com/RoboTeamTwente/ERC-Protobufs.git
  GIT_TAG <commit_hash>
)
```

<span style="white-space: pre-wrap;">Once fetched, </span>`<span class="editor-theme-code">protoc</span>`<span style="white-space: pre-wrap;"> generates </span>`<span class="editor-theme-code">.pb.h</span>`<span style="white-space: pre-wrap;"> and </span>`<span class="editor-theme-code">.pb.cc</span>`<span style="white-space: pre-wrap;"> files into the build directory, which are then compiled into the </span>`<span class="editor-theme-code">comms</span>`<span style="white-space: pre-wrap;"> node. This means that whenever new proto files are added to ERC-Protobufs and committed, the </span>`<span class="editor-theme-code">GIT_TAG</span>`<span style="white-space: pre-wrap;"> in </span>`<span class="editor-theme-code">CMakeLists.txt</span>`<span style="white-space: pre-wrap;"> must be updated to the new commit hash before the changes will be picked up by the build. The full procedure is covered in the </span>**Adding a New Message**<span style="white-space: pre-wrap;"> page.</span>

## The PBEnvelope

<span style="white-space: pre-wrap;">All protobuf messages on this rover are wrapped inside a </span>`<span class="editor-theme-code">PBEnvelope</span>`<span style="white-space: pre-wrap;"> before being transmitted over UDP. The PBEnvelope acts as a typed container that allows the receiver to identify which type of message is inside before deserializing the payload. See the dedicated </span>[PBEnvelope](https://bookstack.roboteamtwente.nl/books/jonny-boi/page/pbenvelope "PBEnvelope")<span style="white-space: pre-wrap;"> page for a full explanation.</span>

## <span style="white-space: pre-wrap;"></span>

# PBEnvelope

## What is the PBEnvelope?

<span style="white-space: pre-wrap;">The </span>`<span class="editor-theme-code">PBEnvelope</span>`<span style="white-space: pre-wrap;"> is a wrapper message defined in </span>`<span class="editor-theme-code">components/common/envelope.proto</span>`<span style="white-space: pre-wrap;"> in the ERC-Protobufs repository. Every protobuf message sent over UDP on this rover — whether from the Basestation, the Jetson, or a hardware microcontroller — is wrapped inside a </span>`<span class="editor-theme-code">PBEnvelope</span>`<span style="white-space: pre-wrap;"> before transmission.</span>

<span style="white-space: pre-wrap;">When a raw UDP datagram arrives, the receiver has no way of knowing what type of message is inside without some kind of type identifier. The PBEnvelope solves this by using proto3's </span>`<span class="editor-theme-code">oneof</span>`<span style="white-space: pre-wrap;"> field, which encodes the message type directly into the serialized bytes. The receiver calls </span>`<span class="editor-theme-code">payload_case()</span>`<span style="white-space: pre-wrap;"> on the deserialized envelope to determine the message type before extracting and deserializing the actual payload.</span>

## Structure

<span style="white-space: pre-wrap;">The PBEnvelope uses a </span>`<span class="editor-theme-code">oneof payload</span>`<span style="white-space: pre-wrap;"> block where each field corresponds to one registered message type. Only one field can be set at a time — that is the nature of </span>`<span class="editor-theme-code">oneof</span>`<span style="white-space: pre-wrap;">. When serialized, proto3 encodes which field is set, allowing the receiver to call </span>`<span class="editor-theme-code">envelope.payload_case()</span>`<span style="white-space: pre-wrap;"> and get back an enum value like </span>`<span class="editor-theme-code">PBEnvelope::kImuInfo</span>`<span style="white-space: pre-wrap;"> or </span>`<span class="editor-theme-code">PBEnvelope::kControlMode</span>`.

## Registered Message Types

Below is the full table of currently registered payload cases, their field numbers, and their communication direction:

<table class="align-center" id="bkmrk-field-numbermessage-"><colgroup><col></col><col></col><col></col></colgroup><tbody><tr><th>Field Number

</th><th>Message Type

</th><th>Direction

</th></tr><tr><td>1

</td><td>SensorBoardPHInfo

</td><td>Hardware → ROS2

</td></tr><tr><td>2

</td><td>ArmBoardControlSignals

</td><td>ROS2 → Hardware

</td></tr><tr><td>3

</td><td>ArmBoardDiagnostics

</td><td>Hardware → ROS2

</td></tr><tr><td>4

</td><td>ArmBoardMovementFeedback

</td><td>Hardware → ROS2

</td></tr><tr><td>5

</td><td>ArmBoardActualPositions

</td><td>Hardware → ROS2

</td></tr><tr><td>6

</td><td>ArmBoardTargetMovement

</td><td>ROS2 → Hardware

</td></tr><tr><td>7

</td><td>ArmBoardObstructions

</td><td>Hardware → ROS2

</td></tr><tr><td>8

</td><td>DrivingBoardDiagnostics

</td><td>Hardware → ROS2

</td></tr><tr><td>9

</td><td>DrivingBoardMotorMessage

</td><td>ROS2 → Hardware

</td></tr><tr><td>10

</td><td>DrivingBoardMotorPeriodicProgress

</td><td>Hardware → ROS2

</td></tr><tr><td>11

</td><td>SensorBoardDiagnostics

</td><td>Hardware → ROS2

</td></tr><tr><td>12

</td><td>SensorBoardGPSInfo

</td><td>Hardware → ROS2

</td></tr><tr><td>13

</td><td>SensorBoardIMUInfo

</td><td>Hardware → ROS2

</td></tr><tr><td>14

</td><td>SensorBoardLoadCellInfo

</td><td>Hardware → ROS2

</td></tr><tr><td>15

</td><td>SensorBoardPressureInfo

</td><td>Hardware → ROS2

</td></tr><tr><td>16

</td><td>BasestationControlMode

</td><td>BS → ROS2

</td></tr><tr><td>17

</td><td>BasestationDetectedObject

</td><td>ROS2 → BS

</td></tr><tr><td>18

</td><td>BasestationObjectSelection

</td><td>BS → ROS2

</td></tr><tr><td>19

</td><td>BasestationRockMeasureRequest

</td><td>BS → ROS2

</td></tr><tr><td>20

</td><td>BasestationRockMeasureResult

</td><td class="align-center">ROS2 → BS

</td></tr><tr><td>21

</td><td>BasestationRoverLocalization

</td><td class="align-center">ROS2 → BS

</td></tr></tbody></table>

**Important:**<span style="white-space: pre-wrap;"> Field numbers in a </span>`<span class="editor-theme-code">oneof</span>`<span style="white-space: pre-wrap;"> block are permanent. Once a field number is assigned to a message type, it must never be reused for a different type — even if the original message is removed. This would break deserialization for any system still using an older version of the envelope. Always use the next available field number when adding a new entry.</span>

## <span style="white-space: pre-wrap;">A note on </span>`<span class="editor-theme-code">BasestationDetectedObject</span>`

<span style="white-space: pre-wrap;">Unlike most messages which carry a complete snapshot of state, </span>`<span class="editor-theme-code">BasestationDetectedObject</span>`<span style="white-space: pre-wrap;"> sends </span>**one detected object per message**<span style="white-space: pre-wrap;">. This is because Embedded requires fixed-size protobuf messages and cannot handle </span>`<span class="editor-theme-code">repeated</span>`<span style="white-space: pre-wrap;"> fields (which are dynamically sized). The Basestation reconstructs the full frame by collecting all messages sharing the same </span>`<span class="editor-theme-code">frame_id</span>`<span style="white-space: pre-wrap;"> until it has received </span>`<span class="editor-theme-code">total_count</span>`<span style="white-space: pre-wrap;"> of them. See </span>`<span class="editor-theme-code">components/basestation/detected_object.proto</span>`<span style="white-space: pre-wrap;"> for the full definition.</span>

## <span style="white-space: pre-wrap;">A note on </span>`<span class="editor-theme-code">message_types.proto</span>`

<p class="callout warning">TLDR: I don't remember what we were doing with this file.</p>

<span style="white-space: pre-wrap;">The ERC-Protobufs repo contains a file called </span>`<span class="editor-theme-code">message_types.proto</span>`<span style="white-space: pre-wrap;"> which defines a </span>`<span class="editor-theme-code">PBMessageType</span>`<span style="white-space: pre-wrap;"> enum. This file is a leftover from an earlier architecture where a manual type ID system was planned. It is explicitly excluded from the build in </span>`<span class="editor-theme-code">CMakeLists.txt</span>`<span style="white-space: pre-wrap;"> because its enum values collide with message names in the global proto3 namespace. It is unused, do not reference it or add new entries to it.</span>

## Trailing Zero Bytes

<span style="white-space: pre-wrap;">The </span>`<span class="editor-theme-code">envelope.proto</span>`<span style="white-space: pre-wrap;"> file includes an important note: all PBEnvelopes sent over the network must have trailing zero bytes removed before transmission. Proto3 serializes unset fields as zero bytes, and stripping them reduces network traffic. The receiver must pad the received datagram back to the full PBEnvelope size before deserializing. This is handled by the protobuf library's </span>`<span class="editor-theme-code">SerializeToString</span>`<span style="white-space: pre-wrap;"> and </span>`<span class="editor-theme-code">ParseFromArray</span>`<span style="white-space: pre-wrap;"> functions when used correctly.</span>

## Adding a New Message Type

<span style="white-space: pre-wrap;">To add a new message type to the PBEnvelope, see the dedicated </span>[Adding a new message](https://bookstack.roboteamtwente.nl/books/jonny-boi/page/adding-a-new-message "Adding a new message")<span style="white-space: pre-wrap;"> page for the full step-by-step procedure.</span>

# Integration with ROS2

## Overview

The Communications node bridges the gap between raw UDP/protobuf packets and the ROS2 ecosystem running on Jonny Boi. This page explains how incoming protobuf messages get converted into ROS2 messages and published on topics, the handler pattern that makes this extensible, and how custom ROS2 message types are defined.

## Custom ROS2 Message Types

ROS2 uses its own message format (`<span class="editor-theme-code">.msg</span>`<span style="white-space: pre-wrap;"> files) to define the structure of data exchanged between nodes over topics. These are separate from protobuf definitions, they exist so that internal ROS2 nodes like the Behavior Node can work with typed, idiomatic ROS2 messages rather than raw protobuf objects.</span>

<span style="white-space: pre-wrap;">All custom message definitions live in the </span>`<span class="editor-theme-code">msg/</span>`<span style="white-space: pre-wrap;"> folder of the </span>`<span class="editor-theme-code">comms</span>`<span style="white-space: pre-wrap;"> package:</span>

```
comms/msg/
  ImuSensorInformation.msg
  SensorBoardGPSInfo.msg
  SensorBoardPHInfo.msg
  SensorBoardDiagnostics.msg
  SensorState.msg
```

<span style="white-space: pre-wrap;">These are registered in </span>`<span class="editor-theme-code">CMakeLists.txt</span>`<span style="white-space: pre-wrap;"> via </span>`<span class="editor-theme-code">rosidl_generate_interfaces</span>`<span style="white-space: pre-wrap;">, which generates the corresponding C++ types at build time. The generated types follow the naming convention </span>`<span class="editor-theme-code">comms::msg::MessageName</span>`<span style="white-space: pre-wrap;"> and can be included in any C++ file as </span>`<span class="editor-theme-code">#include "comms/msg/message_name.hpp"</span>`<span style="white-space: pre-wrap;">. For more on ROS2 interfaces, refer to the official documentation: </span>[https://docs.ros.org/en/humble/Concepts/Basic/About-Interfaces.html](https://docs.ros.org/en/humble/Concepts/Basic/About-Interfaces.html)

## The Handler Pattern

<span style="white-space: pre-wrap;">The handler pattern is the core architectural decision that makes adding new message types straightforward without ever touching the dispatch logic in </span>`<span class="editor-theme-code">udp_forwarder_node.cpp</span>`.

<span style="white-space: pre-wrap;">Every message type that needs to be converted and published as a ROS2 topic is represented by a handler class. All handlers extend the abstract </span>`<span class="editor-theme-code">Handler</span>`<span style="white-space: pre-wrap;"> base class defined in </span>`<span class="editor-theme-code">include/comms/udp/handler.hpp</span>`:

```cpp
class Handler {
public:
    virtual ~Handler() = default;
    virtual void handle(const PBEnvelope& envelope) = 0;
};
```

<span style="white-space: pre-wrap;">Each concrete handler class — one per message type — lives in </span>`<span class="editor-theme-code">include/comms/udp/handlers/</span>`<span style="white-space: pre-wrap;"> and its corresponding </span>`<span class="editor-theme-code">.cpp</span>`<span style="white-space: pre-wrap;"> in </span>`<span class="editor-theme-code">src/handlers/</span>`. Its job is exactly three things:

1. <span style="white-space: pre-wrap;">Extract the specific protobuf message from the </span>`<span class="editor-theme-code">PBEnvelope</span>`
2. Map the protobuf fields to the equivalent ROS2 message fields
3. Publish the ROS2 message on the appropriate topic

<span style="white-space: pre-wrap;">As an example, </span>`<span class="editor-theme-code">ImuHandler</span>`<span style="white-space: pre-wrap;"> extracts </span>`<span class="editor-theme-code">SensorBoardIMUInfo</span>`<span style="white-space: pre-wrap;"> from the envelope, maps its accelerometer, gyroscope and magnetometer fields to a </span>`<span class="editor-theme-code">comms::msg::ImuSensorInformation</span>`<span style="white-space: pre-wrap;"> message, and publishes it on the </span>`<span class="editor-theme-code">imu_data</span>`<span style="white-space: pre-wrap;"> topic.</span>

## Handler Registration

<span style="white-space: pre-wrap;">Handlers are registered in the constructor of </span>`<span class="editor-theme-code">udp_forwarder_node.cpp</span>`<span style="white-space: pre-wrap;"> using a simple </span>`<span class="editor-theme-code">unordered_map</span>`<span style="white-space: pre-wrap;"> keyed by </span>`<span class="editor-theme-code">PBEnvelope::PayloadCase</span>`:

```cpp
handlers_.emplace(
    static_cast<int>(PBEnvelope::kImuInfo),
    std::make_unique<ImuHandler>(this, "imu_data", 10)
);
```

<span style="white-space: pre-wrap;">The three constructor arguments are the node pointer (so the handler can create a ROS2 publisher), the topic name to publish on, and the publisher queue size. To add support for a new message type, you write a new handler class and add one line here. The dispatch logic in </span>`<span class="editor-theme-code">rx_loop</span>`<span style="white-space: pre-wrap;"> requires no changes at all.</span>

## The Full Inbound Flow

The complete path from a raw UDP packet to a ROS2 topic looks like this:

[![image.png](https://bookstack.roboteamtwente.nl/uploads/images/gallery/2026-04/scaled-1680-/Mjfimage.png)](https://bookstack.roboteamtwente.nl/uploads/images/gallery/2026-04/Mjfimage.png)

<span style="white-space: pre-wrap;">If no handler is registered for a given </span>`<span class="editor-theme-code">payload_case</span>`, the node logs a throttled warning and drops the packet. This means unhandled message types fail silently, keep this in mind when debugging missing data.

## The Outbound Flow

The outbound direction works in reverse, the Communications node subscribes to a ROS2 topic, converts the incoming message back into a protobuf, wraps it in a PBEnvelope, and sends it as a UDP packet to the appropriate destination. This path is not yet fully implemented for all message types and will be extended as the Behavior Node develops.

## Adding a New Message Type

<span style="white-space: pre-wrap;">For a step-by-step guide on adding a new message type end-to-end, from the proto definition all the way to a registered handler — see the dedicated </span>[Adding a new message](https://bookstack.roboteamtwente.nl/books/jonny-boi/page/adding-a-new-message "Adding a new message")<span style="white-space: pre-wrap;"> page.</span>

# Adding a new message



# Testing with udp_client



# Dev Environment Setup



# Known Limitations