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

@csllc/cs-pcan-usb

v0.5.0

Published

PCAN-USB module for NodeJS

Readme

NodeJS PCAN-USB Module

This module provides a NodeJS interface to the PEAK-System PCAN-USB adapter, and possibly others that are supported by the PCAN-Basic library.

This module is meant to be compatible with a wrapper like @csllc/cs-canbus-universal, providing application support for PCAN-USB adapters.

This module uses native code through Node-API and supports Windows x86_64 and macOS (Intel and Apple Silicon). Node 24 or later is recommended.

Getting Started

The following assumes that NodeJS is already installed.

On Windows, install the PEAK-System device driver package, including the PCAN-Basic API. It provides PCANBasic.dll, which is not distributed with this module.

On macOS, nothing else is needed: the PCBUSB library is bundled with the prebuilt binary.

To install this module, run:

npm install @csllc/cs-pcan-usb

If the PCAN library can't be loaded (for example, the PEAK driver is not installed), the module still loads: list() returns an empty list, and other calls fail with an error whose code is ERR_PCAN_NOT_INSTALLED. This lets applications that support several kinds of CAN adapter keep working on machines without PCAN hardware.

The following is a sample script that opens the CAN port, writes a single message to the bus, and listens for and prints received messages from the bus.

const Can = require('@csllc/cs-pcan-usb');

let can = new Can({
  canRate: 250000,
});

can.list()
  .then((ports) => {
    console.log(ports);
    return can.open(ports[0].path)
      .catch((err) => {
	console.error(err);
	process.exit(1);
      });
  })
  .then(() => {
    can.write({ id: 0x10EF8001,
                ext: true,
                buf: Buffer.from([0x45, 0x00, 0x00, 0x04, 0x00, 0x00]) });
  })
  .catch((err) => {
    console.error(err);
    can.close();
    process.exit(1);
  });


can.on('data', function(msg) {
  console.log(JSON.stringify(msg, (key, value) => {
    if ((typeof value === 'number') && (key == "id")) {
      return value.toString(16).toUpperCase().padStart(8, '0');
    } else if (typeof value === 'number') {
      return value.toString(16).toUpperCase().padStart(2, '0');
    } else {
      return value;
    }
  }));
});

Examples

Both JavaScript and Win32 examples are available in the example folder.

Configuration

The constructor accepts an object that specifies the desired configuration. The CAN adapter is set up before the open method resolves, so once it is complete, the CAN interface is ready for use.

The default options are shown in the following example. Any of them may be omitted from the constructor's option object to use their default value.

let can = new Can({

  // bit rate on the CAN bus
  canRate: 250000,

  // filters for incoming packets
  filters: [
  ],

  // useful for testing, each sent packet is also received
  loopback: false,

  // how often to check the status while the port is open, in milliseconds,
  // so that changes are noticed (and 'status' events emitted) even when no
  // frames are sent or received; 0 to only check when frames are sent or
  // received
  statusInterval: 1000,
  });

Streaming

cs-pcan-usb extends the NodeJS stream interface, so it can be piped into other stream instances.

Filtering

:warning: Due to limitations of MacCAN and PCBUSB, CAN message filtering is not supported on macOS.

By default, all CAN message are captured, but this can be limited by applying a message filter. The following are examples of filter definitions that are passed to the open method.

{
  ext: true, // Applies to extended (29-bit IDs)
  id: '10EF0000 10EFFFFF', // Allow IDs 0x10EF0000 through 0x10EFFFFF
}

{
  ext: true, // Applies to extended (29-bit IDs)
  fromID: 0x10EF0000, // Allow IDs 0x10EF0000 through 0x10EF7777
  toID: 0x10EF7777,
}

{
  ext: false, // Applies to standard (11-bit IDs)
  code: 0x081, // Acceptance code
  mask: 0x0F8, // Acceptance mask
}

The PCAN-USB adapter supports one message filter at a time. If multiple filters are applied, the resulting filter spans the range of the highest to lowest IDs of all filters.

Three filter formats are supported by the package:

  • code and mask format, directly specifying the acceptance filter with a code and mask

     `code: 0x10EF0000, mask: 0xFFFF0000`
  • fromID and toID format, specifying a range of IDs to be accepted by the filter:

     `fromID: 0x10EF0000, toID: 0x10EFFFFF`
  • id format, specifying a string with a range of IDs to be accepted by the filter:

     `id: '10EF0000 10EFFFFF'`

When using the start/stop or id formats, it is not guaranteed that all messages outside of the specified range will be filtered out. This is due to a hardware limitation reported in the PCAN-Basic documentation.

Given a range of IDs to allow, it is possible to convert to an acceptance code and mask pair:

acceptance_code = fromID & toID
acceptance_mask = ~(fromID ^ toID)

Mixing Filter Types

Internally, the PCAN-USB adapter does not differentiate between standard and extended ID filtering. The ext property is only made available for compatibility with @csllc/can-usb-com and @csllc/cs-canlib.

As such, a filer specification like

{ ext: true, id: '00000000 FFFFFFFFF' }

will allow all messages with standard IDs (000 through 7FF) through to the receive buffer. This is not consistent with @csllc/can-usb-com or cs-canlib, which follow CAN-USB-COM's specification:

  • When [filtering is] disabled, all CAN messages are received regardless of the definitions in the list
  • When [filtering is] enabled, only messages which match filter definitions in the list will be passed through.

JavaScript Events

The module emits the following events:

  • open when the serial port is successfully opened
  • error if an error occurs
  • data when an incoming CAN bus frame is received (error frames are not emitted; see Error Statistics)
  • write when an outgoing CAN bus frame is sent to the device (the event is emitted before the frame is actually sent on the bus)
  • status when the status of the port changes (see Status)
  • close when the port is closed

close() removes all listeners from the instance, including the application's, and stops anything piped from it. If the same instance is opened again, attach the listeners (and pipes) again after opening it. (@csllc/cs-canbus-universal does this for its own listeners, so listeners attached to it are not affected.)

To listen for the events, use the typical NodeJS EventEmitter pattern:

  can.on('open', function(){
    console.log( 'CAN bus opened');
  })

  can.on('data', function(msg){
    console.log( msg );
  })

  can.on('close', function(err){
    if( err && err.disconnected ) {
      console.log( 'CAN bus disconnected' );
    }
    else {
      console.log( 'CAN bus closed by application' );
    }
  })

  can.on('error', function(err){
    console.log( 'CAN bus error: ', err );
  })

Asynchronous Message Reception and Callbacks

This module takes advantage of the asynchronous message receive notification offered by the PCAN-Basic API. It implements a 'light' callback function in the native code, which simply calls the JavaScript function provided when the event is enabled using pcan.EventEnable(). The JavaScript function is then expected to call pcan.Read() to retrieve the newly received message, which uses the API's CAN_Read() function behind-the-scenes.

In the future, it may make sense to move the call to CAN_Read() directly into the native code to improve performance.

Win32 Events

The Windows version of this module provides a Win32 event to the PCAN-Basic API through the CAN_SetValue(PCAN_RECEIVE_EVENT) API call, which gets signaled upon receipt of a CAN message that is accepted by the message filter. A Win32 thread waits on the event using WaitForMultipleObjects, invokes a callback function when signaled, and resets itself for the next message.

Unix Pipes

Under the MacCAN PCBUSB library, this module uses the read-end of a POSIX pipe, received through the CAN_GetValue(PCAN_RECEIVE_EVENT) API call. A POSIX thread waits for data to be written to the pipe using select(2), invokes a callback function when signaled, and resets itself for the next message.

API

This module's API functions generally return Promises, which are resolved or rejected when a request is complete.

Refer to the documentation on Promises for details on how to chain requests together, detect errors, etc.

Refer to the PCAN-Basic documentation for details on the full API that this module implements.

Error Statistics

errorStats() returns a Promise that resolves with bus error statistics. The object has the same fields in all of the CAN adapter modules used by @csllc/cs-canbus-universal; a field whose value is not available is null.

{
  since: Date,            // when counting started (when the port was opened); null if never opened
  txFrames: 0,            // data/remote frames sent
  rxFrames: 0,            // data/remote frames received
  errorFrames: 0,         // bus errors (error frames on the bus) reported by the adapter
  errorPercent: 0,        // errorFrames / (txFrames + rxFrames + errorFrames) * 100; 0 if nothing was counted
  errors: {               // errorFrames by type; these add up to errorFrames
    bit: 0,               // bit errors, including bit0/bit1 errors
    stuff: 0,             // stuffing errors
    form: 0,              // form errors
    crc: 0,               // CRC errors
    ack: 0,               // acknowledgement errors (no ACK on transmission)
    other: 0,             // bus errors of an unreported or other type
  },
  arbitrationLost: 0,     // transmissions that lost arbitration (normal on a busy bus; not an error frame)
  overruns: 0,            // receive or transmit buffer overflows (frames were lost)
  busOff: 0,              // times the controller went bus-off
  state: 'error-active',  // 'error-active', 'error-warning', 'error-passive', 'bus-off', 'stopped', 'sleeping', or null
  txErrorCounter: 0,      // transmit error counter (TEC)
  rxErrorCounter: 0,      // receive error counter (REC)
}

The counters are reset when the port is opened, and cover only this instance. After the port is closed, the counters keep their final values and state, txErrorCounter and rxErrorCounter are null.

In this module:

  • Error frames are received by turning on the PCAN-Basic PCAN_ALLOW_ERROR_FRAMES parameter when the port is opened. If the hardware or library doesn't support it (for example, an older PCAN-Basic or the macOS PCBUSB library), errorFrames, errorPercent, errors, txErrorCounter and rxErrorCounter are null.
  • PCAN-Basic reports the kind of error (bit, form, stuff or other) and where in the frame it happened. Other errors in the CRC sequence or delimiter are counted as crc, and other errors in the ACK slot as ack.
  • txErrorCounter and rxErrorCounter are the values reported in the last error frame, and are null until one is received.
  • state, busOff and overruns come from CAN_GetStatus. overruns counts the times a receive overrun (PCAN_ERROR_OVERRUN or PCAN_ERROR_QOVERRUN) was reported, not the number of frames lost.
  • arbitrationLost is always null, since PCAN-Basic doesn't report it.
  • Error frames and status messages are counted, and are not emitted as data events.

Status

status() returns a Promise that resolves with the status of the port. The object has the same fields in all of the CAN adapter modules used by @csllc/cs-canbus-universal; a field whose value is not available is null. Whenever the status changes, a status event is emitted with the new status.

{
  port: 0x51,              // the port that is open (as passed to open()); null if closed
  connection: 'connected', // 'closed', 'connected', or 'disconnected' (the adapter stopped responding, e.g. was unplugged, while the port was open)
  state: 'error-active',   // 'error-active', 'error-warning', 'error-passive', 'bus-off', 'stopped', 'sleeping', or null
  bitrate: 250000,         // bit rate in use, in bit/s
}

state is the same as in Error Statistics, and is null unless connection is 'connected'. A port that is 'disconnected' stays that way until it is closed; isConnected() returns false for it.

can.on('status', (status) => {
  console.log(status.connection, status.state);
});

In this module, state comes from CAN_GetStatus, and bitrate from the PCAN_BUSSPEED_NOMINAL parameter (or the canRate option, if the library can't report it). If CAN_GetStatus reports that the hardware or its handle is no longer valid, the adapter is taken to be disconnected; isOpen() still returns true until the port is closed.

Development

Before updating or modifying this package, please

  • Lint any changes using npm run lint.
  • Confirm that unit tests pass, noting the exceptions described in the next section.

In order to run unit tests (npm test), at least one PCAN-USB device must be connected to the computer.

The pingpong example (example/js/pingpong.js) uses @csllc/cs-pcan-usb for one adapter, and @csllc/can-usb-com for the other. These must be connected via a properly terminated bus. It also needs @csllc/can-usb-com and @csllc/j1939, which are not dev dependencies of this package; install them with npm install --no-save @csllc/can-usb-com @csllc/j1939.

Do not run any tests on a bus with active traffic, since receiving unexpected CAN packets may confuse the tests.

Build Environment Setup

The vendor libraries needed to build the module are not part of this repository. npm run fetch-deps downloads them from their publishers into deps/ (which git ignores), checking each against a pinned SHA-256 hash:

  • Windows: the PEAK PCAN-Basic API (header and import library). PEAK only publishes the latest version at a fixed URL, so when PEAK releases a new version the hash check fails. Check the new version, then update the version and hash in tools/fetch-deps.js. Alternatively, unpack PCAN-Basic.zip yourself and set the PCAN_BASIC_SDK environment variable to that folder.
  • macOS: the PCBUSB library release, pinned to version 0.11.1. Alternatively, set the PCBUSB_DIR environment variable to an unpacked PCBUSB release folder, or install PCBUSB with its own install.sh, which puts it in /usr/local.

Windows x86_64

  1. Install NodeJS 24 or later. Ensure that node and npm are found when invoked through the Command Prompt.
  2. Install the native build tools, either by checking "Automatically install the necessary tools" in the NodeJS installer, or manually:
    • Visual Studio (or Visual Studio Build Tools) with the "Desktop development with C++" workload
    • Python 3
  3. Install the PEAK-System device driver package, including the PCAN-Basic API, to run the module and its tests.
  4. Clone the cs-pcan-usb repository: git clone https://github.com/csllc/cs-pcan-usb
  5. Download the PCAN-Basic API, then install dependencies (which also builds the module):
    • cd cs-pcan-usb\
    • npm run fetch-deps
    • npm install
  6. Build:
    • npm run rebuild
  7. Test:
    • npm test

macOS (Intel or Apple Silicon)

  1. Install NodeJS 24 or later.
  2. Install the Xcode Command Line Tools: xcode-select --install
  3. Clone the cs-pcan-usb repository: git clone https://github.com/csllc/cs-pcan-usb
  4. Download the PCBUSB library, then install dependencies (which also builds the module):
    • cd cs-pcan-usb
    • npm run fetch-deps
    • npm install
  5. Build:
    • npm run rebuild
  6. Test:
    • npm test

Build Notes

The module is loaded with node-gyp-build, which uses a prebuilt binary from prebuilds/<platform>-<arch>/ if one is present and otherwise falls back to build/Release/. When working on the C code, delete the prebuilds/ folder (if you made one) so that your local build is the one that gets loaded.

On macOS, npm run build and npm run rebuild copy the complete PCBUSB release folder into build/Release/PCBUSB/. The addon looks for libPCBUSB there first, then in /usr/local/lib.

If the test stage fails, useful error messages may be hidden by mocha. When tracking down the cause of an error, running the example code directly, for example node example/js/test.js, may be helpful.

It may also be helpful to recompile the module with PCAN_DEBUG (and, on macOS, PCAN_EVENT_DARWIN_DEBUG) defined to enable debugging messages.

Editor Support

A compile_flags.txt is included for clangd. It uses the Node-API headers from the node-api-headers dev dependency and the vendor headers in deps/, so run npm run fetch-deps -- --all and npm install first.

Distribution

The module uses only Node-API, so a single binary works with every supported NodeJS version. The published npm package includes prebuilt binaries in prebuilds/. If no prebuilt binary matches, npm install falls back to building from source, which requires the build environment described above.

The macOS prebuilt binaries include the complete, unmodified PCBUSB release folder in prebuilds/darwin-*/PCBUSB/. If you bundle this module into an application (for example with Electron), keep that whole folder next to the .node file, outside of any .asar archive: PCBUSB's license only allows redistributing complete copies, including its LICENSE file.

To make the prebuilt binary locally (after npm run fetch-deps):

  • npm run prebuildify

Releasing

Releases are built and published by the GitHub Actions workflow in .github/workflows/release.yml, which runs when a version tag is pushed:

  1. Run the tests locally with a PCAN-USB adapter connected, on Windows and on macOS: npm test (the CI runners have no CAN hardware, so they only check that the binaries build and load)
  2. npm version patch (or minor / major), which updates package.json and creates the tag vX.Y.Z
  3. git push --follow-tags

The workflow builds the Windows x86_64 binary and the macOS Apple Silicon and Intel binaries, each on its own runner, using the vendor libraries downloaded by tools/fetch-deps.js. It checks that each binary loads, that the macOS binaries load the bundled PCBUSB copy, and that the bundled copy is identical to the PCBUSB release. It then publishes the package to npm with the binaries included, after checking that no PEAK PCAN-Basic files would be published. Publishing uses npm trusted publishing, so no npm token is stored in GitHub; the package's trusted publisher on npmjs.com must name this repository and the workflow file release.yml.

Functionality Limitations

Questionable Functionality

The following have been observed as potential sources of unexpected behavior in the future.

  • No error is thrown when the PCAN-USB dongle is unplugged while the module is running.
  • The PCAN-Basic API does not provide the means to enable a CAN adapter's loopback ("self-receive" or "echo") feature. This is instead simulated in cs-pcan-usb by pushing data passed to the write method back into the receive data stream.
  • As a result of the above, the expected outcome of the test units (npm test) is that all pass except those in filter.test.js that test should.not.emit('data'). This does not indicate that the filter functionality does not work.
  • The macOS build of this addon does not support filtering at all, and any attempts to use pcan.FilterMessages, pcan.AcceptanceFilter11Bit, and pcan.AcceptanceFilter29Bit will fail silently.
  • The PCAN-USB adapter does not differentiate between extended and standard filters; see the notes under Filtering, above.

Untested Functionality

The following have not been tested due to hardware availability.

  • All FD functions (CAN_InitializeFD, CAN_ReadFD, CAN_WriteFD)

Licensing and Dependencies

This project is licensed under the MIT license - see the LICENSE file for details.

It uses the following third-party components:

  • PEAK PCAN-Basic API - Copyright PEAK-System Technik GmbH, Germany. On Windows, the module is compiled against PCAN-Basic's header file and import library, and uses PCANBasic.dll at runtime. These are subject to PEAK-System's license and are not included in this repository or in the npm package. Install the PEAK-System device driver package to use the module, and see Build Environment Setup to build it.
  • PCBUSB library - Copyright UV Software, Berlin. On macOS, the module is compiled against PCBUSB and uses libPCBUSB at runtime. PCBUSB is not included in this repository. As its license permits, a complete, unmodified copy of the PCBUSB release, including its license, is included with the macOS prebuilt binaries in the npm package.