Overview
The SIMATIC IoT2050 is Siemens' industrial IoT gateway based on a Texas Instruments Sitara application processor. Operators commonly attach a Realtek RTL8188CUS-based USB WiFi adapter for wireless commissioning, for connecting to engineering tablets, or for out-of-band diagnostics when both wired Ethernet ports are reserved for plant traffic. Two recurring problems appear in field escalations: first, building the Realtek driver from source against the Siemens Industrial OS image fails with a kernel header mismatch; second, the assumption that the two on-board Ethernet ports labelled X1P1 and X1P2 can host independent IP stacks is incorrect. The two interfaces that accept independent IPv4 configurations on the IoT2050 are X1P1 and X2P1, and they must be placed in different subnets.
This reference covers both issues. Apply the procedures in the order shown: do not skip the kernel-headers verification, because every later build step depends on a build environment that matches the running kernel. The same set of rules applies whether the host is a freshly imaged IoT2050 Advanced or a Basic variant updated in place.
Hardware Identification and Interface Layout
Before installing any driver or touching the network stack, identify the exact IoT2050 variant. The label on the front bezel, the printed ordering code, and the image version string (visible with cat /etc/os-release and uname -r) determine which Ethernet PHYs are exposed to Linux and how they enumerate.
| Front-Panel Label | Physical Position | Typical Linux Interface Name | Variant |
|---|---|---|---|
| X1 P1 | Left RJ45 on base board | eth0 | Basic and Advanced |
| X1 P2 | Right RJ45 on base board | eth1 | Advanced only (Basic has no X1P2) |
| X2 P1 | RJ45 on plug-in expansion | eth2 | Advanced only |
The naming shown above is the predictable name assigned by systemd/udev when the board boots the stock Siemens Industrial OS image. Image variants built on older kernels, or images where net.ifnames=0 has been set on the kernel command line, can revert to eth0/eth1/eth2 directly. Always confirm the mapping with ip link before configuring addresses.
Network Interface Clarification: X1P1 vs X2P1
The IoT2050 Advanced exposes three RJ45 connectors. The two that the Linux network stack treats as fully independent Ethernet devices are X1P1 (eth0) and X2P1 (eth2). They sit on different internal buses and have separate MAC addresses, so the kernel accepts two IPv4 configurations in two different subnets without ARP or routing conflicts. Assigning the same subnet to both is allowed but produces non-deterministic behaviour: the kernel will reply for inbound traffic on both, and a remote host will not know which physical port to use for return traffic.
For deterministic operation, place X1P1 in the OT/cell network and X2P1 in the IT/diagnostic network (or vice versa), and apply firewall rules between the two with nftables. Each interface must carry a unique IPv4 address in a unique subnet, for example:
- X1P1: 192.168.1.10/24 (OT/cell network)
- X2P1: 192.168.200.10/24 (IT/diagnostic network)
Configuring both interfaces with NetworkManager or systemd-networkd is covered in the section Network Configuration.
Problem: RTL8188CUS Driver Compilation Error
The Realtek RTL8188CUS is a 2.4 GHz 802.11b/g/n single-stream USB 2.0 chip found on a wide range of low-cost WiFi sticks. Mainline Linux supports it through the rtl8192cu driver in drivers/net/wireless/realtek/rtlwifi/rtl8192cu/, and Realtek publishes a vendor tree that is sometimes preferred for monitor mode. On a stock SIMATIC IoT2050 image, attempting to build the vendor source typically fails with errors of the form:
make[1]: *** /lib/modules/$(uname -r)/build: No such file or directory. Stop.
*** Error: Cannot find kernel build headers. Stop.
or, if the headers are present but version-skewed:
error: conflicting types for 'ieee80211_rx_irqsafe'
error: implicit declaration of function 'ieee80211_channel_to_frequency'
The first error means the kernel development package is missing entirely. The second error means the headers are present but were generated against a different kernel configuration than the one currently running. In both cases the external module build cannot proceed.
Root Cause
The Siemens Industrial OS image shipped for the IoT2050 is a minimal userspace. It does not include the kernel development package (linux-headers-$(uname -r)) and does not include the full kernel source tree. Without the headers, the external module build system cannot resolve symbols such as module_init, module_exit, or the wireless stack structures. The Realtek vendor source is sensitive to kernel version; small differences in struct layout between 4.19.x and 5.10.x break the build. The fix is always the same: provide a build environment that exactly matches uname -r.
Prerequisites
- SIMATIC IoT2050 with Siemens Industrial OS or a Debian-based image installed
- RTL8188CUS USB WiFi adapter, verified on a PC first (avoid counterfeit RTL8188EUS rebranded as CUS)
- SSH or serial console access to the device
- Root or sudo privileges
- At least 2 GB free on the boot media for the kernel source tree
- Network connectivity to a Debian/Ubuntu apt mirror, or the OpenSourceSoftware.zip archive (1.8 GB) from Siemens support
Quick Triage: Check Whether the Driver Is Already Present
Recent mainline kernels (5.10 and later) ship with the rtl8192cu driver and the matching firmware blob rtw8192cufw.bin. The Siemens-supplied kernel for the IoT2050 typically builds this as a module. Before doing any compilation, check whether the module is already present and simply not loaded:
find /lib/modules/$(uname -r) -name "8192cu*" -o -name "rtl8192cu*"
modinfo 8192cu 2>/dev/null || modinfo rtl8192cu 2>/dev/null
If a module is found, plug the stick in, load the module, and watch the kernel ring buffer for the device's product ID 0x8179 (typical for the RTL8188CUS):
sudo modprobe 8192cu
dmesg | tail -20
iw dev
If iw dev lists wlan0 (or the predictable name wlp1s0u1), the driver is already working and the user-reported "compilation error" was a red herring. The module was always present and only needed loading, or the firmware blob was missing from /lib/firmware/rtw/. Install the firmware with sudo apt install linux-firmware if it is not already there.
Solution Path A: Source the Matching Kernel Headers from the Image
The cleanest path is to build the module against the exact kernel that is running. The kernel source for the Siemens IoT2050 lives inside the OpenSourceSoftware.zip file (1.8 GB) shipped with each firmware release. The zip also contains the prebuilt .ko files for the shipped kernel, so it is worth checking whether the RTL8188CUS module is already built and ready to insmod before attempting a manual compile.
- Download the OpenSourceSoftware.zip that matches the installed image from the Siemens support portal (search for "SIMATIC IoT2050" and select the entry that matches the firmware version on the device).
- Transfer the file to the IoT2050 using scp or a USB stick with at least 4 GB free.
- Unpack only the kernel directory to save space:
unzip -d /tmp/oss OpenSourceSoftware.zip "Source/oss/linux-kernel*" - Install the matching kernel headers package if a .deb is present:
or, for an image without a deb, pointsudo dpkg -i linux-headers-$(uname -r)_*.deb/lib/modules/$(uname -r)/buildat the unpacked source tree:sudo ln -sfn /tmp/oss/Source/oss/linux-kernel-src /lib/modules/$(uname -r)/build - Verify the headers are detected:
The file must exist before continuing. If it does not, the source tree layout has changed; consult the README inside the zip.ls /lib/modules/$(uname -r)/build/include/linux/kernel.h
Solution Path B: Install via DKMS
If you cannot source the matching kernel headers from the zip but the IoT2050 has internet access, Dynamic Kernel Module Support (DKMS) is the next-best option. DKMS rebuilds the module automatically when the kernel changes, and it is the path most often used on rolling-update Debian installations. For the RTL8188CUS, two upstream DKMS packages work on recent kernels:
-
rtl8192cu-fixes(recommended; includes rtlwifi stack patches and improved roaming behaviour) -
aircrack-ng/rtl8188eus(monitor-mode capable; useful for wireless diagnostics but not for production traffic)
- Install the toolchain and headers:
sudo apt update sudo apt install -y build-essential dkms git bc linux-headers-$(uname -r) - Clone and register the driver with DKMS:
cd /usr/src git clone https://github.com/keystonehub/rtl8192cu-fixes.git sudo dkms add ./rtl8192cu-fixes sudo dkms build rtl8192cu-fixes/$(cat rtl8192cu-fixes/VERSION) sudo dkms install rtl8192cu-fixes/$(cat rtl8192cu-fixes/VERSION) - Blacklist the conflicting
rtl8xxxudriver (it claims the same USB ID and produces a wedged stick if both load):echo -e "blacklist rtl8xxxu\nblacklist rtw88_8821cu" | sudo tee /etc/modprobe.d/rtl8188cus-blacklist.conf - Reboot and verify:
lsmod | grep 8192cu ip link show wlan0 iw dev
linux-headers-$(uname -r) package. If apt cannot find it for your kernel version, fall back to Path A or build the headers from the matching kernel source tree.Solution Path C: Build the Vendor Tree by Hand
If the in-tree driver is too old for your kernel and DKMS is not viable, build the Realtek vendor tree. The vendor source is shipped as either RTL8188CUS (for CU) or RTL8188EU (for EU) - they are not interchangeable.
- Obtain the source from the manufacturer or a trustworthy mirror. Verify the SHA256 against the value published with your adapter.
- Extract to
/usr/src/rtl8188cus-<ver>: - Build against the verified kernel tree:
cd /usr/src/rtl8188cus-<ver> make -j$(nproc) KSRC=/lib/modules/$(uname -r)/build sudo make install KSRC=/lib/modules/$(uname -r)/build sudo depmod -a - Load the module and confirm the device enumerated:
sudo modprobe 8188cu dmesg | grep -i rtl iw dev - Persist the load at boot:
echo 8188cu | sudo tee /etc/modules-load.d/rtl8188cus.conf
Network Configuration: Assigning Distinct Subnets to X1P1 and X2P1
With the WiFi driver installed (or independent of it), the two Ethernet interfaces must be configured for different subnets. The following example uses systemd-networkd, which is the default on Siemens Industrial OS images. Confirm the interface names on your device with ip -br link before applying the configuration; replace eth0 and eth2 with whatever the kernel reports for X1P1 and X2P1.
Create /etc/systemd/network/10-x1p1.network:
[Match]
Name=eth0
[Network]
Address=192.168.1.10/24
IPv6AcceptRA=false
LinkLocalAddressing=no
[Route]
Gateway=192.168.1.1
Metric=10
Create /etc/systemd/network/20-x2p1.network:
[Match]
Name=eth2
[Network]
Address=192.168.200.10/24
IPv6AcceptRA=false
LinkLocalAddressing=no
[Route]
Gateway=192.168.200.1
Metric=20
Disable NetworkManager if it is present, because it can race with systemd-networkd and remove addresses that the latter configured:
sudo systemctl disable --now NetworkManager
sudo systemctl enable --now systemd-networkd
sudo systemctl enable --now systemd-resolved
If you use NetworkManager instead, the equivalent nmcli sequence is:
sudo nmcli con add type ethernet ifname eth0 con-name "cell-net" ip4 192.168.1.10/24 gw4 192.168.1.1
sudo nmcli con add type ethernet ifname eth2 con-name "diag-net" ip4 192.168.200.10/24 gw4 192.168.200.1
sudo nmcli con up "cell-net"
sudo nmcli con up "diag-net"
Validate that both interfaces are up and on separate subnets:
ip -br addr
ip route
ping -I eth0 192.168.1.1
ping -I eth2 192.168.200.1
Verification Matrix
| Check | Command | Expected Result |
|---|---|---|
| Module loaded | lsmod | grep -E '8192cu|rtl8' |
Module present, refcount > 0 after stick insert |
| Interface enumerated | iw dev |
wlan0 (or predictable name) listed with phy0 |
| Firmware loaded | dmesg | grep -i firmware |
No "firmware: failed to load" entries |
| Station associated | iw dev wlan0 link |
SSID and frequency populated |
| X1P1 has address | ip -4 addr show eth0 |
Address in 192.168.1.0/24 |
| X2P1 has address | ip -4 addr show eth2 |
Address in 192.168.200.0/24 |
| Routes distinct | ip route |
Two default routes with different metrics |
| Firewall active | sudo nft list ruleset |
Forward chain policy drop, with explicit accept rules between eth0 and eth2 |
| DNS resolving | resolvectl status |
systemd-resolved active, DNS server populated |
Troubleshooting Matrix
| Symptom | Likely Cause | Remedy |
|---|---|---|
No such file or directory /lib/modules/$(uname -r)/build |
Kernel headers not installed | Use Solution Path A; extract headers from OpenSourceSoftware.zip |
conflicting types for 'ieee80211_rx_irqsafe' |
Header/kernel version skew | Replace headers with the exact version matching uname -r
|
| Stick not detected, no dmesg line | USB power budget exceeded on X4 | Use a powered hub; check lsusb -v for power claims |
Interface renamed to wlp1s0u1
|
Predictable naming active | Pin the name with a udev rule or a systemd-networkd [Match] block |
| eth0 and eth2 same subnet, traffic non-deterministic | Both interfaces in cell LAN | Move one to a different subnet per the X1P1/X2P1 requirement |
DKMS build fails on bc missing |
bc not installed |
sudo apt install bc and rebuild |
| Driver loads but cannot scan | Regulatory domain unset |
sudo iw reg set DE (or your country) and reboot |
| Association succeeds but throughput < 5 Mbit/s | USB 2.0 stick on a 2.4 GHz congested channel | Set channel and width manually, or accept the limitation of the RTL8188CUS |
| Both 8192cu and rtl8xxxu loaded | Blacklist not applied | Recreate /etc/modprobe.d/rtl8188cus-blacklist.conf and rebuild initramfs |
Field-Commissioning Notes
When commissioning an IoT2050 with both X1P1 and X2P1 attached, label both patch cables at the cabinet with the subnet they belong to. Operators frequently confuse the two because the on-bezel labels are not always visible once the device is DIN-rail mounted. A second adhesive label with the IP and subnet on the cable side eliminates the most common source of field tickets and reduces mean time to repair on network outages.
For OT segmentation, leave X2P1 on a dedicated diagnostic VLAN. Disable any routing or forwarding between X1P1 and X2P1 with nft add rule inet filter forward iifname "eth0" oifname "eth2" drop. This is a hardening measure that prevents a compromised IT-side host from reaching OT-side equipment via the gateway, and it satisfies the IEC 62443 zone-conduit requirement that the IoT2050 acts as a conduit, not a bridge, between the IT and OT zones.
For the WiFi side, do not use the RTL8188CUS as a permanent OT link. The chip is single-stream 2.4 GHz, has no industrial temperature rating, and is best reserved for commissioning tablets, maintenance laptops, or temporary diagnostic access. If persistent wireless is required, plan for a SIMATIC IWLAN client (SCALANCE) on a certified M12 connector instead.
FAQ
Why does my driver build fail with "No such file or directory /lib/modules/$(uname -r)/build"?
The Siemens Industrial OS image does not ship the kernel development package. Install the matching linux-headers .deb from the OpenSourceSoftware.zip archive (1.8 GB) associated with your firmware version, or rebuild the headers from the kernel source tree that the same archive contains.
Can I assign different IP addresses to X1P1 and X1P2?
No. The two interfaces that accept independent IPv4 configurations on the IoT2050 are X1P1 and X2P1. X1P2, if present on the Advanced variant, sits on the same internal switch fabric and is not a candidate for a second routable subnet without software bridging.
Do X1P1 and X2P1 need to be in different subnets?
Yes. The two physical interfaces are exposed to the kernel as separate Ethernet devices, and Siemens support guidance is to place them in different IPv4 subnets. Configuring both into the same subnet produces non-deterministic routing and ARP behaviour and breaks OT segmentation guarantees.
Can I use DKMS if the linux-headers package is not in the apt repository for my kernel?
DKMS still requires a matching build environment. If apt cannot provide the headers, build them from the Siemens-supplied kernel source tree (OpenSourceSoftware.zip) and point /lib/modules/$(uname -r)/build at that tree before running dkms build.
Will the RTL8188CUS work out of the box on the IoT2050?
Often yes. Mainline kernels from 5.10 onward include the rtl8192cu driver, and the linux-firmware package supplies the rtw8192cufw.bin blob. Plug the stick in, run modprobe 8192cu and check iw dev before attempting a manual compile.
How do I tell RTL8188CUS from a counterfeit RTL8188EUS?
Run lsusb -v -d 0bda:8179 and confirm the bcdDevice and manufacturer strings. Many cheap sticks report the CUS USB ID but contain an EUS chip; the EUS uses a different driver (8188eu) and will not bind to 8192cu. The cleanest fix is to replace the stick with a known-good industrial-grade adapter.