Connecting Baltech RF1070R RFID Reader to SIMATIC IOT2050

David Krause11 min read
Sensor IntegrationSiemensTroubleshooting
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

1. Overview

The SIMATIC IOT2050 is a Linux-based industrial edge gateway from Siemens designed to retrofit existing plants with IIoT connectivity without changing existing controllers. The Baltech RF1070R is a 13.56 MHz multi-protocol RFID reader (ISO 14443 A/B, ISO 15693, NFC) that natively enumerates on a host PC as a USB composite device with several logical interfaces: a CCID smartcard channel, a RawSerial channel, and an HID keyboard channel. Integration on the IOT2050 therefore requires (a) selecting the correct logical interface inside the reader, (b) mapping the resulting USB endpoint to a stable /dev/ttyACMx or /dev/ttyUSBx node under the IOT2050 Example Image, and (c) consuming the resulting frames from Node-RED, a Python script, or the Baltech C/C++ SDK.

This reference walks through the full integration path for the IOT2050 Advanced (article number 6ES7647-0BA00-1YA2) using the Baltech SDK and Node-RED, and provides the troubleshooting matrix that resolves the most common failure modes reported during field bring-up: the reader not responding on the chosen tty node, the Baltech ConfigEditor HID profile not actually emitting frames, and Node-RED serial nodes receiving no data despite lsusb confirming enumeration.

2. Hardware Identification

2.1 SIMATIC IOT2050 Variants

Two hardware variants of the gateway are available and they must not be confused when selecting tty nodes or device-tree overlays:

Feature IOT2050 Basic (6ES7647-0BA00-0YA2) IOT2050 Advanced (6ES7647-0BA00-1YA2)
CPU ARM TI AM6528 dual-core ARM TI AM6548 quad-core
RAM / Flash 1 GB / 4 GB 2 GB / 16 GB
Arduino interface (internal) Available (X10/X11 headers) Available (X10/X11 headers)
X30 RS-232/RS-422/RS-485 port Not populated Populated, exposed as /dev/ttyUSB0
USB host ports 2× USB 2.0 Type-A 2× USB 3.0 Type-A
Typical use with RF1070R Direct USB enumeration only USB enumeration plus optional X30 serial

Both variants ship with the SIMATIC IOT2050 Example Image (a Debian-based Linux distribution with Node-RED, Grafana, and the Arduino toolchain preinstalled) and both expose the Arduino Mega-compatible microcontroller on /dev/ttyUSB0 by default on the Advanced variant. Refer to the official product page at SIMATIC IOT2050: Edge and cloud connectivity and the operating manual available through the SIMATIC IoT2050 Industrial Edge Device documentation portal for the binding pin-out and DT overlay names.

2.2 Baltech RF1070R Hardware

The RF1070R is a USB-powered desktop RFID reader with a 100 mm read range on ISO 15693 transponders. It exposes a composite USB device (VID 0x0BC7, PID 0x1006) that contains three logical functions selectable through the Baltech ConfigEditor:

  • HID Keyboard – UID is emitted as keystrokes terminated by CR/LF.
  • RawSerial – Frame-based binary protocol on a CDC-ACM tty.
  • CCID – PC/SC smartcard interface usable through pcscd and the Baltech CCID driver.

The default factory profile on most units is RawSerial at 115200 8N1. The reader's commissioning manual is published at SIMATIC RF1070R product page on the Siemens Industry Online Support portal.

3. SIMATIC IOT2050 USB-to-Serial Device Mapping

Plugging the RF1070R into a USB Type-A port on the IOT2050 creates a CDC-ACM device node. On the Advanced variant the stable mapping is:

Hardware Kernel node (Example Image) Notes
Arduino ATmega (internal, X10) /dev/ttyUSB0 Owned by the Arduino sketch loader, do not attach RFID frames.
RF1070R (USB Type-A) /dev/ttyACM0 or /dev/ttyACM1 Created by the cdc_acm driver when RF1070R is in RawSerial mode.
Arduino serial console /dev/ttyACM0 (only when board is in bootloader) Conflicts with RF1070R if both are connected simultaneously.

The IOT2050 Advanced also exposes a true RS-232/RS-422/RS-485 port on terminal block X30; this port is wired to a dedicated UART on the SoC and surfaces as /dev/ttyUSB0 only after the iio-hwmon-overlay and the appropriate UART DT overlay are loaded. Because that node overlaps with the Arduino interface, it is strongly recommended that RFID integration rely on the USB CDC-ACM path (/dev/ttyACM0) rather than X30.

4. Baltech RF1070R Operating Modes and Frame Format

4.1 RawSerial Binary Frame

When configured as RawSerial, the RF1070R emits and accepts binary frames delimited by STX (0x02) and ETX (0x03), followed by a 1-byte XOR-8 checksum. A successful tag read of a sample 8-byte UID appears on the line as:

02 04 93 02 08 04 6B 30 12 AB CD EF 03 E8
^---^ length n       ^--- n data bytes ---^  ^chk

Command 0x93 is the "Inventory" command in the Baltech protocol; command 0xB0 reads a single block, and 0xB1 writes a single block. The complete command reference ships with the Baltech SDK in the file doc/Baltech_Protocol.pdf.

4.2 HID Keyboard Emulation

HID mode requires no driver. The reader behaves like a USB keyboard and emits the UID as ASCII hex followed by a configurable terminator (default CR). This is the easiest path to integrate with Node-RED on the IOT2050 because the data appears in any tty that reads from the HID device, but it cannot differentiate between UID bytes and operator keystrokes and cannot perform block read/write.

4.3 CCID / PC/SC

In CCID mode the RF1070R is consumed by pcscd and exposed through the PC/SC API. This is the recommended mode when the application must run unmodified against the PC/SC abstraction or when contactless smartcards (ISO 14443-4) are involved. On the IOT2050 install pcscd with apt install pcscd libpcsclite-dev, then verify with pcsc_scan.

5. Step-by-Step Integration Procedure

Step 1 – Confirm USB enumeration

Connect the RF1070R to a free USB Type-A port, wait five seconds, and inspect the kernel:

lsusb
# expected: Bus 001 Device 004: ID 0bc7:1006 Baltech CardEngine

usb-devices
# expected: ... P: /dev/bus/usb/001/004 ... Cdc=02 ...

dmesg | tail -20
# expected: cdc_acm 1-1:1.0: ttyACM0: USB ACM device

If lsusb returns ID 0bc7:1006 but no ttyACM0 node appears, the reader is currently in HID mode and the cdc_acm driver is not loading for it. Proceed to Step 2.

Step 2 – Reconfigure the reader to RawSerial

  1. On a Windows PC, install the Baltech ConfigEditor (shipped with the reader).
  2. Connect the RF1070R, launch the ConfigEditor, and select the device in the tree view.
  3. Open Reader Settings → Interface and select RawSerial, 115200 8N1.
  4. Click Write Configuration, then power-cycle the reader.
  5. Reconnect to the IOT2050 and confirm that lsusb now lists two interfaces (HID + CDC) and that /dev/ttyACM0 exists.

A common failure mode is that the reader continues to behave as a keyboard after the configuration is "written". The ConfigEditor writes only to volatile RAM on some firmware revisions; you must explicitly tick Save as default (or run Save User Defaults) before power-cycling. Without this, the reader boots back into HID on every cold start.

Step 3 – Build the Baltech SDK from source

On the IOT2050, clone the SDK and build the sample applications. The canonical instructions are at Baltech Developer Try-Appnotes:

sudo apt update
sudo apt install -y cmake g++ libusb-1.0-0-dev pkg-config

git clone https://git.baltech.de/scm/sdk/baltech_sdk.git ~/baltech_sdk
cd ~/baltech_sdk
mkdir build && cd build
cmake .. -DCMAKE_BUILD_TYPE=Release
make -j4
sudo make install
sudo ldconfig

Verify the build by listing a connected reader:

~/baltech_sdk/build/bin/BR_READER_ListReader
# expected output: Reader 0: /dev/ttyACM0, firmware x.x.x

Step 4 – Run the Python wrapper from the SDK

The Baltech SDK ships a Python 3 binding in bindings/python. A minimal inventory loop looks like:

#!/usr/bin/env python3
import baltech

with baltech.Reader("/dev/ttyACM0") as r:
    while True:
        tags = r.inventory()
        for t in tags:
            print(t.uid.hex().upper(), flush=True)

Save this as /opt/baltech/inventory.py and run it under a systemd service so it survives reboots.

Step 5 – Bridge the inventory stream into Node-RED

Install the node-red-node-serialport palette entry and add an exec node configured to launch the Python helper above with stdout piped to msg.payload. The simplest equivalent that requires no extra palette entries uses an exec node set to spawn /opt/baltech/inventory.py:

[{"id":"x1","type":"exec","z":"...","command":"/opt/baltech/inventory.py",
  "addpay":false,"append":"","useSpawn":"false","timer":"","oldrc":false,
  "name":"RFID Inventory"}]

Wire the stdout output to a function node that converts the newline-separated UIDs into JSON, and from there to MQTT, an HTTP POST, or a Siemens S7 connector if the gateway must forward tags to a SIMATIC S7-1500 via the Node-RED S7 palette.

Step 6 – Fallback: HID keyboard path

If the SDk build chain is not available on the target (for example on the IOT2050 Basic with the 4 GB flash image), reconfigure the reader back to HID mode and read its keystrokes through a userspace helper such as evtest or through Node-RED's exec node reading /dev/input/eventN with cat. This loses the ability to issue block read/write but is the only path that works without the Baltech SDK installed.

6. Troubleshooting Matrix

Symptom Likely Cause Diagnostic Corrective Action
lsusb lists 0bc7:1006 but no ttyACM0 Reader still in HID profile usb-devices shows only HID interface Run Baltech ConfigEditor, switch to RawSerial, save defaults, power-cycle
/dev/ttyACM0 exists but Node-RED serial node receives nothing Baud rate mismatch or wrong logical port stty -F /dev/ttyACM0 115200 cs8 -cstopb -parenb -ixon Force 115200 8N1, no flow control; verify with cat /dev/ttyACM0
Data is keyboard garbage, not UIDs ConfigEditor was set to ASCII keyboard output, not raw hex Open ConfigEditor, Reader Settings → Output Format Select Hexadecimal UID in HID mode, or switch to RawSerial entirely
BR_READER_ListReader reports "no reader found" Permissions on /dev/ttyACM0 or conflicting process ls -l /dev/ttyACM0, lsof | grep ttyACM0 Add the service user to dialout group; stop any other process holding the tty
Reader responds once then hangs CDC-ACM driver not flushing on close dmesg shows "bulk timeout" Add echo 0 > /sys/module/cdc_acm/parameters/break_control to a startup script
Arduino serial console conflicts with RFID Both devices land on /dev/ttyUSB0 udevadm info -e | grep ttyUSB Write a udev rule that binds the Arduino symlink to /dev/iot2050_arduino
HID mode works on Windows but not on IOT2050 Windows HID driver auto-loads; Linux needs usbhid modprobe usbhid && lsmod | grep usbhid Ensure usbhid is loaded; blacklisting usbhid (done by some embedded images) must be reverted
Wine-based Windows app crashes Wine does not bridge USB devices by default wine cmd /c "regedit" missing entries Stop attempting to use Wine; use native Baltech SDK on Linux instead
SDK build fails on missing libusb-1.0 Developer package not installed pkg-config --modversion libusb-1.0 sudo apt install libusb-1.0-0-dev
Frames contain ETX followed by checksum but payload looks shifted Baud rate mismatch on host side Check with minicom -b 9600 vs -b 115200 Confirm reader and host agree on 115200 8N1, no flow control

7. Verification Checklist

After completing Steps 1–5, run the following checks to confirm a working installation:

  1. lsusb shows 0bc7:1006 with both HID and CDC interfaces.
  2. /dev/ttyACM0 exists and is readable by the Node-RED user.
  3. cat /dev/ttyACM0 (with the reader held over a tag) prints STX-delimited frames within 200 ms.
  4. ~/baltech_sdk/build/bin/BR_READER_ListReader lists at least one reader.
  5. The Python helper inventory.py prints UIDs continuously while a tag is in range.
  6. Node-RED exec node receives msg.payload strings matching the printed UIDs.
  7. After a power-cycle of the RF1070R (USB unplug / replug) the configuration persists.

If any step fails, return to the troubleshooting matrix and apply the corrective action associated with the failed symptom.

8. Security and Operational Notes

The RF1070R exposes a USB HID keyboard by default. On any production IOT2050 deployment, the HID profile must be disabled (switch to RawSerial or CCID) so that an attacker cannot inject keystrokes into a connected HMI session by holding a programmable tag near the antenna. On the Example Image, install usbguard and write a policy that allows only the Baltech VID/PID pair. The Siemens operating manual for the IOT2050 (delivered as PDF with the unit and mirrored on the SIMATIC IoT2050 Industrial Edge Device documentation portal) provides the hardened-image variants that ship with this policy preinstalled.

Which SIMATIC IOT2050 variant is recommended for RF1070R integration?

The IOT2050 Advanced (6ES7647-0BA00-1YA2) is recommended because it provides two USB 3.0 ports and 2 GB of RAM, leaving enough headroom for the Baltech SDK, Node-RED, and the Linux CDC-ACM driver to coexist. The Basic variant works but cannot run additional containers such as Grafana without swap pressure.

Why does the RF1070R enumerate as HID on the IOT2050 even after I configure it?

Most RF1070R units ship with RawSerial as the factory default, but the Baltech ConfigEditor writes to volatile RAM only unless the "Save as default" (also labelled "Save User Defaults") button is explicitly clicked before the device is power-cycled. Always perform the save, then unplug and replug the USB cable, then re-check with lsusb on the IOT2050.

What is the correct tty node to use on the IOT2050 Advanced?

Use /dev/ttyACM0 for the RF1070R. The IOT2050 Advanced exposes its internal Arduino microcontroller on /dev/ttyUSB0 and its X30 RS-232/RS-422/RS-485 port also on /dev/ttyUSB0; either of these will collide with a USB-serial adapter. The CDC-ACM node created by plugging the RF1070R into a USB Type-A port is the only unambiguous attachment point.

Can I run the Baltech Windows ConfigEditor on the IOT2050 via Wine?

No. Wine does not provide a working USB stack for the Baltech driver, and the ConfigEditor will crash or fail to detect the reader. Use a separate Windows PC for one-time configuration, then deploy the configured reader to the IOT2050 Example Image and communicate with it through the Linux-native Baltech SDK or through Node-RED's serialport node.

How do I persist the RF1070R configuration across reboots of the IOT2050?

Two layers are required: (1) inside the reader, save the RawSerial 115200 8N1 profile as the user default using the Baltech ConfigEditor, and (2) on the IOT2050, persist a udev rule such as SUBSYSTEM=="tty", ATTRS{idVendor}=="0bc7", ATTRS{idProduct}=="1006", SYMLINK+="rf1070r" in /etc/udev/rules.d/99-rf1070r.rules so that the device is always reachable at /dev/rf1070r regardless of enumeration order.

Back to blog