Node-RED

Node-RED S7: Read Siemens PLC Variables and Publish to MQTT

Connect Node-RED to a Siemens S7 PLC with node-red-contrib-s7: enable PUT/GET in TIA Portal, address data block variables correctly, publish changes to MQTT and handle setpoint writes safely.

Node-RED S7 flow reading a Siemens PLC data block and publishing machine values to MQTT topics
Run this on your own MQTT brokerGet a private, managed MQTT broker on the Free plan and start from ready-made code samples.
Try it free Code samples No card required

Node-RED S7 flows are a practical way to get data out of Siemens S7-1200, S7-1500 and older S7-300/400 PLCs and into MQTT without changing the PLC program much. In this tutorial you will prepare the PLC in TIA Portal, read data block variables with node-red-contrib-s7, and publish them as JSON to an MQTT broker whenever they change. You will also add a guarded write path for setpoints and learn the addressing rules that cause most first-time failures.

How Node-RED Talks to an S7 PLC

node-red-contrib-s7 uses the S7 communication protocol (ISO-on-TCP, port 102) that Siemens engineering tools and HMIs also use. Node-RED acts as a client: it connects to the CPU, reads a list of addresses at a fixed cycle time, and optionally writes values back. No extra hardware or PLC licence is needed, but the CPU must allow this kind of access, which modern CPUs block by default.

Once the values are in Node-RED, MQTT takes over distribution. Instead of every dashboard, historian and analytics tool connecting to the PLC, one gateway reads it and a broker delivers named values to all subscribers. If you want a broader comparison of approaches, the guide to Siemens S7 and MQTT covers native PLC MQTT clients and commercial gateways as well.

Step 1: Prepare the PLC in TIA Portal

For S7-1200 and S7-1500 CPUs, make two changes and download them to the PLC:

  1. Permit PUT/GET. Open the CPU properties, go to Protection & Security → Connection mechanisms, and enable "Permit access with PUT/GET communication from remote partner".
  2. Disable optimized block access for every data block Node-RED should read. Right-click the data block, open Properties → Attributes, and clear "Optimized block access". Compile the block; it now shows fixed offsets for each variable.

The second step is what makes absolute addresses such as DB10,REAL0 meaningful. With optimized access, the PLC rearranges variables internally and the S7 protocol cannot address them by offset.

PUT/GET access is a security trade-off: any client that reaches port 102 can read and write those areas. Keep the PLC on an isolated OT network, let only the gateway reach it, and prefer a dedicated data block that contains just the values you want to expose.

S7-300 and S7-400 CPUs do not need these settings. Use rack 0 and slot 2 for them, instead of slot 1 for the newer families.

Step 2: Design a Data Block for the Gateway

Create a non-optimized data block, for example DB10 "GatewayData", and copy the values you want to publish into it from your program. A dedicated block keeps the interface stable even when the main program changes.

Variable Type Offset node-red-contrib-s7 address
temperature Real 0.0 DB10,REAL0
speed Int 4.0 DB10,INT4
running Bool 6.0 DB10,X6.0
partCount DInt 8.0 DB10,DINT8
setpoint Real 12.0 DB10,REAL12

Other areas use the same syntax: M0.0 and MW10 for markers, I0.0 and IW64 for inputs, Q0.1 for outputs, and DB10,S20.16 for a string at offset 20 with a maximum length of 16. Always take the offsets from TIA Portal's compiled data block, not from counting by hand.

Step 3: Install and Configure node-red-contrib-s7

Install the package:

cd ~/.node-red
npm install node-red-contrib-s7
# restart Node-RED afterwards

Add an s7 in node and create a new endpoint config:

  • Transport: Ethernet (ISO-on-TCP)
  • Address: the PLC's IP address, for example 192.168.0.10, port 102
  • Mode: Rack/Slot, rack 0, slot 1 (S7-1200/1500)
  • Cycle time: 1000 ms
  • Timeout: 2000 ms

On the Variables tab, add each row from the table above with a name and address. Then configure the s7 in node itself:

  • Mode: "All variables", which emits one message whose payload is an object with every variable.
  • Enable "Emit only when value changes" so unchanged cycles produce no messages.

Deploy and add a debug node. You should see payloads such as {"temperature":61.4,"speed":1480,"running":true,"partCount":20391,"setpoint":60}. The node status shows "online" when the connection is healthy.

Step 4: Shape the Data and Publish to MQTT

The raw object is almost ready for MQTT, but it is worth adding a timestamp, rounding floats, and building a topic that reflects the machine's place in the plant. A function node does it:

// Function node "s7 to mqtt"
const v = msg.payload;
if (typeof v !== "object" || v === null) {
    return null;
}

const round = (x, d) => Math.round(x * Math.pow(10, d)) / Math.pow(10, d);

msg.topic = "connectmq/demo/a7f3k2/plant1/line3/press2/telemetry";
msg.payload = {
    temperature: round(Number(v.temperature), 1),
    speed: Number(v.speed),
    running: v.running === true,
    partCount: Number(v.partCount),
    setpoint: round(Number(v.setpoint), 1),
    ts: Date.now()
};
return msg;

Wire it to an mqtt out node with an empty topic field. For testing, use broker.connectmq.com on port 1883 with a unique client ID; that is the public MQTT test broker. Replace a7f3k2 with your own random string, and never send real production data there, because anyone can subscribe.

Verify from a terminal:

mosquitto_sub -h broker.connectmq.com -p 1883 -i sub-s7-a7f3k2 -v \
  -t 'connectmq/demo/a7f3k2/plant1/#'

If you prefer one topic per variable, which suits Unified Namespace designs, select the "All variables, one per message" mode instead. Each message then carries the variable name in msg.topic, and the function node can append it to the machine path.

Also publish the gateway's own health. Add a status node scoped to the s7 in node and publish its text to .../press2/gateway/status with retain enabled. Subscribers can then tell the difference between "the machine stopped" and "the gateway lost the PLC".

Step 5: Write Setpoints from MQTT, With Guardrails

The s7 out node writes a single variable defined in the endpoint config. A safe command path looks like this:

  1. An mqtt in node subscribes to one dedicated topic, connectmq/demo/a7f3k2/plant1/line3/press2/setpoint/set. On a real system this must be a private, authenticated broker.
  2. A function node validates the value.
  3. An s7 out node configured for the setpoint variable writes it.
  4. The next read cycle publishes the new setpoint in telemetry, which acts as confirmation.
// Function node "validate setpoint" -> s7 out (variable: setpoint)
const MIN = 40.0;
const MAX = 80.0;
const value = Number(msg.payload);

if (!Number.isFinite(value) || value < MIN || value > MAX) {
    node.warn(`Rejected setpoint ${msg.payload}; allowed ${MIN}-${MAX}`);
    return null;
}

msg.payload = Math.round(value * 10) / 10;
node.status({ fill: "blue", shape: "dot", text: "wrote " + msg.payload });
return msg;

Keep the PLC in charge of safety. The PLC program should still clamp the setpoint and ignore it in modes where remote changes are not allowed. A gateway that writes directly into control logic without such checks in the PLC is an accident waiting to happen.

Troubleshooting Node-RED S7 Connections

Symptom Likely cause Fix
Connection timeout Wrong IP, firewall, port 102 blocked Ping the PLC and test port 102 from the gateway
Connected, but reads fail PUT/GET not permitted Enable it in CPU properties and download
"Address out of range" or wrong values Optimized block access still on Clear the attribute, recompile, use new offsets
Values look scrambled Offsets changed after editing the DB Re-read offsets from TIA Portal and update variables
Works on S7-1200, not on S7-300 Wrong slot Use rack 0, slot 2 for S7-300/400
PLC CPU load rises Cycle time too short, many scattered reads Group values in one DB, raise the cycle time

A quick port check from the gateway machine saves a lot of guessing:

# Linux: succeeds if port 102 is reachable
nc -zv 192.168.0.10 102

Production Checklist

  1. Use a dedicated, non-optimized data block as the gateway interface.
  2. Restrict network access to port 102 to the gateway only.
  3. Publish on change with a periodic heartbeat, not on every cycle.
  4. Publish gateway health as a retained status topic.
  5. Validate every write in Node-RED and again in the PLC.
  6. Use a private MQTT broker with TLS and per-gateway credentials.
  7. Version-control your flows so address changes are reviewed.

Where the Data Goes Next

With S7 values on MQTT, everything else becomes a subscriber: dashboards, OEE calculations, historians and cloud analytics. The same approach works for other controllers, which the Node-RED Modbus guide and the Node-RED OPC UA tutorial show for Modbus devices and OPC UA servers. For a wider view of getting controller data off the shop floor, read the PLC to cloud guide.

The gateway only connects outbound to the broker, which keeps the OT network closed to inbound traffic. A fully managed MQTT service such as ConnectMQ gives that gateway a TLS endpoint on port 8883, credentials generated in the portal, and IP blocking after repeated failed logins. ConnectMQ also recognises ISA-95 style topics like plant1/line3/press2/telemetry and lists devices by those levels, so you can see at a glance which machines are reporting. If you are still deciding on architecture, start with what an MQTT broker does, and see the Node-RED MQTT tutorial for the MQTT node basics.

Connect Your S7 Gateway to ConnectMQ

Sign up at app.connectmq.com with the free Serverless plan, no credit card required. Create a broker, paste the generated username, password and client ID into the Node-RED MQTT config node, enable TLS on port 8883, and drop the demo prefix. Your PLC values are then published privately and you can watch them arrive in the live topic explorer.

Frequently Asked Questions

Which Siemens PLCs work with node-red-contrib-s7?

It supports S7-1200 and S7-1500 as well as older S7-300 and S7-400 CPUs over Ethernet. The newer families need PUT/GET access enabled and non-optimized data blocks.

Why can't Node-RED read my S7-1500 data block?

The data block most likely uses optimized block access, which has no fixed offsets. Disable it in the block attributes, recompile, download and use the new offsets in Node-RED.

What rack and slot should I use for an S7 PLC?

Use rack 0 and slot 1 for S7-1200 and S7-1500 CPUs, and rack 0 and slot 2 for most S7-300 and S7-400 CPUs.

Is it safe to write PLC values from MQTT?

Only with guardrails: a dedicated command topic on an authenticated broker, validation in Node-RED, and range checks in the PLC program itself.

Related articles

Node-RED OPC UA Client to MQTT with ISA-95 Topics

Use node-red-contrib-opcua to subscribe to OPC UA variables on a PLC or simulation server, map node IDs to ISA-95 style MQTT topics, and publish values with quality and timestamps.

9 min read

All articles →

Your private MQTT broker is a few clicks away

Create an account, get your broker credentials and connect your first device — no servers to install, patch or scale.