Enabling Internal UART on Siemens IOT2050 X11 Header

David Krause11 min read
Other TopicSiemensTutorial / How-to
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 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.

Both UARTs exposed through X11/X12 are 3.3 V LVTTL, not RS-232. Connecting directly to a 5 V or true RS-232 device will not work without a level shifter such as a MAX3232 or TI TRS3232E.

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

  1. A SIMATIC IOT2050 Basic (6ES7647-0BA00-0YA2) or Advanced (6ES7647-0BB00-0YA2) gateway.
  2. 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.
  3. 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).
  4. For the Industrial OS: the gateway booted into the pre-installed Siemens Industrial OS image.
  5. 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:

  1. Log in to the gateway. The default user is debian with password temppwd on older images; newer images force a password change on first boot.
  2. From a shell prompt, run:
    sudo iot2050setup
  3. Navigate to Peripherals and press Enter.
  4. Select Configure Arduino I/O.
  5. Highlight Enable UART and press Enter. The dialog shows the current state (Disabled by default).
  6. Select Apply. The tool will rewrite the device tree overlay and reboot the gateway.
  7. 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:

  1. Log in as admin via console or SSH.
  2. Run:
    sudo iot2050-config
  3. Open Peripherals.
  4. Select Configure Arduino Shield I/O and toggle UART to Enabled.
  5. 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:

  1. Copy the overlay to the boot partition:
    sudo cp k3-am65-iot2050-arduino-uart.dtbo /boot/overlays/
  2. Edit /boot/uEnv.txt and add:
    name_overlays=arduino-uart.dtbo
  3. Reboot and confirm with:
    sudo dtoverlay -l
The overlay name and directory are image-version specific. Check the release notes of the matching Siemens Industry Online Support download before copying the file.

Verification

After the gateway reboots with the UART overlay applied, perform the following checks:

  1. Device node presence
    ls -l /dev/ttyS1 /dev/ttyS3
    Both nodes should be present and owned by root:dialout.
  2. Pin function in pinmux
    sudo cat /sys/kernel/debug/pinctrl/pinctrl-handles | grep -i uart
    The handles should reference UART1 and UART3.
  3. 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.
  4. Scope check
    Probe X11.0 (D0 / TXD) with the oscilloscope set to 1 V/div and 100 µs/div. Running echo test > /dev/ttyS1 at 115200 8N1 must produce a clean 8-bit frame (start bit low, then 0x74, 0x65, 0x73, 0x74, two stop bits high).
  5. Driver binding
    cat /sys/devices/platform/bus@100000/2800000.serial/driver_override should return none and the parent device should show 2800000.serial as 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 -> UART after 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 stty profile after a kernel bump; you may need to set uart_8250.fifo=1 on 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.

Back to blog