npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

modbus-webserial

v0.11.2

Published

ESM library for speaking Modbus-RTU from the browser via Web Serial

Downloads

1,762

Readme

modbus-webserial

Zero-dependency library for communicating with a Modbus-RTU serial device from the browser via WebSerial.

Mentioned in Awesome npm size dependencies CI License types esm

Usage

Try the web UI at modbuswebui.dev

Establish connection and read/write in browser

import { ModbusRTU } from 'modbus-webserial';

// Prompt the WebSerial dialog and open port
const client = await ModbusRTU.openWebSerial({ baudRate: 9600 });
client.setID(1);

// Read two holding registers from 0x0000 (0x0000 and 0x0001)
const { data } = await client.readHoldingRegisters(0, 2);
console.log('HR0=', data[0], 'HR1=', data[1]);

// Write values to holding registers 0x00 and 0x01 (i.e. two registers from 0x0000)
await client.writeRegisters(0, [0x0A, 0x0B]);

You can also manually supply the port

const [port] = await navigator.serial.getPorts()
const client = await ModbusRTU.openWebSerial({ baudRate: 9600, port })

// or on connect
navigator.serial.addEventListener("connect", ({target: port}) => {
   const client = await ModbusRTU.openWebSerial({ baudRate: 9600, port })
})

Can also be used without WebSerial for building modbus frames in any environment

import {
  buildReadHoldingRegisters,
  buildWriteRegisters
} from 'modbus-webserial';

// Build a “Read Holding Registers” frame (ID=1, addr=0, qty=2)
const rawRead = buildReadHoldingRegisters(1, 0x00, 2);
console.log(rawRead);
// Uint8Array [0x01, 0x03, 0x00, 0x00, 0x00, 0x02, CRC_LO, CRC_HI]

// Build a “Write Multiple Registers” frame (ID=1, addr=0, values=[10,11])
const rawWrite = buildWriteRegisters(1, 0x00, [0x0A, 0x0B]);
console.log(rawWrite);
// Uint8Array [0x01, 0x10, 0x00, 0x00, 0x00, 0x02, 0x04, 0x00,0x0A, 0x00,0x0B, CRC_LO, CRC_HI]

[!TIP] Check src/index.ts (or dist/index.js) for all exports


Supported Functions

Modbus Data Functions

The following Modbus-RTU function calls are implemented:

| Function | Description | | --------------------------------- | ---------------------------------------- | | readCoils(addr, qty) | FC 01 – Read coil status | | readDiscreteInputs(addr, qty) | FC 02 – Read discrete input status | | readHoldingRegisters(addr, qty) | FC 03 – Read holding registers | | readInputRegisters(addr, qty) | FC 04 – Read input registers | | writeCoil(addr, state) | FC 05 – Write single coil | | writeRegister(addr, value) | FC 06 – Write single holding register | | writeCoils(addr, states) | FC 15 – Write multiple coils | | writeRegisters(addr, values) | FC 16 – Write multiple holding registers | | readFileRecord(file, rec, len) | FC 20 – Read file record (single ref) | | writeFileRecord(file, rec, vals)| FC 21 – Write file record (single ref) | | maskWriteRegister(addr, and, or)| FC 22 – Mask write register | | readWriteRegisters(rAddr, rQty, wAddr, vals) | FC 23 – Read/write multiple regs | | readFifoQueue(addr) | FC 24 – Read FIFO queue |

[!CAUTION] Not all slave libraries support file records, FIFO queues, mask writes or read-write calls

Auxiliary Client Methods

Utility and configuration methods exposed on ModbusRTU:

| Method | Purpose | | ------------------------ | ----------------------------------- | | openWebSerial(options) | Open a serial port via WebSerial | | close() | Close the current serial connection | | setID(id) | Set the Modbus slave ID | | getID() | Get the current slave ID | | setTimeout(ms) | Set transaction timeout (ms) | | getTimeout() | Get current timeout (ms) | | getPort() | Get 'SerialPort' instance[^1] |

[^1]: The returned SerialPort instance from getPort can be used to access properties such as usbVendorId and usbProductId for retrieving information about the connected USB device.


Examples

The following demos are fully self‑contained HTML files, served via GitHub Pages:


Behavior

CRC policy (strict vs resync)

By default the transport uses strict CRC checking: if a received Modbus RTU frame fails CRC validation, the request fails with CrcError.

You can switch to a more tolerant mode that tries to recover from occasional line noise:

const client = await ModbusRTU.openWebSerial({
  baudRate: 9600,
  crcPolicy: { mode: "resync", maxResyncDrops: 32 },
});

Concurrency

The WebSerial transport is single-flight: it can only have one in-progress Modbus RTU transaction at a time.

This is intentional. Modbus RTU does not include a transaction identifier, so overlapping requests on the same serial link makes it ambiguous which response belongs to which request.

There is no internal request queue. To avoid abstracting away the serial “one request at a time” nature of Modbus RTU, starting a new transaction while another is in progress will throw an error immediately.

Wrong (concurrent calls on the same client/transport):

// Two requests started without awaiting the first one.
// This will throw (single-flight).
const p1 = client.readHoldingRegisters(0x0000, 2);
const p2 = client.readHoldingRegisters(0x0010, 2); 
// -> Uncaught Error: Concurrent transact() calls are not supported

await Promise.all([p1, p2]);

Right (serialize with await):

await client.readHoldingRegisters(0x0000, 2);
await client.readHoldingRegisters(0x0010, 2);

Post-timeout recovery

When a request times out, a Modbus RTU slave may still send a late reply afterwards. If you immediately send a new request (that is similar enough to the old one), that late reply can be mistaken as the response for the new request.

To reduce that risk (along with strong request/response matching), the transport supports a post-timeout “quarantine” window.

  • If postTimeoutWaitPeriod is 0 (default), the next request is sent immediately after a timeout.
  • If postTimeoutWaitPeriod is > 0, the next transact() call will delay sending the request for the specified period, while discarding any bytes received during that window.
const client = await ModbusRTU.openWebSerial({
  // ...
  postTimeoutWaitPeriod: 20, // ms
});

Notes:

  • This affects only the request after a timeout.
  • When you use the client sequentially (with await), this shows up as a small delay before the next request. It does not change how timeouts are thrown or how you catch them.

Current state

  • v0.11: Transport rework to handle edge cases explicitly
  • v0.10: Full modbus data-access coverage
  • v0.9: Full passing tests, smoke test passed, complete README, build scripts in place
  • Beta: Full Modbus RTU function‑code coverage
  • Alpha: Basic structure and layout

Roadmap

  • v1.0.0: Create and document more tests for different boards using different browsers.