Get data from hazardous-area machines into InduQT with certified wireless instruments

Updated Oct 31, 2026 · InduQT engineering team · Guide, not a customer case study

The short answer

In a classified hazardous area (Zone 1/2 or Class I Division 1/2) only certified instruments are allowed, so the off-the-shelf sensors in our other guides are out. The pattern that works is: certified wireless transmitters (WirelessHART, ISA100 or LoRaWAN) in the zone, their gateway in a safe area or a rated enclosure, and the gateway’s OPC UA or Modbus TCP output read by InduQT, either directly or through Node-RED and the bridge. Most sites also already have the data in the DCS or a PLC, which can be read the same way. InduQT itself never goes into the hazardous area.

  • Machine data from Zone 1/2 or Class I Div 1/2 without uncertified equipment in the zone
  • One path for new wireless instruments and data the DCS already has
  • The same InduQT analysis and alerts as for safe-area machines
Costinstrument and gateway prices on quotation from the makers
Timedays to weeks, with the site’s instrument and safety teams
SkillWiring and a little code
  1. What to buy
  2. The pattern
  3. Read the gateway
  4. Check the readings with Claude before you connect anything
  5. Wire it to InduQT
  6. OT security

What to buy

Certified WirelessHART instruments and a Wireless Gateway 1410Son quotation

Emerson offers devices certified for intrinsically safe and explosion-proof areas under FM, CSA, ATEX and IECEx; its wireless gateways output HART-IP, OPC UA and Modbus TCP (Emerson, read 2026-10-31). Maker: Emerson. Maker’s page

ISA100 field wireless transmitters and gatewayson quotation

Wireless pressure and temperature transmitters and adapters with ATEX, IECEx and FM approval options; the FN120 gateway is IECEx Type n with Modbus for PLC/RTU (Yokogawa). Maker: Yokogawa. Maker’s page

Sushi Sensor wireless vibration and temperature (LoRaWAN)on quotation

Wireless vibration and surface-temperature sensor described by Yokogawa as explosion-proof for hazardous areas; check the certificate for your zone. Maker: Yokogawa. Maker’s page

Node-RED on a PC or industrial PC in the safe areafree

Reads the gateway (Modbus TCP or OPC UA) and forwards to InduQT through the bridge, when InduQT cannot reach the gateway directly. Maker: OpenJS Foundation. Maker’s page

Prices are typical street prices in US dollars when we wrote this, not quotes; buy from the maker or a local distributor. InduQT does not sell sensors and is not paid by any maker named here.

Safety. Everything that goes into a classified area must carry a certificate valid for that zone, gas group and temperature class, and be installed and inspected by competent people under the site’s permit system (IEC 60079-14/-17). Check each certificate yourself; do not rely on a product page or on this guide. Keep the gateway, Node-RED and any network equipment in a safe area unless they carry their own rating.

The pattern

From the hazardous area to InduQT
WhereWhatTalks
Zone 1/2 (Class I Div 1/2)Certified wireless transmitters: vibration, pressure, temperatureWirelessHART, ISA100 or LoRaWAN
Safe area or rated enclosureThe maker’s gatewayOPC UA, Modbus TCP, HART-IP (per the maker)
Safe area, plant networkNode-RED + bridge (or nothing, if InduQT reads OPC UA directly)Outbound HTTPS to InduQT, or OPC UA
InduQTNormal behaviour, warnings, causes, expert review—

Before buying anything, check what the DCS, the PLCs or an existing historian already record: pump discharge pressures, bearing temperatures, compressor vibration from machine-protection systems. Reading those through the control system’s OPC UA server is often the fastest start, with no new equipment in the zone.

Read the gateway

  1. Ask the instrument team for the gateway’s Modbus register map or OPC UA node list for the points you want.
  2. OPC UA reachable from InduQT (Plant Monitor): add an OPC UA connector for the gateway or the DCS server, read-only, and pick the nodes. In Claude with the InduQT connector you can browse the server first: “browse the OPC UA server at opc.tcp://… and list the vibration and temperature nodes”.
  3. Not reachable from outside (usual on these sites): run Node-RED on a PC in the safe area, read the gateway with the Modbus flow from the PLC guide (or an OPC UA client node), publish to the local broker, and let the bridge forward the readings over outbound HTTPS.
  4. Name the topics factory/<machine>/<what>, as in every other guide.
Node-RED flow: Modbus TCP → MQTT (edit the IP, unit id and registers for the gateway)
Node-RED flow: Modbus TCP → MQTT (edit the IP, unit id and registers for the gateway)
[
  {
    "id": "iqm_tab",
    "type": "tab",
    "label": "PLC (Modbus TCP) → MQTT",
    "disabled": false,
    "info": "Reads 4 holding registers every 5 s and publishes them as JSON on factory/press-2/modbus. Needs node-red-contrib-modbus (Manage palette)."
  },
  {
    "id": "iqm_client",
    "type": "modbus-client",
    "name": "Press PLC",
    "clienttype": "tcp",
    "bufferCommands": true,
    "stateLogEnabled": false,
    "queueLogEnabled": false,
    "tcpHost": "192.168.1.50",
    "tcpPort": "502",
    "tcpType": "DEFAULT",
    "serialPort": "",
    "serialType": "RTU-BUFFERD",
    "serialBaudrate": "9600",
    "serialDatabits": "8",
    "serialStopbits": "1",
    "serialParity": "none",
    "serialConnectionDelay": "100",
    "serialAsciiResponseStartDelimiter": "0x3A",
    "unit_id": "1",
    "commandDelay": "1",
    "clientTimeout": "1000",
    "reconnectOnTimeout": true,
    "reconnectTimeout": "2000",
    "parallelUnitIdsAllowed": true
  },
  {
    "id": "iqm_read",
    "type": "modbus-read",
    "z": "iqm_tab",
    "name": "Holding 0–3",
    "topic": "",
    "showStatusActivities": false,
    "logIOActivities": false,
    "showErrors": true,
    "unitid": "1",
    "dataType": "HoldingRegister",
    "adr": "0",
    "quantity": "4",
    "rate": "5",
    "rateUnit": "s",
    "delayOnStart": false,
    "startDelayTime": "",
    "server": "iqm_client",
    "useIOFile": false,
    "ioFile": "",
    "useIOForPayload": false,
    "emptyMsgOnFail": false,
    "x": 130,
    "y": 80,
    "wires": [
      [
        "iqm_shape"
      ],
      []
    ]
  },
  {
    "id": "iqm_shape",
    "type": "function",
    "z": "iqm_tab",
    "name": "Scale and name",
    "func": "// Registers 0..3 from the PLC → named signals with scaling. Edit to match your register map.\nconst r = msg.payload;                    // array of 16-bit values\nmsg.topic = 'factory/press-2/modbus';\nmsg.payload = {\n  hydraulic_pressure_bar: r[0] / 10,       // 0.1 bar per count\n  oil_temp_c: r[1] / 10,\n  motor_current_a: r[2] / 10,\n  cycle_count: r[3]\n};\nreturn msg;",
    "outputs": 1,
    "timeout": 0,
    "noerr": 0,
    "initialize": "",
    "finalize": "",
    "libs": [],
    "x": 330,
    "y": 80,
    "wires": [
      [
        "iqm_out"
      ]
    ]
  },
  {
    "id": "iqm_broker",
    "type": "mqtt-broker",
    "name": "Factory broker",
    "broker": "localhost",
    "port": "1883",
    "clientid": "plc-modbus",
    "autoConnect": true,
    "usetls": false,
    "protocolVersion": "4",
    "keepalive": "60",
    "cleansession": true
  },
  {
    "id": "iqm_out",
    "type": "mqtt out",
    "z": "iqm_tab",
    "name": "factory/press-2/modbus",
    "topic": "",
    "qos": "1",
    "retain": "false",
    "broker": "iqm_broker",
    "x": 560,
    "y": 80,
    "wires": []
  }
]

Check the readings with Claude before you connect anything

Before the data goes anywhere, make sure the sensor is telling the truth. Record two minutes of messages to a file and ask Claude (or ChatGPT) to check them. This catches the usual first-day problems (a clamp on the wrong cable, a probe reading the room instead of the pipe, a sensor that only reports when something changes) while you are still standing at the hazardous-area equipment.

Record two minutes of messages (Raspberry Pi, Mac or Linux)
# Record 2 minutes of messages to a file you can give Claude or ChatGPT
mosquitto_sub -h 192.168.1.20 -u viewer -P 'viewer-password' \
  -t 'factory/#' -v -W 120 | while read -r line; do
  echo "$(date -u +%Y-%m-%dT%H:%M:%SZ) $line"
done > sensor-sample.txt
The same on Windows
# Windows PowerShell, with Mosquitto installed from mosquitto.org
& "C:\Program Files\mosquitto\mosquitto_sub.exe" -h 192.168.1.20 -u viewer -P viewer-password `
  -t "factory/#" -v -W 120 | ForEach-Object { "$(Get-Date -Format o) $_" } | Out-File sensor-sample.txt
Prompt
Paste into Claude or ChatGPT with sensor-sample.txt attached
I've attached sensor-sample.txt: two minutes of MQTT messages from a sensor I just fitted to the hazardous-area equipment.
Each line is: time, topic, payload.

Please check it for me:
1. List each topic and every numeric field in it, with the min, max and typical value.
2. Tell me whether the values are physically plausible for the hazardous-area equipment. Compare each value with what the DCS or the gateway’s web page shows at the same moment. A value that never changes may be a stale wireless point; check the device’s update rate and battery status on the gateway.
3. Point out gaps longer than 30 seconds, repeated identical values, or fields that are always 0.
4. Say which fields are worth sending to InduQT as signals and which I can ignore.
Answer in a short table, then a list of anything I should fix before I wire it up.

Claude Code or Claude desktop with a terminal can run the mosquitto_sub command for you: ask it to "subscribe to factory/# on 192.168.1.20 as viewer for two minutes and check the readings". Either way the readings stay on your laptop: nothing is stored in InduQT until you wire it up in the next step.

Using a cloud broker (HiveMQ Cloud, EMQX Serverless or your own server with TLS) instead of one on the plant network? Add the free InduQT connector to Claude (https://claude.ai/directory/connectors/induqt) and Claude can listen to the broker for a few seconds itself:

Prompt
Claude with the InduQT connector
Use InduQT to preview my MQTT broker before I connect it:
url mqtts://<your-cluster>.hivemq.cloud:8883, topic factory/#,
username viewer, password <viewer password>. Listen for 10 seconds.
Then tell me which topic level names the machine and which fields are numeric.

Wire it to InduQT

Once the readings look right, send them to InduQT. The simplest way is a small bridge on the same Raspberry Pi: it listens to your broker and forwards every number to InduQT over HTTPS. It works on every InduQT plan, including the free one (up to 3 machines), and nothing on the plant network has to be opened to the internet.

  1. In Claude with the InduQT connector, paste the prompt below. Claude creates the site, the machine and a key that can only send readings to that site. (No Claude? Do the same in the InduQT app: Add machine with the same externalId, then Settings → API keys → a key with the ingest scope, limited to the site.)
  2. Copy the key Claude shows you. It is shown once.
  3. Start the bridge on the Raspberry Pi with that key.
  4. Ask Claude for the latest values, and set the normal bands.
Prompt
1 · Claude creates the machine and the key
Set up InduQT for this sensor:
1. Create a site called "Plant 1" (skip if it already exists).
2. Create a machine named "p-101" with externalId "p-101" and family "pumps".
3. Create an ingest key for the site called "Raspberry Pi bridge" and show it to me once.
After I start the bridge, I'll ask you to check the latest values. The signals will be the pressures, temperatures and vibration values from the gateway.
2 · Start the bridge
python3 -m venv ~/induqt && source ~/induqt/bin/activate
pip install "paho-mqtt>=2.0" requests

export MQTT_HOST=localhost MQTT_USER=viewer MQTT_PASSWORD='viewer-password'
export MQTT_TOPICS='factory/#'
export INDUQT_API_KEY='iq_live_…'   # the site key Claude or the app created for you
python3 induqt_bridge.py
induqt_bridge.py (the whole bridge, about 120 lines)
induqt_bridge.py (the whole bridge, about 120 lines)
#!/usr/bin/env python3
"""
induqt_bridge.py — forward MQTT sensor messages to InduQT.

Subscribes to your local broker, turns each message into numbers, and posts them
to the InduQT ingest API in batches. Works on every InduQT plan, including the free one,
and your broker never has to be reachable from the internet.

  factory/<machine>/<anything>        ->  machine = <machine> (matched to the machine's externalId)
  factory/pump-3/vibration {"vib_rms_x_g": 0.12}  ->  signal vib_rms_x_g
  factory/comp-1/status/em:0 {"a_current": 12.4}  ->  signal em_0.a_current
      (two or more levels after the machine: the last level names the group, so
       two probes or two channels never overwrite each other)
  {"temperature": {"tC": 41.2}}       ->  signal temperature.tC
  factory/furnace-1/temp  21.7        ->  signal temp (a bare number takes the last level)

pip install "paho-mqtt>=2.0" requests
"""
import json
import os
import re
import threading
import time

import paho.mqtt.client as mqtt
import requests

MQTT_HOST = os.environ.get("MQTT_HOST", "localhost")
MQTT_PORT = int(os.environ.get("MQTT_PORT", "1883"))
MQTT_USER = os.environ.get("MQTT_USER")
MQTT_PASSWORD = os.environ.get("MQTT_PASSWORD")
TOPICS = [t.strip() for t in os.environ.get("MQTT_TOPICS", "factory/#").split(",") if t.strip()]
MACHINE_LEVEL = int(os.environ.get("MACHINE_LEVEL", "1"))  # factory/<machine>/... is level 1
SEND_EVERY = float(os.environ.get("SEND_EVERY", "10"))     # seconds between posts
IGNORE = set(os.environ.get("IGNORE_FIELDS", "id,ts,time,timestamp,unixtime,Time").split(","))
INGEST_URL = os.environ.get("INDUQT_URL", "https://api.induqt.ai/v1/ingest")
API_KEY = os.environ["INDUQT_API_KEY"]  # a site key from InduQT (Settings -> API keys)

BAD = re.compile(r"[^A-Za-z0-9_.\-]")
buffer, lock = [], threading.Lock()


def key(name):
    return BAD.sub("_", name)[:64]


def numbers(prefix, value, out):
    """Collect every number in a JSON value; true/false become 1/0."""
    if isinstance(value, bool):
        out[key(prefix)] = 1 if value else 0
    elif isinstance(value, (int, float)):
        out[key(prefix)] = value
    elif isinstance(value, str):
        text = value.strip().lower()
        if text in ("on", "true", "online"):
            out[key(prefix)] = 1
        elif text in ("off", "false", "offline"):
            out[key(prefix)] = 0
        else:
            try:
                out[key(prefix)] = float(text)
            except ValueError:
                pass
    elif isinstance(value, dict):
        for k, v in value.items():
            if k not in IGNORE:
                numbers(f"{prefix}.{k}" if prefix else k, v, out)


def on_connect(client, userdata, flags, reason_code, properties):
    print(f"MQTT connected ({reason_code}); subscribing to {TOPICS}")
    for t in TOPICS:
        client.subscribe(t, qos=1)


def on_message(client, userdata, msg):
    levels = msg.topic.split("/")
    if len(levels) <= MACHINE_LEVEL:
        return
    machine = levels[MACHINE_LEVEL]
    try:
        payload = json.loads(msg.payload.decode("utf-8", "replace"))
    except ValueError:
        payload = msg.payload.decode("utf-8", "replace")
    values = {}
    group = levels[-1] if len(levels) - MACHINE_LEVEL > 2 else ""
    if isinstance(payload, dict):
        numbers(group, payload, values)
    else:
        numbers(levels[-1], payload, values)
    if values:
        with lock:
            buffer.append({"machine": machine, "ts": int(time.time() * 1000), "values": values})


def sender():
    while True:
        time.sleep(SEND_EVERY)
        with lock:
            batch = buffer[:500]
        if not batch:
            continue
        try:
            r = requests.post(
                INGEST_URL,
                headers={"Authorization": f"Bearer {API_KEY}"},
                json={"readings": batch, "autoCreate": True},
                timeout=15,
            )
            if r.status_code < 300:
                with lock:
                    del buffer[: len(batch)]
                print(f"sent {len(batch)} readings: {r.json()}")
            elif r.status_code in (400, 401, 403):
                print(f"InduQT refused the batch ({r.status_code}): {r.text}")
                with lock:
                    del buffer[: len(batch)]  # fix the key or mapping; retrying will not help
            else:
                print(f"InduQT returned {r.status_code}; will retry")
        except requests.RequestException as e:
            print(f"network error, will retry: {e}")
        with lock:
            del buffer[:-5000]  # keep at most ~5,000 readings while offline


client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2, client_id="induqt-bridge")
if MQTT_USER:
    client.username_pw_set(MQTT_USER, MQTT_PASSWORD)
client.on_connect = on_connect
client.on_message = on_message
threading.Thread(target=sender, daemon=True).start()
client.connect(MQTT_HOST, MQTT_PORT, keepalive=60)
client.loop_forever()
Keep it running after a reboot (systemd)
Keep it running after a reboot (systemd)
# /etc/systemd/system/induqt-bridge.service  — keeps the bridge running after a reboot
[Unit]
Description=MQTT to InduQT bridge
After=network-online.target mosquitto.service

[Service]
User=pi
EnvironmentFile=/home/pi/induqt-bridge.env
ExecStart=/home/pi/induqt/bin/python3 /home/pi/induqt_bridge.py
Restart=always
RestartSec=10

[Install]
WantedBy=multi-user.target

# then:
# sudo systemctl daemon-reload && sudo systemctl enable --now induqt-bridge
# journalctl -u induqt-bridge -f

The bridge treats the topic level after factory/ as the machine (it matches the machine's externalId and creates it if needed), turns every number in the message into a signal (nested fields become names like temperature.tC; when the topic has a group after the machine, such as Shelly's status/em:0, the group goes in front, as em_0.a_current, so two channels never overwrite each other; true/false and on/off become 1/0) and sends a batch every 10 seconds. It was run end to end on 2026-10-31 against Mosquitto 2 with Shelly Gen2, Theengs and plain-number messages. Prefer Node-RED? The same bridge as a Node-RED flow is on the gateway guide.

Prompt
3 · Check it arrived and set what normal looks like
Show me the latest values InduQT has for p-101.
Then set these normal bands so InduQT knows what "normal" looks like: each signal’s normal operating range from the process data sheet.

On the Plant Monitor plan InduQT can also subscribe to a broker directly. Point your sensors (or a Mosquitto bridge) at a cloud broker with TLS, then ask Claude to "create an MQTT connector for this broker, machine from topic level 1", or add it in the app under the site → Connect data → MQTT broker.

OT security

  • Read-only: use read functions (Modbus FC 3/4) and a read-only OPC UA user.
  • Nothing inbound: the bridge makes outbound HTTPS calls only; no port is opened from the internet to the plant.
  • Follow your site’s OT rules (IEC 62443, and in Saudi Arabia the NCA Operational Technology Cybersecurity Controls). Put the PC in the DMZ if that is how your site does it.
  • If data must not leave the site, InduQT IQ Enterprise runs on your own servers with local AI models.

Frequently asked questions

Can I use a Shelly or ESP32 in Zone 2?

No. Equipment in any classified zone must be certified for it. Use certified instruments in the zone and keep cheap hardware in the safe area.

Does InduQT read WirelessHART directly?

No. It reads what the gateway publishes (OPC UA or Modbus TCP, through Node-RED if needed), or what the DCS already has.

Which makers did you check?

Emerson and Yokogawa, from their own pages on 31 October 2026. Others (ABB, Honeywell, Pepperl+Fuchs, Phoenix Contact) make certified wireless instruments and gateways too; check their gateway outputs and certificates the same way.

Related