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

node-ch347

v0.0.4

Published

Node.js library for CH347 USB interface - GPIO, SPI flash programming, and UART

Readme

node-ch347

CI npm version npm downloads license

A Node.js library for interfacing with WCH CH347 USB devices. Supports GPIO control and SPI flash programming.

Note: This is an early release (v0.0.1). The API may change in future versions.

Features

  • GPIO Control: 8 GPIO channels with input/output support
  • SPI Flash Programming: Read, write, erase, and verify SPI flash chips
    • Supports common chips: W25Qxx, GD25Qxx, MX25Lxx, MT25Qxx, etc.
    • JEDEC ID detection
    • Sector (4KB), block (32KB/64KB), and chip erase
  • UART Path Discovery: Get the serial port path for use with external libraries
  • Cross-Platform: Works on Linux, macOS, and Windows

Installation

npm install node-ch347

Prerequisites

Linux:

# Install libusb
sudo apt-get install libusb-1.0-0-dev

# Add udev rules for non-root access
sudo tee /etc/udev/rules.d/99-ch347.rules << EOF
SUBSYSTEM=="usb", ATTRS{idVendor}=="1a86", ATTRS{idProduct}=="55db", MODE="0666"
EOF
sudo udevadm control --reload-rules
sudo udevadm trigger

macOS:

# Install libusb via Homebrew
brew install libusb

Windows:

Windows requires a USB driver that exposes the CH347. Choose one of these options:

Option 1: UsbDk (Recommended) - Coexists with WCH vendor driver

  1. Download and install UsbDk from: https://github.com/daynix/UsbDk/releases
  2. Both node-ch347 and WCH's official tools will work

Option 2: WinUSB via Zadig - Replaces vendor driver

  1. Download Zadig from: https://zadig.akeo.ie/
  2. Run Zadig as Administrator
  3. Options → List All Devices
  4. Select your CH347 device (VID: 1A86, PID: 55DB)
  5. Select "WinUSB" as the target driver
  6. Click "Replace Driver"

Note: After installing WinUSB, WCH's official tools (CH347Demo, etc.) will no longer work. Use UsbDk if you need both.

Option 3: WCH DLL - Use WCH's proprietary CH347DLL.dll

  1. Download CH347EVT from: https://www.wch.cn/downloads/CH341PAR_ZIP.html
  2. Extract CH347DLL.dll (or CH347DLLA64.dll for 64-bit) to your application directory or system PATH
  3. Install FFI dependencies: npm install ffi-napi ref-napi
  4. Use the CH347WCH class instead of CH347Device

Note: The WCH DLL is NOT included in this package due to licensing concerns. You must obtain it from WCH directly.

Windows USB Backend Selection

By default, the library uses "auto" mode which tries UsbDk first, then falls back to WinUSB.

import { setWindowsBackend } from 'node-ch347';

// Use UsbDk backend (recommended, coexists with vendor driver)
setWindowsBackend('usbdk');

// Use native WinUSB (requires Zadig driver replacement)
setWindowsBackend('winusb');

// Use WCH's CH347DLL.dll (requires DLL in PATH)
setWindowsBackend('wch');

// Auto-detect (try UsbDk first, fall back to WinUSB)
setWindowsBackend('auto');

Or set via environment variable:

set CH347_USB_BACKEND=usbdk
set CH347_USB_BACKEND=winusb
set CH347_USB_BACKEND=wch

Using WCH DLL Backend

The WCH DLL backend works with the same CH347Device class - just set the backend:

import CH347Device, { setWindowsBackend, SPISpeed } from 'node-ch347';

// Option 1: Set globally before opening any device
setWindowsBackend('wch');

// Option 2: Set per-device via options
const device = new CH347Device({
  backend: 'wch',
  spi: { speed: SPISpeed.CLK_15M }
});

await device.open();

// Same API as other backends
const flashInfo = await device.flashReadId();
console.log(`Flash: ${flashInfo.name}`);

await device.gpioWrite(3, true);
const states = await device.gpioReadAll();

device.close();

You can also use the low-level CH347WCH class directly for advanced use cases.

Quick Start

import CH347Device, { SPISpeed } from 'node-ch347';

async function main() {
  // Create device instance
  const ch347 = new CH347Device({
    spi: { speed: SPISpeed.CLK_15M }
  });

  // Open connection
  await ch347.open();

  // GPIO: Set pin 3 high
  await ch347.gpioWrite(3, true);

  // SPI Flash: Read chip ID
  const flashInfo = await ch347.flashReadId();
  console.log(`Flash: ${flashInfo.name}, ${flashInfo.size / 1024 / 1024} MB`);

  // SPI Flash: Read data
  const data = await ch347.flashRead(0, 4096);

  // Close connection
  ch347.close();
}

API Reference

CH347Device

Main class that provides unified access to all functionality.

const device = new CH347Device(options?: {
  spi?: Partial<SPIConfig>;
});

Static Methods

  • CH347Device.listDevices(): List all connected CH347 devices

Instance Methods

Connection:

  • open(deviceIndex?: number): Open device connection
  • close(): Close connection
  • isConnected(): Check if connected
  • getUARTPath(): Get the tty path for this device (e.g., /dev/ttyACM0)

GPIO:

  • gpioReadAll(): Read all GPIO pin states
  • gpioRead(pin): Read single GPIO pin
  • gpioWrite(pin, value): Set GPIO output
  • gpioPulse(pin, duration, activeHigh): Pulse GPIO pin

SPI Flash:

  • spiInit(config?): Initialize SPI interface
  • flashReadId(): Read JEDEC ID
  • flashRead(address, length, onProgress?): Read data
  • flashWrite(address, data, onProgress?): Write data
  • flashEraseSector(address): Erase 4KB sector
  • flashEraseChip(onProgress?): Erase entire chip
  • flashProgram(address, data, options?): Erase + write + verify
  • flashProgramFile(filePath, address?, options?): Program flash from binary file (if address omitted, file size must match flash size)
  • flashReadToFile(filePath, address?, length?, onProgress?): Read flash to binary file

SPI Configuration

interface SPIConfig {
  speed: SPISpeed;      // Clock speed (default: CLK_15M)
  mode: SPIMode;        // SPI mode 0-3 (default: MODE_0)
  chipSelect: 0 | 1;    // CS line (default: 0)
  bitOrder: 'MSB' | 'LSB'; // Bit order (default: 'MSB')
}

enum SPISpeed {
  CLK_60M = 0,
  CLK_30M = 1,
  CLK_15M = 2,
  CLK_7_5M = 3,
  CLK_3_75M = 4,
  CLK_1_875M = 5,
  CLK_937_5K = 6,
  CLK_468_75K = 7,
}

Examples

GPIO Control

import CH347Device from 'node-ch347';

const ch347 = new CH347Device();
await ch347.open();

// Read all GPIO states
const states = await ch347.gpioReadAll();
states.forEach(s => console.log(`GPIO${s.pin}: ${s.value}`));

// Set output on pin 3
await ch347.gpioWrite(3, true);

// Pulse pin 0 (for button press simulation)
await ch347.gpioPulse(0, 100);

ch347.close();

Flash Programming

import CH347Device, { SPISpeed } from 'node-ch347';

const ch347 = new CH347Device({ spi: { speed: SPISpeed.CLK_30M } });
await ch347.open();

// Detect flash
const info = await ch347.flashReadId();
console.log(`Detected: ${info.name}`);

// Backup flash to file
await ch347.flashReadToFile('backup.bin', 0, info.size, (p) => {
  console.log(`Reading: ${p.percentage}%`);
});

// Program flash from file (erase + write + verify)
await ch347.flashProgramFile('new_firmware.bin', 0, {
  erase: true,
  verify: true,
  onProgress: (p) => console.log(`${p.operation}: ${p.percentage}%`)
});

ch347.close();

UART Path Discovery

The library provides UART path discovery on Linux and macOS. Use an external serial library like serialport for actual communication:

Note: Windows UART path discovery is not yet implemented. On Windows, manually specify the COM port (e.g., COM3).

import CH347Device from 'node-ch347';
import { SerialPort } from 'serialport'; // npm install serialport

const ch347 = new CH347Device();
await ch347.open();

// Get UART path for this device
const uartPath = ch347.getUARTPath();
console.log('UART path:', uartPath); // e.g., '/dev/ttyACM0'

// Use serialport library for UART communication
if (uartPath) {
  const port = new SerialPort({ path: uartPath, baudRate: 115200 });
  port.write('AT\r\n');
  // ... handle data with port.on('data', ...)
}

ch347.close();

CH347 Hardware

The CH347 is a USB 2.0 high-speed (480 Mbps) bus converter chip by WCH (Nanjing Qinheng Microelectronics).

Supported mode:

  • Mode 1 (PID 0x55DB): UART + SPI + I2C + GPIO

Pin assignments for GPIO (Mode 1):

  • GPIO0: CTS0/SCK/TCK
  • GPIO1: RTS0/MISO/TDO
  • GPIO2: DSR0/SCS0/TMS
  • GPIO3: SCL
  • GPIO4: ACT
  • GPIO5: DTR0/TNOW0/SCS1/TRST
  • GPIO6: CTS1
  • GPIO7: RTS1

Development

Git Hooks Setup

This project uses git hooks for pre-push validation. To enable them:

git config core.hooksPath .githooks

The pre-push hook runs:

  • Build (npm run build)
  • Tests (npm test)
  • Type checking (npx tsc --noEmit)

Claude Code Integration

If you have Claude Code installed, the pre-push hook will automatically run /check-style to analyze code for API consistency and style issues. This is optional and non-blocking.

Disclaimer

This project was developed with AI-assisted code generation. Not all functions have been fully tested. Use at your own risk.

License

This project is released into the public domain under The Unlicense.