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/j1939

v2.0.0

Published

J1939 transport layer for CANBUS communication

Readme

@csllc/j1939

A TypeScript implementation of the SAE J1939 CANBUS protocol for Node.js. Provides J1939 transport layer functionality — including address claiming (J1939-81) and multi-packet transport protocol (J1939-21) — on top of a compatible CANBUS interface driver.

This package is intended primarily for testing and diagnostic purposes. The implementation is not fully J1939 compliant, does not necessarily implement strict packet timings, and may have missing or broken features.

Installation

npm install @csllc/j1939

Module Formats

The package ships with dual build outputs:

  • ESM (import): dist/esm/index.js
  • CommonJS (require): dist/cjs/index.js
  • TypeScript declarations: dist/esm/index.d.ts / dist/cjs/index.d.ts

Node.js automatically resolves the correct format via the exports field in package.json.

Exports

Classes

J1939

The main protocol handler class. Extends EventEmitter.

import { J1939 } from '@csllc/j1939';
// or
const { J1939 } = require('@csllc/j1939');

Constructor: new J1939(bus: CanBus, options?: J1939Options)

| Parameter | Description | |-----------|-------------| | bus | A CANBUS driver instance implementing the CanBus interface (Node.js Duplex stream with write(), isOpen(), close(), _flushRequestQueue()) | | options | Optional configuration (see J1939Options below) |

Methods:

| Method | Signature | Description | |--------|-----------|-------------| | write | (msg: J1939WriteMessage) => void | Send a J1939 PGN. Messages <= 8 bytes are sent as single CAN frames. Larger messages automatically use Transport Protocol (BAM for broadcast, RTS/CTS for addressed). | | setOptions | (options: J1939Options) => void | Update configuration options and reset address claim state. | | setAddress | (name?, preferredAddress?, addressRange?) => Promise<void> | Run the J1939-81 address claim procedure. Called automatically on bus open. | | isReady | () => boolean | Returns true if address claim is complete and bus is open. | | isOpen | () => boolean | Returns true if the underlying bus connection is open. | | close | () => void | Close the bus and clean up all pending transactions. | | buildId | (pgn: number, to: number, pri?: number) => number | Build a 29-bit CAN identifier from a PGN, destination, and priority. | | compareName | (a: number[], b: number[]) => number | Compare two 8-byte J1939 NAMEs for arbitration priority. Returns -1 if a wins, 1 if b wins. |

Events:

| Event | Payload | Description | |-------|---------|-------------| | open | (address: number) | Address claim succeeded; protocol is ready. | | data | (msg: J1939Message) | Incoming J1939 PGN addressed to this node or broadcast. | | rx | (msg: CanMessage) | Incoming non-J1939 message (11-bit standard CAN frame). | | address | (status: AddressClaimStatus, attemptedAddress: number) | Address claim status update. | | close | () | Bus and protocol shut down. | | error | (err: Error) | Error from J1939 processing or the CANBUS interface. |

Enums

AddressClaimStatus

import { AddressClaimStatus } from '@csllc/j1939';

AddressClaimStatus.NONE          // 0 - No claim attempted
AddressClaimStatus.IN_PROGRESS   // 1 - Claim in progress
AddressClaimStatus.SUCCESSFUL    // 2 - Address claimed successfully
AddressClaimStatus.FAILED        // 3 - Claim failed
AddressClaimStatus.LISTEN_ONLY   // 4 - Listen only (not implemented)

Interfaces

J1939Options

Configuration options for the J1939 constructor.

interface J1939Options {
  address?: number;              // Preferred source address (0x00-0xFD), default 0xFE
  preferredAddress?: number;     // Alias for address
  name?: number[];               // 8-byte J1939 NAME for address arbitration
  addressRange?: [number, number]; // [min, max] fallback address range
  bamInterval?: number;          // BAM packet interval in ms (default 50)
  priority?: number;             // Default message priority 0-7 (default 4)
  sendClaimRequest?: boolean;    // Send Request for Address Claimed during claim
}

J1939WriteMessage

Message format for write().

interface J1939WriteMessage {
  pgn: number;                   // Parameter Group Number
  dst: number;                   // Destination address (0xFF for broadcast)
  buf: Buffer;                   // Payload data
  src?: number;                  // Source address override
  priority?: number;             // Priority 0-7 (0 = highest)
  cb?: (err: Error | null) => void; // Completion callback for TP transfers
}

J1939Message

Received message format emitted by the data event.

interface J1939Message {
  pri: number;   // Priority 0-7
  src: number;   // Source address
  dst: number;   // Destination address
  pgn: number;   // Parameter Group Number
  buf: Buffer;   // Payload data
}

CanMessage

Raw CAN frame as seen on the bus.

interface CanMessage {
  id: number;                    // 11-bit or 29-bit CAN identifier
  ext?: boolean;                 // true for 29-bit extended frame
  buf: Buffer | number[];        // Frame payload
}

CanBus

Interface that a CANBUS driver must implement. Must be a Node.js Duplex-compatible stream that emits open, data, error, and close events.

interface CanBus extends EventEmitter {
  write(msg: CanMessage): any;
  isOpen(): boolean;
  close(): void;
  _flushRequestQueue(): void;
}

Quick Start

CommonJS

const Canbus = require('can-usb-com');
const { J1939 } = require('@csllc/j1939');

const bus = new Canbus({ canRate: 250000 });

const j1939 = new J1939(bus, {
  address: 0x80,
  name: [0, 0, 0, 0, 0, 0, 0, 0],
});

j1939.on('open', (address) => {
  console.log('J1939 ready with address', address);

  j1939.on('data', (msg) => {
    console.log('Received PGN', msg.pgn.toString(16), 'from', msg.src);
  });

  // Send a short message (single CAN frame)
  j1939.write({
    pgn: 0xEF00,
    dst: 100,
    priority: 7,
    buf: Buffer.from([0x01, 0x02, 0x03]),
  });
});

ESM / TypeScript

import { J1939, AddressClaimStatus } from '@csllc/j1939';
import type { J1939Options, J1939Message, CanBus } from '@csllc/j1939';

const bus: CanBus = getCanBusInterface(); // your CANBUS driver

const options: J1939Options = {
  address: 0x80,
  name: [1, 2, 3, 4, 5, 6, 7, 8],
  addressRange: [0x80, 0xEF],
  priority: 6,
};

const j1939 = new J1939(bus, options);

j1939.on('address', (status, address) => {
  if (status === AddressClaimStatus.SUCCESSFUL) {
    console.log('Claimed address', address);
  }
});

j1939.on('data', (msg: J1939Message) => {
  console.log(`PGN 0x${msg.pgn.toString(16)} from ${msg.src}:`, msg.buf);
});

Multi-Packet Transfer

Messages larger than 8 bytes are automatically sent using the J1939 Transport Protocol:

const { J1939 } = require('@csllc/j1939');

const j1939 = new J1939(bus, { address: 0x80 });

j1939.on('open', () => {
  // Send a large message via TP (BAM for broadcast)
  j1939.write({
    pgn: 0xEF00,
    dst: 0xFF,            // broadcast
    priority: 7,
    buf: Buffer.alloc(200).fill(0x55),
  });

  // Send via RTS/CTS to a specific address, with completion callback
  j1939.write({
    pgn: 0xEF00,
    dst: 0x10,
    priority: 7,
    buf: Buffer.alloc(200).fill(0xAA),
    cb: (err) => {
      if (err) {
        console.error('Transfer failed:', err.message);
      } else {
        console.log('Transfer complete');
      }
    },
  });
});

Address Claiming with Fallback Range

const { J1939 } = require('@csllc/j1939');

const j1939 = new J1939(bus, {
  address: 0x80,                    // preferred address
  name: [1, 2, 3, 4, 5, 6, 7, 8],  // 8-byte NAME for arbitration
  addressRange: [0x80, 0xEF],       // fallback range if preferred is taken
  priority: 6,
});

j1939.on('address', (status, address) => {
  console.log('Address claim status:', status, 'address:', address);
});

j1939.on('open', (address) => {
  console.log('Ready at address', address);
});

j1939.on('error', (err) => {
  console.error('Error:', err.message);
});

Examples

See the example/ directory for complete working examples:

  • network_management.js — Demonstrates J1939-81 address claiming with a @csllc/cs-canbus-universal adapter
  • pingpong.js — Bidirectional multi-packet messaging between two CAN-USB-COM devices

Development

Building

npm run build         # Build both ESM and CJS
npm run build:esm     # Build ESM only
npm run build:cjs     # Build CJS only
npm run clean         # Remove dist/

Testing

npm test                              # Build + run all tests
npx mocha test/offline/claim.test.js  # Run a specific test file

Offline tests in test/offline/ use a mock bus and do not require CAN hardware.

Linting

npm run lint

ESLint rules are stored in .eslintrc.js.

License

MIT