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

@danidoble/webserial-board2droid

v1.0.0

Published

A strongly-typed, event-driven USB board2droid driver for the Web Serial API, built on top of webserial-core.

Readme

@danidoble/webserial-board2droid

TypeScript driver for Board2Droid vending-machine controllers, built on top of webserial-core v2.

Installation

npm install @danidoble/webserial-board2droid webserial-core
# or
pnpm add @danidoble/webserial-board2droid webserial-core

webserial-core is a peer dependency — you must install it alongside this package.

Quick start

import { Board2Droid } from '@danidoble/webserial-board2droid';

const board = new Board2Droid({ filters: [{ usbVendorId: 0x1a86 }] });

board.on('b2d:pong', ({ firmware, deviceNumber }) =>
  console.log('Board connected — firmware:', firmware, '— device:', deviceNumber),
);

await board.connect();  // opens the serial port and runs handshake
await board.sendPing(); // optional extra ping

Protocol

Binary, fixed 16-byte frames.

[SOF=0xF1][CMD][D0..D11][EOF=0xF2][CHK]
  • CHK = (CMD + D0 + … + D11 + EOF) & 0xFF
  • D11 = target device number (0 = broadcast)
  • Multi-board addressing over RS-232 bus

Constructor options (Board2DroidOptions)

| Option | Type | Description | |-------------------|-------------------------|--------------------------------------------------| | filters | SerialPortFilter[] | USB vendor/product filters for requestPort() | | provider | SerialProvider | Custom transport (WebUSB, BT, WebSocket, …) | | polyfillOptions | SerialPolyfillOptions | Extra options forwarded to the core polyfill |

Defaults: baudRate=115200, 8N1, bufferSize=512, commandTimeout=3000 ms, autoReconnect=true.

Events (incoming)

Listen with board.on(event, handler).

| Event | Payload type | Fires when… | |----------------------------|-----------------------|-------------------------------------------------------| | b2d:pong | PongEvent | Board replies to PING — carries firmware + deviceNumber | | b2d:coin-in | CoinInEvent | A coin was inserted (raw, type, centavos) | | b2d:bill-in | BillInEvent | Bill activity — discriminated by kind ('bill' or 'status') | | b2d:coin-config | — | Coin changer config accepted | | b2d:bill-config | BillConfigEvent | Bill acceptor denomination config (config: number[]) | | b2d:tubes | TubesEvent | Coin tube fill levels (counts: number[], fullMask) | | b2d:bill-channels | BillChannelsEvent | Bill channel config read from EEPROM | | b2d:coins-out | — | Coin dispense cycle completed | | b2d:product | ProductEvent | Motor dispense finished (success: boolean) | | b2d:door | DoorEvent | Cabinet door status changed (open: boolean) | | b2d:temperature | TemperatureEvent | ADC temperature reading (adcRaw: number) | | b2d:relay | RelayEvent | Relay acknowledgement (relay, state) | | b2d:save-memory | — | EEPROM write completed | | b2d:read-memory | ReadMemoryEvent | EEPROM read result (addr, value) | | b2d:cashless | CashlessEvent | MDB cashless sub-event (subEvent, data) | | b2d:motor-status | MotorStatusEvent | Motor/slot connectivity (selector, connected) | | b2d:device-number | DeviceNumberEvent | Device number confirmed after assignment | | b2d:hopper | HopperEvent | Hopper result code + optional data | | b2d:temperature-report | TemperatureReportEvent | Periodic auto-temp report (adcRaw, relayState, autoActive) | | b2d:unknown | UnknownFrameEvent | Frame arrived but EVT code not recognised (frame) |

BillInEvent discriminated union

board.on('b2d:bill-in', (evt) => {
  if (evt.kind === 'bill') {
    console.log(evt.routing, evt.billType, evt.centavos);
  } else {
    console.log('Bill status:', evt.statusCode);
  }
});

Commands (outgoing)

All commands accept an optional device parameter (default 0 = broadcast).

General

| Method | Parameters | Description | |--------|-----------|-------------| | sendPing(device?) | — | Ping the board — fires b2d:pong | | sendSetupBill(device?) | — | Re-initialise bill acceptor/recycler | | sendSetupCoin(device?) | — | Re-initialise coin changer | | sendReadTubes(device?) | — | Request tube fill levels → b2d:tubes | | sendReadBillChannels(device?) | — | Read bill denom config from EEPROM → b2d:bill-channels | | sendEscrow(accept, device?) | accept: boolean | Stack (true) or return (false) escrowed bill | | sendDispenseBills(count0, count1, device?) | counts | Dispense bills from recycler | | sendDispenseCoins(counts, device?) | counts: number[] (up to 6) | Dispense coins by denomination | | sendProductOut(sel1, sel2?, mode?, device?) | selector | Activate product motor | | sendSaveMemory(addr, value, device?) | 16-bit addr, byte | Write byte to EEPROM → b2d:save-memory | | sendReadMemory(addr, device?) | 16-bit addr | Read byte from EEPROM → b2d:read-memory | | sendLed(state, device?) | 0=off, 1=on, 2=pulse | Control product LED | | sendReadTemperature(device?) | — | Request ADC temperature → b2d:temperature | | sendRelay(relay, state, device?) | relay number, 0/1 | Control relay output → b2d:relay | | sendMotorStatus(selector, device?) | motor selector | Check slot connectivity → b2d:motor-status | | sendSetDeviceNumber(newNum, currentDevice?) | new number | Assign device number → b2d:device-number | | sendTemperatureConfig(options, device?) | TemperatureConfigOptions | Configure auto-temperature control |

TemperatureConfigOptions

interface TemperatureConfigOptions {
  enable: boolean;   // enable auto-relay control
  adcHigh: number;   // ADC threshold to activate relay
  adcLow: number;    // ADC threshold to deactivate relay
  relay: number;     // relay number to control
  interval: number;  // report interval in seconds
}

Cashless (MDB)

| Method | Parameters | Description | |--------|-----------|-------------| | sendCashlessEnable(device?) | — | Enable cashless reader | | sendCashlessDisable(device?) | — | Disable cashless reader | | sendCashlessSetPrice(price, sel, device?) | centavos, selection | Pre-configure price | | sendCashlessVendRequest(price, sel, device?) | centavos, selection | MDB VEND REQUEST | | sendCashlessVendSuccess(sel, device?) | selection | MDB VEND SUCCESS | | sendCashlessVendFailure(device?) | — | MDB VEND FAILURE | | sendCashlessVendCancel(device?) | — | Cancel pending vend | | sendCashlessSessionComplete(device?) | — | MDB SESSION COMPLETE | | sendCashlessCashSale(price, sel, device?) | centavos, selection | Log equivalent cash sale | | sendCashlessRevalueRequest(amount, device?) | centavos | Request card revalue (L2+) | | sendCashlessRevalueLimit(device?) | — | Query max revalue amount | | sendCashlessTimeDateResponse(date?, device?) | Date | Reply to time/date request (L2+) | | sendCashlessDataEntryResponse(data, device?) | number[] | Reply to data-entry/PIN request (L2+) |

Hopper

| Method | Parameters | Description | |--------|-----------|-------------| | sendHopperInit(device?) | — | Initialise hopper → b2d:hopper | | sendHopperStatus(device?) | — | Query tube fill levels | | sendHopperDispense(count, tubeType, device?) | count, type | Dispense N coins of one denomination | | sendHopperPayout(amounts, device?) | amounts: number[] (up to 8) | Multi-denomination payout | | sendHopperStop(device?) | — | Emergency stop |

Protocol constants

All constant maps are exported for direct use:

import { CMD, EVT, CL_CMD, CL_EVT, HOPPER_ACT, HOPPER_EVT, BILL_ROUTING, BILL_STATUS, LED_STATE, MOTOR_MODE } from '@danidoble/webserial-board2droid';

Multi-board addressing

Set the device parameter on any command to target a specific board over the RS-232 bus. 0 broadcasts to all boards.

await board.sendPing(2);        // ping board #2 only
await board.sendRelay(1, 1, 3); // turn relay 1 on — board #3 only

TypeScript

All events and method signatures are fully typed. The package ships with .d.mts / .d.cts declaration files. Commonly used types and all built-in providers are re-exported:

import {
  Board2Droid,
  WebUsbProvider,
  createBluetoothProvider,
  createWebSocketProvider,
} from '@danidoble/webserial-board2droid';

import type {
  Board2DroidOptions,
  PongEvent,
  CoinInEvent,
  BillInEvent,
  BillConfigEvent,
  BillChannelsEvent,
  ReadMemoryEvent,
  DeviceNumberEvent,
  TemperatureReportEvent,
  TemperatureConfigOptions,
  SerialPortFilter,
  SerialProvider,
} from '@danidoble/webserial-board2droid';

License

GPL-3.0-only © Danidoble