Fixing node-red-contrib-modbus Install Errors on SIMATIC IoT2000

David Krause9 min read
ModbusSiemensTroubleshooting
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

Problem Description

When running npm install node-red-contrib-modbus on a SIMATIC IoT2000 (IoT2020 or IoT2040) gateway, the installation fails during the serialport postinstall phase. The build chain invokes node-pre-gyp, which first attempts to fetch a pre-compiled binary from GitHub. When no IA-32 / ARM binary is available for the running Node.js ABI (node-v48), it falls back to building from source via node-gyp. That source build then aborts with a TLS error returned by OpenSSL.

The failure surfaces in two distinct TLS variants depending on the embedded node-serialport release pulled transitively:

serialport version node-pre-gyp version TLS error observed
4.0.7 0.6.32 Error: certificate has expired
5.0.0 0.6.36 Error: certificate is not yet valid

npm then prints npm ERR! code ELIFECYCLE and the install script exits with status 1. The node-red-contrib-modbus node never lands in /usr/lib/node_modules/, and Node-RED cannot load the modbus palette.

Environment Analysis

The problem originates in the SIMATIC IoT2000 firmware image shipped with older Example Image releases. Capture the baseline first so you can compare against expected values:

Parameter Reported value Recommended value
Hardware platform SIMATIC IoT2020 (6ES7647-0AA00-0YA2) / IoT2040 (6ES7647-0AA00-1YA2) —
OS Yocto Poky, kernel 4.4.18-yocto-standard Update to latest image ≥ V2.6.x (kernel 4.19+)
Architecture linux-ia32 (Galileo/Quark) or linux-arm Match prebuilt ABI
Node.js v6.9.2 (node-v48 ABI, EOL April 2019) ≥ v14 LTS (node-v83 ABI minimum)
npm v3.10.9 ≥ v6.14.x (ships with Node 14) or ≥ v8.x (Node 16)
node-gyp v3.4.0 v8.4.x (Node 14) or v9.x (Node 16)
node-pre-gyp v0.6.32 / v0.6.36 matches serialport 9.x / 10.x
OpenSSL in image 1.0.2j (system CA store from 2016) 1.1.1 or newer
System clock Often 2016-2017 if NTP disabled Synchronized via ntpdate or systemd-timesyncd

Reference the SIMATIC IoT2020 / IoT2040 Operating Instructions on the Siemens Industry Online Support portal for image partitioning and update procedures.

Root Cause Analysis

The error chains together three independent defects. Each must be resolved to reach a successful install.

Cause 1 - Expired CA Store on the Yocto Image

The bundled OpenSSL trust anchors expired in 2017. When node-gyp downloads process.h or invokes a Python helper that hits an HTTPS endpoint, OpenSSL validates the chain and rejects it. The stack frame at TLSSocket._finishInit (_tls_wrap.js:603:8) confirms the verification happens inside the Node 6.x TLS wrapper, not inside the application layer.

Cause 2 - Outdated Node.js / node-gyp / node-pre-gyp Trio

Node 6.9.2 (ABI v48) has been unsupported since 2019. The node-serialport project dropped IA-32 binary builds in release 9.x and later. With no prebuilt matching node-v48-linux-ia32, node-pre-gyp falls back to source compilation. node-gyp 3.4.0 does not understand the build requirements of modern nan, bindings, and cmake-js packages, so configure fails before any C++ file is touched.

Cause 3 - Clock Skew in IoT2000 Field Deployments

SIMATIC IoT2000 units without internet or NTP keep the RTC at the factory burn-in date (commonly 2017). The TLS variant certificate is not yet valid surfaces when the local date is earlier than the notBefore field on the certificate. This is a clock problem, not a trust problem.

Diagnostic Verification

Run these commands from the IoT2000 shell (ssh root@iot2000 or via the serial console) and record the output before applying fixes:

  1. uname -a - confirm kernel 4.4.18-yocto-standard and IA-32 / ARM build.
  2. node -v and npm -v - capture ABI generation.
  3. date - verify RTC; compare with NTP source.
  4. openssl version - check the bundled library version.
  5. curl -vI https://nodejs.org/dist/index.json 2>&1 | grep -E 'expire|expired|date' - verify TLS handshake behavior.
  6. npm config get registry and npm config get strict-ssl.
  7. cat /usr/lib/node_modules/node-red/package.json | grep version - capture Node-RED version in the image.
Note: The transport UART (RS232/RS485) exposed at /dev/ttyS0 and /dev/ttyS1 on the IoT2020 must remain free. Detach any application that opens the port before running npm install, or the postinstall build will hang on file locks.

Solution 1 - Bring the System Clock Forward and Refresh Trust Anchors

The fastest zero-touch path when you cannot replace the Node.js runtime (locked firmware image, regulatory re-qualification). Apply both clock and certificate patches because they reinforce each other.

  1. Set the clock manually if NTP is unavailable:
    date -s "$(wget -qO- http://worldtimeapi.org/api/timezone/Etc/UTC | grep -oE '\\d{4}-\\d{2}-\\d{2}T[0-9:]+' | head -c 16)Z"
  2. Enable NTP persistence so the unit survives power cycles:
    /usr/sbin/ntpd -q -g
  3. Refresh the system CA bundle by replacing /etc/ssl/certs/ca-certificates.crt with a current Mozilla bundle:
    curl -L https://curl.se/ca/cacert.pem -o /etc/ssl/certs/ca-certificates.crt
  4. Symptom-only escape hatch: tell npm to skip peer and TLS verification for the modbus package only:
    npm config set strict-ssl false

Solution 2 - Upgrade Node.js on the SIMATIC IoT2000

Long-term fix. Node.js 6.x cannot build current native modules. Use a maintained LTS line that still ships IA-32 binaries.

  1. Download a prebuilt tarball that matches the architecture:
    wget https://nodejs.org/dist/v14.21.3/node-v14.21.3-linux-ia32.tar.xz
  2. Extract into a non-removable mount and link:
    tar -xJf node-v14.21.3-linux-ia32.tar.xz -C /opt/
    ln -sf /opt/node-v14.21.3-linux-ia32/bin/node /usr/bin/node
    ln -sf /opt/node-v14.21.3-linux-ia32/bin/npm /usr/bin/npm
  3. Verify:
    node -v should report v14.21.3.
    npm -v should report 6.14.18.
  4. Re-run the install:
    cd /usr/lib/node_modules
    npm install node-red-contrib-modbus

For ARM-based IoT2040 units, substitute node-v14.21.3-linux-armv7l.tar.xz and confirm with uname -m. Refer to the Node.js Previous Releases index for the latest 14.x IA-32 / ARMv7 tarballs.

Note: Node.js 15.x and later dropped IA-32 binaries. SIMATIC IoT2020 owners are constrained to Node 14 LTS as the highest supported line.

Solution 3 - Build serialport from a Vendored Source

When the runtime upgrade is blocked by another application, download a pre-vetted node-serialport tarball and build offline.

  1. Install the build toolchain shipped on the SDK image:
    opkg install gcc g++ make python3-native nodejs-native
  2. Fetch the exact serialport version required by node-red-contrib-modbus at the time:
    npm view node-red-contrib-modbus dependencies
  3. Pre-stage the archive in the npm cache or pass --offline after manually copying the tarball:
    npm install --offline --build-from-source=false node-red-contrib-modbus
  4. If configure still fails, force a source build of just serialport:
    npm install [email protected] --build-from-source
    npm_config_build_from_source=true npm install node-red-contrib-modbus

Solution 4 - Skip the Serial Dependency with Modbus TCP-Only

For installations that never use RTU, replace the heavyweight palette with a Modbus TCP-only package. This removes the serialport chain entirely.

  1. Remove any partial install:
    rm -rf /usr/lib/node_modules/node-red-contrib-modbus
  2. Install the TCP-only modbus server:
    cd ~/.node-red
    npm install node-red-contrib-modbustcp
  3. Configure the Modbus-Get/Modbus-Read nodes to point at the PLC IP:port (default 502).
  4. Test with:
    curl http://127.0.0.1:1880/red and confirm the modbus palette loads without errors.

Solution 5 - Patch npm Build Recipes on Locked Firmware

When the firmware is locked but you can edit the Image SDK recipe (typical during development on the Yocto Poky BSP), pin the dependencies so the next image build resolves cleanly.

DEPENDS += "nodejs-native"
RDEPENDS_${PN} += "ca-certificates"
SRC_URI += "file://ca-certificates.pem"
do_install_append() {
    install -m 0644 ${WORKDIR}/ca-certificates.pem ${D}${sysconfdir}/ssl/certs/
}

This ships a known-good CA set inside the read-only image, eliminating the certificate has expired TLS failure forever.

Verification Procedures

After applying any of the above solutions, verify with a checklist of observable signals.

Check Command / Action Expected outcome
modbus palette visible open Node-RED editor and inspect the left palette modbus, modbus-read, modbus-write, modbus-server, modbus-respond nodes appear
module listed on disk ls -la /usr/lib/node_modules/node-red-contrib-modbus directory exists with package.json showing version
no ELIFECYCLE on reinstall npm install node-red-contrib-modbus returns 0, no gyp ERR! output
serialport native binding present find /usr/lib/node_modules -name 'serialport.node' binary located under build/Release/
TLS handshake to nodejs.org curl -vI https://nodejs.org HTTP/1.1 200 OK, no certificate problem
live Modbus TCP poll deploy a modbus-read flow pointing at 127.0.0.1:502 (or remote PLC) msg.payload updates on each poll without connection timed out
device restart resilience reboot and inspect systemctl status node-red the modbus nodes survive restart, palette list intact

Troubleshooting Matrix

Symptom Likely cause Resolution
certificate has expired stale CA bundle in image refresh /etc/ssl/certs/ca-certificates.crt
certificate is not yet valid RTC set before 2017 or before issuer notBefore synchronize clock via ntpd -q -g
Pre-built binaries not found for [email protected] and [email protected] Node 6 / ABI v48 unsupported upgrade Node to 14.x LTS IA-32
ELIFECYCLE with exit 1 from node-pre-gyp install --fallback-to-build toolchain missing or Python mismatch install gcc g++ make python2-native via opkg
Cannot find module 'serialport' at Node-RED start serialport skipped, install recorded incomplete npm install serialport --build-from-source
Not compatible with your Node version modbus 5.43 requires Node-Red 4.x pin [email protected] on Node-Red 3.x
listen EADDRINUSE :::1880 after redeploy stale Node-RED process kill $(pidof node) && systemctl restart node-red
palette empty after reboot user dir vs /usr/lib/node_modules mismatch install inside ~/.node-red not /home/pi

Hardware-Specific Caveats for SIMATIC IoT2000

  • Quark CPU, IA-32 only: Node 17+ removed IA-32 support. IoT2020 owners are limited to Node 14 LTS as the most recent line that still ships IA-32 binaries.
  • Read-only rootfs: If the firmware is locked, install into ~/.node-red/node_modules instead of /usr/lib/node_modules. This is the user-local install path that survives firmware updates.
  • Limited RAM (256 MiB on IoT2020 Rev A): Native builds consume up to 180 MiB of resident memory. Close other Node-RED instances or stop services before compiling.
  • RS232/485 buffer: The serialport native binding depends on libc.so.6 from the image. Verify with ldd /usr/lib/node_modules/node-red-contrib-modbus/node_modules/serialport/build/Release/serialport.node.
  • Yocto Opkg: Some SDK images ship python3-native only. Force npm config set python /usr/bin/python3 when node-gyp rejects Python 2.

Architectural Constraints

The failure mode highlights a deeper constraint: the SIMATIC IoT2000 was qualified at a software bill-of-materials that included Node 6.9.2 and a 2017-era CA store. Modern modbus palettes pull dozens of nested dependencies, most of which assume Node 12+ tooling and a fresh trust anchor set. Engineering teams should treat the palette install as a controlled change rather than a routine npm install. Document the resulting dependency tree with npm ls --json > audit.json so future audits have a baseline to diff against.

Safety notice: When the IoT2000 is the Modbus gateway to a SIMATIC S7-1500, S7-1200, or SINUMERIK controller, disconnect the fieldbus or set the controller to STOP mode before running npm install. A misconfigured socket can leave the /dev/ttyS0 half-duplex line in an indeterminate state that produces CRC errors on the next controller scan cycle.

Alternative Forks

When the upstream node-red-contrib-modbus chain remains unworkable on legacy Node 6, an actively maintained alternative is the mikakaraila/node-red-contrib-modbus mirror, which preserves a compatible manifest for older Node releases. Verify any fork against your corporate supply-chain controls before deploying.

FAQ

Why does node-red-contrib-modbus fail only on the SIMATIC IoT2000 and not on a Raspberry Pi?

The IoT2000 ships with Node.js 6.9.2 and an OpenSSL 1.0.2 trust store dated 2016-2017. Raspberry Pi OS images ship Node ≥14 and refresh the CA bundle on every OS update, so the same package installs cleanly there.

Can I disable strict SSL to skip the certificate error?

Yes, npm config set strict-ssl false bypasses validation, but it also weakens every TLS dependency npm fetches. Use it only as a temporary diagnostic step, then refresh the CA bundle and re-enable strict SSL.

Do I have to upgrade Node.js to fix the install?

No. The shortest path is synchronizing the system clock and replacing ca-certificates.crt with a current bundle. Upgrading Node to 14.x LTS is the durable fix that also prevents future ABIs from breaking.

Where should node-red-contrib-modbus be installed on the IoT2000?

Run npm install from ~/.node-red (the user-local Node-RED directory) when the rootfs is read-only, or from /usr/lib/node_modules for system-wide installs on a writable image. Never install from /home/root because Node-RED scans only the user-dir for palettes.

Does node-red-contrib-modbus 5.43.0 work with Node-RED 3.x?

No. Release 5.43.0 requires Node-RED 4.x. On older gateways, pin [email protected] for Node-RED 3.x compatibility, or upgrade Node-RED to 4.1.x and Node.js to 18 LTS.

How do I confirm the modbus palette is actually loaded after install?

Open http://<iot2000>:1880 in a browser, drag a modbus-read node onto the canvas, and watch for Missing serialport binding in the debug tab. If the node deploys and accepts a host/port without error, the palette is healthy.

Back to blog