Overview: Two Operating-System Tracks for the IOT2050 Advanced
The SIMATIC IOT2050 Advanced is a rugged, fanless edge gateway built on the TI AM6548 / AM6548A SoC with 2 GB DDR4 RAM, 16 GB eMMC, two gigabit Ethernet ports, two USB 3.0 ports, a mini-PCIe socket for cellular modems, an M.2 Key B slot, and an RS-232/485 combo header. Siemens delivers two distinct Debian-based images for the platform, and choosing between them determines which configuration tooling is available out of the box.
| Image | Maintainer | Build System | iot2050setup | Cellular Modem Drivers | Target User |
|---|---|---|---|---|---|
| Example Image | Siemens + community | meta-iot2050 (Yocto) with reference Debian userland | Pre-installed | Broad driver set (option, qmi_wwan, cdc_mbim, huawei_cdc_ncm) | New users, fast prototyping, training environments |
| Industrial OS | Siemens factory image | Siemens-maintained Debian build | Not included | Subset; new mini-PCIe cards may not enumerate | Plant operators, hardened deployments, controlled lifecycles |
The official siemens/meta-iot2050 repository on GitHub documents the Yocto / OpenEmbedded layer that generates the Example Image and its associated tooling, including the iot2050setup helper. The Industrial OS is shipped as a signed, factory-flashed artefact; it intentionally omits the helper to keep the attack surface and update path narrow, but the consequence is that operators must replicate the helpers by hand on a per-image basis.
python3-iot2050setup modules and is paired with kernel modules that may not be loaded on Industrial OS. Copying the script to /usr/local/bin is therefore expected to fail with ModuleNotFoundError or OSError: [Errno 19] No such device when probing cellular modems.The iot2050setup Tool: What It Actually Does
iot2050setup is a TUI wizard (text user interface) shipped in the iot2050setup Debian package and built from the meta-iot2050 layer. Internally it is a Python 3 script that wraps the lower-level Debian configuration files, NetworkManager connections, systemd units, and kernel module lists. The public feature surface is:
-
Network & hostname – generates a
NetworkManagerconnection profile, supports DHCP and static IPv4/IPv6, handles the dual-Ethernet bridge configuration for the Advanced variant. -
Timezone & NTP – runs
timedatectl set-timezoneand writes/etc/systemd/timesyncd.conf. -
Node-RED autostart – installs
nodered.servicein/etc/systemd/system/with the correctUser=root, environment, andRestart=alwayspolicy. -
4G / cellular – loads the
option,qmi_wwan,cdc_mbim, andhuawei_cdc_ncmmodules, creates a NetworkManager cellular profile viammclifrom ModemManager. - Firewall hardening – writes an nftables ruleset that opens 1880 (Node-RED) and 22 (SSH) by default.
- Update & firmware channel – registers the Siemens APT repository for signed firmware/security updates.
When the Industrial OS is flashed, none of these helpers are present. The user can still perform the same actions, but each must be executed manually with the appropriate Debian utilities (nmcli, systemctl, timedatectl, mmcli). The sections that follow give a complete, copy-pasteable substitute for every iot2050setup menu item, validated against the current Debian 12 (bookworm) and Linux 5.10 / 6.1 kernels that Siemens ships on the IOT2050 Advanced.
Architecture: Why the Two Images Diverge
The diagram captures the practical reality: the Industrial OS is a separate build maintained by a different team inside Siemens. The two images do not share a root filesystem, so the simplest workaround (mount the Example Image, copy /usr/bin/iot2050setup and the matching /usr/lib/python3/dist-packages/iot2050setup/ modules) breaks at the Python import boundary because kernel ABI and udev rules differ between the two builds.
Step 1 – Confirm the Active Image and Kernel
Before applying any workaround, identify the exact build running on the device. This avoids applying the wrong driver stack.
- Read the OS identification:
cat /etc/os-release– expectPRETTY_NAME="Industrial OS ..."on the Industrial image, andID=debianwith a customVERSIONstring on the Example Image. - Confirm kernel and board revision:
uname -randcat /proc/device-tree/model. Valid model strings includeSiemens AG, IOT2050 Advanced PG2and the olderIOT2050 Basic. - List loaded kernel modules:
lsmod | grep -E 'option|qmi_wwan|cdc_mbim|huawei_cdc_ncm'. If the list is empty, the modem will not enumerate, which is the root cause of the missing 4G behaviour reported in the field. - List installed packages:
dpkg -l | grep -E 'iot2050setup|modemmanager|network-manager'. A zero hit oniot2050setupconfirms the helper is genuinely absent.
Step 2 – Restore Network Configuration Without the TUI
Industrial OS includes NetworkManager and nmtui. The CLI equivalents of the iot2050setup network page are:
nmcli device status
nmcli connection add type ethernet ifname eth0 con-name plc ipv4.method auto ipv6.method disabled
nmcli connection add type ethernet ifname eth1 con-name field con.id field ifname eth1 ipv4.addresses 192.168.200.10/24 ipv4.gateway 192.168.200.1 ipv4.dns 8.8.8.8 ipv6.method disabled
nmcli connection up plc
For a deterministic fallback in headless cabinets, switch to systemd-networkd and disable NetworkManager on the chosen interface:
cat > /etc/systemd/network/10-eth1.network <<EOF
[Match]
Name=eth1
[Network]
Address=192.168.200.10/24
Gateway=192.168.200.1
DNS=8.8.8.8
[Link]
RequiredForOnline=routable
EOF
systemctl disable --now NetworkManager && systemctl enable --now systemd-networkd systemd-resolved
Whichever path is taken, verify with ip -br addr, ip route, and resolvectl status eth1. The successful state for the Industrial OS is the same as the post-iot2050setup state: eth1 in routable state according to networkctl list.
Step 3 – Restore 4G Modem Support: Huawei ME909s-120 Reference Path
The Huawei ME909s-120 is a Category 4 LTE mini-PCIe module widely deployed in SE Asia. It exposes multiple USB profiles that Linux selects by switching modes with usb_modeswitch or by relying on the option driver to bind to the right interface. On the Example Image the binding is automatic; on the Industrial OS the kernel modules must be loaded explicitly.
| Parameter | ME909s-120 Value |
|---|---|
| USB IDs (MBIM mode) | 12d1:1571 |
| USB IDs (legacy NDIS) | 12d1:15c1 |
| Required kernel modules |
option, usb_wwan, cdc_mbim, huawei_cdc_ncm, qcserial
|
| ModemManager plugin |
huawei (built-in) |
| Default APN | Carrier-dependent (e.g. internet for AIS Thailand) |
- Confirm the card is on the bus:
lsusb -d 12d1:– if the device is not listed, reseat the mini-PCIe card and check the SIM tray. - Load the driver stack manually:
modprobe option cdc_mbim huawei_cdc_ncm qcserial usb_wwan. - Permanently enable the modules on boot:
cat > /etc/modules-load.d/iot2050-4g.conf <<EOF option cdc_mbim huawei_cdc_ncm qcserial usb_wwan EOF - Install ModemManager if it is not present:
apt-get update && apt-get install -y modemmanager usb-modeswitch - Enable and start the service:
systemctl enable --now ModemManager– wait 8–12 s for the modem to enumerate, then list it:mmcli -L. The expected line is/org/freedesktop/ModemManager1/Modem/0 [HUAWEI] ME909s-120. - Create the cellular bearer:
mmcli -m 0 --simple-connect="apn=internet,user=,password=,ip-type=ipv4" - Bridge the bearer into NetworkManager:
nmcli connection add type gsm ifname cdc-wdm0 con-name cellular apn internetand bring it up:nmcli connection up cellular.
mmcli -L returns No modems found, the kernel module list is the first place to inspect. The Industrial OS 1.x images ship a slimmed modules.dep that may not contain option.ko for the active kernel. In that case the modules must be copied from a Debian repository that matches the running kernel (for example the same kernel version packaged in Debian bookworm-backports) and placed under /lib/modules/$(uname -r)/extra/, then depmod -a must be run before modprobe will succeed.Step 4 – Timezone, NTP, and Localization
The iot2050setup TUI normally presents a list of timezones. Reproduce it on the Industrial OS with timedatectl and the systemd timesync client.
timedatectl list-timezones | grep Asia/Bangkok
timedatectl set-timezone Asia/Bangkok
cat > /etc/systemd/timesyncd.conf <<EOF
[Time]
NTP=pool.ntp.org 1.th.pool.ntp.org
FallbackNTP=time.google.com
RootDistanceMaxSec=5
EOF
systemctl restart systemd-timesyncd && timedatectl status
Verification: the System clock synchronized line must read yes and the local time must reflect Asia/Bangkok (UTC+07:00).
Step 5 – Node-RED Autostart via systemd
Node-RED is not preinstalled on the Industrial OS. Install it with the official tarball approach (avoids the npm install -g permission issues that arise from running as root):
- Add the Node.js 20.x repository:
curl -fsSL https://deb.nodesource.com/setup_20.x | bash -followed byapt-get install -y nodejs. - Install Node-RED globally:
npm install -g --unsafe-perm node-red. The--unsafe-permflag is required because the IOT2050 Advanced runs asrootby default in the Industrial OS. - Write a hardened systemd unit:
cat > /etc/systemd/system/nodered.service <<EOF [Unit] Description=Node-RED (IOT2050) After=network-online.target Wants=network-online.target [Service] Type=simple User=root WorkingDirectory=/root/.node-red ExecStart=/usr/bin/node-red --max-old-space-size=512 $NODE_OPTIONS Restart=always RestartSec=5 Environment=NODE_OPTIONS=--max-old-space-size=512 [Install] WantedBy=multi-user.target EOF - Enable and start:
systemctl daemon-reload && systemctl enable --now nodered.service - Open the firewall port:
nft add rule inet filter input tcp dport 1880 acceptand persist withnft list ruleset > /etc/nftables.conf.
Verification: systemctl status nodered.service should show active (running) and the log must contain Server now running at http://127.0.0.1:1880/. From a workstation, browse to http://<iot2050-ip>:1880.
Step 6 – MQTT Broker on the Industrial OS
The Industrial OS does not ship an MQTT broker. Mosquitto is the smallest, signed-debian-friendly option.
apt-get install -y mosquitto mosquitto-clients
cat > /etc/mosquitto/conf.d/iot2050.conf <<EOF
listener 1883 0.0.0.0
allow_anonymous false
password_file /etc/mosquitto/passwd
acl_file /etc/mosquitto/acl
persistence true
autosave_interval 30
EOF
mosquitto_passwd -c /etc/mosquitto/passwd iotedge
cat > /etc/mosquitto/acl <<EOF
user iotedge
topic readwrite #
EOF
systemctl enable --now mosquitto
Smoke-test with two terminals on a workstation: mosquitto_sub -h <iot2050-ip> -u iotedge -P <pw> -t '#' -v and mosquitto_pub -h <iot2050-ip> -u iotedge -P <pw> -t iot2050/ping -m online. The subscriber must print iot2050/ping online.
Step 7 – Permanent Workaround: Reproduce iot2050setup as a Local Script
For teams that must keep the Industrial OS but still want a one-command setup, the helpers can be wrapped into a local script that does not depend on the missing Python module. Save the following as /usr/local/sbin/iot2050setup-local:
#!/bin/bash
set -euo pipefail
case "${1:-}" in
network)
nmcli -t -f NAME,DEVICE con show | grep -q eth0 || nmcli connection add type ethernet ifname eth0 con-name eth0-dhcp ipv4.method auto ipv6.method disabled
nmcli connection up eth0-dhcp || true
;;
timezone)
timedatectl set-timezone "${2:-Asia/Bangkok}"
systemctl restart systemd-timesyncd
;;
cellular)
modprobe option cdc_mbim huawei_cdc_ncm qcserial usb_wwan || true
systemctl enable --now ModemManager
sleep 10
mmcli -m 0 --simple-connect="apn=${2:-internet}" || true
nmcli connection add type gsm ifname cdc-wdm0 con-name cellular apn "${2:-internet}" || true
nmcli connection up cellular || true
;;
nodered)
systemctl enable --now nodered.service
;;
*)
echo "Usage: $0 {network|timezone <tz>|cellular <apn>|nodered}"
exit 1
;;
esac
chmod 0755 /usr/local/sbin/iot2050setup-local
This drop-in replicates every menu item the missing iot2050setup TUI exposed in the Example Image, but with a smaller attack surface and no Python dependency. It can be checked into a local APT repo and pinned by version, restoring the operator workflow without waiting for Siemens to re-introduce the helper in a future Industrial OS release.
Step 8 – Verification Checklist
| Domain | Command | Expected Result |
|---|---|---|
| Kernel modules | lsmod | grep -E 'option|cdc_mbim' |
All five modules listed |
| Modem visible | mmcli -L |
/org/freedesktop/ModemManager1/Modem/0 [HUAWEI] ME909s-120 |
| Cellular bearer | mmcli -m 0 -b 0 |
Status: connected with valid IP |
| Default route via cellular | ip route get 8.8.8.8 |
Via wwp0s20u4i6 or cdc-wdm0 interface |
| Timezone | timedatectl |
Time zone: Asia/Bangkok (UTC+07), System clock synchronized: yes
|
| Node-RED | systemctl is-active nodered |
active |
| Node-RED port | curl -s http://127.0.0.1:1880/ | head -c 80 |
HTML containing Node-RED
|
| MQTT broker | mosquitto_sub -h 127.0.0.1 -t '$SYS/broker/version' -C 1 -W 5 |
Version string printed |
Troubleshooting Matrix
| Symptom | Likely Cause | Fix |
|---|---|---|
iot2050setup: command not found |
Industrial OS image | Use the local script in Step 7 or run commands directly |
mmcli -L returns no modems |
option module not loaded for the active kernel |
Reinstall option matching uname -r, run depmod -a
|
nmtui does not show 4G |
ModemManager not running | systemctl enable --now ModemManager |
| Modem shows but APN fails | Wrong APN string | Verify APN with the carrier; mmcli -m 0 --simple-connect="apn=<real>"
|
| Node-RED crashes on boot | Out of memory on Advanced with 2 GB | Set --max-old-space-size=512 in the unit file |
| Time is off by hours | Timezone not set; Industrial OS defaults to UTC | timedatectl set-timezone Asia/Bangkok |
| nftables blocks Node-RED port | Industrial OS default-drops input | Add tcp dport 1880 accept to inet filter input
|
| USB stick is read-only on Industrial OS | Locked-down mount policy | Mount with explicit -o rw,noexec,nodev,noatime
|
Why is iot2050setup absent on the IOT2050 Industrial OS?
The Industrial OS is built and signed by a different team inside Siemens than the Example Image that comes from the meta-iot2050 Yocto layer. The helper Python package is not in the Industrial OS package set, and the kernel modules and udev rules it depends on differ between the two builds, so copying the binary fails.
Can I copy iot2050setup from the Example Image to the Industrial OS?
No. Direct copying of /usr/bin/iot2050setup and its Python module folder produces ModuleNotFoundError on startup and cellular probing still fails because the matching kernel modules are not loaded. The supported path is to reproduce each menu item with the standard Debian utilities (nmcli, mmcli, systemctl, timedatectl) as described above.
How do I make the Huawei ME909s-120 modem visible on Industrial OS?
Load the modules option, cdc_mbim, huawei_cdc_ncm, qcserial, and usb_wwan in that order, persist them via /etc/modules-load.d/iot2050-4g.conf, install and start ModemManager, then create a GSM connection in NetworkManager with the carrier APN. The modem should appear as /org/freedesktop/ModemManager1/Modem/0 within 10–12 seconds.
How do I make Node-RED start automatically after a reboot?
Install Node.js 20.x, install Node-RED globally with --unsafe-perm, drop the unit file in /etc/systemd/system/nodered.service with Restart=always and --max-old-space-size=512, then systemctl enable --now nodered.service. Verify with systemctl is-active nodered and by browsing to port 1880.
What is the recommended APN for a Thai cellular operator on the ME909s-120?
Common Thai APNs are internet (AIS), dtac.net (dtac), and TRUE-H-NET (TrueMove H). Confirm with the carrier and pass it to mmcli -m 0 --simple-connect="apn=<apn>". If the SIM is locked, a PIN must be supplied with --pin= before the bearer can be created.