Resolving Telit HE910-D Modem Setup on Siemens IOT2040 Images

David Krause11 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

Hardware Overview: Telit HE910-D on Siemens IOT2040

The Siemens SIMATIC IOT2040 is an Intel Quark (x86) industrial gateway with a mini-PCIe slot that accepts the Telit HE910-D cellular modem. The HE910-D is a HSPA+ (UMTS/GSM) module on a PCIe form factor, and the gateway's built-in SIM holder drives its USIM line. The "D" variant differs from the plain HE910 by supporting a second diversity antenna path, which improves receiver performance in low-RSRP installations but otherwise shares the same AT command set and PPP interfaces. The module exposes its diagnostic and AT ports as a USB CDC-ACM device at /dev/ttyACM0 once the option and cdc_acm kernel modules bind the USB endpoints.

Verify that the SIM card is seated in the SIM holder under the service hatch on the IOT2040 mainboard. The HE910-D powers up and answers AT commands even with no SIM inserted, but packet service registration fails at the network-attach step.
Key hardware identifiers for the HE910-D on IOT2040
Item Value
Modem VID:PID (USB) 1bc7:0021 (Telit HE910-D)
Diagnostic / AT port /dev/ttyACM0
PPP-capable port /dev/ttyACM0 (after ATD*99#)
Default baud 115200 bps, 8N1
SIM holder Mainboard-mounted micro-SIM
Antenna connectors MAIN (primary) + AUX (diversity)

Custom SD Image Build: Required Yocto Layers

The Siemens-provided IOT2000 image is built on the Yocto Project (Kirkstone / Dunfell branches depending on the release). When the standard iot2000setup-tool is included but the cellular stack is missing, the build must add the meta-openembedded networking and cellular layers, plus a custom bbappend for the modem-aware recipes. Source the upstream image configuration from the official siemens/iot2000 GitHub repository.

The following recipe set must be merged into local.conf or a custom image recipe before bitbaking:

IMAGE_INSTALL_append = " \\
    ppp pppd \\
    ofono \\
    connman connman-client \\
    iot2000setup \\
    usbutils usb-modeswitch \\
    kernel-module-ppp-generic \\
    kernel-module-ppp-async \\
    kernel-module-ppp-deflate \\
    kernel-module-ppp-mppe \\
    kernel-module-slhc \\
    kernel-module-option \\
    kernel-module-cdc-acm \\
    kernel-module-usbserial \\
"
The kernel-module-option driver is mandatory for the HE910-D to enumerate its USB endpoints as /dev/ttyACM0. Without it, the modem powers up but produces no /dev/ttyACM* nodes. That is the most common cause of list-modems reporting Type=hardware with Powered=0 and Online=0.

After bitbaking, flash the resulting .wic image to a Class 10 SD card (8 GB minimum) using dd or balenaEtcher. Boot the IOT2040, log in as root via the debug UART or HDMI console, and confirm the kernel log shows the option driver binding the Telit VID:

dmesg | grep -i telit
# expected output:
# option 1-1:1.0: GSM modem (1-port) converter detected
# usb 1-1: GSM modem (1-port) converter now attached to ttyACM0

Required Kernel Modules and Device Nodes

A persistent failure mode in the field is the absence of /dev/ppp after pppd launches. The IOT2000 default kernel does not auto-load ppp_generic and slhc, and the Yocto image's udev rules do not create the /dev/ppp character device. Load the modules explicitly and create the device node:

modprobe ppp_generic
modprobe slhc
modprobe ppp_async
modprobe ppp_deflate
modprobe ppp_mppe
mknod /dev/ppp c 108 0
chmod 600 /dev/ppp

Persist the modules by listing them in /etc/modules-load.d/ppp.conf so they survive reboot:

# /etc/modules-load.d/ppp.conf
ppp_generic
slhc
ppp_async
ppp_deflate
ppp_mppe
option
cdc_acm

A correct enumeration at boot should look like this:

lsmod | grep -E "ppp|option|cdc"
ls -l /dev/ppp /dev/ttyACM0
# crw------- 1 root root 108, 0 Jan  1 00:00 /dev/ppp
# crw-rw---- 1 root dialout 166, 0 Jan  1 00:00 /dev/ttyACM0

The 108 major number is the canonical assignment for the PPP multiplexer and must match what the kernel's ppp_generic registers. If mknod uses a different major, pppd silently fails with "pppd is unable to open the /dev/ppp device".

Verifying the Modem with AT Commands

Before any high-level daemon (ofono, connman, iot2000setup) is engaged, validate the radio on the raw serial port. This separates hardware faults from stack faults and is the single most useful first step when list-modems reports a powered-down hardware device. The Telit HE910 AT command reference covers the full instruction set used below.

stty -F /dev/ttyACM0 115200 cs8 -cstopb -ixon raw
cat /dev/ttyACM0 &  # background the reader

In a second terminal:

printf 'AT\\r\\n' > /dev/ttyACM0          # expect: OK
printf 'AT+CPIN?\\r\\n' > /dev/ttyACM0    # expect: +CPIN: READY
printf 'AT+CSQ\\r\\n' > /dev/ttyACM0      # expect: +CSQ: <rssi>,99
printf 'AT+CREG?\\r\\n' > /dev/ttyACM0    # expect: +CREG: 0,1
printf 'AT+COPS?\\r\\n' > /dev/ttyACM0    # expect: +COPS: 0,0,"<op>"
printf 'AT+CGDCONT=1,"IP","internet"\\r\\n' > /dev/ttyACM0
printf 'AT+CGACT=1,1\\r\\n' > /dev/ttyACM0
AT command diagnostics reference
Command Purpose Healthy response
AT Modem alive OK
AT+CPIN? SIM state +CPIN: READY
AT+CSQ RSSI / BER +CSQ: 12,99 (≥10 workable, ≥15 good)
AT+CREG? CS registration +CREG: 0,1 or +CREG: 0,5 (roaming)
AT+COPS? Operator name +COPS: 0,0,"Vodafone IT"
AT+CGDCONT? PDP context +CGDCONT: 1,"IP","internet"

If the modem answers AT with OK and the SIM reports READY, the radio path is healthy and the next step is ofono. If the modem fails to answer, swap in a known-good test SIM, then verify antenna cabling (primary on MAIN, diversity on AUX) before suspecting the modem itself.

ofono Modem Activation Workflow

ofono is the Linux cellular daemon that translates dbus calls into the Telit AT command sequence. The reference implementation is maintained at the ofono project. On the IOT2040, the modem is enumerated through the cdc_acm and option drivers, but the activation sequence requires the modem to be in Powered=1 and Online=1 state before the cellular context can be brought up. A common field failure is the "Operation failed" error from activate-context, which usually traces back to either an unfinished network registration or an incorrect APN.

Enable the ofono debug log to capture the failure reason:

systemctl stop ofono
/usr/lib/ofono/test/ofonod -d '*' --foreground 2>/tmp/ofono.log &

The expected happy-path sequence is:

# In a second session, using ofono's bundled test tools:
/usr/lib/ofono/test/list-modems
# [ /telit_0 ]
#   Powered = 1
#   Online = 1
#   Features = gprs net sms
#   Interfaces = org.ofono.Modem org.ofono.NetworkRegistration org.ofono.ConnectionManager
#   Type = hardware
#   Emergency = 0

/usr/lib/ofono/test/activate-context 1 "internet" ""
# expect: /telit_0/context1 active

If list-modems returns Powered=0 and Online=0 (the symptom reported in the original troubleshooting thread), the most common cause is the option driver not having claimed the modem's USB endpoints. Confirm:

lsusb | grep -i telit
# 1bc7:0021  Telit Wireless Solutions HE910-D
ls -l /dev/ttyACM*

If /dev/ttyACM0 is missing, force-bind the driver:

echo "1bc7 0021" > /sys/bus/usb/drivers/option/new_id

If list-modems shows Powered=1 but activate-context returns org.ofono.Error.Failed, capture the ofono log and look for these patterns:

  • "Operation not allowed" or "+CME ERROR: 514" in the log — APN rejected. Confirm the APN string with the carrier (e.g. internet, web.vodafone.de, internet.t-d1.de).
  • "PDP context activation failed" — roaming restriction or no data on the SIM. Insert a SIM that has an active data plan.
  • "Not registered" — antennas disconnected or RSSI too low. Check AT+CSQ; values below 5 fail to attach reliably on HSPA cells.
  • "Sequencing error" in the GPRS state machine — the option driver claimed the diagnostic endpoint but not the modem-management endpoint. Re-seat the SIM and re-plug the modem, or reboot.

PPP Daemon Setup for the ppp0 Interface

When the application requires a ppp0 interface directly (legacy SCADA bridges, IPsec transport, or a PLC that does not understand the connman dbus API), pppd can be invoked against /dev/ttyACM0. Use a chat script that walks the modem through PIN entry, APN selection, and the data call.

Create /etc/ppp/peers/iot2040-cellular:

/dev/ttyACM0
115200
noipdefault
defaultroute
replacedefaultroute
persist
holdoff 10
maxfail 0
debug
nodetach
crtscts
lock
noauth
novj
novjccomp
noccp
ipcp-accept-local
ipcp-accept-remote
lcp-echo-interval 30
lcp-echo-failure 5
connect '/usr/sbin/chat -v -f /etc/ppp/chat-iot2040'

Create /etc/ppp/chat-iot2040:

ABORT          "BUSY"
ABORT          "ERROR"
ABORT          "NO DIALTONE"
SAY            "Initializing cellular modem..."
TIMEOUT        30
""             AT
OK             ATE0
OK             AT+CPIN?
"READY"        AT+CFUN=1
OK             AT+CREG?
OK             AT+CGDCONT=1,"IP","internet"
OK             ATD*99#
CONNECT        ""

Launch with:

pppd call iot2040-cellular
# expect in syslog:
# Connect: ppp0 <--> /dev/ttyACM0
# local  IP address 10.x.x.x
# remote IP address 10.64.64.64

A persistent ppp0 can be added to /etc/network/interfaces or wrapped as a systemd service that depends on ofono.service coming up first. lcp-echo-interval 30 and lcp-echo-failure 5 prevent the link from idling out on networks that aggressively time out inactive PDP contexts.

iot2000setup Cellular Configuration

The iot2000setup tool is a Siemens-maintained curses-based configuration front-end for the IOT2000 image. It includes a Cellular menu that drives both the ofono context and the connman service. When the menu shows options greyed out or returns "configuration not supported", the underlying ofono/connman stack is missing or unreachable.

Before using iot2000setup, validate the prerequisites:

systemctl status ofono
systemctl status connman
dbus-send --system --dest=org.ofono / org.ofono.Manager.GetModems
# expected: array containing /telit_0

If both services are up and list-modems returns a healthy HE910-D, the iot2000setup menu will offer:

  1. Provider selection (auto-scan via AT+COPS=?)
  2. APN entry (string, e.g. internet)
  3. Authentication (none / PAP / CHAP / both)
  4. PIN entry (left blank if SIM has no PIN, or the 4–8 digit value)

A successful run writes /var/lib/connman/cellular.config and reloads connman. The interface should appear as cellular0 in ip link output within 15 seconds.

If the menu aborts with a Python traceback, run it under strace to capture the failing dbus call, then re-check that the ofono user (typically ofono) is in the dialout group:

usermod -aG dialout ofono
systemctl restart ofono

HE910 vs HE910-D: What Changes

The two modules share the same Telit AT firmware family and the same USB enumeration (VID 0x1bc7), but the HE910-D adds a second antenna connector for downlink diversity. For ofono and pppd this is invisible — the option driver binds the same /dev/ttyACM0 and the AT command set is identical. Where it does matter is in field installation: on the HE910-D the second antenna must be terminated with a 50 Ω load or attached to a second antenna. Leaving it floating does not prevent registration but degrades throughput and increases the AT+CSQ variance, which causes ofono context activations to fail intermittently in marginal coverage areas.

HE910 vs HE910-D practical differences for the IOT2040
Aspect HE910 HE910-D
USB VID:PID 1bc7:0021 1bc7:0021
Antenna connectors 1 (MAIN) 2 (MAIN + AUX)
ofono binding /telit_0 /telit_0
PPP port /dev/ttyACM0 /dev/ttyACM0
AT firmware branch HE910 HE910-D (T0D or later)
3G/HSPA+ bands Quad-band Quad-band + diversity gain

Troubleshooting Matrix

Observed symptoms, root causes, and corrective actions
Symptom Likely root cause Corrective action
/dev/ttyACM0 absent option/cdc_acm modules not loaded modprobe option cdc_acm; force-bind 1bc7:0021
list-modems: Powered=0, Online=0 USB driver not bound to modem endpoints Re-check lsusb; bind driver manually via sysfs
pppd: cannot open /dev/ppp ppp_generic/slhc not loaded, /dev/ppp node missing modprobe ppp_generic slhc; mknod /dev/ppp c 108 0
activate-context: org.ofono.Error.Failed APN wrong, no data plan, or low RSSI Verify AT+CSQ, AT+CREG, AT+CGDCONT
iot2000setup: cellular menu greyed out ofono/connman not running or not in dialout group systemctl restart; usermod -aG dialout ofono
ppp0 appears but no default route replacedefaultroute missing from peers file Add replacedefaultroute to /etc/ppp/peers/...
ppp0 drops after 60 s Idle timeout or LCP echo failure Add lcp-echo-interval 30 lcp-echo-failure 5
AT+CPIN: SIM PIN SIM locked AT+CPIN="1234"; disable PIN via phone if persistent
AT+CREG: 0,3 Registration denied Re-seat SIM, check IMEI with AT+CGSN
High CSQ variance (5–18) Diversity antenna floating on HE910-D Terminate AUX with 50 Ω or attach second antenna
connman cellular0 never appears iot2000setup wrote config but connman not reloaded connmanctl enable cellular; systemctl restart connman

Verification Procedure

After configuration, run the following end-to-end check before deploying to the field. All five steps must pass for the IOT2040 to have a working cellular data path on the HE910-D.

# 1. Hardware path
lsusb | grep -i 1bc7:0021
ls -l /dev/ttyACM0 /dev/ppp

# 2. AT-level
stty -F /dev/ttyACM0 115200 raw -echo
printf 'AT+CSQ\\r\\n'   > /dev/ttyACM0
printf 'AT+CREG?\\r\\n' > /dev/ttyACM0

# 3. ofono state
/usr/lib/ofono/test/list-modems
# expect: Powered=1, Online=1, Interfaces includes ConnectionManager

# 4. PPP or connman interface
ip link show ppp0        # if using pppd
ip link show cellular0   # if using iot2000setup

# 5. End-to-end ICMP over the cellular link
ping -I ppp0 8.8.8.8 -c 4

If list-modems reports Powered=1, Online=1 and the ICMP echoes return, the link is healthy. Record the IMEI (AT+CGSN), the operator name, and the assigned IP for the as-built documentation that accompanies the cabinet.

For long-term deployments, enable a watchdog that runs AT+CREG? every 300 s and re-dials the PDP context if registration drops to 0,0. The IOT2040 watchdog daemon (systemd's WatchdogSec) is sufficient when paired with a small shell script that calls systemctl restart pppd-iot2040.

FAQ

Why does ofono's list-modems show the HE910-D with Powered=0 even though AT commands work on ttyACM0?

The raw AT path is opened by the cdc_acm driver on the diagnostic endpoint, but ofono also needs the modem-management endpoints bound to the option driver. If only cdc_acm is loaded, the modem replies to AT but ofono cannot transition it to Powered=1. Run modprobe option and verify lsusb shows the Telit 1bc7:0021 bound to the option driver rather than only to cdc_acm.

What kernel modules are required for ppp0 to come up on the IOT2040?

Load ppp_generic, slhc, ppp_async, ppp_deflate, and ppp_mppe in that order, then create the character device with mknod /dev/ppp c 108 0 and chmod 600. Without slhc, pppd starts but the kernel rejects the SLHC-compressed frames; without ppp_async, the TTY-side discipline is missing and ppp0 never appears under ifconfig.

Why does iot2000setup's cellular menu fail to apply the configuration?

The setup tool writes a config file and reloads connman. If connman is not running, the menu reports success but no interface comes up. Verify systemctl status connman returns active and that the ofono user belongs to the dialout group so it can read/write /dev/ttyACM0.

Can I use the HE910 and the HE910-D interchangeably on a custom IOT2040 image?

Yes. Both share the same USB VID:PID (1bc7:0021) and the same AT command set. The only practical difference is the second antenna connector on the HE910-D, which improves downlink diversity but is not required for registration or ppp0 setup.

Which APN should I configure for a European industrial SIM?

It depends on the operator, not on the modem. For Vodafone Germany use web.vodafone.de, for T-Mobile Germany internet.t-d1.de, for Vodafone Italy web.omnitel.it, and for generic MVNOs internet is the most common default. The APN is configured with AT+CGDCONT=1,"IP","<apn>" before the data call and is what ofono's activate-context passes through to the modem.

Back to blog