# @csllc/cs1816

> Package used to communicate with CS1816-type BLE and USB Dongles

Latest version **3.3.0** (published 2026-04-09) · MIT license · 0 weekly downloads

## Install

```sh
npm install @csllc/cs1816
pnpm add @csllc/cs1816
yarn add @csllc/cs1816
bun add @csllc/cs1816
```

## Health

**Score 60/100 (C)** — status: active.

Positive: has types; esm support; no vulnerabilities; high quality score.

Warnings: low downloads.

## Facts

| | |
|---|---|
| Version | 3.3.0 |
| Published | 2026-04-09 |
| First published | 2022-06-24 |
| Weekly downloads | 0 |
| License | MIT |
| TypeScript types | bundled |
| Module format | ESM + CommonJS |
| Node | >= 14 |
| Dependencies | 3 |
| Unpacked size | 348.5 KB |
| Known vulnerabilities | 0 |
| Install scripts | no |
| GitHub stars | 0 |
| Author | Bill Glase |
| Maintainers | gfang, cwalden-cs, sean.kudrna.cs, incuiti, dayaeger |

## Links

- npm: https://www.npmjs.com/package/@csllc/cs1816
- Repository: https://github.com/csllc/CS1816
- Homepage: https://github.com/csllc/CS1816#readme
- Issues: https://github.com/csllc/CS1816/issues
- npm.io page: https://npm.io/package/@csllc/cs1816

## Dependencies (3)

- [@csllc/blejs-types](https://npm.io/package/@csllc/blejs-types.md) *
- [@csllc/modbus-types](https://npm.io/package/@csllc/modbus-types.md) *
- [@csllc/mb-conn-node-serial](https://npm.io/package/@csllc/mb-conn-node-serial.md) *

## Recent versions

- 3.3.0 (latest) — 2026-04-09
- 3.2.0 — 2026-04-08
- 3.1.0 — 2026-04-06
- 2.1.1 — 2024-04-23
- 2.1.0 — 2023-08-15
- 2.0.0 — 2023-03-24
- 1.1.0 — 2022-11-08
- 1.0.3 — 2022-07-14
- 1.0.2 — 2022-07-14
- 1.0.1 — 2022-07-06
- 1.0.0 — 2022-06-24

## 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

```bash
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

```typescript
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

```typescript
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

```typescript
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

```typescript
// 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

```typescript
// 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

```typescript
// 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

```typescript
// 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

```typescript
// 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()`.

```typescript
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.

```typescript
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

```typescript
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

```typescript
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

```typescript
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

```typescript
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:

```typescript
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()`.

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

---

### `interface DongleLogger`

```typescript
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

---
_Source: https://npm.io/package/@csllc/cs1816 · Machine-readable twin of the npm.io package page. Health data is recomputed on every publish._
