1. Overview
Siemens SIMATIC S7-1500 controllers dominate the mid- to high-end PLC segment, and pushing real-time process data from the controller to Microsoft Azure IoT Hub unlocks remote dashboards, predictive maintenance, and cloud-side analytics. The Siemens IOT2040 (6ES7647-0AA00-0YA2) — succeeded by the IOT2050 (6ES7647-0BA00-0YA2) — is an industrial edge gateway that runs a Yocto-based Linux image with Node-RED pre-installed. It bridges the ISO-on-TCP / PROFINET world of the S7-1500 with the MQTT- and AMQP-based telemetry of Azure IoT Hub.
This guide consolidates a field-proven path for moving tag data from an S7-1500 data block to Azure IoT Hub through Node-RED on the IOT2040. The first attempts failed for three reasons: (a) the wrong Node-RED Azure package, (b) a payload that was not string-encoded for the cloud SDK, and (c) AMQP transport errors caused by outbound port 5671 being blocked on industrial networks. The corrected topology uses node-red-contrib-s7 for S7 polling, node-red-contrib-azureiothubnode for MQTT-style device-to-cloud (D2C) telemetry, and a function node that stringifies the JSON body Azure expects.
2. Prerequisites
| Component | Version / Specification |
|---|---|
| S7-1500 CPU | e.g., CPU 1515-2 PN (6ES7515-2AM02-0AB0), firmware ≥ V2.0 |
| TIA Portal | V16 or later for S7-1500 project engineering and download |
| IOT2040 | 6ES7647-0AA00-0YA2 with Example Image V2.6.x (Yocto Linux) |
| Node-RED | Pre-installed on the IOT2040 image; upgrade Node.js ≥ V10 |
| Azure subscription | Active subscription with permission to create an IoT Hub |
| Network | TCP/IP reachability from IOT2040 to S7-1500 (port 102) and outbound to Azure (port 8883 for MQTT or 5671 for AMQP) |
| Azure CLI workstation | Windows or Linux PC with Azure CLI ≥ 2.40 for device identity management |
3. System Architecture
The data path is:
S7-1500 CPU → ISO-on-TCP (TCP 102) → IOT2040 Node-RED → MQTT TLS (TCP 8883) → Azure IoT Hub endpoint → Stream Analytics / Time Series Insights / Blob Storage
Node-RED flows inside the IOT2040:
- An Inject node (or a schedule node) drives periodic polling.
- The s7 node reads DB100 (or any configured DB) and emits
msg.payload. - A function node converts native S7 values into a JSON string.
- The azureiothub node publishes the JSON to the device-to-cloud stream.
4. S7-1500 PLC Configuration in TIA Portal
4.1 Enable PUT/GET access on the CPU
Open the S7-1500 device properties in TIA Portal → Protection & Security → Connection mechanisms and tick Permit access with PUT/GET communication from remote partner. The official SIMATIC S7-1500 System Manual documents that PUT/GET access via the integrated PROFINET interface is required for S7-200/300/400/1200/1500 communication drivers.
4.2 Define a global data block, e.g., DB100
-
Booltags for status,Realtags for analog values,Inttags for counters. - For early versions of
node-red-contrib-s7, disable Optimized block access on the DB so the byte-offset addressing is reliable. Newer versions support symbolic access; keep the DB optimized if you switch the S7 node to symbol mode.
4.3 Compile, download, and verify
After download, confirm the IP address and the rack/slot (default 0/1 for an S7-1500). Use the Online & Diagnostics view to verify the PLC is in RUN.
5. IOT2040 Image and Node-RED Preparation
The IOT2040 ships with the SIMATIC IOT2000 SD card example image. SSH into the device and prepare the environment:
opkg update
opkg install nodejs nodejs-npm
For a clean reflash, download the Siemens Example Image from the Siemens support portal and reflash the SD card. Launch Node-RED:
node-red-pi --max-old-space-size=256
Open the editor at http://<iot2040-ip>:1880. The official Siemens IOT2000 setup page documents the exact procedure for first commissioning and Node-RED startup parameters.
6. Installing the Required Node-RED Nodes
From the Node-RED palette manager or from the command line on the IOT2040:
cd ~/.node-red
npm install node-red-contrib-s7
npm install node-red-contrib-azureiothubnode
node-red-contrib-azure-iot-hub alongside node-red-contrib-azureiothubnode. The two packages register conflicting transports in the Node.js SDK and produce intermittent authentication failures. The original failing deployment on the IOT2040 had both installed, which contributed to the AMQP errors discussed in section 12.node-red-contrib-s7 is the standard Node-RED gateway for S7-300/400/1200/1500 communication. node-red-contrib-azureiothubnode wraps the Microsoft azure-iot-device MQTT client and accepts a string body for D2C messages.
7. Configuring the S7 Node
Double-click the S7 node and enter:
| Field | Value |
|---|---|
| IP | IP of the S7-1500 CPU, e.g., 192.168.0.10 |
| Port | 102 (default ISO-on-TCP) |
| Rack | 0 |
| Slot | 1 |
| Mode | Single variable (debug) or All variables (production) |
| Variable (single mode) | e.g., DB100,REAL0 for a Real at offset 0; DB100,X0.0 for a Bool at byte 0, bit 0 |
The S7 node injects msg.payload with the typed value (e.g., 23.7). Wire the S7 output to a debug node and verify the values appear in the Node-RED debug tab before continuing.
8. Provisioning the Device in Azure IoT Hub
In the Azure portal, create an IoT Hub and register a device identity:
- Azure Portal → IoT Hub → Devices → Add Device.
- Device ID: e.g.,
iot2040-plant1. - Authentication type: Symmetric key (default).
- Copy the Primary Connection String.
The full connection string is required for node-red-contrib-azureiothubnode. Format:
HostName=<your-hub>.azure-devices.net;DeviceId=<device-id>;SharedAccessKey=<base64-key>
iothub-explorer Node.js tool used in older Siemens guides is deprecated by Microsoft. Replace it with the Azure CLI on a workstation: az iot hub device-identity create --hub-name <hub> --device-id <device-id> and az iot hub device-identity show-connection-string --hub-name <hub> --device-id <device-id>. The Azure CLI itself is not runnable on the IOT2040 (no x86_64 Python 3 toolchain in the Yocto image) — manage the device identity from a Windows or Linux workstation.9. Selecting the Right Azure Node
| Node | Transport | Notes |
|---|---|---|
| node-red-contrib-azure-iot-hub | AMQP | Default transport; clashes with azureiothubnode; difficult to debug on IOT2040 |
| node-red-contrib-azureiothubnode | MQTT (TLS 8883) | Stable, accepts a string payload, recommended for IOT2040 |
| node-red-contrib-azure-iot-hub-cds (community forks) | AMQP / MQTT | Variable maintenance; review before production use |
Use node-red-contrib-azureiothubnode and feed it a stringified JSON object. The Siemens application example "Data transfer to Microsoft Azure with an S7-1x00" also uses MQTT over port 8883 with the LMQTT library on the PLC side, which is consistent with the MQTT choice on the gateway side.
10. Payload Construction Function Node
Place a function node between the S7 node and the Azure node. For a single variable read:
// msg.payload arrives as the typed S7 value (e.g., 23.7)
var body = {
deviceId: "iot2040-plant1",
timestamp: new Date().toISOString(),
tag: {
value: msg.payload
}
};
msg.payload = JSON.stringify(body);
return msg;
For a multi-tag S7 read using mode All variables — the S7 node returns an object keyed by the variable name you entered — the function simplifies to:
var body = {
deviceId: "iot2040-plant1",
timestamp: new Date().toISOString(),
tags: msg.payload
};
msg.payload = JSON.stringify(body);
return msg;
Wire the function node's output to the Azure IoT Hub node. The Node.js azure-iot-device SDK requires a string body for D2C telemetry; sending a raw object or number causes silent rejection in some SDK versions.
11. Configuring the Azure Node
In node-red-contrib-azureiothubnode:
- Connection String: paste the Azure primary connection string captured in section 8.
- Method: send.
- Topic: optional; for telemetry, leave at default device-to-cloud.
Once deployed, the device-to-cloud messages appear in Azure IoT Hub. Verify with the Azure CLI from the workstation:
az iot hub monitor-events --hub-name <your-hub> --device-id iot2040-plant1
12. AMQP Troubleshooting
The original traceback reported:
Error: Error while trying to send message:Error: AMQP Transport: Could not connect
Common causes and fixes:
- Outbound firewall blocking AMQP port 5671 — open TCP 5671 or switch to MQTT (port 8883). Industrial sites often only allow 443/8883 outbound.
-
Conflicting Azure node packages — remove the AMQP-based node and restart Node-RED:
npm uninstall node-red-contrib-azure-iot-hub node-red-stop && node-red-start - Wrong connection string or expired SAS key — re-issue from the Azure portal.
-
Old
azure-iot-deviceversion — pull a current version:npm install azure-iot-device@latest -
Yocto Linux missing CA certificate bundle — install the Mozilla CA bundle and restart Node-RED;
azure-devices.netcannot complete the TLS handshake without it.
The Microsoft community thread "Siemens CPU S7-1200/-1500 to Azure IoT-Hub" confirms that direct S7-1500 → Azure IoT Hub MQTT works on recent CPU firmware (≥ V2.6) but is unreliable for production use, which is why the IOT2040 gateway approach is the recommended path.
13. Verification Checklist
- S7 node debug tab shows real values when the PLC is online.
- Function node debug tab shows a JSON string in
msg.payload. - Azure node debug tab shows Message sent.
-
az iot hub monitor-eventsdisplays the device-to-cloud payload in the Azure CLI shell. - Azure IoT Hub metrics show successful
telemetryMessages.usedcount.
14. Common Errors and Resolution Matrix
| Symptom | Likely Cause | Fix |
|---|---|---|
| Error: AMQP Transport: Could not connect | Blocked port 5671 or conflicting node | Switch to MQTT (8883); remove old Azure node |
| iothub-explorer: command not found | Tool deprecated | Use az iot hub commands on a workstation |
| Login incorrect on Azure CLI | Wrong key / typo | Re-copy Primary Connection String |
| msg.payload is not a string | Sending object to Azure node | JSON.stringify in function node |
| S7: connection timed out | Wrong IP / CPU protection enabled | Verify CPU Permit access with PUT/GET |
| iothub-connection-string invalid | Hub name with capitals or extra spaces | Trim and use lowercase hub name |
| Empty payload in Azure monitor-events | Function node did not return msg | Add return msg; at end of function |
| Cert error on TLS handshake | Outdated CA bundle | Update ca-certificates package |
15. Extended Topics
15.1 Direct S7-1500 to Azure MQTT (LMQTT library)
Siemens publishes a library "LMQTT" that allows the S7-1500 CPU to publish MQTT messages directly, eliminating the IOT2040. This is suitable for cells without an edge gateway. The library, sample blocks, and certificate handling are documented in the Siemens application example. The trade-off is that direct CPU-to-cloud has less buffering, no local store-and-forward, and exposes the PLC to cloud-side outages.
15.2 Storage and Analytics
Once data lands in Azure IoT Hub, route it to Azure Blob Storage, Time Series Insights, or Stream Analytics. The Azure Import/Export service is a related but distinct bulk-data mechanism for offline seeding of storage accounts with physical drives and is not used for live telemetry.
15.3 Security Hardening
- Use X.509 certificates rather than symmetric keys when scaling to more than one device.
- Enable Azure IoT Hub IP filtering and DPS (Device Provisioning Service) for production.
- Disable PUT/GET on the S7-1500 once data is flowing and rely on the OPC UA server instead, if a future migration is planned.
- Place the IOT2040 on a dedicated OT VLAN and restrict outbound traffic to
*.azure-devices.net:8883.
16. References
- Siemens Application Example: Data transfer to Microsoft Azure with an S7-1x00
- Microsoft Q&A: Siemens CPU S7-1200/-1500 to Azure IoT-Hub
- Microsoft Learn: Using Azure Import/Export to transfer data to and from Azure Storage
FAQ
Why does node-red-contrib-azure-iot-hub fail on IOT2040 with "AMQP Transport: Could not connect"?
The IOT2040 Yocto image ships without an AMQP-friendly TLS stack and most field networks block TCP 5671. Switch to node-red-contrib-azureiothubnode (MQTT over 8883) and uninstall the AMQP-based node to avoid the SDK conflict.
Can I install the Azure CLI directly on the IOT2040?
No. The IOT2040 Yocto image lacks the x86_64 Python 3 toolchain that the Azure CLI requires. Register devices and pull connection strings from a Windows or Linux workstation using az iot hub device-identity commands.
Which S7-1500 firmware supports direct MQTT to Azure?
CPU firmware V2.6 or later with the LMQTT library is required for direct CPU-to-Azure MQTT. For IOT2040 gateway topologies, any firmware that allows PUT/GET access (typically V2.0+) is sufficient.
Should I send a number or a string to the Azure node?
Send a JSON-encoded string produced by a function node. node-red-contrib-azureiothubnode accepts a string body and forwards it to the device-to-cloud endpoint; sending a raw number causes silent rejection in some Node.js SDK versions.
Does the S7 node require the data block to be non-optimized?
For older versions of node-red-contrib-s7, non-optimized blocks with absolute byte addresses are the most reliable path. If you keep the DB optimized, use symbolic names and the S7 node's S7_SYMBOL mode, or upgrade to a current version of the node package.