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.