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:
-
uname -a- confirm kernel4.4.18-yocto-standardand IA-32 / ARM build. -
node -vandnpm -v- capture ABI generation. -
date- verify RTC; compare with NTP source. -
openssl version- check the bundled library version. -
curl -vI https://nodejs.org/dist/index.json 2>&1 | grep -E 'expire|expired|date'- verify TLS handshake behavior. -
npm config get registryandnpm config get strict-ssl. -
cat /usr/lib/node_modules/node-red/package.json | grep version- capture Node-RED version in the image.
/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.
- 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" - Enable NTP persistence so the unit survives power cycles:
/usr/sbin/ntpd -q -g - Refresh the system CA bundle by replacing
/etc/ssl/certs/ca-certificates.crtwith a current Mozilla bundle:
curl -L https://curl.se/ca/cacert.pem -o /etc/ssl/certs/ca-certificates.crt - 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.
- Download a prebuilt tarball that matches the architecture:
wget https://nodejs.org/dist/v14.21.3/node-v14.21.3-linux-ia32.tar.xz - 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 - Verify:
node -vshould reportv14.21.3.
npm -vshould report6.14.18. - 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.
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.
- Install the build toolchain shipped on the SDK image:
opkg install gcc g++ make python3-native nodejs-native - Fetch the exact serialport version required by
node-red-contrib-modbusat the time:
npm view node-red-contrib-modbus dependencies - Pre-stage the archive in the npm cache or pass
--offlineafter manually copying the tarball:
npm install --offline --build-from-source=false node-red-contrib-modbus - 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.
- Remove any partial install:
rm -rf /usr/lib/node_modules/node-red-contrib-modbus - Install the TCP-only modbus server:
cd ~/.node-red
npm install node-red-contrib-modbustcp - Configure the Modbus-Get/Modbus-Read nodes to point at the PLC IP:port (default 502).
- Test with:
curl http://127.0.0.1:1880/redand 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_modulesinstead 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
serialportnative binding depends onlibc.so.6from the image. Verify withldd /usr/lib/node_modules/node-red-contrib-modbus/node_modules/serialport/build/Release/serialport.node. -
Yocto Opkg: Some SDK images ship
python3-nativeonly. Forcenpm config set python /usr/bin/python3whennode-gyprejects 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.
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.