SIMATIC IOT2050 to S7-1200 List Data Transfer via Node-RED

David Krause12 min read
S7-1200SiemensTutorial / 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

1. System Overview

The SIMATIC IOT2050 (and its predecessor IOT2020) is a rugged industrial gateway that bridges shop-floor devices with cloud or higher-level systems. The S7-1200 is a compact PLC programmed with TIA Portal. Together they form a common pattern:

  • The IOT2050 runs a Linux-based Industrial OS (Debian-based) and executes Node-RED flows.
  • Node-RED communicates with the S7-1200 over native S7 communication using the node-red-contrib-s7 node (also known as node-s7 for the underlying library).
  • A typical use case is to push a record (a "list") of values from a sensor, MES, or MQTT broker into the PLC, where the PLC program can then index, sort, and react to the data.

This reference covers the exact syntax to write structured lists (strings, character arrays, integer arrays) to an S7-1200 data block from Node-RED and the standard techniques used in the S7-1200 to parse, sequence, and consume those records.

Note: The IOT2020 (Intel Galileo Gen 2) reached end of life and was replaced by the IOT2050 (ARK-2250L or similar x86 platform) running a 64-bit Linux. The S7 communication stack and Node-RED configuration steps are identical between the two platforms.

2. Prerequisites

Component Requirement
SIMATIC IOT2050 Industrial OS image (V1.4 or later) or Debian Buster, Node.js 14+, Node-RED 2.x or 3.x
S7-1200 CPU Firmware V4.2 or later (V4.4+ recommended for full S7 node compatibility). Tested on CPU 1214C DC/DC/DC and 1215C.
TIA Portal V15.1 or later (V17/V18 used in this guide)
Node-RED palette node-red-contrib-s7 v3.x (depends on node-s7 library)
Network Both devices on the same subnet; PG/PC, IOT2050, and S7-1200 reachable on TCP/102 (ISO-on-TCP / TPKT)
CPU Protection PUT/GET access permitted (Configuration in TIA Portal)

Reference: SIMATIC IOT2050 product page and the S7-1200 System Manual on the Siemens Industry Online Support portal.

3. S7-1200 Data Block Configuration in TIA Portal

The S7-1200 stores all data exchanged with Node-RED in a global Data Block (DB). The DB must be configured so that the S7 communication stack can reach its addresses.

3.1 Create the DB

  1. In the TIA Portal project tree, expand Program Blocks.
  2. Add a new Data Block (e.g. DB_IOT_Data, number 10).
  3. Disable Optimized block access if you want byte-exact addressing. With optimized blocks, the S7 node still works but you must use the symbolic names that TIA Portal shows in the DB's offset column.
  4. Define the variables used by Node-RED.

3.2 Recommended DB Layout for List Transfer

Symbolic Name Type Length (bytes) Purpose
qty UInt 2 Quantity / record counter
name String 32 (2 header + 30 chars) Name field, e.g. "John"
regNumber DInt 4 Register number
listPayload String[254] 256 (2 header + 254 chars) Bulk JSON or CSV payload
orderBuffer Array[0..9] of String[30] 10 × 32 = 320 Order history (10 records)
writeTrigger Bool 0.0 Rising edge = new record available
STRING storage format in S7-1200: A String[n] occupies n + 2 bytes. The first byte holds the actual length, the second byte is the maximum length (set automatically by the compiler), and the remaining n bytes hold the ASCII characters. A String[30] therefore consumes 32 bytes even when the string is empty.

4. S7-1200 PUT/GET and Security Settings

Native S7 communication (used by node-red-contrib-s7) requires the S7-1200 to accept PUT/GET operations from a partner without a configured S7 connection. The setting is in the CPU properties:

  1. Open the CPU device configuration in TIA Portal.
  2. Select Properties → Protection & Security → Connection mechanisms.
  3. Check Permanent access via PUT/GET communication from remote partner (PLC, HMI, OPC, ...) enabled.
  4. Compile and download the hardware configuration to the CPU.

Reference: see the S7-1200 System Manual on Siemens Industry Online Support for the exact location of the PUT/GET toggle across firmware versions.

Security: PUT/GET does not authenticate the partner. On a plant network, restrict the IOT2050 IP and the PLC IP at the firewall/switch level, and avoid exposing the S7-1200 to the office network. If authentication is required, use OPC UA on the S7-1200 (firmware V4.4+) or the S7 communication over a configured connection.

5. Node-RED S7 Node Installation

  1. SSH into the IOT2050 (ssh iotuser@<ip>).
  2. Open Node-RED in a browser at http://<iot2050>:1880.
  3. Menu → Manage palette → Install.
  4. Search for node-red-contrib-s7 and click Install.
  5. Restart Node-RED after install.

The node provides two palette entries: s7 in (read from PLC) and s7 out (write to PLC). Drag the s7 out node onto a flow and configure it:

  • IP Address: the S7-1200 IP (e.g. 192.168.0.10).
  • Port: 102 (default).
  • Rack / Slot: 0 / 1 (typical S7-1200 setup).
  • Variable address: see the addressing syntax below.

6. S7 Addressing Syntax for Strings and Arrays

The node-red-contrib-s7 node uses a compact address string per variable. Common forms relevant to list transfer:

Address Meaning Storage on PLC side
DB10,S20.30 Single STRING in DB10 at byte offset 20, max length 30 32 bytes (2 header + 30 chars)
DB10,S20.30.3 Array of 3 STRING[30] elements in DB10 starting at offset 20 3 × 32 = 96 bytes
DB10,C22.30 CHAR array (no length byte) at offset 22, length 30 30 bytes
DB10,B0 BYTE at offset 0 1 byte
DB10,INT2 INT at byte offset 2 2 bytes (little-endian)
DB10,REAL4 REAL at byte offset 4 4 bytes
DB10,DINT6 DINT at byte offset 6 4 bytes
DB10,X0.0 BOOL at byte 0, bit 0 1 bit

Two common patterns for "list data":

  • Pattern A — single large STRING carrying a JSON or CSV payload: The S7-1200 receives one block of text and parses it on the PLC using string functions.
  • Pattern B — array of STRINGs (multiple records): The IOT2050 writes a record at a known index; the PLC increments an index on each new record.

7. Building a List/Record in Node-RED

7.1 Pattern A — One JSON String per Sensor Event

Construct a JSON object in a function node, then write it to a STRING[254] in DB10.

// Build a record from a sensor or MQTT message
const record = {
    QTY: 2,
    NAME: "John",
    RegisterNumber: 10
};

msg.payload = JSON.stringify(record);
// Sends a STRING[254] (256 bytes) to DB10, offset 6
msg.topic = "DB10,S6.254";
return msg;

Wire the function node output to an s7 out node configured with that address. The full DB10 layout used in this example:

Offset Symbol Type Bytes
0.0 writeTrigger BOOL 0.0
2 qty UINT 2
4 regNumber DINT 4
6 listPayload STRING[254] 256

7.2 Pattern B — Multi-Record Order Buffer

Use a function node to append incoming records into a JS array, then push the array to the S7-1200 as a STRING[254] of comma-separated values (CSV). The PLC splits the CSV and stores each token in orderBuffer[i].

// Maintain a rolling 10-record list
let list = context.get("list") || [];
list.push(msg.payload);    // e.g. {QTY:1, NAME:"John", REG:10}
if (list.length > 10) list.shift();
context.set("list", list);

// Convert to CSV: QTY,NAME,REG;QTY,NAME,REG;...
msg.payload = list.map(o => [o.QTY, o.NAME, o.RegisterNumber].join(",")).join(";");
msg.topic = "DB10,S6.254";   // listPayload STRING[254]
return msg;

With S7-1200 STRING max 254 chars, ten short records of ~25 bytes each fit comfortably. For longer records, switch to CHAR arrays (DB10,C6.512) and parse byte-by-byte on the PLC.

7.3 Triggering and Acknowledgement

Write a rising edge to DB10,X0.0 in the same s7 out call so the S7-1200 program knows new data is available:

// In the same function node, after building msg.payload:
msg.trigger = true;
return [msg, { payload: true, topic: "DB10,X0.0" }];

Connect the second output to a second s7 out node. The PLC acknowledges by clearing the trigger bit in its own scan.

8. Receiving and Parsing Strings in the S7-1200

The S7-1200 receives the string as one continuous STRING. The PLC program is responsible for splitting fields. Standard library instructions to use:

  • FC / FB — custom split routine using SCL (Structured Control Language): scan for the delimiter (; for record, , for field) and copy each token to a STRING element of orderBuffer.
  • Standard > String + Char instructions: LEFT, RIGHT, MID, FIND, DELETE, INSERT.
  • Type conversion: STRING_TO_INT, STRING_TO_DINT for numeric fields once the substring is isolated.

8.1 Example SCL Split Routine

Add the following FB to the S7-1200 program (TIA Portal → Program Blocks → Add new FB in SCL). This reads listPayload from DB10 and fills orderBuffer[0..9] with up to 10 records.

FUNCTION_BLOCK "FB_ParseList"
VAR
    iPos      : INT;
    iField    : INT;
    sRow      : STRING[254];
    sField    : STRING[32];
    chDelim   : CHAR := ';';   // record separator
    chField   : CHAR := ',';   // field separator
END_VAR
BEGIN
    // Copy incoming payload into a working string
    sRow := "DB_IOT_Data".listPayload;
    iField := 0;
    FOR iPos := 1 TO 10 DO
        // Extract substring up to next record separator
        IF FIND(sRow, chDelim) > 0 THEN
            sField := LEFT(sRow, FIND(sRow, chDelim) - 1);
            sRow   := DELETE(sRow, 1, FIND(sRow, chDelim));
        ELSE
            sField := sRow;
            sRow   := '';
        END_IF;

        // Split sField into QTY, NAME, REG and assign to orderBuffer
        // (omitted for brevity; use FIND and MID against chField)

        // For this example, store the full record:
        "DB_IOT_Data".orderBuffer[iPos - 1] := sField;
    END_FOR;

    // Clear the trigger bit to acknowledge receipt
    "DB_IOT_Data".writeTrigger := FALSE;
END_FUNCTION_BLOCK

Call this FB in OB1 on the rising edge of DB_IOT_Data.writeTrigger. Use a positive-edge detector (-|P|- on a contact, or R_TRIG in SCL) so each trigger is processed exactly once.

8.2 Storing the List on the PLC Side

The PLC keeps the order in orderBuffer[0..9], a 10-element STRING[30] array. To retrieve a record on demand, address it by index. To append a new record, increment a circular index modulo 10. This gives the PLC complete control over history without re-asking Node-RED.

Best practice: Keep the master list in Node-RED's context store, but mirror the last 10 records on the PLC. The PLC is authoritative for the consumer side (HMI, SCADA); Node-RED is authoritative for the producer side (MES, sensors, MQTT).

9. Sequencing Records by Sensor Input

The original question asked whether a string can be stored "in an orderly manner such that I will be able to give the output depending on a input from the S7-1200." The answer is yes: route by sensor input on the PLC side.

9.1 Architecture

  1. The IOT2050 listens to MQTT, an OPC UA server, or another source, and assembles the record set.
  2. On every update from that source, Node-RED writes the latest payload to DB10.listPayload and pulses writeTrigger.
  3. The S7-1200 parses the payload, increments a record counter, and stores the entry in orderBuffer[recordIndex MOD 10].
  4. A second trigger from the sensor (e.g. an HMI button or a physical input) causes the PLC to read orderBuffer[requestedIndex] and display it on the HMI or push it via OPC UA.

9.2 Re-Request on Demand

If the HMI or a higher-level system wants a specific record, the PLC reads it directly from the DB. If the consumer is Node-RED itself, use the s7 in node to poll the S7-1200:

// s7 in node address: DB10,S0.30.10  (array of 10 STRING[30])
// poll every 1000 ms
// output msg.payload is an array of 10 strings

10. Verification and Diagnostics

  1. Watch the trigger bit in TIA Portal: Open Online & diagnostics → Watch table. Monitor DB_IOT_Data.writeTrigger, DB_IOT_Data.listPayload, and one element of orderBuffer. Trigger an update from Node-RED; the watch table should refresh within < 100 ms on a healthy network.
  2. Enable the S7 node's debug output: Set the s7 out node to log on every write. The Node-RED debug sidebar will show the resolved payload bytes sent on TCP/102.
  3. Wireshark on TCP/102: If communication is intermittent, capture the S7-1200 port and verify TPKT/COTP traffic. Look for 0x32 (Write request) and 0x33 (Write response) PDUs.
  4. Length sanity check: The first byte of an S7 STRING is the actual length. Confirm it matches the JSON/CSV length in Node-RED. If the byte reads 0, the S7 node wrote an empty payload (common cause: the function node returned a non-string).
  5. PLC cycle time: A parse of a 254-character STRING with three delimiters per record on a CPU 1214C takes < 1 ms. If the cycle balloons, you are calling the parser in a tight loop; gate it with the trigger edge.

11. Troubleshooting Matrix

Symptom Probable Cause Fix
s7 out shows "Error: connection refused" PUT/GET not enabled on S7-1200, or wrong IP / rack-slot Enable PUT/GET in CPU protection settings; verify rack 0 / slot 1
No error, but DB never updates Optimized block access; offsets differ from expectation Disable optimized block access or read the symbolic offset from TIA Portal
String arrives but the length byte is wrong / 0 Function node sent an object, not a string Use JSON.stringify(...) or convert with Buffer.from(...).toString()
Payload truncated at 254 characters STRING[254] overflow Increase to STRING[1024] in the DB, or split into multiple STRINGs (e.g. S6.254 and S262.254)
PLC only sees the first record of the list Delimiter not found — wrong char or encoding Verify ASCII code (59 = ';', 44 = ',') in the watch table
Trigger fires repeatedly, parser runs every cycle PLC never clears writeTrigger Clear the trigger bit in the FB, or set it back to FALSE on the falling edge of the edge detector
Intermittent timeouts after a few hours S7 connection not re-established by Node-RED Enable Reconnect on the s7 node; the S7 library auto-reconnects on TCP RST
Records arrive out of order Node-RED context lost on restart; PLC index not synchronized Persist the list to a file (flow.set in functionGlobalContext) or to the IOT2050 file system

Can the S7-1200 receive a list of multiple strings from the IOT2050 in one write?

Yes. Use the array form DB10,S0.30.3 in the s7 out node to write three STRING[30] elements (96 bytes) in a single S7 Write request. The S7-1200 stores them back-to-back in the DB; the S7 protocol handles the multi-byte write atomically.

What is the maximum size of a single STRING the S7-1200 supports?

The S7-1200 accepts STRING up to 254 characters (STRING[254] = 256 bytes including the two-byte header). For larger payloads, use an Array of Char (Array[0..1023] of Char in the DB) and address it from Node-RED as DB10,C0.1024.

Do I need TIA Portal changes on the S7-1200 to allow Node-RED to write?

Yes. Enable PUT/GET access in the CPU's protection and security settings and download the hardware configuration. Without it the S7-1200 rejects S7 Write requests from partners that have no configured S7 connection.

Should I disable optimized block access on the DB?

For simple projects, disabling optimized block access makes byte-offset addressing from Node-RED trivial. With optimized blocks, the offsets are still available in TIA Portal and the S7 node respects them, but every variable rename in TIA Portal can shift the layout. Pick one convention per project and document it.

How do I keep the list in order if the S7-1200 restarts?

Node-RED's context is volatile. Either persist the list to the IOT2050 file system (a JSON file under /home/iotuser/.node-red/) or use a retain tag on the S7-1200 side. The S7-1200 DB is retained by default, so the PLC can keep the last ten records across a warm restart without help from Node-RED.

Back to blog