modbus-serial: speaking Modbus RTU and TCP from Node without native code
A pure JavaScript implemetation of MODBUS-RTU (and TCP) for NodeJS
At a glance
- What is it?
- modbus-serial is a pure JavaScript implementation of the Modbus protocol for Node, covering the read, write and mask function codes over serial, TCP, UDP and Telnet ports. The serialport package is optional, so the install stays free of native builds unless you actually need RTU.
- Who is it for?
- modbus-serial fits a Node developer talking to inverters, irrigation controllers, meters or robots over Modbus who wants one package for both serial RTU and TCP and who does not want a native dependency in the install path. It does not suit a project that needs a Modbus server on a serial line out of the box, since the README lists only a TCP server, or a team that cannot tolerate a package whose serial support silently disappears at runtime.
- Can I use it commercially?
- Yes. ISC is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 1 day ago.
- What is it written in?
- Mainly JavaScript, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.
Editorial analysis
The protocol coverage is listed function code by function code
Modbus is a serial communications protocol first used in 1979, and the README describes it as simple, openly published, royalty-free and easy to deploy and maintain. The library's job is to make calls against that protocol painless from JavaScript. The supported operations are tabulated by function code, which is the clearest way to judge coverage. FC1 Read Coil Status maps to readCoils(coil, len), FC2 Read Input Status to readDiscreteInputs(addr, arg), FC3 Read Holding Registers to readHoldingRegisters(addr, len), and FC4 Read Input Registers to readInputRegisters(addr, len). The write side covers FC5 Force Single Coil with writeCoil, FC6 Preset Single Register with writeRegister, FC15 Force Multiple Coil with writeCoils, FC16 Preset Multiple Registers with writeRegisters, and FC22 Mask Write Register with maskWriteRegister. Two extras stand out: FC43/14 Read Device Identification, supported on TCP and RTU ports, and a CustomFC entry that takes an arbitrary function code and data. The device identification call is the one industrial tooling tends to need first.
Six transport options, one client class
The transport list is longer than a typical serial library offers, and it is the reason the package is named modbus-serial while also handling TCP. On the serial side there are three client variants, all requiring the node serialport package: modbus-RTU over a SerialPort, modbus-RTU over a buffered serial line through RTUBufferedPort, and modbus-ASCII over a serial line through AsciiPort. On the TCP side there are four: modbus-TCP over a TCP/IP line through TcpPort, modbus-RTU over UDP to a C701 server which is a commercial UDP to serial bridge, modbus-RTU over TCP through TcpRTUBufferedPort for a TCP/IP serial RTU buffered device, and modbus-RTU over a Telnet server acting as a TCP/IP serial bridge. On the server side only one is listed, modbus-TCP through ServerTCP. That asymmetry is a real limit. If you want to expose a serial slave to Modbus clients, you need something else, and the README does not document one.
serialport is optional, and the failure mode is a runtime error
This is the most useful decision in the package and the README explains the consequences in detail. serialport is an optional dependency: npm tries to install it, but if the install fails, or you skip optionals, modbus-serial still installs and TCP and UDP usage works. The version range pulled in targets serialport 13.x, which requires Node.js 20 or newer. If you need serial on an older Node release, the README says to install a compatible serialport major explicitly alongside modbus-serial, or to use --no-optional and TCP-only flows. The failure mode is what deserves attention. Without serialport, the serial-specific APIs fail at runtime when used: ModbusRTU.getPorts(), connectRTU, connectRTUBuffered and connectAsciiSerial, and the RTUBufferedPort and ServerSerial exports are simply not there if serialport cannot be loaded. So a build passes, a test suite passes if it only covers TCP, and the failure arrives in production when the serial path is first touched.
A read and write round trip on a serial port
The README's first example is the shape you will end up writing. You create an empty client, open a buffered serial connection with a baud rate and a callback, set the device id, write two registers, and then read them back.
// create an empty modbus client
const ModbusRTU = require("modbus-serial");
const client = new ModbusRTU();
// open connection to a serial port
client.connectRTUBuffered("/dev/ttyUSB0", { baudRate: 9600 }, write);
function write() {
client.setID(1);
// write the values 0, 0xffff to registers starting at address 5
// on device number 1.
client.writeRegisters(5, [0 , 0xffff])
.then(read);
}
function read() {
// read the 2 registers starting at address 5
// on device number 1.
client.readHoldingRegisters(5, 2)
.then(console.log);
}The promise-based chaining is the API's main ergonomic feature, and the setID call inside the connection callback is the part that trips people up: the device address is set per transaction rather than per connection, which is what you want when one port serves several devices.
Reading many meters on one bus, one at a time
The second README example addresses the situation every RS485 installation has, several devices hanging off one serial line. It opens a buffered serial connection, sets a timeout so a silent slave does not block forever, and then loops over a list of meter ids, awaiting each read and pausing 100 milliseconds before moving to the next device. The setTimeout call is documented in the example as the way to bound the wait when a slave does not reply back. The sequential nature of the loop is not incidental: Modbus RTU on a shared line is a request and response protocol, so devices have to be polled one at a time. The example files in the repository follow this pattern, with separate polling examples for RTU, TCP and UDP under the examples directory, alongside a buffered server, an async server, a logger, a debug script and a device-identification example.
The package ships types, a build step and a generated docs tree
The repository layout shows a package assembled from several source directories. The published files list is explicit, and it includes index.js, the apis, ports, servers, utils and worker directories, and five type declaration files: index.d.ts, ModbusRTU.d.ts, ServerTCP.d.ts, ServerSerial.d.ts and TestPort.d.ts. The build script copies apis, ports, servers and utils into a modbus-serial directory, so the source layout in the repo and the published layout differ. Documentation is generated with JSDoc 4 and the clean-jsdoc-theme, and a separate task copies the examples folder into the generated tree, so the API pages ship with runnable samples. Tests run under mocha with nyc producing lcov and text coverage, and the devDependencies include chai with chai-as-promised, sinon, mockery, and both webbluetooth and a web-bluetooth-mock, which points to Bluetooth transport work in the codebase.
ISC licensed, released often, with a caveat about Node versions
The licence is ISC, which is permissive and short, and the manifest names the author as Yaacov Zamir. The release history is busy for a library of this age, with v8.0.25 and v8.0.24 both landing on 2026-03-20 and v8.0.23 in October 2025. The repository is not archived and the last push was on 2026-10-13. One compatibility note deserves attention because it is easy to read past: the README says this module has not been tested on every single version of NodeJS, and recommends sticking to LTS versions, denoted by even major version numbers such as 4.x, 6.x and 8.x. Combined with the serialport 13.x requirement of Node 20 or newer, that gives you a fairly narrow band of configurations where the serial path is actually exercised.
Editorial conclusion
modbus-serial fits a Node developer talking to inverters, irrigation controllers, meters or robots over Modbus who wants one package for both serial RTU and TCP and who does not want a native dependency in the install path. It does not suit a project that needs a Modbus server on a serial line out of the box, since the README lists only a TCP server, or a team that cannot tolerate a package whose serial support silently disappears at runtime. Verify first whether you actually need serial by installing with the optional dependency skipped, check the Node version against the serialport major you need, and read the list of runtime failures the README names before you decide the optional dependency is not for you.
Frequently asked questions
How do I install modbus-serial?
The README documents npm install modbus-serial, with the options --unsafe-perm --build-from-source to try if you have installation problems, and npm install modbus-serial --no-optional to skip the native serial build for a smaller tree. Setting optional=false in .npmrc has the same effect.
Do I need the serialport package to use modbus-serial?
Not for TCP or UDP. serialport is an optional dependency, and the README says modbus-serial still installs and TCP/UDP usage works if it fails or is skipped. Without it, the serial APIs fail at runtime, including ModbusRTU.getPorts(), connectRTU, connectRTUBuffered and connectAsciiSerial.
Which Node.js version does modbus-serial need?
The serialport version range pulled in by the package targets serialport 13.x, which requires Node.js 20 or newer. The README recommends sticking to LTS versions of Node, and notes that the module has not been tested on every Node version.
Does modbus-serial support acting as a Modbus server?
The README lists a single server implementation, modbus-TCP through ServerTCP over a TCP/IP line. The serial server class ModbusRTU.ServerSerial is mentioned only in the list of APIs that are not exported when serialport cannot be loaded.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/yaacov-node-modbus-serial)Was this useful?