npm.io
3.3.0 • Published 5 months ago

@csllc/cs1816

Licence
MIT
Version
3.3.0
Deps
3
Size
349 kB
Vulns
0
Weekly
0

@csllc/cs1816

TypeScript package for communicating with CS181x BLE/USB dongles — small hardware devices that provide wireless access to an attached local device or system over Bluetooth Low Energy or USB.

Installation

npm install @csllc/cs1816

Overview

The CS181x dongle acts as a BLE peripheral and connects by wire to a motor controller or other local device. This package runs on the BLE central side (mobile app, Node.js desktop, etc.) and handles:

  • Inspecting and reading dongle device information
  • Reading and writing device memory and objects
  • Configuring the dongle's hardware interface (I2C, serial, CAN)
  • Monitoring variables via watchers and the super watcher
  • Sending commands such as keyswitch control and device reset

Two concrete dongle implementations are provided:

  • BleDongle — for BLE connections. Requires a Ble-compatible adapter such as @csllc/noble-ble or @csllc/mb-conn-noble-mac.
  • UsbDongle — for USB/serial connections. Requires a Connection from @csllc/mb-conn-node-serial.

Both follow the same lifecycle: construct, then call initialize() to open the connection, create the MODBUS Master, and inspect device objects.

The exported serviceUuid string is the BLE service UUID that CS181x dongles advertise. Use it with your BLE manager's scan function to discover dongles.

Usage

BLE dongle
import { BleDongle, serviceUuid } from '@csllc/cs1816';
import { NobleMacBleManager } from '@csllc/mb-conn-noble-mac';

await NobleMacBleManager.initialize();

// Scan for a dongle advertising serviceUuid
await NobleMacBleManager.startScan([serviceUuid], (peripheral) => {
  console.log('Found dongle:', peripheral.name);
});
// ... stop scan, get peripheral reference ...

// Construct and initialize — BleDongle handles BLE connect, UART discovery,
// and Master creation internally
const dongle = new BleDongle(NobleMacBleManager, peripheral, {
  logger: myLogger,
});
await dongle.initialize();

const info = await dongle.readDongleInfo();
console.log('Model:', info.model, 'SW:', info.sw);
USB dongle
import { UsbDongle } from '@csllc/cs1816';
import { SerialConnection } from '@csllc/mb-conn-node-serial';

const connection = new SerialConnection(
  { id: '/dev/ttyUSB0', type: 'serial', path: '/dev/ttyUSB0' },
  { baudRate: 9600 },
);

// Construct and initialize — UsbDongle opens the serial port and creates
// the MODBUS Master internally
const dongle = new UsbDongle(connection, { logger: myLogger });
await dongle.initialize();

const info = await dongle.readDongleInfo();
console.log('Model:', info.model, 'SW:', info.sw);
Reading dongle info
const info = await dongle.readDongleInfo();
// info: { model, serial, fw, hw, sw, mfr, product }

// After readDongleInfo(), softwareRevision is also available in multiple formats:
console.log(dongle.softwareRevision.string); // "1.10.0"
console.log(dongle.softwareRevision.scalar); // 0x010A00
console.log(dongle.softwareRevision.bytes); // { major: 1, minor: 10, patch: 0 }
Reading and writing memory
// Read 128 bytes starting at address 0x0300 from unit 1
const data = await dongle.readMemory(1, 0x0300, 128);

// Write bytes to unit 1 at address 0x0310
await dongle.writeMemory(1, 0x0310, [0x01, 0x02, 0x03]);

// Write with readback verification
await dongle.writeVerifyMemory(1, 0x0310, [0x01, 0x02, 0x03]);
Reading and writing objects
// Read object 0 from the dongle itself (unit ID is dongle.ID = 0xFE)
const config = await dongle.readObject(dongle.ID, 0);

// Write bytes to object 5 on unit 1
await dongle.writeObject(1, 5, [0xab, 0xcd]);
Access key
// Read the 128-byte cloud access key (object 0 on the dongle)
const key = await dongle.readAccessKey();

// Write a new access key
await dongle.writeAccessKey([...Array(128).keys()]);
Configuration and keyswitch
// Configure I2C interface
await dongle.configureI2c();

// Enable the keyswitch output
await dongle.keyswitch(true);

// Monitor interface status changes
await dongle.onInterfaceUpdate((iface, dongle) => {
  console.log('Interface status changed:', iface);
});
Watchers
// Watch 1 byte at address 0x0086 on unit 1
const slot = await dongle.watch(
  1,
  0x0086,
  1,
  undefined,
  (data, address, unit) => {
    console.log('Variable changed:', data[0]);
  },
);

// Cancel the watcher
dongle.unwatch(slot);

// Cancel all watchers
await dongle.unwatchAll();
Super watcher

The super watcher monitors a larger number of single-byte variables. Register all variables before calling updateSuperWatcher().

await dongle.clearSuperWatcher();

await dongle.superWatch(1, 0x0086, (data, address, unit) => {
  console.log(`Address ${address} on unit ${unit} changed:`, data);
});
await dongle.superWatch(1, 0x0087, (data, address, unit) => {
  console.log(`Address ${address} changed:`, data);
});

await dongle.updateSuperWatcher();
Batch registration

registerWatchers() and registerSuperWatchers() provide a simpler way to set up multiple watchers at once, without needing to handle BLE vs USB differences — the dongle subclass manages the transport-specific details internally.

import type { WatcherRegistration, SuperWatcherRegistration } from '@csllc/cs1816';

const watchers: WatcherRegistration[] = [
  { unit: 1, address: 0x0064, length: 2, cb: (value) => console.log('Voltage:', value) },
  { unit: 1, address: 0x0038, length: 1, cb: (value) => console.log('Fault:', value) },
];

const slots = await dongle.registerWatchers(watchers);
console.log('Registered in slots:', slots);

// Tear down all watchers (e.g. when the device interface goes down)
dongle.teardownWatchers();
Resetting a device
await dongle.reset(1);

API Reference

Exported symbols
Symbol Type Description
Dongle abstract class Abstract base class for all dongle types.
UsbDongle class extends Dongle USB/serial dongle. Creates RTU Master internally.
BleDongle class extends Dongle BLE dongle. Creates IP Master internally.
DeviceInterface class Decoded hardware interface status (mode, protocol, up/down).
Watcher class Represents a registered watcher slot.
serviceUuid string BLE service UUID advertised by CS181x dongles.
DONGLE_UUIDS object All BLE service and characteristic UUIDs for CS181x.
ConfigureCommand class Configure dongle hardware interface (mode, protocol).
DongleMode enum Hardware interface mode values.
DongleProtocol enum Protocol values.
KeySwitchCommand class Set keyswitch output state.
WatchCommand class Set watcher slots on the device.
WatchMaskedCommand class Set watcher slots with per-bit change masks.
UnwatchCommand class Cancel specific watcher slots.
UnwatchAllCommand class Cancel all watcher slots.
SuperWatchCommand class Set superwatcher addresses.
GetWatcherCommand class Query the device's watcher table.
WatcherType enum Watcher query type.
ResetCommand class Send a reset command to a device.
DongleSlaveIdRequest class Report slave ID request.
WatcherRegistration interface Configuration for registering a watcher.
SuperWatcherRegistration interface Configuration for registering a superwatcher.
WatcherCallback type Callback type for watcher/superwatcher updates.

abstract class Dongle

Abstract base class managing MODBUS commands, watcher registry, and high-level dongle operations. All MODBUS operations use Master.requestAsync() with typed Request/Response classes.

Constructor
new Dongle(options: DongleOptions)

The constructor is lightweight — it stores options and initializes watcher arrays. The Master and connection are created by subclasses during initialize().

DongleOptions

Property Type Default Description
logger DongleLogger (stub) Winston/pino-compatible logger for debug output.
maxConcurrentRequests number 2 Maximum simultaneous in-flight requests.
defaultMaxRetries number 2 Default retry count per transaction.
defaultTimeout number 6500 Default response timeout in milliseconds.
retryOnException number[] [0x04, 0x06, 0x0A, 0x0B] Exception codes that trigger a retry.
Properties
Property Type Description
ID number The dongle's Modbus unit address (0xFE).
master Master The MODBUS Master instance (set during initialize()).
peripheral Connection The underlying connection (set during initialize()).
softwareRevision DongleSwVersion Software version (set after readDongleInfo()).
watchers Watcher[] Array of registered watcher slots (max 15).
superWatchers Watcher[] Array of super watcher slots (max 25).
Methods
Method Returns Description
initialize() Promise<void> Opens connection, creates Master, inspects device objects. Must be called after construction.
destroy() void Destroys the master and clears watcher state.
readDongleInfo() Promise<DongleInfo> Reads model, serial, firmware, hardware, software revision, and manufacturer.
readInterface() Promise<DeviceInterface> Reads the current interface status object.
readMemory(unit, address, length, options?) Promise<number[]> Reads bytes from device memory.
writeMemory(unit, address, data, options?) Promise<any> Writes bytes to device memory.
writeVerifyMemory(unit, address, data, options?) Promise<any> Writes and verifies bytes at device memory.
readObject(unit, objectId, options?) Promise<number[]> Reads a MODBUS object by ID.
writeObject(unit, objectId, data, options?) Promise<number> Writes data to an object. Returns status.
readAccessKey() Promise<number[]> Reads the dongle's 128-byte cloud access key (object 0).
writeAccessKey(data) Promise<any> Writes the dongle's cloud access key.
command(unit, opcode, data?) Promise<any> Sends an FN_COMMAND with opcode and optional data.
configure(options?) Promise<any> Sends the configuration command.
configureI2c(options?) Promise<void> Configures I2C hardware interface.
keyswitch(isEnabled, options?) Promise<any> Sets the keyswitch output state.
onInterfaceUpdate(cb) Promise<void> Registers a callback for interface status changes.
watch(unit, address, length, mask?, cb) Promise<number> Sets up a watcher. Returns the slot number.
unwatch(slot) void Removes watcher at slot.
unwatchAll(options?) Promise<any> Removes all watchers.
superWatch(unit, address, cb) Promise<number> Registers a super watcher address. Returns slot.
updateSuperWatcher(options?) Promise<void> Activates all registered super watchers.
clearSuperWatcher(options?) Promise<any> Disables and clears super watcher slots.
registerWatchers(registrations) Promise<number[]> Registers watchers from an array of configs. Returns slot indices.
registerSuperWatchers(registrations) Promise<number[]> Registers superwatchers from an array of configs. Returns slot indices.
teardownWatchers() void Resets all watcher and superwatcher slots to empty.
reset(unit, options?) Promise<number> Sends a reset command.

class UsbDongle extends Dongle

USB/serial dongle implementation. Creates a MODBUS Master with RTU transport over the supplied Connection.

Constructor
new UsbDongle(connection: Connection, options: DongleOptions)

The connection is a SerialConnection (or any Connection subclass). The Master is created during initialize(), not in the constructor.


class BleDongle extends Dongle

BLE dongle implementation. Connects to the BLE peripheral, discovers UART characteristics, creates an internal BLE UART connection, and creates a MODBUS Master with IP transport.

Constructor
new BleDongle(ble: Ble, blePeripheral: BlePeripheral, options: DongleOptions)
  • ble — a Ble-compatible adapter (e.g. NobleBleManager, NobleMacBleManager)
  • blePeripheral — the discovered BlePeripheral from scanning

initialize() handles BLE connect, characteristic discovery, UART connection setup, and Master creation.


DONGLE_UUIDS

Object containing all BLE service and characteristic UUIDs for CS181x dongles:

import { DONGLE_UUIDS } from '@csllc/cs1816';

DONGLE_UUIDS.uuidUartService; // '49535343-FE7D-4AE5-8FA9-9FAFD205E455'
DONGLE_UUIDS.uuidRx; // UART RX characteristic
DONGLE_UUIDS.uuidTx; // UART TX characteristic
DONGLE_UUIDS.uuidControllerService; // '6765ED1F-4DE1-49E1-4771-A14380C90000'
DONGLE_UUIDS.uuidDeviceInformation; // '180A'
// ... and more

class Watcher

Represents a single registered watcher slot. Normally constructed internally by Dongle.watch().

Properties
Property Type Description
unit number Slave unit being watched.
address number Memory address being watched.
length number Number of bytes being watched.
hasMask boolean Whether a change mask is active.
cb function Callback: (value: number[], address: number, unit: number) => void

interface DongleInfo

Returned by readDongleInfo().

interface DongleInfo {
  model: string;
  serial: string;
  fw: string;
  hw: string;
  sw: string | undefined;
  mfr: string;
  product: string;
}

interface DongleLogger
interface DongleLogger {
  log(level: string, message: string, ...args: any[]): void;
  child(context: Record<string, any>): DongleLogger;
}

Message Classes

All message classes extend CommandRequest from @csllc/modbus-types:

  • ConfigureCommand — Configure hardware interface
  • KeySwitchCommand — Set keyswitch state
  • WatchCommand / WatchMaskedCommand — Set watchers
  • UnwatchCommand / UnwatchAllCommand — Cancel watchers
  • SuperWatchCommand — Set super watcher addresses
  • GetWatcherCommand — Query watcher table
  • ResetCommand — Reset device
  • DongleSlaveIdRequest — Report slave ID