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-s7node (also known asnode-s7for 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.
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
- In the TIA Portal project tree, expand Program Blocks.
- Add a new Data Block (e.g.
DB_IOT_Data, number 10). - 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.
- 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[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:
- Open the CPU device configuration in TIA Portal.
- Select Properties → Protection & Security → Connection mechanisms.
- Check Permanent access via PUT/GET communication from remote partner (PLC, HMI, OPC, ...) enabled.
- 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.
5. Node-RED S7 Node Installation
- SSH into the IOT2050 (
ssh iotuser@<ip>). - Open Node-RED in a browser at
http://<iot2050>:1880. - Menu → Manage palette → Install.
- Search for
node-red-contrib-s7and click Install. - 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 oforderBuffer. -
Standard > String + Char instructions:
LEFT,RIGHT,MID,FIND,DELETE,INSERT. -
Type conversion:
STRING_TO_INT,STRING_TO_DINTfor 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.
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
- The IOT2050 listens to MQTT, an OPC UA server, or another source, and assembles the record set.
- On every update from that source, Node-RED writes the latest payload to
DB10.listPayloadand pulseswriteTrigger. - The S7-1200 parses the payload, increments a record counter, and stores the entry in
orderBuffer[recordIndex MOD 10]. - 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
-
Watch the trigger bit in TIA Portal: Open Online & diagnostics → Watch table. Monitor
DB_IOT_Data.writeTrigger,DB_IOT_Data.listPayload, and one element oforderBuffer. Trigger an update from Node-RED; the watch table should refresh within < 100 ms on a healthy network. - 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.
- 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.
- 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).
- 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.