Overview
The SIMATIC IOT2050 is a rugged industrial IoT gateway built around a Texas Instruments ARM Cortex-A53 SoC (AM6528 family) running either a Siemens Industrial OS (Yocto-based) or a Debian-based example image. The 40-pin Arduino-compatible footprint on the front face of the unit exposes four 10-pin high-density headers (X10, X11, X12, X13) that map digital I/O, analog inputs, I2C, SPI, and up to two asynchronous serial ports.
By factory default, the on-board UART transceivers are disabled in the device tree to keep the shields from interfering with applications that do not need serial I/O. As a result, scoping the Tx and Rx pins on X11 with an oscilloscope after a fresh install shows 0 V on both lines and writes to /dev/ttyS1 or /dev/ttyS3 produce no waveform. This is a configuration issue, not a hardware failure.
X10, X11, X12, X13 Header Architecture
The Arduino-shield socket on the IOT2050 mirrors the Arduino Uno R3 mechanical layout, but the electrical pinout is partitioned into four 10-pin dual-row connectors. Each connector carries a specific bus group:
| Header | Primary Function | Linux Device | Notes |
|---|---|---|---|
| X10 | Power + SPI + PWM | /dev/spidevX.Y |
5 V and 3.3 V rails, 6 PWM channels |
| X11 | Digital I/O + UART-A | /dev/ttyS1 |
Digital 0-7, A0-A5, I2C, INT |
| X12 | Digital I/O + UART-B | /dev/ttyS3 |
Digital 8-13, I2C, second SPI |
| X13 | Power + Reference | n/a | IOREF, RESET, 3V3, 5V, GND, VIN |
The two asynchronous serial channels are mapped to the AM6528 UART controllers as follows:
| Linux Device | AM6528 Instance | SoC Pad Group | Shield Pin | Logic Level |
|---|---|---|---|---|
/dev/ttyS1 |
UART1 | UART1_RXD / UART1_TXD | X11.0 (D0) / X11.1 (D1) | 3.3 V LVTTL |
/dev/ttyS3 |
UART3 | UART3_RXD / UART3_TXD | X12.0 (D3) / X12.1 (D4) | 3.3 V LVTTL |
The Linux kernel binds the AM6528 UARTs to the standard 8250 serial driver framework. Baud rates from 50 bps to 3 Mbaud are supported in hardware, with software-driven operation possible up to 4 Mbaud. For details on the 8250 driver architecture and its use in TI Sitara devices, see the TI Processor SDK AM64x UART documentation - the AM65x family shares the same IP block and driver.
Prerequisites
- A SIMATIC IOT2050 Basic (6ES7647-0BA00-0YA2) or Advanced (6ES7647-0BB00-0YA2) gateway.
- Access to the console either through the DisplayPort + USB keyboard, via SSH over the Ethernet management port (eth0, default
192.168.200.1), or through the serial debug port on the bottom of the unit. - For the Debian example image: a microSD card flashed with the IOT2050 example image from the Siemens Industry Online Support portal (search order number 6ES7647-0BA00-0YA2 or 6ES7647-0BB00-0YA2).
- For the Industrial OS: the gateway booted into the pre-installed Siemens Industrial OS image.
- For signal verification: an oscilloscope or logic analyzer with a minimum 10 MHz bandwidth, and a UART loopback header or shielded cable to the target peripheral.
Root Cause of the 0 V Symptom
The AM6528 system-on-chip multiplexes most of its peripheral pins. On the IOT2050, the kernel device tree is compiled with a default pinmux that routes the pads to GPIO, not to the UART function. The transceiver on the shield socket is therefore unpowered in functional terms: the SoC pads are inputs with weak pull-downs, and the Tx line is held at logic low.
This is intentional - it keeps the IOT2050 compatible with shields that treat the digital pins as plain GPIO - and it is the reason the standard recipe for enabling an Arduino shield on this gateway is a software configuration step rather than a hardware jumper. Until the configuration is applied, no signal will appear on the UART pins regardless of how the user-space write() call is performed.
Solution A: Enable UART on the Example Image (Debian)
On the Debian-based example image the configuration is performed through the iot2050setup TUI:
- Log in to the gateway. The default user is
debianwith passwordtemppwdon older images; newer images force a password change on first boot. - From a shell prompt, run:
sudo iot2050setup - Navigate to Peripherals and press Enter.
- Select Configure Arduino I/O.
- Highlight Enable UART and press Enter. The dialog shows the current state (Disabled by default).
- Select Apply. The tool will rewrite the device tree overlay and reboot the gateway.
- After reboot, verify the new device nodes exist:
ls -l /dev/ttyS1 /dev/ttyS3
For a non-interactive deployment on a headless unit, the same tool can be scripted:
# Force-enable UART in a CI/CD or golden-image scenario
sudo iot2050setup --set peripherals.arduino-uart enabled
sudo reboot
Solution B: Enable UART on the Industrial OS
On the Siemens Industrial OS image, the configuration is performed through the iot2050-config companion tool:
- Log in as
adminvia console or SSH. - Run:
sudo iot2050-config - Open Peripherals.
- Select Configure Arduino Shield I/O and toggle UART to Enabled.
- Save and reboot.
Industrial OS persists the configuration in /etc/iot2050/state.json and re-applies the overlay on every boot, so a power loss will not silently disable the UART.
Solution C: Manual Device Tree Overlay
For users who build a custom Yocto layer, embed the gateway into a container, or run a fully headless deployment without iot2050setup, the UART can be enabled by installing a prebuilt overlay from Siemens and pointing the U-Boot environment at it:
- Copy the overlay to the boot partition:
sudo cp k3-am65-iot2050-arduino-uart.dtbo /boot/overlays/ - Edit
/boot/uEnv.txtand add:name_overlays=arduino-uart.dtbo - Reboot and confirm with:
sudo dtoverlay -l
Verification
After the gateway reboots with the UART overlay applied, perform the following checks:
-
Device node presence
ls -l /dev/ttyS1 /dev/ttyS3
Both nodes should be present and owned byroot:dialout. -
Pin function in pinmux
sudo cat /sys/kernel/debug/pinctrl/pinctrl-handles | grep -i uart
The handles should referenceUART1andUART3. -
Loopback test
Short X11.0 to X11.1 with a jumper, then run:stty -F /dev/ttyS1 115200 cs8 -cstopb -parenb raw
cat /dev/ttyS1 &
echo "IOT2050-UART1-OK" > /dev/ttyS1
The string should echo back to the terminal. -
Scope check
Probe X11.0 (D0 / TXD) with the oscilloscope set to 1 V/div and 100 µs/div. Runningecho test > /dev/ttyS1at 115200 8N1 must produce a clean 8-bit frame (start bit low, then 0x74, 0x65, 0x73, 0x74, two stop bits high). -
Driver binding
cat /sys/devices/platform/bus@100000/2800000.serial/driver_overrideshould returnnoneand the parent device should show2800000.serialas the active driver node.
UART Signal Parameters
The IOT2050 UARTs use a 48 MHz functional clock sourced from the AM6528 peripheral PLL. Supported parameter ranges on a stock Industrial OS 1.3.x or Example Image 1.3.x are:
| Parameter | Range | Default |
|---|---|---|
| Baud rate | 50 bps - 3 Mbaud (hardware), up to 4 Mbaud (software divisor) | 9600 bps |
| Data bits | 5, 6, 7, 8 | 8 |
| Parity | none, odd, even | none |
| Stop bits | 1, 2 | 1 |
| Flow control | none, RTS/CTS, XON/XOFF (software) | none |
| FIFO threshold | 1, 4, 8, 14 bytes | 8 bytes |
Configure with stty:
stty -F /dev/ttyS1 115200 cs8 -cstopb -parenb crtscts raw echo
For background on the asynchronous serial protocol itself, refer to the Analog Devices UART primer, which covers framing, baud-rate tolerance, and parity implementation that all 8250-derived controllers (including the AM6528 UART IP) follow.
Programming the GPIO Pins with libmraa
Once the UART overlay is active, the same mraa library that manages the shield pins can drive digital I/O without conflicting with the serial driver, as long as the pins are not in the UART set. libmraa ships in the Siemens example image as a Debian package and in the Industrial OS as a sysroot component.
Pin mapping reference for X11 (without a shield installed, the pin numbers follow the Arduino convention):
| Shield Label | mraa Index | Kernel GPIO (sysfs, when exported) |
|---|---|---|
| X11.5 - Digital 4 | MRAA_PIN_X11_D4 |
GPIO4 (SoC pad ball G4) |
| X11.6 - Digital 5 | MRAA_PIN_X11_D5 |
GPIO5 |
| X11.7 - Digital 6 | MRAA_PIN_X11_D6 |
GPIO6 |
| X12.0 - Digital 8 | MRAA_PIN_X12_D8 |
GPIO8 |
| X12.1 - Digital 9 | MRAA_PIN_X12_D9 |
GPIO9 |
Note: the kernel GPIO numbers are not stable across board revisions; the mraa index is the supported API. Treat any direct sysfs mapping as informational only and prefer mraa in production code.
Example: blink a LED on X11.5 (Digital 4):
// mraa_blink.c
#include <mraa.h>
#include <unistd.h>
int main(void) {
mraa_init();
mraa_gpio_context led = mraa_gpio_init(X11_D4);
if (led == NULL) {
fprintf(stderr, "Failed to initialise GPIO\n");
return 1;
}
mraa_gpio_dir(led, MRAA_GPIO_OUT);
for (int i = 0; i < 10; i++) {
mraa_gpio_write(led, 1);
usleep(200000);
mraa_gpio_write(led, 0);
usleep(200000);
}
mraa_gpio_close(led);
mraa_deinit();
return 0;
}
Build and run on the example image:
sudo apt install libmraa-dev gcc
gcc mraa_blink.c -o mraa_blink -lmraa
sudo ./mraa_blink
For scripted access without writing C code, mraa provides three command-line utilities:
-
mraa-gpio- read/write/export GPIO pins -
mraa-i2c- talk to I2C devices on the shield bus -
mraa-uart- configure and use the shield UARTs from a shell
Example: toggle the LED using mraa-gpio:
mraa-gpio set X11_D4 1
sleep 1
mraa-gpio set X11_D4 0
Direct sysfs GPIO Access (Legacy)
Modern mraa releases still work with the deprecated /sys/class/gpio interface, but the pin-to-GPIO mapping is not fixed across firmware versions and Siemens has stated that no published mapping table is guaranteed. If a deployment requires sysfs access for an existing driver, capture the mapping on a reference unit with:
for n in $(seq 0 200); do
if [ -d /sys/class/gpio/gpio$n ]; then
label=$(cat /sys/class/gpio/gpio$n/label 2>/dev/null)
echo "gpio$n -> $label"
fi
done
Use the result as a frozen reference, and re-validate after every firmware or mraa upgrade.
Troubleshooting Matrix
| Symptom | Likely Root Cause | Action |
|---|---|---|
| Tx and Rx sit at 0 V with no transitions | UART overlay not loaded | Re-run iot2050setup -> Enable UART; reboot |
/dev/ttyS1 missing |
Overlay failed to apply or image version mismatch | Check dmesg | grep serial for pinmux errors; verify overlay matches firmware |
| TXD toggles, RXD always high | Shield not driving RXD, or RXD pin not configured | Confirm wiring to D0/D1; verify peripheral is at 3.3 V |
| Garbage characters, correct framing | Baud rate mismatch | Match stty baud on both sides; check for clock-related baud error > 2 % |
| Characters received inverted | Peripheral uses RS-232 levels | Add MAX3232 or TRS3232E level shifter; do not tie RS-232 directly to shield pins |
mraa returns NULL on mraa_gpio_init
|
Pin already owned by UART overlay | Select a different digital pin or remove the UART overlay for the affected header |
Permission denied on /dev/ttyS1
|
User not in dialout group |
sudo usermod -aG dialout $USER and re-login |
| Loopback works, peripheral does not respond | Half-duplex wiring or 5 V device | Verify pinout, level, and that peripheral expects 3.3 V CMOS UART |
Integration with the Same Header Stack
The X11/X12 sockets carry more than UART. With the UART overlay applied, the following buses become available simultaneously:
| Bus | Linux Interface | Speed |
|---|---|---|
| UART-A (X11) | /dev/ttyS1 |
up to 3 Mbaud |
| UART-B (X12) | /dev/ttyS3 |
up to 3 Mbaud |
| I2C | /dev/i2c-2 |
100 / 400 / 1000 kHz |
| SPI | /dev/spidev0.0 |
up to 25 MHz |
If an application needs both UART-A and the digital I/O on X11.5-X11.7, mraa allows concurrent ownership because the UART IP does not consume those digital pads. The pinmux is per-pin, not per-bus.
Edge Cases and Field Notes
-
Industrial OS upgrades wipe the overlay state. The user must re-apply
iot2050-config -> Peripherals -> UARTafter any firmware update, even if the update is supposed to preserve configuration. - Loop-power and shield supply. Arduino shields drawing more than 200 mA from the 3V3 rail can pull the IOT2050 into brown-out. Use an external 3.3 V regulator when stacking motor or relay shields.
- ESD on unshielded headers. X10-X13 are not protected to industrial ESD levels. Add a TVS array (e.g. PESD3V3L4UG) on the cable harness if the unit is installed outside a cabinet.
-
Kernel upgrade from 5.10 to 6.1. The 8250 driver changed default FIFO threshold handling. Re-test with the same
sttyprofile after a kernel bump; you may need to setuart_8250.fifo=1on the kernel command line.
FAQ
Why does my scope show 0 V on X11 Tx and Rx after a fresh install?
The AM6528 SoC pads default to GPIO with weak pull-downs. The UART function must be enabled through the device tree via iot2050setup -> Peripherals -> Configure Arduino I/O -> Enable UART (Example Image) or iot2050-config (Industrial OS), followed by a reboot.
What is the difference between /dev/ttyS1 and /dev/ttyS3 on the IOT2050?
/dev/ttyS1 maps to UART1 and is exposed on X11 pins D0 (TX) and D1 (RX). /dev/ttyS3 maps to UART3 and is exposed on X12 pins D3 (TX) and D4 (RX). Both run at 3.3 V LVTTL and use the AM6528 8250-compatible controller.
Can I drive digital pins and the UART on the same header at the same time?
Yes. The X11 overlay binds only the D0/D1 pads to the UART function; D2-D7 remain available for GPIO. Use libmraa (preferred) or the mraa command-line tools to toggle them.
Is there a published mapping table for the digital pins to /sys/class/gpio/gpioXXX?
No. Siemens does not publish a fixed mapping because the GPIO numbers can change between firmware versions. Use the mraa index (for example, MRAA_PIN_X11_D4) as the supported API instead of hard-coded sysfs paths.
Do I need a level shifter to connect the shield UART to a 5 V Arduino?
Yes. The IOT2050 shield pins are 3.3 V and are not 5 V tolerant. Use a bidirectional level shifter such as a TXS0108E or a discrete MOSFET-based translator when connecting to a 5 V MCU. True RS-232 signals must be conditioned with a MAX3232 or TRS3232E before reaching the shield.