Recommended MQTT to Cyphal Mappings

I was wondering if there was interest in creating a common MQTT to Cyphal mapping scheme to help shepherd users into good mappings which work in similar ways to the existing protocol (UDP) and away from bad mapping paradigms which don’t work like Cyphal. The mapping should aim to preserve essential Cyphal features such as:

  • Versioned Message Types
  • Port IDs
  • Node IDs
  • Broadcasting to all Nodes
  • 1:1 Service Calls
  • Overall Cyphal Protocol Version

While respecting MQTT’s PUB/SUB architecture. Some features may not map well.

  • Priority
  • Frame Index
  • Sequence Index

To that end I was thinking of something along these lines.

MQTT

MQTT is at the top of the OSI layers and so it’s typically at the application and has encryption built in. Some brokers also limit packet size to fairly large sizes like 128KB relative to Cyphal sizes. Each Topic can have it’s own Quality of Service (0,1,2) as well.

Topics

Broadcast Topics would then look something like this:

“v<cyphal_version>/cyphal/broadcast/<subject_id>/v<major_version>"

For example, a node would PUB and SUB on

"v1/cyphal/broadcast/7509/v1"

To send and receive all Heartbeats. All nodes on the same endpoint will receive all messages of this type. Using subject ID here is simply a method to map the type name. One down side is that it will receive it’s own heartbeat using this scheme due to how MQTT Brokers work.

Services topics would then use something far more specific to preserve the 1:1 relationship.

"v<cyphal_version>/cyphal/service/<node_id>/<service_id>/v<major_version>/request"
"v<cyphal_version>/cyphal/service/<node_id>/<service_id>/v<major_version>/response"

For example a client would PUB to this topic and a server would SUB to it.

"v1/cyphal/service/42/340/v1/request"

And the Server would PUB to this topic and a client would SUB to it.

"v1/cyphal/service/42/340/v1/response"

This is a decent tradeoff of topic length (which if the DSDL type name was used, could get quite long) versus “secret” knowledge (port mappings to types).

Versioning

These topics need versioning in order to guarantee compatibility with the message data per the specification. Certainly the extent system is not the same here, however we want all v1 messages to be compatible. Once a significant change is made (field subtraction for example) then v2 is made. This would follow SEMVAR as the normal versioning does.

Domains

A domain system can be added to the topic string to further split nodes into groups on the same MQTT endpoint (broker).

"v<cyphal_version>/cyphal/<domain>/..."

This could be used to isolate two or more groups of nodes together to achieve some goal without them interfering with each other. PUBing to v1/cyphal/foo/broadcast/7509/v1 would not show up in v1/cyphal/bar/broadcast/7509/v1 and vice versa.

Alternatively, different domains could use different endpoints (server URLs) altogether.

Contents

The contents of the messages can be binary or UTF-8 (like JSON dicts)

Headers

Assuming a JSON format. A message layout could be:

{
    "header": {
        "port_id": int,
        "sender_node_id": int,
        "destination_node_id": int, // anonymous for broadcasts
        "kind": str, // "broadcast" | "request" | "response" or an int to save space?
        "timestamp": int, // uses the existing time sync'd format. TAI? UTC?
        ...
    }, 
    "payload": {...}
}

Payloads

Base64

In order to keep compatibility with existing serialization, users could base64 encode payload data into a string and transmit that as the value of the payload. Then receivers would simply reverse the base64 process, then deserialize if needed. For forwarding or proxying services this may be ideal to allow the MQTT client to know the least about the contents.

At this point, considering where v1.1 is going, preserving transport-level entities like the port- or node-ID exposed to the application probably doesn’t make sense. Perhaps a better solution could be something along the lines of simply mapping Cyphal topics to MQTT topics, one to one, ideally without any additional naming rules.

I am going to try and ask Claude to prototype a Cyphal to MQTT bridge using named topics and share the results here.

We don’t really have an equivalent notion of a topic like ROS or MQTT. We just have Versioned Message Types, which aren’t the same but can be (and are) repurposed into being that. If we’re clear what a topic is meant to be then we’ll have better idea how to use them with MQTT topics (which admittedly suffer from the same problem). Today MQTT topics are just some hierarchical string system to group data transfer styles, N:N, 1:1. 1:N, N:1, etc using a PUB/SUB system within your own endpoint.

Ideally they’re grouped by component or function or some high level notion, but it’s open ended and left to the user to design well. This is by design in both Cyphal and MQTT to allow the users to make their own choices about their architecture. Having some informal (which is this post I suppose) or format guidance here would be ideal to prevent frustration with using existing 1.0 Cyphal in scenarios where systems have to cross talk with other protocols (so other guides may be in order).

It of course can evolve and change over time as v1.1 comes out.