Learn how to create the connection between MQTT and the most powerful gateway.

PHANTOM® Gateway 2.0 MQTT Support

The Gateway 2.0 used in the wireless sensor system ERBESSD INSTRUMENTS® PHANTOM®, and the DEFIANT™ wired monitor, support the MQTT IoT protocol. All sensor data is available through MQTT, including temperature, RPM, frequency bands and wired channel overalls, as well as full time waveforms from vibration sensors (the FFT must be processed by the user from the TWF).

The gateway acts as an MQTT client that will connect to an MQTT broker of your choice, and publishes everything it collects as plain, self-describing JSON.

You can use the MQTT support at the same time as all other protocols supported by the Gateway, including EI-Analytic™, EI Monitor, OPC UA and Modbus.

Some of the options described here were added in later gateway releases. If you cannot find one in your admin console, update your gateway to the latest firmware.

Protocol support at a glance

Item What the gateway does
Role MQTT client. The gateway connects out to your broker, so no inbound firewall rule is needed.
MQTT version MQTT 3.1.1 (protocol level 4). Any MQTT 3.1.1 or MQTT 5.0 broker accepts the connection, because 5.0 brokers are backward compatible with 3.1.1 clients.
Transports mqtt:// (plain TCP), mqtts:// (TLS), ws:// (WebSocket), wss:// (WebSocket over TLS).
Port Free choice, 1-65535. Defaults to 1883.
QoS 0, 1 or 2, selectable independently for publishing and for the management subscription.
Authentication Anonymous, or username + password. Optional mutual TLS with an X.509 client certificate.
Client ID PhantomGW-<gatewaySerial> by default; can be set to any value you choose.
Payload format Open UTF-8 JSON. Optionally gzip-compressed. No binary or proprietary encoding on the wire.
Local broker Optional embedded broker on port 1883 (PHANTOM® Gateway hardware only).
Reconnection Automatic, retried every 10 s. Waveforms that fail to publish are retried from the gateway offline storage.
Reverse channel Optional. The gateway can subscribe to a management topic and accept the full Gateway Management API over MQTT.

Enabling MQTT Support

To enable the MQTT support in the Gateway 2.0 admin console, you need to activate it in the MQTT config page by clicking the “Enable MQTT connection” checkbox.

Then fill your MQTT broker options accordingly. We support the following protocols for connecting to the MQTT broker:

  • mqtt://
  • mqtts:// (encrypted)
  • ws:// (websocket)
  • wss:// (websocket encrypted)

You need to enter your MQTT broker hostname/IP address, the port, and optionally the user name and password for authentication.

If you wish to receive full time waveforms from vibration sensors please mark the corresponding checkbox. Time waveforms are large messages, so if you are not intending to use them you may leave the option unchecked.

The “Allow Gateway management though MQTT” option allows you to use the full management API to control the gateway and its sensors using the MQTT connection.

MQTT screen

After you save the settings the Gateway 2.0 will restart. After it restarts it will try to connect to the MQTT broker and show a state on the same MQTT section at the top if there are any issues: connecting, connected, reconnecting, closed or error, including the broker error message when a connection is refused.

Field reference

Field Description
Protocol mqtt://, mqtts://, ws:// or wss://.
MQTT server Hostname or IP address of your broker.
Port Broker port. 1883 plain, 8883 TLS, 443/8083 WebSocket are the usual choices.
Certificate validation Shown for mqtts:// and wss:// only. See the security section below.
CA certificate (PEM format) Paste your own CA when validation is set to Custom CA.
Username / Password Leave both empty for an anonymous connection.
Publish Topic Base topic for every published message. Required.
Client Id Overrides the default PhantomGW-<serial>. Leave empty to keep the default.
Publish QoS 0, 1 or 2.
Append gateway and sensor serial number as subtopics Turns the flat base topic into a per-sensor topic tree. See the topic section below.
Publish vibration waveforms & thermal images Enables the large collection messages. Off by default.
Send wired channels overalls DEFIANT™ only: Never, As fast as possible, or Timeout.
Wired overall send timeout DEFIANT™ only, when cadence is Timeout: minimum seconds between publishes per channel (1-216000).
Compress (gzip) payload gzip-compresses every payload.
Allow Gateway management through MQTT Subscribes to a command topic. See the management section below.
Subscribe topic Topic the gateway listens on when management is enabled.
Subscribe QoS QoS used for that subscription.
Client Certificate / Client Key (PEM format) Under Advanced, for mqtts:// and wss:// only. Enables mutual TLS.

Security: TLS and authentication

Select mqtts:// or wss:// to encrypt the link. The Certificate validation selector controls how the broker certificate is verified:

Mode Behaviour When to use
Skip certificate validation (insecure) The certificate is not verified. The channel is encrypted but the broker identity is not authenticated. Bench and lab testing only.
Validate against included CA list Standard verification against the public CA bundle shipped with the gateway. Public cloud brokers (AWS IoT Core, Azure, HiveMQ Cloud, EMQX Cloud, …).
Custom CA Verified against a CA certificate you paste in PEM format. Private or self-signed PKI, on-premise brokers.

The Advanced panel accepts a client certificate and its private key, both in PEM format, used only with mqtts:// or wss://. This is what brokers that require X.509 device identity, such as AWS IoT Core, expect.

Username and password travel in the MQTT CONNECT packet. Over mqtt:// or ws:// they are sent in clear text, so always pair credentials with mqtts:// or wss://.

The on-gateway MQTT broker

PHANTOM® Gateway hardware can also run its own embedded MQTT broker, so a PLC, SCADA or edge application on the same network can subscribe directly to the gateway with no external infrastructure. It is not available on the USB/PC gateway. Enable it with Enable local MQTT broker.

Property Value
Listener 1883, all interfaces
Authentication None (anonymous)
TLS Not available on the local broker
Persistence Disabled, messages are not stored across reboots

The local broker has its own Publish Topic, Append serial switch, waveform switch, compression switch and management switch, independent from the remote broker settings. Both can run at once: the same reading is then published to your remote broker and to the local one.

mosquitto_sub -h <gateway-ip> -p 1883 -t "phantom/#" -v

Because the local broker is unauthenticated, keep it on a trusted OT network segment.

Topic structure

With Append gateway and sensor serial number as subtopics disabled, every message is published on the base Publish Topic exactly as typed.

With it enabled, the gateway builds a topic tree. Using a base topic of phantom, a gateway serial of EIGW12345 and a sensor serial (phantomCode) of 40217:

Message Topic dataType
Sensor state update phantom/EIGW12345/40217/stateupdate stateupdate
Frequency bands update phantom/EIGW12345/40217/bands bandsUpdate
Waveform / thermal image phantom/EIGW12345/40217/collection collection
Wired channel overalls (DEFIANT™) phantom/EIGW12345/WIRED0/stateupdate wiredOveralls
Wired channel waveform (DEFIANT™) phantom/EIGW12345/<serial>/collection wiredCollection
Modbus / custom unit tags phantom/EIGW12345/customunits customUnitStateUpdate
Management API replies phantom/EIGW12345 (varies)

Subscribe to everything with phantom/#, to one gateway with phantom/EIGW12345/#, or to one sensor with phantom/EIGW12345/40217/#.

Always branch on the dataType field rather than on the topic. When Append serial is off, every message type arrives on the same topic.

Payload format

The Gateway will start publishing messages every time a sensor updates its data (in its own internal update interval) or when a time waveform measurement is taken. The messages are UTF-8 JSON objects, one object per message. Nothing is binary, proprietary or schema-registry dependent: you can consume them with JSON.parse, json.loads, a Node-RED json node, the Telegraf json_v2 parser, or any SCADA JSON tag reader.

Two rules apply to every message:

  • Fields a given sensor or firmware does not report are omitted, not sent as null. Read defensively.
  • timestamp is Unix epoch time in seconds, not milliseconds.

Common Variables for All Sensors

Variable Name Description
dataType Message discriminator: stateupdate, bandsUpdate, collection, wiredOveralls, wiredCollection or customUnitStateUpdate. collection refers to time waveforms and thermal images; every other sensor update is stateupdate.
type Sensor type (see table below).
phantomCode Sensor’s serial number. Note: vibration collection messages use serialNumber instead.
gwSerial Gateway’s serial number.
timestamp The time in seconds since 1970 when this measurement was taken.
friendlyName The name given to the sensor in the console. Omitted when none is set.
seq Sensor update sequence counter. Wraps at 255; a jump means advertisements were missed over the air.
battery Battery voltage in volts.
batteryType Battery type (see table below).
temperature Sensor’s internal temperature (in Celsius).
updateInterval The sensor’s configured update interval, in seconds.
txPower Radio transmit power, in dBm.
advFlags Bit field with the sensor’s internal advertising flags.
version Firmware version.
rssi Sensor’s signal strength, in dBm (stateupdate only).
Sensor code Sensor type
3 Triaxial accelerometer (KX222, high range)
5 Triaxial accelerometer (KX122, low range)
6 Triaxial accelerometer (KX134)
7 Triaxial universal (vibration + thermocouple + current)
8 Triaxial PHANTOM® Gen 4, high range
9 Triaxial PHANTOM® Gen 4, low range
10 Thermal camera
12 Repeater
14 PHANTOM® MAX, high range
15 PHANTOM® MAX, low range
20 Thermocouple
21 Thermocouple v2
22 Thermocouple v3
25 Infrared temperature
26 4-20mA sensor’s node
27 Voltmeter (0-10 VDC)
30 Current module v1
31 Dry contact
32 Current module v2 (4 channel)
40 Velocity (RPM module)
50 Distance
60 General purpose module
202 Display
batteryType Battery
1 ⅙ D
2 AAA
3 CR2032
4 CR2477
5 AA
6 D

The following are the properties included for each sensor type.

Triaxial Accelerometer (3, 5, 6, 8 and 9)

Variable Name Description
rms Three float array with the Velocity RMS in mm/s corresponding to each axis.
arms Three float array with the Acceleration RMS in g corresponding to each axis.
minRMSFreq Lower limit of the velocity RMS band, in Hz.
maxRMSFreq Upper limit of the velocity RMS band, in Hz.
minRMSFreqAccel Lower limit of the acceleration RMS band, in Hz.
maxRMSFreqAccel Upper limit of the acceleration RMS band, in Hz.
range Accelerometer full-scale range currently configured, in g.
magneticspeed PHANTOM® MAX (14 and 15) only: magnetically derived running speed, in Hz.

Example stateupdate message:

{
  "rssi": -63,
  "type": 6,
  "version": 195,
  "phantomCode": 40217,
  "gwSerial": "EIGW12345",
  "timestamp": 1756231043,
  "dataType": "stateupdate",
  "friendlyName": "Pump 3 - Motor DE",
  "seq": 184,
  "advFlags": 0,
  "batteryType": 1,
  "battery": 3.61,
  "temperature": 41.25,
  "updateInterval": 300,
  "txPower": 4,
  "minRMSFreq": 10,
  "maxRMSFreq": 1000,
  "rms": [1.42, 0.88, 2.07],
  "range": 16,
  "recordingSettings": 3,
  "minRMSFreqAccel": 1000,
  "maxRMSFreqAccel": 10000,
  "arms": [0.412, 0.298, 0.677]
}

Triaxial Accelerometer for dataType “collection”

Variable Name Description
serialNumber Sensor’s serial number. This message uses serialNumber, not phantomCode.
sampleRate The sample rate at which this measurement was taken, in Hz.
data This will contain the time wave form data. It contains three arrays, one for each axis. Two of the axis arrays may be empty if a single axis mode was selected. The numbers in the array are in G’s (9.8 m/s²).
isAlarm true when the recording was triggered by an alarm condition.
rpm Running speed at capture time. Omitted when not available.
temperature Sensor internal temperature at capture, in Celsius. Reported by sensors that measure it.
{
  "serialNumber": 40217,
  "sampleRate": 3200,
  "gwSerial": "EIGW12345",
  "timestamp": 1756231043,
  "type": 6,
  "dataType": "collection",
  "friendlyName": "Pump 3 - Motor DE",
  "isAlarm": false,
  "rpm": 1478,
  "temperature": 41.25,
  "data": [
    [0.0123, -0.0044, 0.0187],
    [0.0091, 0.0102, -0.0058],
    [-0.021, 0.0176, 0.0031]
  ]
}

The three sample arrays are truncated in this example; a real message carries the full record. Samples per axis is data[n].length, and record duration in seconds is data[n].length / sampleRate.

Frequency bands message (dataType “bandsUpdate”)

Triaxial sensors that advertise extended band data publish a bandsUpdate alongside the state update.

Variable Name Description
rpmMin / rpmMax Running speed window the bands were evaluated against.
bands[].min / .max Band limits, expressed in unit: hz (integers) or orders (one decimal).
bands[].measurement velocity or acceleration.
bands[].output Detector applied to the band (rms, peak, …).
bands[].values Per-axis result, { x, y, z }.
bands[].valueUnit mm/s for velocity bands, mg for acceleration bands.
{
  "dataType": "bandsUpdate",
  "serialNumber": 40217,
  "phantomCode": 40217,
  "type": 6,
  "version": 195,
  "gwSerial": "EIGW12345",
  "timestamp": 1756231043,
  "seq": 184,
  "rpmMin": 1450,
  "rpmMax": 1495,
  "bands": [
    {
      "min": 1.0,
      "max": 2.5,
      "measurement": "velocity",
      "unit": "orders",
      "output": "rms",
      "values": { "x": 1.12, "y": 0.74, "z": 1.88 },
      "valueUnit": "mm/s"
    }
  ]
}

Thermal camera message (10)

For dataType: "stateupdate" the camera reports avgImageTemperature, the average image temperature in Celsius.

For dataType: "collection" the full image is published:

Variable Name Description
columns Image width in pixels.
rows Image height in pixels.
frames Number of frames in the message.
frameRate Capture frame rate.
data Array of frames images; each image is an array of rows rows; each row holds columns temperatures in Celsius.
{
  "phantomCode": 60912,
  "serialNumber": 60912,
  "gwSerial": "EIGW12345",
  "timestamp": 1756231400,
  "type": 10,
  "dataType": "collection",
  "columns": 32,
  "rows": 24,
  "frames": 1,
  "frameRate": 1,
  "data": [
    [
      [24.31, 24.55, 25.02],
      [24.28, 24.61, 25.14]
    ]
  ]
}

Thermocouple temperature node message (20, 21 and 22)

Variable Name Description
tcTemperature Three float array with the temperature of each thermocouple channel in celsius.

Infrared temperature node message (25)

Variable Name Description
ambientTemperature Ambient temperature as measured by the infrared sensor in celsius.
objectTemperature Temperature of the object the infrared sensor points at, in celsius.
emissivity Emissivity configured for the target object.

4-20mA sensor (26) and Voltmeter (27)

Variable Name Description
voltage A float array of size 4 with the voltage 4-20mA sensor.

Velocity (RPM) sensor (40)

Variable Name Description
rpm Float value with the current RPM speed.
rpmTimeout Seconds without a pulse after which the sensor reports zero speed.

Distance sensor (50)

Variable Name Description
distance Measured distance, in mm.

General purpose module (60)

Variable Name Description
rms Float array with the RMS per axis. Velocity in mm/s, or acceleration in g, per gpFlags.
sensitivity Configured probe sensitivity, in mV/g.
gpFlags Bit field. Bit 0 selects whether rms is a velocity or an acceleration measurement.

Current sensor (30 and 32)

Variable Name Description
instCurrent A float array size 4 with the Instantaneous current for each of the four channels of the sensor in amps.
averageCurrent A float array size 4 with the Average current for each of the four channels of the sensor in amps.
minCurrent A float array size 4 with the minimum current for each of the four channels of the sensor in amps.
maxCurrent A float array size 4 with the maximum current for each of the four channels of the sensor in amps.
accumulatedCurrent A float array size 4 with the accumulated current for each of the four channels of the sensor.
accumulatedStart Start of the accumulation window, in seconds since 1970.
accumulatedEnd End of the accumulation window, in seconds since 1970.
currentProbeType Probe type configured on each channel.
currentOffsets Calibration offset applied to each channel.
currentMultiplier Calibration multiplier applied to each channel.
{
  "dataType": "stateupdate",
  "type": 32,
  "phantomCode": 51844,
  "gwSerial": "EIGW12345",
  "timestamp": 1756231102,
  "rssi": -71,
  "version": 118,
  "battery": 3.58,
  "batteryType": 4,
  "temperature": 33.1,
  "instCurrent": [12.4, 12.1, 12.8, 0.0],
  "averageCurrent": [12.3, 12.0, 12.7, 0.0],
  "minCurrent": [11.9, 11.6, 12.2, 0.0],
  "maxCurrent": [13.1, 12.9, 13.4, 0.0],
  "accumulatedCurrent": [842.5, 838.1, 851.9, 0.0],
  "accumulatedStart": 1753553200,
  "accumulatedEnd": 1756231102
}

Dry contacts Sensor (31)

Variable Name Description
dryContactsState A boolean array size 4 with the state of the input. True means the contact is closed and false means open.
debounce Debounce time applied to the inputs.

DEFIANT™ wired channels

Wired channel overalls are published according to the Send wired channels overalls cadence (Never, As fast as possible, or throttled by Wired overall send timeout). wiredChannel is 1-based in the payload, and only the overalls actually computed for that channel are included.

{
  "dataType": "wiredOveralls",
  "wiredChannel": 1,
  "gwSerial": "EIDF98765",
  "timestamp": 1756231500,
  "sensorType": "accelerometer",
  "friendlyName": "Fan 12 - Inboard",
  "accel_rms": 0.482,
  "velocity_rms": 2.14,
  "displacement_rms": 18.7,
  "gE": 0.93,
  "rpm": 1786,
  "accel_truepeak": 1.84,
  "accel_peaktopeak": 3.52,
  "accel_kurtosis": 3.11
}

Available keys:

Group Keys
Acceleration accel_rms, accel_truepeak, accel_peaktopeak, accel_lowfreq, accel_medfreq, accel_highfreq, accel_kurtosis, accel_skewness
Velocity velocity_rms, velocity_truepeak, velocity_peaktopeak, velocity_lowfreq, velocity_medfreq, velocity_highfreq, velocity_kurtosis, velocity_skewness
Acceleration envelope gE, ge_truepeak, ge_peaktopeak, ge_lowfreq, ge_medfreq, ge_highfreq, ge_kurtosis, ge_skewness
Displacement displacement_rms, displacement_truepeak, displacement_peaktopeak, displacement_offset
Speed rpm
Analog / discrete voltage_discrete, voltage_average, discrete_value
Cross-channel phase phase_channel_1phase_channel_16

While the DEFIANT™ is in project (route/recording) mode, wiredOveralls publishing is suspended.

Wired waveforms are published as wiredCollection, with a single data array already scaled by the channel calibration and sensor sensitivity:

{
  "dataType": "wiredCollection",
  "wiredChannel": 1,
  "gwSerial": "EIDF98765",
  "timestamp": 1756231500,
  "sensorType": "accelerometer",
  "sampleRate": 25600,
  "data": [0.0041, -0.0093, 0.0128]
}

Modbus and custom unit tags

Values the gateway reads from third-party Modbus devices, or from wired custom units, are republished on MQTT as customUnitStateUpdate. units and values are parallel arrays, and a wiredChannel field is added when the value comes from a DEFIANT™ wired channel.

{
  "dataType": "customUnitStateUpdate",
  "gwSerial": "EIGW12345",
  "timestamp": 1756231600,
  "source": "gateway",
  "clientName": "Compressor PLC",
  "tagName": "DischargePressure",
  "functionType": "holdingRegister",
  "address": 40012,
  "byteOrder": "ABCD",
  "tagDataType": "float32",
  "machineCode": "COMP-01",
  "pointIndex": 2,
  "units": ["bar"],
  "values": [7.42]
}

Payload compression

The Compress (gzip) payload option wraps every JSON payload in a gzip stream before publishing, which typically cuts waveform messages by 60-80%. The payload is then raw gzip bytes, not text, so decompress before parsing:

import gzip, json
import paho.mqtt.client as mqtt

def on_message(client, userdata, msg):
    raw = msg.payload
    if raw[:2] == b"\x1f\x8b":            # gzip magic number
        raw = gzip.decompress(raw)
    data = json.loads(raw)
    print(data["dataType"], data.get("phantomCode") or data.get("serialNumber"))

client = mqtt.Client()
client.username_pw_set("phantom", "your-password")
client.tls_set()                          # required for mqtts://
client.on_message = on_message
client.connect("broker.example.com", 8883)
client.subscribe("phantom/#", qos=1)
client.loop_forever()

Leave compression off if your platform cannot decompress payloads; many SCADA JSON tag readers cannot.

Remote management over MQTT

Ticking Allow Gateway management through MQTT makes the gateway subscribe to the Subscribe topic and act on JSON commands received there. Replies are published on the normal publish topic, with /<gwSerial> appended when Append serial is on. Every command must carry the target gateway serial in gwSerial; a gateway ignores commands addressed to another serial.

Request a waveform from a specific sensor:

{
  "type": "requestWaveform",
  "gwSerial": "EIGW12345",
  "phantomCode": 40217
}

Call the Gateway Management API:

{
  "type": "observeerequest",
  "gwSerial": "EIGW12345",
  "action": "getstate"
}

The gateway answers with the same JSON documents the admin console receives over WebSocket, enriched with a gwSerial field so you can route replies. The action vocabulary covers the full console feature set, including getstate, deviceinfo, setbasicconfig, setfriendlyname, pair, unpair, restart, startfwupdate, getofflinefiles and wiredcollectnow. Please contact an ERBESSD INSTRUMENTS® support representative to receive a copy of the management API documentation.

This is a privileged control channel. Enable it only on an authenticated, TLS-protected broker, with an ACL restricting who may publish to the subscribe topic. Leave it disabled if you only need telemetry.

MQTT version and payload format

MQTT version. The gateway client connects at protocol level 4, that is MQTT 3.1.1. This is the interoperable baseline: MQTT 5.0 brokers accept 3.1.1 clients, so an MQTT 5.0 infrastructure works without changes. What the gateway session does not use are 5.0-only features such as shared subscriptions, topic aliases, request/response correlation properties, session expiry and reason codes. If you need those downstream, terminate the gateway 3.1.1 session at your broker and speak 5.0 between the broker and your applications.

Is the payload proprietary? No. It is open JSON with stable field names, fully documented above. No schema registry, licence or SDK is required to read it.

Quick start with mosquitto_sub

mosquitto_sub -h broker.example.com -p 8883 --cafile /etc/ssl/certs/ca-certificates.crt -u phantom -P "your-password" -t "phantom/#" -q 1 -v

Filter down to just the light-weight state updates:

mosquitto_sub -h broker.example.com -t "phantom/+/+/stateupdate" -v