Installing RTL8188CUS USB WiFi Driver on SIMATIC IoT2050

David Krause13 min read
Industrial NetworkingSiemensTroubleshooting
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

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.

SIMATIC IoT2050 Ethernet Port Labelling and Linux Enumeration
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.

The original field question was whether X1P1 and X1P2 can carry different IP addresses. The correct answer, confirmed by Siemens support, is that the two interfaces that can host independent IPv4 stacks on the IoT2050 are X1P1 and X2P1. X1P2, if physically present on the Advanced variant, sits on the same internal switch fabric as X1P1 and is not a candidate for a second routable subnet without additional software bridging. Do not assume X1P2 maps to a second routable Linux interface.

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.

  1. 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).
  2. Transfer the file to the IoT2050 using scp or a USB stick with at least 4 GB free.
  3. Unpack only the kernel directory to save space:
    unzip -d /tmp/oss OpenSourceSoftware.zip "Source/oss/linux-kernel*"
  4. Install the matching kernel headers package if a .deb is present:
    sudo dpkg -i linux-headers-$(uname -r)_*.deb
    or, for an image without a deb, point /lib/modules/$(uname -r)/build at the unpacked source tree:
    sudo ln -sfn /tmp/oss/Source/oss/linux-kernel-src /lib/modules/$(uname -r)/build
  5. Verify the headers are detected:
    ls /lib/modules/$(uname -r)/build/include/linux/kernel.h
    The file must exist before continuing. If it does not, the source tree layout has changed; consult the README inside the zip.

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)
  1. Install the toolchain and headers:
    sudo apt update
    sudo apt install -y build-essential dkms git bc linux-headers-$(uname -r)
  2. 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)
  3. Blacklist the conflicting rtl8xxxu driver (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
  4. Reboot and verify:
    lsmod | grep 8192cu
    ip link show wlan0
    iw dev
DKMS requires the matching 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.

  1. Obtain the source from the manufacturer or a trustworthy mirror. Verify the SHA256 against the value published with your adapter.
  2. Extract to /usr/src/rtl8188cus-<ver>:
  3. 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
  4. Load the module and confirm the device enumerated:
    sudo modprobe 8188cu
    dmesg | grep -i rtl
    iw dev
  5. 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
Setting both interfaces into the same subnet (for example, 192.168.1.0/24) without 802.1Q VLAN tagging is not a valid configuration. The kernel will respond to ARP for both addresses, but routing will be non-deterministic and OT-segmentation guarantees are lost. The Siemens support clarification is firm: different subnets are mandatory.

Verification Matrix

Field verification checklist after driver install and network bring-up
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

Common faults and remedies on the SIMATIC IoT2050
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.

Back to blog