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
}
}
| Field | Type | Required | Description |
|---|---|---|---|
type | string | yes | "subscribe" |
topic | string | yes | DDS topic name |
qos | object | no | QoS configuration |
qos.reliability | string | no | "reliable" or "best_effort" |
qos.history_depth | number | no | History 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
}
}
| Field | Type | Required | Description |
|---|---|---|---|
type | string | yes | "publish" |
topic | string | yes | DDS topic name |
data | any | yes | JSON 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"
}
}
| Field | Type | Description |
|---|---|---|
topic | string | Topic name |
subscription_id | string | Subscription identifier |
sample | object | The data payload (JSON) |
sample.data | any | Decoded data (if string type) |
sample._raw | string | Base64-encoded raw CDR bytes |
info | object | Sample metadata (optional) |
info.source_timestamp_ms | number | Source timestamp (ms since epoch) |
info.reception_timestamp_ms | number | Reception timestamp |
info.sequence | number | Sample sequence number |
info.writer_guid | string | Writer 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:
| Code | Description |
|---|---|
INVALID_MESSAGE | Malformed JSON or unknown message type |
TOPIC_NOT_FOUND | Topic does not exist |
ALREADY_SUBSCRIBED | Already subscribed to this topic |
NOT_SUBSCRIBED | Not subscribed to this topic |
PUBLISH_FAILED | Failed to publish message |
INTERNAL_ERROR | Internal server error |
RATE_LIMITED | Too 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);