node-ch347 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/gpio.js ADDED
@@ -0,0 +1,196 @@
1
+ "use strict";
2
+ /**
3
+ * CH347 GPIO Control
4
+ *
5
+ * Protocol from ch347-gpio.c:
6
+ * - GPIO buffer is 11 bytes: [0xCC, length(8), 0x00, pin0, pin1, pin2, pin3, pin4, pin5, pin6, pin7]
7
+ * - Each pin byte has control bits:
8
+ * - Bit 7 (0x80): In response, indicates output direction
9
+ * - Bit 6 (0x40): Enable pin change (out) / Current value (in)
10
+ * - Bit 5-4 (0x30): Set direction to output
11
+ * - Bit 3 (0x08): Set pin value high
12
+ */
13
+ Object.defineProperty(exports, "__esModule", { value: true });
14
+ exports.CH347GPIO = void 0;
15
+ const constants_1 = require("./constants");
16
+ class CH347GPIO {
17
+ usb;
18
+ pinStates = [];
19
+ constructor(usb) {
20
+ this.usb = usb;
21
+ // Initialize pin states
22
+ for (let i = 0; i < constants_1.CH347_GPIO_COUNT; i++) {
23
+ this.pinStates[i] = {
24
+ pin: i,
25
+ direction: 'input',
26
+ value: false,
27
+ };
28
+ }
29
+ }
30
+ /**
31
+ * Build GPIO command buffer
32
+ */
33
+ buildCommandBuffer(pinConfigs) {
34
+ const buf = Buffer.alloc(11);
35
+ buf[0] = constants_1.CH347_CMD_GPIO;
36
+ buf[1] = 8; // Length of pin data
37
+ buf[2] = 0;
38
+ // Set pin bytes
39
+ for (const [pin, config] of pinConfigs) {
40
+ if (pin >= 0 && pin < constants_1.CH347_GPIO_COUNT) {
41
+ let pinByte = 0;
42
+ if (config.setDir || config.setValue) {
43
+ pinByte |= constants_1.GPIO_PIN_ENABLE; // Enable change (0xC0)
44
+ }
45
+ if (config.setDir && config.dirOut) {
46
+ pinByte |= constants_1.GPIO_PIN_DIR_OUT; // Set output direction (0x30)
47
+ }
48
+ if (config.setValue && config.value) {
49
+ pinByte |= constants_1.GPIO_PIN_VALUE_HIGH; // Set value high (0x08)
50
+ }
51
+ buf[3 + pin] = pinByte;
52
+ }
53
+ }
54
+ return buf;
55
+ }
56
+ /**
57
+ * Parse GPIO response buffer
58
+ */
59
+ parseResponse(buf) {
60
+ const states = [];
61
+ for (let i = 0; i < constants_1.CH347_GPIO_COUNT; i++) {
62
+ const pinByte = buf[3 + i];
63
+ states.push({
64
+ pin: i,
65
+ direction: (pinByte & constants_1.GPIO_PIN_IS_OUTPUT) ? 'output' : 'input',
66
+ value: (pinByte & constants_1.GPIO_PIN_VALUE) !== 0,
67
+ });
68
+ }
69
+ return states;
70
+ }
71
+ /**
72
+ * Read all GPIO states
73
+ */
74
+ async readAll() {
75
+ // Send read command (empty pin config)
76
+ const cmdBuf = this.buildCommandBuffer(new Map());
77
+ const response = await this.usb.transfer(cmdBuf, 11);
78
+ this.pinStates = this.parseResponse(response);
79
+ return this.pinStates;
80
+ }
81
+ /**
82
+ * Read single GPIO pin state
83
+ */
84
+ async read(pin) {
85
+ if (pin < 0 || pin >= constants_1.CH347_GPIO_COUNT) {
86
+ throw new Error(`Invalid pin number: ${pin}`);
87
+ }
88
+ const states = await this.readAll();
89
+ return states[pin];
90
+ }
91
+ /**
92
+ * Configure GPIO pin direction
93
+ */
94
+ async setDirection(pin, direction) {
95
+ if (pin < 0 || pin >= constants_1.CH347_GPIO_COUNT) {
96
+ throw new Error(`Invalid pin number: ${pin}`);
97
+ }
98
+ const pinConfig = new Map();
99
+ pinConfig.set(pin, {
100
+ setDir: true,
101
+ dirOut: direction === 'output',
102
+ setValue: true,
103
+ value: this.pinStates[pin]?.value ?? false,
104
+ });
105
+ const cmdBuf = this.buildCommandBuffer(pinConfig);
106
+ const response = await this.usb.transfer(cmdBuf, 11);
107
+ this.pinStates = this.parseResponse(response);
108
+ }
109
+ /**
110
+ * Set GPIO output value
111
+ */
112
+ async write(pin, value) {
113
+ if (pin < 0 || pin >= constants_1.CH347_GPIO_COUNT) {
114
+ throw new Error(`Invalid pin number: ${pin}`);
115
+ }
116
+ // First ensure pin is set as output
117
+ const pinConfig = new Map();
118
+ pinConfig.set(pin, {
119
+ setDir: true,
120
+ dirOut: true,
121
+ setValue: true,
122
+ value: value,
123
+ });
124
+ const cmdBuf = this.buildCommandBuffer(pinConfig);
125
+ const response = await this.usb.transfer(cmdBuf, 11);
126
+ this.pinStates = this.parseResponse(response);
127
+ }
128
+ /**
129
+ * Set multiple GPIO pins at once
130
+ */
131
+ async writeMultiple(pins) {
132
+ const pinConfig = new Map();
133
+ for (const { pin, value } of pins) {
134
+ if (pin < 0 || pin >= constants_1.CH347_GPIO_COUNT) {
135
+ throw new Error(`Invalid pin number: ${pin}`);
136
+ }
137
+ pinConfig.set(pin, {
138
+ setDir: true,
139
+ dirOut: true,
140
+ setValue: true,
141
+ value: value,
142
+ });
143
+ }
144
+ const cmdBuf = this.buildCommandBuffer(pinConfig);
145
+ const response = await this.usb.transfer(cmdBuf, 11);
146
+ this.pinStates = this.parseResponse(response);
147
+ }
148
+ /**
149
+ * Configure multiple GPIO pins
150
+ */
151
+ async configure(configs) {
152
+ const pinConfig = new Map();
153
+ for (const config of configs) {
154
+ if (config.pin < 0 || config.pin >= constants_1.CH347_GPIO_COUNT) {
155
+ throw new Error(`Invalid pin number: ${config.pin}`);
156
+ }
157
+ pinConfig.set(config.pin, {
158
+ setDir: true,
159
+ dirOut: config.direction === 'output',
160
+ setValue: config.value !== undefined,
161
+ value: config.value ?? false,
162
+ });
163
+ }
164
+ const cmdBuf = this.buildCommandBuffer(pinConfig);
165
+ const response = await this.usb.transfer(cmdBuf, 11);
166
+ this.pinStates = this.parseResponse(response);
167
+ }
168
+ /**
169
+ * Pulse a GPIO pin (useful for buttons)
170
+ */
171
+ async pulse(pin, durationMs = 100, activeHigh = true) {
172
+ await this.write(pin, activeHigh);
173
+ await this.delay(durationMs);
174
+ await this.write(pin, !activeHigh);
175
+ }
176
+ /**
177
+ * Toggle GPIO pin
178
+ */
179
+ async toggle(pin) {
180
+ const state = await this.read(pin);
181
+ const newValue = !state.value;
182
+ await this.write(pin, newValue);
183
+ return newValue;
184
+ }
185
+ /**
186
+ * Get cached pin states (call readAll() to refresh)
187
+ */
188
+ getPinStates() {
189
+ return [...this.pinStates];
190
+ }
191
+ delay(ms) {
192
+ return new Promise((resolve) => setTimeout(resolve, ms));
193
+ }
194
+ }
195
+ exports.CH347GPIO = CH347GPIO;
196
+ //# sourceMappingURL=gpio.js.map
@@ -0,0 +1,149 @@
1
+ /**
2
+ * CH347 Library
3
+ *
4
+ * A Node.js library for interfacing with CH347 USB devices.
5
+ * Supports GPIO and SPI flash programming.
6
+ * UART path discovery is provided; use external serial libraries for UART communication.
7
+ *
8
+ * Cross-platform: Linux and macOS
9
+ */
10
+ export { CH347USB } from './usb';
11
+ export { CH347GPIO } from './gpio';
12
+ export { CH347SPI } from './spi';
13
+ export { CH347Flash } from './flash';
14
+ export { listDevicesWithSerial, CH347DeviceWithSerial, } from './config';
15
+ export * from './types';
16
+ export * from './constants';
17
+ import { CH347GPIO } from './gpio';
18
+ import { CH347SPI } from './spi';
19
+ import { CH347Flash } from './flash';
20
+ import { CH347DeviceWithSerial } from './config';
21
+ import { CH347DeviceInfo, SPIConfig, FlashInfo, GPIOState } from './types';
22
+ export interface CH347DeviceOptions {
23
+ spi?: Partial<SPIConfig>;
24
+ }
25
+ /**
26
+ * Main CH347 device class
27
+ * Provides unified access to GPIO, SPI, and Flash functionality
28
+ */
29
+ export declare class CH347Device {
30
+ private usb;
31
+ private _gpio;
32
+ private _spi;
33
+ private _flash;
34
+ private options;
35
+ constructor(options?: CH347DeviceOptions);
36
+ /**
37
+ * List all connected CH347 devices
38
+ */
39
+ static listDevices(): CH347DeviceInfo[];
40
+ /**
41
+ * List all connected CH347 devices with their serial numbers
42
+ */
43
+ static listDevicesWithSerial(): Promise<CH347DeviceWithSerial[]>;
44
+ /**
45
+ * Open connection to CH347 device
46
+ * @param deviceIndex Index of device to open (default 0)
47
+ */
48
+ open(deviceIndex?: number): Promise<void>;
49
+ /**
50
+ * Close connection
51
+ */
52
+ close(): void;
53
+ /**
54
+ * Check if device is connected
55
+ */
56
+ isConnected(): boolean;
57
+ /**
58
+ * Get the UART tty path for this CH347 device
59
+ * Returns the serial port path (e.g., '/dev/ttyACM0' on Linux, '/dev/tty.usbmodem*' on macOS)
60
+ */
61
+ getUARTPath(): string | null;
62
+ /**
63
+ * Get GPIO controller
64
+ */
65
+ get gpio(): CH347GPIO;
66
+ /**
67
+ * Get SPI controller
68
+ */
69
+ get spi(): CH347SPI;
70
+ /**
71
+ * Get Flash programmer
72
+ */
73
+ get flash(): CH347Flash;
74
+ /**
75
+ * Read all GPIO pin states
76
+ */
77
+ gpioReadAll(): Promise<GPIOState[]>;
78
+ /**
79
+ * Set GPIO pin output value
80
+ */
81
+ gpioWrite(pin: number, value: boolean): Promise<void>;
82
+ /**
83
+ * Read GPIO pin state
84
+ */
85
+ gpioRead(pin: number): Promise<GPIOState>;
86
+ /**
87
+ * Pulse GPIO pin (for button press simulation)
88
+ */
89
+ gpioPulse(pin: number, durationMs?: number, activeHigh?: boolean): Promise<void>;
90
+ /**
91
+ * Initialize SPI interface
92
+ */
93
+ spiInit(config?: Partial<SPIConfig>): Promise<void>;
94
+ /**
95
+ * Read flash JEDEC ID
96
+ */
97
+ flashReadId(): Promise<FlashInfo>;
98
+ /**
99
+ * Read data from flash
100
+ */
101
+ flashRead(address: number, length: number, onProgress?: (progress: {
102
+ percentage: number;
103
+ }) => void): Promise<Buffer>;
104
+ /**
105
+ * Write data to flash
106
+ */
107
+ flashWrite(address: number, data: Buffer, onProgress?: (progress: {
108
+ percentage: number;
109
+ }) => void): Promise<void>;
110
+ /**
111
+ * Erase flash sector (4KB)
112
+ */
113
+ flashEraseSector(address: number): Promise<void>;
114
+ /**
115
+ * Erase entire flash chip
116
+ */
117
+ flashEraseChip(onProgress?: (progress: {
118
+ percentage: number;
119
+ }) => void): Promise<void>;
120
+ /**
121
+ * Program flash (erase + write + verify)
122
+ */
123
+ flashProgram(address: number, data: Buffer, options?: {
124
+ erase?: boolean;
125
+ verify?: boolean;
126
+ onProgress?: (progress: {
127
+ percentage: number;
128
+ }) => void;
129
+ }): Promise<boolean>;
130
+ /**
131
+ * Program flash from a binary file
132
+ * If address is not specified, requires file size to match flash size (call flashReadId first)
133
+ */
134
+ flashProgramFile(filePath: string, address?: number, options?: {
135
+ erase?: boolean;
136
+ verify?: boolean;
137
+ onProgress?: (progress: {
138
+ percentage: number;
139
+ }) => void;
140
+ }): Promise<boolean>;
141
+ /**
142
+ * Read flash contents to a binary file
143
+ */
144
+ flashReadToFile(filePath: string, address?: number, length?: number, onProgress?: (progress: {
145
+ percentage: number;
146
+ }) => void): Promise<void>;
147
+ }
148
+ export default CH347Device;
149
+ //# sourceMappingURL=index.d.ts.map
package/dist/index.js ADDED
@@ -0,0 +1,222 @@
1
+ "use strict";
2
+ /**
3
+ * CH347 Library
4
+ *
5
+ * A Node.js library for interfacing with CH347 USB devices.
6
+ * Supports GPIO and SPI flash programming.
7
+ * UART path discovery is provided; use external serial libraries for UART communication.
8
+ *
9
+ * Cross-platform: Linux and macOS
10
+ */
11
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
12
+ if (k2 === undefined) k2 = k;
13
+ var desc = Object.getOwnPropertyDescriptor(m, k);
14
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
15
+ desc = { enumerable: true, get: function() { return m[k]; } };
16
+ }
17
+ Object.defineProperty(o, k2, desc);
18
+ }) : (function(o, m, k, k2) {
19
+ if (k2 === undefined) k2 = k;
20
+ o[k2] = m[k];
21
+ }));
22
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
23
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
24
+ };
25
+ Object.defineProperty(exports, "__esModule", { value: true });
26
+ exports.CH347Device = exports.listDevicesWithSerial = exports.CH347Flash = exports.CH347SPI = exports.CH347GPIO = exports.CH347USB = void 0;
27
+ // Core modules
28
+ var usb_1 = require("./usb");
29
+ Object.defineProperty(exports, "CH347USB", { enumerable: true, get: function () { return usb_1.CH347USB; } });
30
+ var gpio_1 = require("./gpio");
31
+ Object.defineProperty(exports, "CH347GPIO", { enumerable: true, get: function () { return gpio_1.CH347GPIO; } });
32
+ var spi_1 = require("./spi");
33
+ Object.defineProperty(exports, "CH347SPI", { enumerable: true, get: function () { return spi_1.CH347SPI; } });
34
+ var flash_1 = require("./flash");
35
+ Object.defineProperty(exports, "CH347Flash", { enumerable: true, get: function () { return flash_1.CH347Flash; } });
36
+ // Device configuration (serial number, etc.)
37
+ var config_1 = require("./config");
38
+ Object.defineProperty(exports, "listDevicesWithSerial", { enumerable: true, get: function () { return config_1.listDevicesWithSerial; } });
39
+ // Types
40
+ __exportStar(require("./types"), exports);
41
+ // Constants
42
+ __exportStar(require("./constants"), exports);
43
+ // Convenience class that combines all functionality
44
+ const usb_2 = require("./usb");
45
+ const gpio_2 = require("./gpio");
46
+ const spi_2 = require("./spi");
47
+ const flash_2 = require("./flash");
48
+ const config_2 = require("./config");
49
+ /**
50
+ * Main CH347 device class
51
+ * Provides unified access to GPIO, SPI, and Flash functionality
52
+ */
53
+ class CH347Device {
54
+ usb;
55
+ _gpio = null;
56
+ _spi = null;
57
+ _flash = null;
58
+ options;
59
+ constructor(options = {}) {
60
+ this.usb = new usb_2.CH347USB();
61
+ this.options = options;
62
+ }
63
+ /**
64
+ * List all connected CH347 devices
65
+ */
66
+ static listDevices() {
67
+ return usb_2.CH347USB.listDevices();
68
+ }
69
+ /**
70
+ * List all connected CH347 devices with their serial numbers
71
+ */
72
+ static async listDevicesWithSerial() {
73
+ return (0, config_2.listDevicesWithSerial)();
74
+ }
75
+ /**
76
+ * Open connection to CH347 device
77
+ * @param deviceIndex Index of device to open (default 0)
78
+ */
79
+ async open(deviceIndex = 0) {
80
+ await this.usb.open(deviceIndex);
81
+ // Initialize GPIO
82
+ this._gpio = new gpio_2.CH347GPIO(this.usb);
83
+ // Initialize SPI with optional config
84
+ this._spi = new spi_2.CH347SPI(this.usb, this.options.spi);
85
+ // Initialize Flash (wraps SPI)
86
+ this._flash = new flash_2.CH347Flash(this._spi);
87
+ }
88
+ /**
89
+ * Close connection
90
+ */
91
+ close() {
92
+ this.usb.close();
93
+ this._gpio = null;
94
+ this._spi = null;
95
+ this._flash = null;
96
+ }
97
+ /**
98
+ * Check if device is connected
99
+ */
100
+ isConnected() {
101
+ return this.usb.isConnected();
102
+ }
103
+ /**
104
+ * Get the UART tty path for this CH347 device
105
+ * Returns the serial port path (e.g., '/dev/ttyACM0' on Linux, '/dev/tty.usbmodem*' on macOS)
106
+ */
107
+ getUARTPath() {
108
+ return this.usb.getUARTPath();
109
+ }
110
+ /**
111
+ * Get GPIO controller
112
+ */
113
+ get gpio() {
114
+ if (!this._gpio) {
115
+ throw new Error('Device not open');
116
+ }
117
+ return this._gpio;
118
+ }
119
+ /**
120
+ * Get SPI controller
121
+ */
122
+ get spi() {
123
+ if (!this._spi) {
124
+ throw new Error('Device not open');
125
+ }
126
+ return this._spi;
127
+ }
128
+ /**
129
+ * Get Flash programmer
130
+ */
131
+ get flash() {
132
+ if (!this._flash) {
133
+ throw new Error('Device not open');
134
+ }
135
+ return this._flash;
136
+ }
137
+ // ==================== GPIO Convenience Methods ====================
138
+ /**
139
+ * Read all GPIO pin states
140
+ */
141
+ async gpioReadAll() {
142
+ return this.gpio.readAll();
143
+ }
144
+ /**
145
+ * Set GPIO pin output value
146
+ */
147
+ async gpioWrite(pin, value) {
148
+ return this.gpio.write(pin, value);
149
+ }
150
+ /**
151
+ * Read GPIO pin state
152
+ */
153
+ async gpioRead(pin) {
154
+ return this.gpio.read(pin);
155
+ }
156
+ /**
157
+ * Pulse GPIO pin (for button press simulation)
158
+ */
159
+ async gpioPulse(pin, durationMs = 100, activeHigh = true) {
160
+ return this.gpio.pulse(pin, durationMs, activeHigh);
161
+ }
162
+ // ==================== SPI Flash Convenience Methods ====================
163
+ /**
164
+ * Initialize SPI interface
165
+ */
166
+ async spiInit(config) {
167
+ return this.spi.init(config);
168
+ }
169
+ /**
170
+ * Read flash JEDEC ID
171
+ */
172
+ async flashReadId() {
173
+ return this.flash.readJedecId();
174
+ }
175
+ /**
176
+ * Read data from flash
177
+ */
178
+ async flashRead(address, length, onProgress) {
179
+ return this.flash.read(address, length, onProgress);
180
+ }
181
+ /**
182
+ * Write data to flash
183
+ */
184
+ async flashWrite(address, data, onProgress) {
185
+ return this.flash.write(address, data, onProgress);
186
+ }
187
+ /**
188
+ * Erase flash sector (4KB)
189
+ */
190
+ async flashEraseSector(address) {
191
+ return this.flash.eraseSector(address);
192
+ }
193
+ /**
194
+ * Erase entire flash chip
195
+ */
196
+ async flashEraseChip(onProgress) {
197
+ return this.flash.eraseChip(onProgress);
198
+ }
199
+ /**
200
+ * Program flash (erase + write + verify)
201
+ */
202
+ async flashProgram(address, data, options) {
203
+ return this.flash.program(address, data, options);
204
+ }
205
+ /**
206
+ * Program flash from a binary file
207
+ * If address is not specified, requires file size to match flash size (call flashReadId first)
208
+ */
209
+ async flashProgramFile(filePath, address, options) {
210
+ return this.flash.programFile(filePath, address, options);
211
+ }
212
+ /**
213
+ * Read flash contents to a binary file
214
+ */
215
+ async flashReadToFile(filePath, address = 0, length, onProgress) {
216
+ return this.flash.readToFile(filePath, address, length, onProgress);
217
+ }
218
+ }
219
+ exports.CH347Device = CH347Device;
220
+ // Default export
221
+ exports.default = CH347Device;
222
+ //# sourceMappingURL=index.js.map
package/dist/spi.d.ts ADDED
@@ -0,0 +1,66 @@
1
+ /**
2
+ * CH347 SPI Interface
3
+ *
4
+ * Protocol based on official WCH SDK and flashrom implementation.
5
+ * Commands:
6
+ * 0xC0 - SPI_SET_CFG: Configure SPI
7
+ * 0xC1 - SPI_CS_CTRL: Chip select control
8
+ * 0xC2 - SPI_OUT_IN: Bidirectional transfer
9
+ * 0xC3 - SPI_IN: Read only
10
+ * 0xC4 - SPI_OUT: Write only
11
+ * 0xCA - SPI_GET_CFG: Get configuration
12
+ */
13
+ import { CH347USB } from './usb';
14
+ import { SPIConfig } from './types';
15
+ export declare class CH347SPI {
16
+ private usb;
17
+ private config;
18
+ private isInitialized;
19
+ constructor(usb: CH347USB, config?: Partial<SPIConfig>);
20
+ /**
21
+ * Initialize SPI interface with configuration
22
+ */
23
+ init(config?: Partial<SPIConfig>): Promise<void>;
24
+ /**
25
+ * Control chip select
26
+ */
27
+ setChipSelect(active: boolean): Promise<void>;
28
+ /**
29
+ * SPI write and read (full duplex transfer)
30
+ * @param data Data to write
31
+ * @param csControl If true, automatically control CS (default true)
32
+ * @returns Data read during transfer
33
+ */
34
+ writeRead(data: Buffer, csControl?: boolean): Promise<Buffer>;
35
+ /**
36
+ * SPI write only
37
+ * @param data Data to write
38
+ * @param csControl If true, automatically control CS (default true)
39
+ */
40
+ write(data: Buffer, csControl?: boolean): Promise<void>;
41
+ /**
42
+ * SPI read only
43
+ * @param length Number of bytes to read
44
+ * @param csControl If true, automatically control CS (default true)
45
+ * @returns Data read
46
+ */
47
+ read(length: number, csControl?: boolean): Promise<Buffer>;
48
+ /**
49
+ * Execute SPI command with CS control
50
+ * Useful for sending flash commands
51
+ */
52
+ command(writeData: Buffer, readLength?: number): Promise<Buffer>;
53
+ /**
54
+ * Execute SPI command with full duplex transfer
55
+ */
56
+ transfer(data: Buffer): Promise<Buffer>;
57
+ /**
58
+ * Get current configuration
59
+ */
60
+ getConfig(): SPIConfig;
61
+ /**
62
+ * Check if SPI is initialized
63
+ */
64
+ isReady(): boolean;
65
+ }
66
+ //# sourceMappingURL=spi.d.ts.map