sondahub / HART-IP simulator
HART-IP simulator
A HART-IP server with a HART 7 loop behind it — pressure, temperature, Coriolis flow, radar level and a valve positioner — answering the commands a host sends, with cold start and configuration changed kept per master, units that convert, burst mode, and a radar that loses its echo now and then.
HART-IP messages over WebSocket, or JSON. Not a registered HART device; every connection gets its own loop.
Connect
- Address
- wss://api.sondahub.com/hart-ip
- Binary messages
- HART-IP: version, message type, message id, status, sequence number, byte count, body
- Text messages
- JSON, answered in JSON (the session opens by itself)
- Subprotocol
- hart-ip (optional)
Open with Session Initiate (message id 0: master type, then the inactivity close timer in milliseconds — kept between 10 seconds and 10 minutes, status 8 when it was moved); then token-passing PDUs (message id 3), Keep Alive (2) and Session Close (1). A PDU before the session is answered with an Error message, status 16. The loop closes the connection when the inactivity timer runs out.
→ 01 00 00 00 00 01 00 0D 01 00 00 EA 60 # primary master, 60 s ← 01 01 00 00 00 01 00 0D 01 00 00 EA 60 → 01 00 03 00 00 02 00 0D 02 80 00 00 82 # STX, short address 0, command 0 ← 01 01 03 00 00 02 00 25 06 80 00 18 00 20 FE 3A 01 …
The loop
| Device | Dynamic variables | Range |
|---|---|---|
| PT-101 Pressure transmitter polling 0 · 3a015a0101 | PV Pressure (kPa) SV Sensor temperature (°C) | 0–1000 kPa |
| TT-102 Temperature transmitter polling 1 · 3a025a0102 | PV Process temperature (°C) SV Terminal temperature (°C) | 0–200 °C |
| FT-103 Coriolis flow meter polling 2 · 3a035a0103 | PV Mass flow (kg/h) SV Density (kg/m³) TV Temperature (°C) QV Mass total (kg) | 0–20000 kg/h |
| LT-104 Radar level gauge polling 3 · 3a045a0104 | PV Level (m) SV Distance (m) TV Volume (m³) QV Echo quality (%) | 0–12 m |
| FV-105 Valve positioner polling 4 · 3a055a0105 | PV Valve position (%) SV Setpoint (%) TV Supply pressure (bar) | 0–100 % write protected |
Reach a device by its polling address in a short frame (delimiter 0x02) or by its 38-bit unique address in a long frame (0x82). Short frames work with every command here, as on HART 5-era devices; a strict HART 7 device takes only command 0 that way. Commands 11 and 21 go to the broadcast address and only the device with that tag answers. A frame with a wrong checksum is answered with the communication error byte 0x88; an address nobody has gets no answer at all.
LT-104 loses its echo for 30 seconds every 10 minutes: its level, distance and volume hold the last good value with status 0x30, the device sets more status available, and command 48 shows bit 0 of the first device-specific byte. FV-105’s write-protect switch is on: every write answers response code 7.
Commands
| Cmd | Name | Notes |
|---|---|---|
| 0 | Read Unique Identifier | Expanded device type, revisions, device id, manufacturer, configuration change counter — what a host polls for first. |
| 1 | Read Primary Variable | PV units and value. |
| 2 | Read Loop Current and Percent of Range | |
| 3 | Read Dynamic Variables and Loop Current | The loop current, then PV, SV, TV and QV with their units — as many as the device has. |
| 6 | Write Polling Addresswrite | Moves the device on the loop (0–63); a polling address another device has is refused. |
| 7 | Read Loop Configuration | |
| 8 | Read Dynamic Variable Classifications | |
| 9 | Read Device Variables with Status | Up to eight device variables (or 244 percent of range, 245 loop current, 246–249 PV–QV), each with classification, units, value and status, and a timestamp. |
| 11 | Read Unique Identifier Associated With Tag | To the broadcast address: only the device with that tag answers. |
| 12 | Read Message | 32 characters, packed ASCII. |
| 13 | Read Tag, Descriptor, Date | |
| 14 | Read Primary Variable Transducer Information | Sensor limits and minimum span. |
| 15 | Read Device Information | Alarm selection, transfer function, range values, damping, write protect. |
| 16 | Read Final Assembly Number | |
| 17 | Write Messagewrite | |
| 18 | Write Tag, Descriptor, Datewrite | |
| 19 | Write Final Assembly Numberwrite | |
| 20 | Read Long Tag | 32 characters, ISO Latin-1. |
| 21 | Read Unique Identifier Associated With Long Tag | To the broadcast address. |
| 22 | Write Long Tagwrite | |
| 33 | Read Device Variables | Up to four, with units. |
| 34 | Write Primary Variable Damping Valuewrite | Seconds, 0–60. |
| 35 | Write Primary Variable Range Valueswrite | Units, upper and lower range value — checked against the sensor limits. |
| 38 | Reset Configuration Changed Flag | For the master that asks; with the configuration change counter, which must match. |
| 40 | Enter/Exit Fixed Current Mode | A loop current in mA (3.8–21), or 0 to leave fixed mode. |
| 41 | Perform Self Test | |
| 42 | Perform Device Reset | The device restarts: cold start again for both masters, fixed current dropped. |
| 44 | Write Primary Variable Unitswrite | The PV and its range convert to the new units. |
| 48 | Read Additional Device Status | Device-specific status, extended status, standardized status. |
| 59 | Write Number of Response Preambleswrite | |
| 103 | Write Burst Periodwrite | In 1/32 ms; shorter than half a second is set to half a second (response code 8). |
| 108 | Write Burst Mode Command Numberwrite | 1, 2, 3, 9, 33 or 48. |
| 109 | Burst Mode Controlwrite | 0 off; 1–4 on. The device then publishes on its own, as HART-IP Publish messages. |
Anything else answers response code 64, command not implemented.
Status, as a host has to read it
Every answer carries the response code and the field device status byte. The flags are kept per master: cold start is set for each master until that master’s first answer, configuration changed is set by any write — and the configuration change counter goes up — until that master sends command 38 with the counter. Writing as the primary leaves the secondary to see the flag too.
| Bit | Field device status |
|---|---|
| 0x80 | device malfunction |
| 0x40 | configuration changed |
| 0x20 | cold start |
| 0x10 | more status available |
| 0x08 | loop current fixed |
| 0x04 | loop current saturated |
| 0x02 | non primary variable out of limits |
| 0x01 | primary variable out of limits |
| Response code | Meaning |
|---|---|
| 0 | Success |
| 2 | Invalid selection |
| 3 | Passed parameter too large |
| 4 | Passed parameter too small |
| 5 | Too few data bytes received |
| 6 | Device-specific command error |
| 7 | In write protect mode |
| 8 | Warning: set to nearest possible value |
| 9 | Range or counter error |
| 10 | Lower range value too low |
| 11 | Upper range value too high |
| 12 | Upper range value too low |
| 14 | Warning: span too small |
| 16 | Access restricted |
| 32 | Busy |
| 64 | Command not implemented |
Unit codes the devices use (HART common table 2): 6 psi · 7 bar · 8 mbar · 11 Pa · 12 kPa · 32 °C · 33 °F · 35 K · 39 mA · 41 L · 43 m³ · 44 ft · 45 m · 47 in · 48 cm · 49 mm · 57 % · 61 kg · 63 lb · 73 kg/s · 74 kg/min · 75 kg/h · 78 t/h · 82 lb/h · 91 g/cm³ · 92 kg/m³ · 237 MPa. Command 44 converts the PV and its range to the new unit; command 35 takes a unit with the range.
Burst mode
Command 108 picks what to publish (1, 2, 3, 9, 33 or 48), command 103 how often (in 1/32 ms; half a second at least), command 109 turns it on. The device then publishes on its own: HART-IP Publish messages (message type 2, id 3) carrying a burst frame — delimiter 0x81, the long address with the burst bit set. Answers from a device in burst mode carry the burst bit too.
JSON, from any WebSocket client
Address a device by address (polling), tag or long (the unique address in hex); give a write its fields by name or its data in hex. Answers come decoded, with the status bits by name and both frames in hex; burst publishes arrive as {"type":"publish",…}.
{"command":0,"address":2}
{"command":3,"tag":"FT-103"}
{"command":9,"tag":"LT-104","variables":[0,246,245]}
{"command":44,"tag":"PT-101","units":"bar"}
{"command":35,"tag":"TT-102","units":"°F","upper":400,"lower":32}
{"command":109,"tag":"PT-101","burst":true}
{"command":1,"long":"3a015a0101","master":"secondary"}
From a tool that only speaks TCP
HART-IP hosts and device managers open a TCP connection to port 5094. The bridge is a small Node script that runs on your computer, listens on the usual TCP port and carries each message to the hub over WebSocket — nothing else happens in it, and nothing is installed:
curl -O https://sondahub.com/industrial/bridge.mjs node bridge.mjs hart # then point the tool at localhost, port 5094
Questions
Can a HART-IP host or a device manager connect?
Through the bridge: it listens on localhost port 5094 (HART-IP over TCP) and carries each message to the loop over WebSocket. Hosts that use HART-IP over UDP need a TCP connection instead.
Why does nothing answer at polling address 7?
Because no device has it, and on a real loop nobody answers an empty address: the host times out. The JSON form says so at once instead; the binary form leaves you to wait, as a loop would.
Is this a registered HART device?
No. The HART specifications belong to FieldComm Group; this follows the public shape of HART 7 and HART-IP for testing hosts, with made-up manufacturer and device type ids. It is not registered, tested or certified.