Skip to main content

WebSocket Protocol

hdds-ws uses a JSON-based protocol for communication between clients and the server.

Message Format

All messages are JSON objects with a type field that identifies the message type.

Client → Server Messages

Subscribe

Subscribe to receive data from a DDS topic.

{
"type": "subscribe",
"topic": "temperature",
"qos": {
"reliability": "reliable",
"history_depth": 10
}
}
FieldTypeRequiredDescription
typestringyes"subscribe"
topicstringyesDDS topic name
qosobjectnoQoS configuration
qos.reliabilitystringno"reliable" or "best_effort"
qos.history_depthnumbernoHistory depth for reliable QoS

Unsubscribe

Stop receiving data from a topic.

{
"type": "unsubscribe",
"topic": "temperature"
}

Publish

Publish data to a DDS topic.

{
"type": "publish",
"topic": "commands",
"data": {
"action": "start",
"value": 42
}
}
FieldTypeRequiredDescription
typestringyes"publish"
topicstringyesDDS topic name
dataanyyesJSON data to publish

ROS 2 String Format:

For ROS 2 std_msgs/String compatibility, use the data field:

{
"type": "publish",
"topic": "chatter",
"data": {
"data": "Hello from browser!"
}
}

List Topics

Request a list of discovered DDS topics.

{
"type": "list_topics"
}

Ping

Keepalive message.

{
"type": "ping",
"id": 1704567890123
}

Server → Client Messages

Welcome

Sent immediately after WebSocket connection is established.

{
"type": "welcome",
"version": "1.0.0",
"domain": 0
}

Subscribed

Confirms a successful subscription.

{
"type": "subscribed",
"topic": "temperature",
"subscription_id": "sub_abc123"
}

Unsubscribed

Confirms unsubscription.

{
"type": "unsubscribed",
"topic": "temperature"
}

Data

Data received from a subscribed topic.

{
"type": "data",
"topic": "temperature",
"subscription_id": "sub_abc123",
"sample": {
"data": "23.5",
"_raw": "BQAAAAMjLjUA"
},
"info": {
"source_timestamp_ms": 1704567890123,
"reception_timestamp_ms": 1704567890125,
"sequence": 42,
"writer_guid": "01.0f.ab.cd.00.00.00.01"
}
}
FieldTypeDescription
topicstringTopic name
subscription_idstringSubscription identifier
sampleobjectThe data payload (JSON)
sample.dataanyDecoded data (if string type)
sample._rawstringBase64-encoded raw CDR bytes
infoobjectSample metadata (optional)
info.source_timestamp_msnumberSource timestamp (ms since epoch)
info.reception_timestamp_msnumberReception timestamp
info.sequencenumberSample sequence number
info.writer_guidstringWriter GUID

Published

Confirms a successful publish.

{
"type": "published",
"topic": "commands",
"sequence": 42
}

Topics

Response to list_topics request.

{
"type": "topics",
"topics": [
{
"name": "temperature",
"type_name": "sensor_msgs::msg::Temperature",
"subscribers": 2,
"publishers": 1
},
{
"name": "commands",
"type_name": "std_msgs::msg::String",
"subscribers": 0,
"publishers": 3
}
]
}

Pong

Response to ping.

{
"type": "pong",
"id": 1704567890123
}

Error

Error notification.

{
"type": "error",
"code": "TOPIC_NOT_FOUND",
"message": "Topic 'foo' not found",
"topic": "foo"
}

Error Codes:

CodeDescription
INVALID_MESSAGEMalformed JSON or unknown message type
TOPIC_NOT_FOUNDTopic does not exist
ALREADY_SUBSCRIBEDAlready subscribed to this topic
NOT_SUBSCRIBEDNot subscribed to this topic
PUBLISH_FAILEDFailed to publish message
INTERNAL_ERRORInternal server error
RATE_LIMITEDToo many requests

Data Encoding

ROS 2 String Messages

hdds-ws automatically detects and decodes ROS 2 std_msgs/String messages. The CDR format is:

[4 bytes: length (LE)] [string bytes] [null terminator]

When publishing, wrap your string in a data field:

{
"type": "publish",
"topic": "chatter",
"data": { "data": "Hello, ROS 2!" }
}

Raw CDR Data

For complex types, the raw CDR bytes are provided as base64 in the _raw field. You can also publish raw CDR by including _raw:

{
"type": "publish",
"topic": "custom_topic",
"data": {
"_raw": "AQIDBA..."
}
}

Connection Management

Automatic Reconnection

The demo UI automatically reconnects after 3 seconds if the connection drops. Implement similar logic in your clients:

function connect() {
const ws = new WebSocket('ws://localhost:9090/ws');

ws.onclose = () => {
console.log('Disconnected, reconnecting...');
setTimeout(connect, 3000);
};

// ... handle messages
}

Keepalive

Send periodic ping messages to keep the connection alive:

setInterval(() => {
if (ws.readyState === WebSocket.OPEN) {
ws.send(JSON.stringify({ type: 'ping', id: Date.now() }));
}
}, 30000);