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

@leeskies/capacitor-usb-serial

v0.1.0

Published

Capacitor v7 plugin exposing the full functionality of mik3y/usb-serial-for-android (USB serial communication on Android).

Readme

capacitor-usb-serial

A Capacitor v7 plugin that exposes the full functionality of mik3y/usb-serial-for-android to JavaScript/TypeScript. Talk to USB‑to‑serial adapters and serial‑over‑USB devices (FTDI, Prolific PL2303, Silicon Labs CP210x, CH340/CH341, CDC/ACM) plugged into an Android device over USB‑C / OTG — no root required.

Platforms: Android only. iOS and web are stubs that reject every call with UNSUPPORTED_PLATFORM (Android is the only platform the underlying library supports).

Install

npm install @leeskies/capacitor-usb-serial
npx cap sync

The Android library is pulled from JitPack. Ensure JitPack is available to your app's Gradle (the plugin's own build.gradle already declares it; if your app overrides repositories, add it):

// android/build.gradle (project) — repositories
maven { url 'https://jitpack.io' }

How objects map across the bridge

Native serial objects can't cross the Capacitor bridge, so the plugin is handle-based:

  • Discovery returns a deviceId per attached device.
  • open() returns a portId; every later call takes that portId.
  • All binary data crosses as base64 strings.
  • Continuous reads are delivered as events (data / error).

Permission model (mirrors the native library)

USB permission must be granted before a port can be opened:

  • requestPermission({ deviceId }) shows the system dialog and resolves { granted }.
  • Concurrent requestPermission calls for the same device coalesce onto one dialog; every pending call settles with the same result, and the coalesced ones resolve with coalesced: true.
  • If the device detaches while the dialog is pending, the promise rejects with NO_DEVICE (distinguishable from the user declining, which resolves { granted: false }).
  • open() rejects with NEEDS_PERMISSION if you never asked, or PERMISSION_DENIED if the user declined.

Verification snippet

Copy‑paste into any Capacitor app to confirm the plugin works end‑to‑end:

import { UsbSerial } from '@leeskies/capacitor-usb-serial';

async function demo() {
  const { devices } = await UsbSerial.listDevices();
  if (devices.length === 0) {
    console.log('No USB serial devices connected');
    return;
  }
  const device = devices[0];
  console.log('Found', device.driverType, device.deviceName);

  if (!device.hasPermission) {
    const { granted } = await UsbSerial.requestPermission({ deviceId: device.deviceId });
    if (!granted) throw new Error('Permission denied');
  }

  const { portId } = await UsbSerial.open({ deviceId: device.deviceId, portNum: 0 });
  await UsbSerial.setParameters({
    portId,
    baudRate: 115200,
    dataBits: 8,
    stopBits: 1,
    parity: 'none',
  });

  // Stream incoming bytes (base64).
  const sub = await UsbSerial.addListener('data', ({ data }) => {
    console.log('RX', atob(data));
  });
  await UsbSerial.startReading({ portId });

  // Send something (base64-encoded).
  await UsbSerial.write({ portId, data: btoa('AT\r\n') });

  // ...later:
  // await UsbSerial.stopReading({ portId });
  // await sub.remove();
  // await UsbSerial.close({ portId });
}

Optional: auto-attach (launch/foreground on plug-in)

Add to your app's android/app/src/main/AndroidManifest.xml inside your main <activity>:

<intent-filter>
  <action android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" />
</intent-filter>
<meta-data
  android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED"
  android:resource="@xml/device_filter" />

A template device_filter.xml ships with the plugin (android/src/main/res/xml/device_filter.xml); copy it into your app's res/xml/ and list the VID/PID pairs you care about. When the app cold-starts from an attach intent the device appears in the next listDevices(), and an attached event is replayed best‑effort to the first registered listener.

API overview

Discovery & prober: listDevices, registerDriver. Permission: requestPermission, hasPermission. Lifecycle: open, close, isOpen, getPortInfo. Config: setParameters. I/O: read, write, writeAsync. Streaming: startReading, stopReading, getStreamState, getStreamConfig, data/error events.

Write semantics during streaming: while a stream is running, write() enqueues onto the stream's async write queue — bytesWritten confirms acceptance, not transmission. With no running stream, write() is synchronous and confirms delivery to the driver.

Stream errors: if a stream dies from a run error you get an error event and the port self-heals — one-shot read()/write() work again immediately, and getStreamState() reports stopped. Call startReading() again to resume streaming. If the error was a disconnect you also get detached, and the port is reaped. Control lines: getControlLines, getSupportedControlLines, getCD/CTS/DSR/DTR/RI/RTS, setDTR, setRTS. Flow control: setFlowControl, getFlowControl, getSupportedFlowControl, getXON (+ CHAR_XON/CHAR_XOFF). Maintenance: purgeHwBuffers, setBreak, setReadQueue, getReadQueueConfig. Events: attached, detached, data, error.

See src/definitions.ts for the complete typed contract and error codes.

Error codes

Every rejection carries a stable code: NO_DEVICE, NEEDS_PERMISSION, PERMISSION_DENIED, PORT_NOT_OPEN, INVALID_PARAMS, INVALID_STATE, IO_ERROR, DEVICE_DISCONNECTED, UNSUPPORTED_OPERATION, UNSUPPORTED_PLATFORM.

Credits

This plugin is a thin bridge — the genuinely hard part, speaking each chip family's proprietary protocol (FTDI's baud math, Prolific's finicky init sequences, the reverse-engineered CH340, Silicon Labs CP210x, CDC/ACM) and absorbing years of per-device bug fixes, is all usb-serial-for-android by Mike Wakerly (mik3y) and its contributors. This package just hands that work to Capacitor. Full credit for the underlying USB serial work goes to that project.

usb-serial-for-android is MIT-licensed (© 2011–2013 Google Inc.; © 2013 Mike Wakerly). Full notice in NOTICE.

License

MIT — see LICENSE. Covers the bridge code in this repository; usb-serial-for-android remains under its own MIT license (see Credits above).