Skip to content
Start free evaluation

OPC UA PubSub over MQTT

You build a setup in which a publisher on the equipment side and a subscriber on the server side share nothing but an MQTT broker:

publisher process Mosquitto subscriber process
(publisher_id 100) ──────▶ 127.0.0.1:1883 ──────▶ (Received messages)
opcua/uadp/data/100/line1 │
└──▶ mosquitto_sub (observer)
  • The publisher runs the headless simulator in scenario mode, sends two DataSets and exits.
  • The subscriber runs the headless simulator interactively so that you can look at what it received.
  • mosquitto_sub uses a wildcard filter to watch the data of every PublisherId.

OPC UA background explains DataSets, WriterGroups and DataSetWriters.

  1. Save the subscriber input file and the publisher scenario file in the mqtt-lab directory.

    subscriber.jsonl
    {"action":"configure","config":{"pubsub":{"transport":"mqtt","role":"subscriber","broker":"127.0.0.1:1883","topic":"opcua/uadp/data/100/line1"}}}
    {"action":"pubsub_start"}

    The subscriber input holds the JSON commands for the headless simulator, one per line.

    publisher-scenario.json
    [
    {
    "command": {
    "action": "configure",
    "config": {
    "pubsub": {
    "transport": "mqtt",
    "role": "publisher",
    "broker": "127.0.0.1:1883",
    "topic": "opcua/uadp/data/100/line1",
    "publisher_id": 100,
    "writer_group_id": 1,
    "dataset_writer_id": 1
    }
    }
    }
    },
    { "command": { "action": "pubsub_start" }, "expect": { "running": true, "transport": "mqtt" } },
    {
    "command": {
    "action": "pubsub_publish",
    "fields": [
    { "type": "Double", "value": 21.5 },
    { "type": "Boolean", "value": true },
    { "type": "String", "value": "RUN" }
    ]
    },
    "expect": { "sequence": 1 }
    },
    {
    "command": {
    "action": "pubsub_publish",
    "fields": [
    { "type": "Double", "value": 21.75 },
    { "type": "Boolean", "value": true },
    { "type": "String", "value": "RUN" }
    ]
    },
    "expect": { "sequence": 2 }
    },
    { "command": { "action": "pubsub_stop" }, "expect": { "running": false } }
    ]

    A scenario file is an array of steps. The simulator runs the command of each step in order and, when a step has expect, compares those values with the result. The publisher uses PublisherId 100, WriterGroupId 1 and DataSetWriterId 1, and publishes to opcua/uadp/data/100/line1, following the OPC UA PubSub topic convention.

  2. In all four terminals, store the path of the simulator package in a variable and go to mqtt-lab:

    터미널 창
    PDCOMM_APPS=<simulator package directory>
    cd mqtt-lab
  3. In the first terminal, start the broker:

    터미널 창
    mosquitto -c local.conf
  4. In the second terminal, watch every OPC UA data topic:

    터미널 창
    mosquitto_sub -V 5 -t 'opcua/uadp/data/+/#' -F '%t qos=%q retain=%r content-type=%C user-properties=%P %l bytes'

    + matches the PublisherId level, and # matches every level below it.

  5. In the third terminal, start the subscriber:

    터미널 창
    cat subscriber.jsonl - | "$PDCOMM_APPS/bin/pdcomm-opcua-headless"

    cat sends the two lines of the file first and then passes your keyboard input on to the simulator (-). When the second output line contains "role":"subscriber","running":true, the subscription is ready:

    {"ok":true,"result":{"acknowledged":0,"broker":"127.0.0.1:1883","destination":null,"dropped":0,"error":null,"listen":null,"queued":0,"received":0,"rejected":0,"role":"subscriber","running":true,"sent":0,"topic":"opcua/uadp/data/100/line1","transport":"mqtt"}}

    In Windows PowerShell, run bin\pdcomm-opcua-headless.exe and paste the two lines of subscriber.jsonl.

  6. In the fourth terminal, run the publisher scenario:

    터미널 창
    "$PDCOMM_APPS/bin/pdcomm-opcua-headless" --scenario publisher-scenario.json

    The simulator prints one result line per step and exits with code 0. The first line, the configuration result, is long and left out here:

    {"result":{"acknowledged":0,"broker":"127.0.0.1:1883","destination":null,"dropped":0,"error":null,"listen":null,"queued":0,"received":0,"rejected":0,"role":"publisher","running":true,"sent":0,"topic":"opcua/uadp/data/100/line1","transport":"mqtt"},"step":2}
    {"result":{"bytes":52,"fields":3,"sequence":1},"step":3}
    {"result":{"bytes":52,"fields":3,"sequence":2},"step":4}
    {"result":{"running":false},"step":5}

    The observer in the second terminal prints two lines:

    opcua/uadp/data/100/line1 qos=0 retain=0 content-type=application/opcua+uadp user-properties=UAMessageType:ua-data 52 bytes
    opcua/uadp/data/100/line1 qos=0 retain=0 content-type=application/opcua+uadp user-properties=UAMessageType:ua-data 52 bytes
  7. In the third terminal (the subscriber), type:

    {"action":"pubsub_received"}
    {"ok":true,"result":{"messages":[{"bytes":52,"datasets":[{"fields":[{"type":"Double","value":21.5},{"type":"Boolean","value":true},{"type":"String","value":"RUN"}],"major":1,"minor":1,"sequence":1}],"publisher_id":100,"source":"mqtt","writer_group":1,"writer_ids":[1]},{"bytes":52,"datasets":[{"fields":[{"type":"Double","value":21.75},{"type":"Boolean","value":true},{"type":"String","value":"RUN"}],"major":1,"minor":1,"sequence":2}],"publisher_id":100,"source":"mqtt","writer_group":1,"writer_ids":[1]}]}}

    publisher_id 100, writer_ids [1] and sequence numbers 1 and 2 match the publisher’s settings. The subscriber receives by topic, whatever its own publisher_id (77 by default) is.

The result is the same when the subscriber is the GUI:

  1. Open the OPC UA simulator GUI and go to the PubSub tab.
  2. Set Transport to UADP over MQTT and Role to Subscriber.
  3. Enter 127.0.0.1:1883 in MQTT broker and opcua/uadp/data/100/line1 in MQTT topic, then click Start PubSub.
  4. Run the publisher scenario from step 6 again and click Show received. The table shows two rows.

The simulator PubSub tab describes the screen.

  1. Type {"action":"shutdown"} in the third terminal to stop the subscriber.
  2. Stop mosquitto_sub in the second terminal and the broker in the first terminal with Ctrl+C.
Symptom Cause Fix
messages from pubsub_received is empty You started the subscriber after the publisher. The simulator doesn’t retain data and starts a new session on every connection. Start the subscriber first, then run the publisher scenario again.
The observer sees the messages but the subscriber doesn’t The subscriber’s topic differs from the publisher’s topic. Compare topic in the two files.
The scenario ends with Scenario step 2 failed: MQTT broker connection failed: … The publisher couldn’t connect to the broker. Check the broker from step 3 and the broker address. Look up the rest of the message in Connection problems.