@csllc/cs1816
v3.3.0
Published
Package used to communicate with CS1816-type BLE and USB Dongles
Downloads
172
Keywords
Readme
@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/cs1816Overview
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 aBle-compatible adapter such as@csllc/noble-bleor@csllc/mb-conn-noble-mac.UsbDongle— for USB/serial connections. Requires aConnectionfrom@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— aBle-compatible adapter (e.g.NobleBleManager,NobleMacBleManager)blePeripheral— the discoveredBlePeripheralfrom 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 moreclass 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 interfaceKeySwitchCommand— Set keyswitch stateWatchCommand/WatchMaskedCommand— Set watchersUnwatchCommand/UnwatchAllCommand— Cancel watchersSuperWatchCommand— Set super watcher addressesGetWatcherCommand— Query watcher tableResetCommand— Reset deviceDongleSlaveIdRequest— Report slave ID
