@csllc/can-usb-com
v6.1.1
Published
NodeJS interface to the GridConnect CAN-USB-COM adapter
Keywords
Readme
NodeJS CAN-USB-COM Module
This module provides a nodejs interface to the gridconnect CAN-USB Converter.
The module implements the device-specific serial protocol and is not compatible with any other device or adapter.
This package is published as @csllc/can-usb-com. It was forked from the unscoped can-usb-com package (version 5.x and earlier), which uses serialport 9 and does not work on current NodeJS versions.
The module communicates with the adapter through serialport, which supports Windows, macOS and Linux. NodeJS 20 or later is required, and Node 24 or later is recommended.
Getting Started
The following assumes that NodeJS is already installed. To install this module, use
npm install @csllc/can-usb-comThe usb device appears as a serial port; depending on your platform you may or may not need to install drivers. If a serial port does not appear when you plug in the board, refer to the board installation documents and/or download drivers from https://gridconnect.com/usb-can-interface.html and correct the problem. Note: on Windows, connecting the can-usb-com may cause the mouse to act erratically. To correct this, go to Device Manager, Ports, and choose the COM port that represents the CAN-USB-COM device. Under Properties | Advanced, there is a checkbox called 'Serial Enumerator'. Uncheck that box and the problem will go away.
Simple script to use the board (change the COM port number as required):
const Can = require('@csllc/can-usb-com');
// Use default settings (do not filter messages)
let board = new Can();
// Handle each incoming message
board.on('data', function( msg ) {
console.log( 'Msg: ', msg.id.toString(16), msg.ext, msg.buf );
});
// Open the com port and configure...
board.open( 'COM3' )
.then( function() {
console.log('Listening....');
// listen for 5 seconds, then end the script
setTimeout( function() {
board.close();
process.exit(0);
}, 5000 );
// send an extended (29-bit ID) message with some data.
board.write( { id: 0x10EF8001, ext: true, buf: [0x49, 0x2B, 0x06, 0x00, 0x02] });
})
.catch( function( err ) {
// If anything goes wrong, report the error and exit
console.error( err );
board.close();
process.exit(-1);
});
Streaming
The CAN-USB-COM extends the NodeJS stream interface, so it can be piped to other stream instances. See basic.test.js in the test folder.
Examples
Several complete examples can be found in the example folder.
Configuration
The constructor accepts an object that specifies the desired configuration. The board/ports are set up before the 'open' command resolves (so once the open operation is complete, the CAN interface is ready to use).
The options are as shown in the following example (if you are happy with the option, you can omit it from the options object and the default will be used).
let board = new Can({
// Serial port baud rate
baudRate: 115200,
// bit rate on the CAN bus
canRate: 250000,
// typical CAN sample point
samplePoint: 75,
// filters for incoming packets
filters: [
],
// useful for testing, each sent packet is also received
loopback: false,
});Filters
By default, all CAN packets are captured. You can limit the number of incoming frames by using filters. They are specified as an array of up to 10 filter definitions. A filter has these fields:
{
// true if the filter applies to 29-bit CAN IDs
ext: true,
// The ID to accept. May also be specified as a range:
// for example '10FF1100 10FF11FF'
id: '10FF1122'
}Events
The board object emits the following events:
openwhen the serial port is successfully openederrorif an error occurs (like the serial port could not be opened)datawhen an incoming CANBUS frame is receivedwritewhen an outgoing CANBUS frame is sent to the device (the event occurs before the frame is actually put on the wire)statuswhen the status of the port changes (see Status)closewhen the port is closed
To listen for the events, use the typical NodeJS EventEmitter pattern:
board.on('open', function(){
console.log( 'Port opened');
})
board.on('data', function(pgn){
console.log( pgn );
})
board.on('close', function(err){
if( err && err.disconnected ) {
console.log( 'Port Disconnected' );
}
else {
console.log( 'Port Closed by application' );
}
})
board.on('error', function(err){
console.log( 'Serial Port error: ', err );
})
API
API functions generally return Promises, which are resolved or rejected when the request is complete. Refer to NodeJS Promise documentation for details on how to chain requests together, detect errors, etc. Refer to the CAN-USB-COM protocol document for additional details on the use of the commands and valid parameter ranges.
Error Statistics
errorStats() returns a Promise that resolves with an object that has the same fields as in the other CAN adapter modules used by @csllc/cs-canbus-universal (see its documentation). The CAN-USB-COM doesn't report bus errors or the state of its CAN controller, so only these fields have values; the rest are null:
{
since: Date, // when counting started (when the port was opened); null if never opened
txFrames: 0, // frames sent
rxFrames: 0, // frames received
...
}With loopback enabled, frames the adapter echoes back are counted as received as well as sent.
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: 'COM9', // 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:
- The CAN-USB-COM doesn't report the state of its CAN controller, so
stateis alwaysnull. bitrateis thecanRateoption that the adapter was configured with.- The adapter is taken to be disconnected when the serial port is closed because the device went away (the
closeevent's error hasdisconnectedset). The serial port is already closed then, soisOpen()returns false, but the status stays'disconnected'untilclose()oropen()is called. - Status changes are noticed from serial port events, so no periodic check is needed.
Development
Please note the following if you wish to update or modify this package:
- eslint rules are included, please lint any changes.
- Confirm that unit tests are working. To run the tests, use the 'npm test' command.
npm test runs test/basic.test.js, which uses a mock serial port binding (test/MockBinding.js) and needs no hardware. The mock is supplied through the Binding constructor option; any object implementing the serialport v10+ binding interface (list() and open()) can be used there.
npm run test:hardware runs test/filter.test.js against real hardware. In order to run it, you will need at least one CAN-USB-COM device connected to your computer. I think you may have to have the CAN-USB-COM device connected to a properly terminated bus. However, do not run the tests on a bus with active traffic, since receiving unexpected CAN packets will probably confuse the tests.
The local folder holds older ad hoc test scripts. They are not part of the npm package and are not linted.
Distribution
The module is plain JavaScript. Its only native code is in serialport's @serialport/bindings-cpp dependency, which ships its own prebuilt Node-API binaries, so nothing needs to be compiled when the module is installed.
Releasing
Releases are published by the GitHub Actions workflow in .github/workflows/release.yml, which runs when a version tag is pushed:
- Run the hardware tests locally with a CAN-USB-COM adapter connected:
npm run test:hardware(the CI runners have no CAN hardware, so they only run the unit tests) npm version patch(orminor/major), which updatespackage.jsonand creates the tagvX.Y.Zgit push --follow-tags
The workflow lints the code and runs the unit tests on Windows, macOS and Linux with NodeJS 20, 22 and 24, and checks that the serialport native binding loads on each. It then publishes the package to npm. 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.
Licensing and Dependencies
This project is licensed under the MIT license - see the LICENSE file for details.
It uses serialport (@serialport/stream and @serialport/bindings-cpp), which is also MIT licensed.
