Configuring Node-RED Modbus RTU RS485 on Siemens IOT2000

David Krause11 min read
ModbusSiemensTutorial / How-to
Licensed PE Working through this on a live machine? A Maine-licensed engineer can take it from here — included with IMD hardware, by the hour for everything else. Book an engineer

Configuring Node-RED Modbus RTU RS485 on Siemens IOT2000

The Siemens SIMATIC IOT2000 (IOT2020 / IOT2040) is a fanless industrial gateway that runs a Yocto-based Linux image. Because Node-RED ships pre-installed on the example image, the platform is a popular choice for serial Modbus RTU collection from energy meters, inverters, and motor drives. This guide covers the complete flow: changing the onboard X30 port from RS232 to RS485, installing the node-red-node-serialport and node-red-contrib-modbus palettes, configuring a master flow, mapping common energy-meter registers, and verifying the read traffic.

Compatibility window: The official IOT2000 V2.2 example image ships Node.js 6.x. The node-red-contrib-modbus 3.6.x branch is the last release that compiles cleanly against Node 6. Later versions (4.x and 5.x) require Node 10+ and will throw node-gyp rebuild errors on V2.2. If you are on V2.4 or later, the bundled Node.js is newer and any 5.x release is acceptable.

1. Prerequisites

Confirm the following before beginning the install.

  • Hardware: SIMATIC IOT2020 or IOT2040, microSD card with the IOT2000 example image (V2.2.x or V2.4.x), 24 V DC supply, and an Ethernet or Wi-Fi connection for the IOT2000 to reach the internet.
  • Energy meter: Any Modbus RTU slave that exposes a 1-15 Modbus address on a half-duplex RS-485 bus. Typical candidates include the Eastron SDM120 / SDM630, Carlo Gavazzi EM340, Schneider iEM3155, ABB M2M, Orno OR-WE-516, or the Yada DCM6636.
  • Bus topology: Two-wire shielded twisted pair from IOT2000 X30 pin 1 (D+) and pin 2 (D-) to the meter A and B terminals. A 120 Ω termination resistor at the bus end is required when the cable run exceeds 10 m or the baud rate is 38400+.
  • Access: SSH terminal (PuTTY on Windows, ssh on Linux/macOS) with root privileges on the IOT2000, or a keyboard/monitor connected directly to the device.

2. Hardware Configuration: Reassigning X30 to RS485

The IOT2000's X30 connector exposes one serial port that can be software-selected as either RS-232 or RS-485. By default the example image routes it to RS-232. The reassignment is performed through the iot2000setup console tool.

  1. Log in to the IOT2000 as root. The default password is blank on older images; change it immediately with passwd.
  2. Run iot2000setup from the command line.
  3. Navigate to Serial Port Configuration.
  4. Set Port to X30 and Mode to RS485.
  5. Save and exit. The tool writes the change to /etc/iot2000setup.conf and prompts for a reboot.
  6. Reboot with reboot and confirm with dmesg | grep tty that the device is enumerated as /dev/ttyS2 or /dev/ttyS1 (depends on firmware variant).
Pinout reference (X30): Pin 1 = D+/A, Pin 2 = D-/B, Pin 3 = GND, Pin 4 = +5 V (50 mA max). Only pins 1, 2, and 3 are needed for a passive two-wire bus. Do not tie the cable shield to GND at both ends; ground one end only to avoid ground loops.

3. Setting the System Clock

Node-RED V0.x and Node.js V6 verify TLS certificates when contacting the npm registry. If the IOT2000 has not been online for a long period, its real-time clock drifts. Without a valid date, every npm install fails with ERR_CERT_DATE_INVALID. Set the date manually before any npm call.

date -s "21 DEC 2017 17:00:00"

For a persistent solution, enable NTP:

systemctl enable systemd-timesyncd
timedatectl set-ntp true

4. Installing the Node-RED Modbus and Serial Palettes

Two palettes are required. The base node-red-node-serialport provides the serial in, serial out, and serial request nodes. The node-red-contrib-modbus palette adds the Modbus Read, Modbus Write, Modbus Response, and Modbus Server nodes. Install them in this exact order; the Modbus build invokes node-gyp against the serialport native module.

  1. npm install node-red-node-serialport
  2. npm -g install node-gyp
  3. npm install -g [email protected] --unsafe-perm --build-from-source (use this pinned version on the V2.2 image; omit the version pin on V2.4+)
  4. reboot

The --unsafe-perm --build-from-source flags force a native compilation against the on-target Node.js headers, which avoids a pre-built binary that targets the wrong architecture. If installation is silent and the Modbus nodes are absent from the palette, the npm cache is probably stale. Clear it and retry:

npm cache clean --force
rm -rf ~/.npm
cd /usr/lib/node_modules
npm install [email protected] --unsafe-perm --build-from-source

After reboot, restart Node-RED and verify the Modbus nodes appear in the palette:

systemctl restart node-red
journalctl -u node-red -f

5. Wiring the Master Flow in Node-RED

Create a flow with one Modbus Read node followed by a debug node.

  1. Drag in a Modbus Read node from the Modbus palette.
  2. Double-click the node. Configure a Modbus Client by clicking the pencil icon next to Server.
  3. Set the following client parameters:
    • Type: Serial (RTU)
    • Serial Port: /dev/ttyS2 (or whatever dmesg shows after the X30 reassignment; if you use a USB-RS485 dongle it will be /dev/ttyUSB0)
    • Baud rate: 9600 (default for most energy meters; some Carlo Gavazzi units run at 19200 or 38400)
    • Data bits: 8
    • Stop bits: 1
    • Parity: None
    • Unit-Id: 1 (must match the meter's Modbus address)
  4. On the Modbus Read node itself set:
    • FC: FC 3 - Read Holding Registers
    • Address: 0
    • Quantity: 2 (typical for a 32-bit energy register pair)
    • Poll rate: 1000 ms (do not poll faster than 250 ms on a half-duplex bus)
  5. Wire the output to a debug node with Output set to complete msg object and deploy.

If the meter is wired correctly and the slave address matches, the debug panel will populate with a payload array of two 16-bit values, and the node status indicator will turn green with the response time in milliseconds.

6. Common Energy-Meter Register Map

Most single-phase and three-phase energy meters use FC 3 (holding registers) or FC 4 (input registers) at the offsets below. Always confirm against the vendor's Modbus protocol guide before commissioning.

Meter family FC Address Quantity Value Scaling
Eastron SDM120 4 0 2 Voltage L-N x 0.1 V
Eastron SDM120 4 6 2 Current x 0.001 A
Eastron SDM120 4 12 2 Active power x 0.1 W
Eastron SDM120 4 72 2 Total energy (kWh) x 0.01 kWh
Eastron SDM630 4 0 2 Line 1-N Voltage x 0.1 V
Eastron SDM630 4 12 2 Total system power x 0.1 W
Eastron SDM630 4 342 4 Total energy (64-bit) x 0.001 kWh
Carlo Gavazzi EM340 4 0x0000 2 Voltage L1 x 0.1 V
Carlo Gavazzi EM340 4 0x0010 2 Total active power x 0.1 W
Schneider iEM3155 3 0x0BB8 (3000) 2 Energy kWh + x 1 Wh
Orno OR-WE-516 4 0 2 Voltage x 0.1 V
Yada DCM6636 4 0x0000 2 Voltage x 0.1 V

To combine the two 16-bit words into a 32-bit IEEE 754 value inside Node-RED, use a function node after the Modbus Read:

const words = msg.payload;
const value = (words[0] << 16 | words[1]);
msg.payload = value * 0.1;   // adjust scaling per register
return msg;

For unsigned 32-bit energy totals, the same shift works because the high word is little-endian in the Modbus response. For signed values, apply a two's-complement check:

let v = (words[0] << 16 | words[1]);
if (v & 0x80000000) v -= 0x100000000;
msg.payload = v * 0.1;
return msg;

7. Bus Termination and Shielding

RS-485 is a multi-drop differential bus. The following rules keep the IOT2000 link stable on industrial sites.

  • Termination: Place a 120 Ω resistor across A and B at each physical end of the bus. The IOT2000 has no built-in termination; the meter end (or last device) must terminate.
  • Bias: If no device is supplying failsafe bias, add a 680 Ω pull-up to +5 V on A and a 680 Ω pull-down to GND on B at the master end. Some DIN-rail gateways provide this; the bare IOT2000 does not.
  • Shielding: Use shielded twisted pair (Belden 3106A or equivalent). Ground the shield at the panel end only, not at the meter end.
  • Max nodes: 32 unit loads per segment. The IOT2000 presents one unit load; each meter typically one or one-half. Above 32 units, add a repeater.
  • Distance: Up to 1200 m at 9600 baud, dropping to 100 m at 115200 baud. Industrial energy metering should never exceed 19200 baud to keep the noise margin acceptable.

8. Verification Procedure

  1. Confirm the X30 port: stty -F /dev/ttyS2 -a | grep -E 'speed|crtscts' - the speed must match the meter and CRTSCTS must be off for software-managed half-duplex.
  2. Confirm the slave responds with a raw Modbus poll. From a shell on the IOT2000 use the modpoll utility (Libre Modbus) or mbpoll:
    mbpoll -m rtu -b 9600 -P none -s 1 -a 1 -r 0 -c 4 /dev/ttyS2
    If this returns registers, the bus and meter are healthy and the problem is at the Node-RED layer.
  3. Confirm Node-RED sees the slave by tailing the log while you deploy:
    journalctl -u node-red -f | grep -i modbus
    Look for lines such as ModbusRTU 127.0.0.1:1 connected or ModbusRTU ERROR.
  4. Inspect the live debug output in the browser. The payload array should contain two numeric values and the responseBuffer buffer should be exactly 9 bytes (1 + 1 + 1 + 2 + 2 + 2 CRC). Any other size indicates an address or quantity mismatch.
  5. Check the slave address with FC 17 (Report Server ID) if your meter supports it. A mis-addressed poll returns Illegal Data Address (0x02) or Illegal Slave Address (0x0A); FC 17 returns the device serial number, telling you the address is correctly bound.

9. Troubleshooting Matrix

Symptom Likely root cause Corrective action
Modbus node not visible in palette after install Wrong directory or cached binary mismatch Install inside /usr/lib/node_modules and restart node-red. Clear ~/.npm first.
Cannot read property 'registerForModbus' of null on first inject Modbus node failed to load because of a JS / Node version mismatch Pin [email protected] on V2.2; upgrade image to V2.4+ for newer versions.
npm install fails with certificate error Real-time clock is far in the past Set the system date with date -s before any npm command.
Status shows ModbusRTU disconnected Wrong device node, or RS-485 not selected Verify /dev/ttyS2 with stty; rerun iot2000setup.
Status shows connected but payload is empty Wrong Unit-Id, wrong FC, or wrong register address Cross-check with mbpoll and consult the meter manual.
Values are zero even though the meter is powered A and B reversed Swap A and B at the meter terminal; some meters label them A+/B-.
Intermittent timeouts during heavy CPU load Inter-character gap too tight, poll rate too high Reduce poll rate to 1000 ms+; set Modbus client option Inter-Frame Timeout to 100 ms.
No carrier detect on RS-485 X30 still in RS-232 mode Rerun iot2000setup and reboot; verify with cat /etc/iot2000setup.conf.
Node-RED crashes on deploy Two Modbus clients bound to the same port Reuse a single Modbus Client across all read/write nodes.

10. Hardware Alternatives and Extension

The same flow runs unchanged on a Raspberry Pi 4, a Beckhoff CX-series embedded controller, or a B&R X20 edge device - the only difference is the device node and the install commands. When the bus is large (more than eight slaves) or runs at a long distance, place a Wago 750-652 or Phoenix Contact PSI-MODEM-RS485 between the IOT2000 and the field to act as a galvanic isolator and repeater. For DIN-rail signal conditioning the Phoenix Contact Interface Catalog lists the FL BT EPA 2 Bluetooth-to-RS485 coupler for wireless commissioning, and the Weidmüller ACT20C gateway for serial-to-Ethernet bridging into a higher-level SCADA.

When the project has to be expanded to OPC UA, the node-red-contrib-opcua palette (or an in-line function node wrapping the Modbus registers into Information Models) can publish each tag into a Beckhoff TwinCAT Analytics, a Siemens WinCC Unified, or an Ignition SCADA server. For cloud forwarding, the node-red-contrib-modbustcp palette wraps the RTU data into a TCP client that a Modbus TCP master can subscribe to across VLAN boundaries.

11. Frequently Asked Questions

Why does the Modbus node fail to install on the IOT2000 V2.2 image?

The V2.2 image ships Node.js 6.x, which is no longer supported by current node-red-contrib-modbus releases. Pin the install to 3.6.1 with the flags --unsafe-perm --build-from-source. Newer images (V2.4+) ship a newer Node.js and can install the latest palette version.

How do I confirm the X30 port is in RS-485 mode?

Run cat /etc/iot2000setup.conf and look for the line x30_mode=RS485. You can also probe the bus: with RS-232 selected the IOT2000 drives ±12 V on pins 1 and 2, while in RS-485 it only biases the bus when the driver is enabled. The setting survives reboots.

What baud rate and framing should I use for a typical energy meter?

9600 8N1 is the de-facto default for Eastron, Orno, and most residential meters. Carlo Gavazzi EM3xx units default to 19200 8N1; Schneider iEM3155 uses 19200 8E1. Always cross-check the meter's Modbus protocol guide and set the matching framing on the Modbus Client node.

Can multiple meters share the same RS-485 bus?

Yes - up to 32 unit loads per segment, each addressed uniquely. Configure a single Modbus Client in Node-RED and parameterise the Unit-Id field per Modbus Read node. Add a 120 Ω termination at each physical end of the bus and a 680 Ω bias network if the slaves are bias-less.

How do I interpret a 32-bit energy register from two 16-bit words?

The Modbus response returns the low word first. Combine them with (words[0] << 16) | words[1] in a function node and apply the meter-specific scaling. For signed values, detect the high bit and subtract 0x100000000 when set. Test against the meter's local display to confirm the conversion.

Back to blog