Configuring LoRaWAN on Siemens IOT2040 with RN2483 Module

David Krause12 min read
Industrial NetworkingSiemensTutorial / 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

1. Overview

The Siemens SIMATIC IOT2040 (and its successor SIMATIC IOT2050) is a ruggedized industrial gateway based on an Intel Quark x86 processor. It runs the Siemens Example Image, a Yocto-derived Linux distribution that exposes RS-232, RS-485, Ethernet, USB, and GPIO for protocol bridging, edge analytics, and SCADA integration. Because the platform exposes a full POSIX userspace, it can host any serial-attached transceiver that speaks an ASCII command set.

This article documents the field-proven integration of Microchip RN2483 LoRaWAN modules with the IOT2040 for bidirectional telemetry in private (self-hosted gateway) and public (operator-hosted) LoRaWAN networks. The configuration works with the IOT2050 as well, since the Example Image tooling and UART mapping are preserved across hardware revisions. Two physical integration paths are covered: direct UART connection to the on-board RS-232 header, and the EXPEMB FlexSensor LoRaWAN Shield stacked on the Arduino-compatible header.

Scope: This guide focuses on the LoRaWAN Class A device-side stack. Gateway selection (e.g., Digi XB-iot gateways), network server configuration (TTN, ChirpStack, Actility), and application server payload encoding are described only at the contract level required by the embedded side.

2. LoRaWAN Protocol Fundamentals

LoRaWAN is a low-power, wide-area network (LPWAN) protocol layered on top of the proprietary LoRa chirp-spread-spectrum modulation. The modulation and the MAC layer are independently specified:

  • LoRa (physical layer): Chirp spread spectrum operating in sub-GHz ISM bands. Regional duty-cycle and channel plans are defined by the LoRa Alliance regional parameters document (RP2-1.0.3 for the latest revision).
  • LoRaWAN (MAC layer): Star-of-stars topology in which end devices communicate exclusively with a gateway that forwards the frame to a network server. The network server handles deduplication, ADR (Adaptive Data Rate), and security.

Three device classes are defined:

Class Receive Windows Power Profile Typical Use
A Two, only after uplink Lowest Battery sensors, metering
B Scheduled ping slots Medium Actuator control
C Continuous (except while transmitting) Highest Mains-powered actuators

The RN2483 ships as a Class A device. To upgrade to Class C, the RN2903 or firmware >= 1.0.5 is required; verify against the Microchip LoRaWAN stack release notes.

3. IOT2040 Hardware Architecture

The IOT2040 platform is documented in the Siemens IOT2040 Operating Instructions and the IOT2050 manual set. Relevant interfaces for this integration:

Interface Connector Use for LoRaWAN
RS-232 (X30 COM1) RJ45, 3-wire Direct RN2483 UART (TX/RX/GND)
RS-485 (X31 COM2) RJ45 Not used; LoRaWAN is point-to-point UART
Arduino shield header (X20/X21) 2x 16-pin EXPEMB FlexSensor shield socket
USB 2.0 Type-A Optional debug console
Ethernet RJ45 x2 Backhaul to WinCC OA / SCADA

The on-board RS-232 transceiver is a TRS3122E or compatible, providing true ±5 V levels. The RN2483 logic is 3.3 V CMOS, so a level shifter (e.g., TXS0108E or discrete MOSFET bridge) is mandatory when wiring directly. The EXPEMB shield integrates the level translation and the SMA antenna connector.

4. Microchip RN2483 Module Specifications

The RN2483 is a fully certified 868 MHz LoRaWAN module from Microchip. Key parameters from the RN2483 datasheet (DS50002346C):

Parameter Value
Frequency band 863–870 MHz (EU/ETSI)
Modulation LoRa (CSS), FSK/OOK for legacy
TX power +14 dBm max (programmable, 1 dB steps)
RX sensitivity -148 dBm at SF12/125 kHz
UART 3.3 V CMOS, 8N1, default 57600 bps
Operating voltage 2.1–3.6 V (typ. 3.3 V)
MAC LoRaWAN 1.0.2 (Class A)
Certifications ETSI EN 300 220, FCC, IC, ARIB

The module exposes a UART-driven ASCII command interface that returns line-terminated responses. Every command line is terminated by \r\n and the module echoes commands unless echo is disabled with sys set echo off.

5. Hardware Integration Paths

5.1 Direct UART (X30 → RN2483)

Wire the RN2483 evaluation board to the IOT2040 COM1 (X30 RJ45) using the Siemens standard RS-232 pinout. The shield ground is pin 4; TXD/RXD are pins 3 and 5 in the Siemens convention.

IOT2040 X30 (RS-232)         RN2483 (3.3V CMOS)
Pin 3  RXD  ------------>  TX (3.3V via level shifter)
Pin 5  TXD  <------------  RX (3.3V CMOS)
Pin 4  GND  -------------  GND
                          VCC  <- 3.3V from IOT2040 5V via LDO

Level translation is mandatory. The IOT2040 RS-232 swings ±5 V and will damage the 3.3 V RN2483 inputs. Use a TXS0102 or four-resistor + NPN bridge.

5.2 EXPEMB FlexSensor LoRaWAN Shield

The EXPEMB FlexSensor LoRaWAN Shield for IOT2000 plugs directly onto the Arduino-compatible shield header (X20/X21) on the IOT2040. It carries the RN2483, SMA antenna connector, level shifters, and a reset line wired to a GPIO. The shield communicates with the host over UART, mapped to /dev/ttyS2 by the Example Image device tree overlay.

Siemens has not shipped a release of the Example Image with native device-tree bindings for the shield; the overlay must be applied manually or the user must reference the UART node under &uart2 in the IOT2040 BSP. Confirm overlay availability against the Example Image release notes for your installed image version.

6. Configuring the Serial Port on the IOT2040

The Example Image ships with stty, setserial, and the Linux serial drivers preinstalled. The RN2483 default baud rate is 57600 8N1 with hardware flow control disabled.

# Identify COM1
ls -l /dev/ttyS*
# /dev/ttyS0  (console on debug UART)
# /dev/ttyS1  (RS-485 COM2)
# /dev/ttyS2  (RS-232 COM1, used for LoRaWAN)

# Configure 57600 8N1, raw mode
stty -F /dev/ttyS2 57600 cs8 -cstopb -parenb -ixon -ixoff raw -echo

To verify the link, send the RN2483 firmware version query:

echo -e 'sys get ver\r' > /dev/ttyS2
cat /dev/ttyS2 &
sleep 1
# Expected: "RN2483 1.0.5 Dec 15 2017 09:38:09" (or similar)

If the version string is not returned within 1 s, check the level shifter polarity (RN2483 TX is high-idle) and confirm the antenna is attached (transmissions without antenna can corrupt firmware on some boards).

7. Joining the LoRaWAN Network

Over-the-air activation (OTAA) is the recommended path because the session keys are derived per session and rotated by the network server. Provision the device on the network server (TTN console, ChirpStack, or Actility ThingPark) and capture the three OTAA parameters:

  • DevEUI — 8-byte device identifier, printed on the module label or returned by sys get hweui (RN2483 uses the hardware EUI as the default DevEUI).
  • AppEUI — 8-byte application identifier, sometimes called JoinEUI in LoRaWAN 1.1.x.
  • AppKey — 16-byte AES-128 root key, provisioned out-of-band.
# Persist credentials on the module
mac set deveui  70B3D57ED0051234
mac set appeui  70B3D57ED000ABCD
mac set appkey  4A5B6C7D8E9F0A1B2C3D4E5F60718293

# Confirm LoRaWAN regional band (EU868 for EU deployments)
mac set ch drrange dn 0 5   # mandatory channels 0..2 in EU868, default DR0..DR5
mac set pwridx 1            # +10 dBm TX power (start low for lab work)

# Save and join
mac save
mac join otaa
# Expected: "accepted" within 5–10 s on a healthy link

If the response is denied or no_free_ch, the gateway is not reachable. Verify uplink on the gateway server, then re-attempt the join with mac join otaa. The join attempt opens two receive windows (RX1 1 s after uplink, RX2 2 s after uplink); an answered join returns accepted.

8. C++ Application on the IOT2040

A minimal C++ wrapper around the POSIX serial port is sufficient for telemetry pipelines. The Example Image toolchain includes GCC 7.x and the standard Boost libraries, but a hand-written termios loop is leaner and avoids dependency overhead.

// rn2483.hpp
#pragma once
#include <string>
#include <termios.h>

class RN2483 {
public:
    explicit RN2483(const std::string& dev = "/dev/ttyS2");
    ~RN2483();
    std::string send(const std::string& cmd, int timeout_ms = 1000);
    bool joinOTAA(const std::string& devEui,
                  const std::string& appEui,
                  const std::string& appKey);
    bool sendUplink(const std::string& payloadHex,
                    bool confirmed = true,
                    int port = 1);
private:
    int fd_ = -1;
    struct termios tio_{};
    void drain();
};
// rn2483.cpp
#include "rn2483.hpp"
#include <fcntl.h>
#include <unistd.h>
#include <poll.h>
#include <sys/select.h>
#include <cstring>
#include <iostream>

RN2483::RN2483(const std::string& dev) {
    fd_ = ::open(dev.c_str(), O_RDWR | O_NOCTTY | O_NONBLOCK);
    if (fd_ < 0) throw std::runtime_error("open failed: " + dev);
    ::tcgetattr(fd_, &tio_);
    ::cfmakeraw(&tio_);
    ::cfsetispeed(&tio_, B57600);
    ::cfsetospeed(&tio_, B57600);
    tio_.c_cc[VMIN]  = 0;
    tio_.c_cc[VTIME] = 1;
    ::tcsetattr(fd_, TCSANOW, &tio_);
}

RN2483::~RN2483() { if (fd_ >= 0) ::close(fd_); }

void RN2483::drain() {
    char buf[256];
    while (::read(fd_, buf, sizeof(buf)) > 0) {}
}

std::string RN2483::send(const std::string& cmd, int timeout_ms) {
    drain();
    std::string out = cmd + "\r\n";
    ::write(fd_, out.data(), out.size());
    std::string resp;
    char c;
    struct timeval tv { timeout_ms / 1000, (timeout_ms % 1000) * 1000 };
    fd_set rfds; FD_ZERO(&rfds); FD_SET(fd_, &rfds);
    while (::select(fd_ + 1, &rfds, nullptr, nullptr, &tv) > 0) {
        ssize_t n = ::read(fd_, &c, 1);
        if (n <= 0) break;
        if (c == '\n') break;
        resp.push_back(c);
    }
    return resp;
}

bool RN2483::joinOTAA(const std::string& devEui,
                      const std::string& appEui,
                      const std::string& appKey) {
    send("mac set deveui " + devEui);
    send("mac set appeui " + appEui);
    send("mac set appkey " + appKey);
    send("mac save");
    auto r = send("mac join otaa", 8000);
    return r.find("accepted") != std::string::npos;
}

bool RN2483::sendUplink(const std::string& payloadHex,
                        bool confirmed, int port) {
    std::string prefix = confirmed ? "mac tx cnfport " : "mac tx uncnfport ";
    auto r = send(prefix + std::to_string(port) + " " + payloadHex, 6000);
    return r.find("ok") != std::string::npos;
}

Compile with g++ -O2 -std=c++17 rn2483.cpp main.cpp -o lora_app and deploy to the IOT2040. A systemd unit can supervise the application across reboots:

# /etc/systemd/system/lora-app.service
[Unit]
Description=RN2483 LoRaWAN uplink
After=network-online.target
Wants=network-online.target

[Service]
ExecStart=/opt/lora/lora_app
Restart=on-failure
RestartSec=5
User=root

[Install]
WantedBy=multi-user.target

Enable with systemctl daemon-reload && systemctl enable --now lora-app.service.

9. WinCC OA Integration

WinCC OA (formerly PVSS) can subscribe to the IOT2040 over OPC UA or Modbus TCP, both of which the Example Image exposes through optional packages. The reference architecture used by the source integration is:

  1. IOT2040 receives LoRaWAN uplinks via RN2483.
  2. A C++ parser on the IOT2040 decodes the CayenneLPP payload and writes measurements to an OPC UA server (e.g., open62541).
  3. WinCC OA connects to the OPC UA endpoint opc.tcp://iot2040.local:4840 and maps each measurement to a datapoint.

CayenneLPP is the recommended payload format because it provides a compact binary encoder for the most common sensor types (temperature, humidity, digital input, analog input). A typical 4-byte temperature frame is 01 67 01 10 (channel 1, temperature, signed 16-bit 0.1 °C, value 272 = 27.2 °C).

When OPC UA is not available on the WinCC OA side, the IOT2040 can publish a Modbus TCP holding register set on port 502 and WinCC OA can poll it via the Modbus/TCP driver. Use libmodbus on the IOT2040 and configure WinCC OA driver type MODBUS with the appropriate register map.

10. Adaptive Data Rate and Channel Planning

EU868 mandates a 1 % duty cycle on the default channels. The RN2483 firmware enforces the duty cycle internally once the channel mask is set correctly:

mac set ch drrange dn 0 5    # ch0  DR0..DR5 (mandatory 868.1 MHz)
mac set ch drrange dn 1 5    # ch1  DR0..DR5 (mandatory 868.3 MHz)
mac set ch drrange dn 2 5    # ch2  DR0..DR5 (mandatory 868.5 MHz)
mac set ch status 3 on       # enable default channels

For links with good RSSI margins, raise the data rate with mac set dr 5 to maximize airtime headroom. ADR negotiation can be enabled with mac set adr on; the network server will then optimize the DR on each uplink.

11. Commissioning Procedure

  1. Flash or update the Example Image to the latest supported release per Siemens Example Image release notes.
  2. Power-cycle the IOT2040 with the antenna attached and verify sys get ver returns the RN2483 firmware string.
  3. Configure the serial port with stty as shown in Section 6.
  4. Provision OTAA credentials from the network server console.
  5. Issue mac join otaa and confirm accepted.
  6. Send a confirmed uplink (mac tx cnfport 1 AAAA) and confirm ok and a server-side reception log entry.
  7. Verify in WinCC OA that the datapoint updates within the expected airtime + backhaul latency budget (typically < 6 s for SF7).
  8. Enable the systemd unit for unattended operation.

12. Troubleshooting Matrix

Symptom Likely Cause Remediation
sys get ver returns nothing Wrong UART, missing level shifter, baud mismatch Confirm /dev/ttyS2 at 57600 8N1; check level translation; verify GND is common
mac join otaa → denied DevEUI/AppEUI/AppKey mismatch or gateway down Recheck credentials against network server; confirm gateway uplink packets reach the network server
mac join otaa → no_free_ch Duty cycle exhausted Wait 60 s and retry; check that channel mask is correct for the region
Uplink ok but no payload on network server Wrong DevAddr or AppSKey after ABP migration Reset to OTAA; do not mix OTAA and ABP key sets on the same DevEUI
Periodic resets of RN2483 Insufficient current on TX bursts Add 100 µF bulk capacitor at module VCC; ensure supply can deliver 120 mA peak
WinCC OA sees stale data OPC UA session timeout Lower publish interval below session timeout; configure keepalive
RX2 misses confirmed downlinks Default RX2 window set to DR0 / 869.525 MHz missing on gateway Confirm RX2 frequency and DR are enabled on the gateway; align mac set rx2 <freq> <dr>
High packet loss at long range Antenna mismatch or SF too low Switch to SF9..SF12; verify antenna SWR with VNA or return-loss meter

13. Migration to IOT2050

The IOT2050 supersedes the IOT2040 and exposes the same Example Image with an updated device tree. The COM1 RS-232 UART is mapped to /dev/ttyS2 on the IOT2050 as well; existing RN2483 wiring harnesses are reusable. The Arduino shield header is preserved on the IOT2050 Advanced variant, so the EXPEMB FlexSensor shield is forward-compatible. When migrating, validate against the latest IOT2050 manual for any UART pinout revisions.

14. Field-Proven Caveats

  • Some RN2483 firmware revisions prior to 1.0.4 have a bug where mac save corrupts the AppKey on a brown-out. Always re-read credentials with mac get appkey after a power cycle.
  • The Example Image console is also routed to a UART; do not connect the RN2483 to /dev/ttyS0.
  • For an industrial installation, place the antenna outside any metal enclosure and use a lightning arrestor on outdoor cable runs. The RN2483 has no internal surge protection.
  • When the IOT2040 is deployed in a cabinet with other RF emitters, mount the antenna at least 30 cm from any switching power supply or VFD cable.

Does the Siemens IOT2040 support LoRaWAN natively?

No. The IOT2040 has no onboard LoRa radio. LoRaWAN connectivity is added with an external module such as the Microchip RN2483 (UART) or the EXPEMB FlexSensor shield stacked on the Arduino header. A device-tree overlay may be required to enable the shield UART.

What serial parameters does the RN2483 require?

57600 bps, 8 data bits, no parity, 1 stop bit (8N1), no hardware flow control. The module idles high; mismatched polarity will appear as a silent UART.

Which LoRaWAN bands does the RN2483 support?

The RN2483 is the EU/ETSI 868 MHz variant. For 915 MHz (US/Asia/Australia) use the RN2903. Mixing modules across regions violates ETSI and FCC rules.

Can I run ABP instead of OTAA?

Yes, but OTAA is recommended for production because it rotates session keys on every join. ABP is acceptable only for permanent test devices with fixed keys; LoRaWAN 1.1.x additionally requires the NwkSKey and AppSKey plus a JoinSKey.

How do I forward LoRaWAN uplinks to WinCC OA?

Decode the payload on the IOT2040 (CayenneLPP or proprietary) and publish the measurements through OPC UA (open62541) or Modbus TCP (libmodbus). WinCC OA connects to the OPC UA endpoint or polls the Modbus registers. Keep the publish interval below the OPC UA session timeout (default 60 s).

Back to blog