Fejemis Bridge ROS2
Back to Fejemis ROS2 Software
Fejemis Bridge
Overview
This document explains how the fejemis_bridge package is organized and how its pieces interact. It focuses on the runtime flow, the core files you need to know, and the responsibilities of each component.
Core idea
The bridge translates a text-based serial protocol used by the Teensy controllers into typed ROS2 messages and back. It is implemented as a ROS2 node that instantiates two serial "sources" (front and drive). Each source registers a set of "proxies" that convert between device text prefixes and ROS message types. The low-level serial framing, CRC, confirmation and retry logic are provided by the raubase serial framework.
Primary components and responsibilities
bridgenode (wrapper): coordinates sources and the ROS2 side. The node registers sources, runs the executor and exposes high-level services. Seesrc/bridge/main.cppandinclude/fejemis_bridge/bridge.hpp.SerialSourceimplementations: the device-specific classes that open the serial port, run the read/write loop, queue outbound messages and dispatch inbound lines to proxies. In this package:FrontTeensyandDriveTeensy(seeinclude/fejemis_bridge/sources/*.hppandsrc/bridge/sources/*.cpp).Proxies: conversion modules that map device text prefixes to ROS message types and back. Each proxy knows a prefix (e.g.hbt,fwl,manage) and implementsfrom_device(...)andto_device(...)conversions. Proxies are declared ininclude/fejemis_bridge/proxies.hppand implemented undersrc/bridge/proxies/.raubaseserial layer: providesMessageobjects, CRC, confirmation handling, thread-safe queues, and the TX/RX loop behavior. Relevant files live in thedeps/raubase_coretree (e.g.message.hpp,source_txrx.cpp).
Key files and what they do
src/bridge/main.cpp— process entry point: constructs theBridgeinstance and runs it.include/fejemis_bridge/bridge.hpp— wrapper aroundraubase::SerialBridge, declares parameters and registers theFrontTeensyandDriveTeensysources.include/fejemis_bridge/sources/*.hppandsrc/bridge/sources/*.cpp—FrontTeensyandDriveTeensyclasses. They register the proxies relevant to each Teensy.include/fejemis_bridge/proxies.hpp— proxy declarations and proxy-to-prefix mappings.src/bridge/proxies/*.cpp— proxy implementation: parse device strings and publish ROS messages; or receive ROS messages and generate device strings.config/bridge.yaml— runtime parameters used by the node (message rates, enabling specific proxies, device defaults).launch/bridge.launch.py— launch-time wiring and remappings (used by operators, not needed for design understanding).deps/raubase_core/.../message.hppand.../source_txrx.cpp— serial framing, CRC, confirm/resend and the TX/RX loop. Read these to understand byte-level behavior.
Runtime flow (concise)
1. Initialization: the Bridge (wrapper) declares parameters and registers both sources. Each source registers its proxies.
2. Each SerialSource opens its serial port and enters a TRX loop that alternates reading bytes and sending queued messages.
3. Inbound line handling:
* The TRX loop assembles bytes into a line starting with;and ending with\n. * CRC is validated (3-char;NNprefix). Confirmation messages are detected separately and handled by queue logic. * Valid data lines are stripped of CRC and passed to proxy candidates; the first proxy whose prefix matches the line handles it and typically publishes a ROS message.
4. Outbound handling:
* A ROS message (or internal code) is converted to device text by a proxy, wrapped in aMessageobject (CRC added, optional!requesting confirmation), and pushed to a thread-safe TX queue. * The TRX loop sends the first message in the queue. If the message requested confirmation, the source waits until a matching confirmation arrives; if none arrives within the timeout, the message is retried (and eventually dropped after a max resend count).
Proxies and topics (how they interact)
Proxies are the translators between raw device lines and typed ROS messages. Think of each proxy as a small adapter that knows:
- the device text prefix it handles (for example
hbt,fwl,manage), - how to parse the device payload into a ROS message, and
- how to format a ROS message back into a device string when sending to the Teensy.
How inbound messages are handled (device → ROS):
1. The SerialSource receives a framed line and strips the CRC prefix.
2. It looks up the first proxy whose prefix matches the start of the line.
3. The proxy's from_device(...) parses the remaining payload and publishes a typed ROS message (typically under the fejemis namespace).
Example: device sends ;NNhbt 123 1\n → SerialSource finds the hbt proxy → hbt proxy parses the numbers and publishes a Heartbeat message.
How outbound messages are handled (ROS → device):
1. A ROS node or bridge logic creates a typed message (or calls a helper API).
2. The relevant proxy's to_device(...) (or equivalent helper) formats the message as a device string.
3. The SerialSource::send() method enqueues that string as a Message (CRC will be added) and the TRX loop transmits it.
Example: a controller publishes DriveManage with cmd_type = START_MOTORS → Drive proxy converts it to manage start\n → bridge sends it to the drive Teensy.
Practical notes
- Proxies are registered per source (see
FrontTeensyandDriveTeensy), so a prefix is handled only by proxies attached to that source. - Only the first matching proxy handles an inbound line; ensure prefixes are unique or ordered appropriately.
- Topic names and remapping are controlled by the node's namespace and launch remaps — check the proxy implementation to see the exact topic names.
Where to look / extend
- Declarations:
include/fejemis_bridge/proxies.hpp(which prefix maps to which proxy). - Implementations:
src/bridge/proxies/*.cpp(parsing and publish/format logic). - To add a new mapping: create or reuse a
msg, implement conversions in a new proxy, and register it in the appropriate source constructor.
If you're unsure what topic a proxy publishes, open its .cpp implementation — it shows the exact ROS topic and message type used.
Extension points (practical recipe)
If you need to add support for a new device message or new telemetry:
1. Add a ROS msg if a new typed payload is required.
2. Implement the conversion functions for the proxy (both directions if needed).
3. Declare the new proxy in include/fejemis_bridge/proxies.hpp.
4. Add the proxy to the appropriate source in the source constructor (FrontTeensy or DriveTeensy).
5. Add any runtime parameters to config/bridge.yaml and, if helpful, a launch remap.