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/LICENSE +24 -0
- package/README.md +234 -0
- package/dist/config.d.ts +20 -0
- package/dist/config.js +110 -0
- package/dist/constants.d.ts +83 -0
- package/dist/constants.js +107 -0
- package/dist/flash.d.ts +134 -0
- package/dist/flash.js +597 -0
- package/dist/gpio.d.ts +67 -0
- package/dist/gpio.js +196 -0
- package/dist/index.d.ts +149 -0
- package/dist/index.js +222 -0
- package/dist/spi.d.ts +66 -0
- package/dist/spi.js +227 -0
- package/dist/types.d.ts +74 -0
- package/dist/types.js +45 -0
- package/dist/uart.d.ts +81 -0
- package/dist/uart.js +343 -0
- package/dist/usb.d.ts +80 -0
- package/dist/usb.js +422 -0
- package/package.json +49 -0
package/dist/uart.js
ADDED
|
@@ -0,0 +1,343 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* CH347 UART Interface
|
|
4
|
+
*
|
|
5
|
+
* The CH347 UART interface appears as a virtual COM port (CDC ACM).
|
|
6
|
+
* On Linux/macOS, it typically shows up as /dev/ttyACM0 or /dev/tty.usbmodem*
|
|
7
|
+
*
|
|
8
|
+
* This module provides a cross-platform wrapper around the serial port.
|
|
9
|
+
*/
|
|
10
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
11
|
+
if (k2 === undefined) k2 = k;
|
|
12
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
13
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
14
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
15
|
+
}
|
|
16
|
+
Object.defineProperty(o, k2, desc);
|
|
17
|
+
}) : (function(o, m, k, k2) {
|
|
18
|
+
if (k2 === undefined) k2 = k;
|
|
19
|
+
o[k2] = m[k];
|
|
20
|
+
}));
|
|
21
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
22
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
23
|
+
}) : function(o, v) {
|
|
24
|
+
o["default"] = v;
|
|
25
|
+
});
|
|
26
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
27
|
+
var ownKeys = function(o) {
|
|
28
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
29
|
+
var ar = [];
|
|
30
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
31
|
+
return ar;
|
|
32
|
+
};
|
|
33
|
+
return ownKeys(o);
|
|
34
|
+
};
|
|
35
|
+
return function (mod) {
|
|
36
|
+
if (mod && mod.__esModule) return mod;
|
|
37
|
+
var result = {};
|
|
38
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
39
|
+
__setModuleDefault(result, mod);
|
|
40
|
+
return result;
|
|
41
|
+
};
|
|
42
|
+
})();
|
|
43
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
44
|
+
exports.CH347UART = void 0;
|
|
45
|
+
const events_1 = require("events");
|
|
46
|
+
const constants_1 = require("./constants");
|
|
47
|
+
// We'll use the native serialport package for UART
|
|
48
|
+
// Since it requires native bindings, we'll make it optional
|
|
49
|
+
let SerialPort = null;
|
|
50
|
+
try {
|
|
51
|
+
// Dynamic import to avoid hard dependency
|
|
52
|
+
SerialPort = require('serialport').SerialPort;
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
// SerialPort not available
|
|
56
|
+
}
|
|
57
|
+
const DEFAULT_UART_CONFIG = {
|
|
58
|
+
baudRate: constants_1.CH347_UART_DEFAULT_BAUD,
|
|
59
|
+
dataBits: 8,
|
|
60
|
+
stopBits: 1,
|
|
61
|
+
parity: 'none',
|
|
62
|
+
flowControl: 'none',
|
|
63
|
+
};
|
|
64
|
+
class CH347UART extends events_1.EventEmitter {
|
|
65
|
+
config;
|
|
66
|
+
port = null;
|
|
67
|
+
portPath = null;
|
|
68
|
+
isOpen = false;
|
|
69
|
+
constructor(config) {
|
|
70
|
+
super();
|
|
71
|
+
this.config = { ...DEFAULT_UART_CONFIG, ...config };
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* List available serial ports that might be CH347 UART
|
|
75
|
+
* Returns array of tty paths (e.g., '/dev/ttyACM0')
|
|
76
|
+
*/
|
|
77
|
+
static async listPorts() {
|
|
78
|
+
if (!SerialPort) {
|
|
79
|
+
throw new Error('serialport package not installed. Install with: npm install serialport');
|
|
80
|
+
}
|
|
81
|
+
const { SerialPort: SP } = await Promise.resolve().then(() => __importStar(require('serialport')));
|
|
82
|
+
const ports = await SP.list();
|
|
83
|
+
// Filter for potential CH347 devices
|
|
84
|
+
// CH347 uses VID 1a86, PID 55db/55dd
|
|
85
|
+
return ports
|
|
86
|
+
.filter((port) => {
|
|
87
|
+
// Include all ports but prioritize CH347 devices
|
|
88
|
+
const vid = port.vendorId?.toLowerCase();
|
|
89
|
+
return vid === '1a86' || port.path.includes('ttyACM') || port.path.includes('usbmodem');
|
|
90
|
+
})
|
|
91
|
+
.map((port) => port.path);
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Open UART port
|
|
95
|
+
*/
|
|
96
|
+
async open(portPath, config) {
|
|
97
|
+
if (!SerialPort) {
|
|
98
|
+
throw new Error('serialport package not installed. Install with: npm install serialport');
|
|
99
|
+
}
|
|
100
|
+
if (this.isOpen) {
|
|
101
|
+
await this.close();
|
|
102
|
+
}
|
|
103
|
+
if (config) {
|
|
104
|
+
this.config = { ...this.config, ...config };
|
|
105
|
+
}
|
|
106
|
+
this.portPath = portPath;
|
|
107
|
+
return new Promise((resolve, reject) => {
|
|
108
|
+
this.port = new SerialPort({
|
|
109
|
+
path: portPath,
|
|
110
|
+
baudRate: this.config.baudRate,
|
|
111
|
+
dataBits: this.config.dataBits,
|
|
112
|
+
stopBits: this.config.stopBits,
|
|
113
|
+
parity: this.config.parity,
|
|
114
|
+
rtscts: this.config.flowControl === 'hardware',
|
|
115
|
+
autoOpen: false,
|
|
116
|
+
});
|
|
117
|
+
this.port.on('data', (data) => {
|
|
118
|
+
this.emit('data', data);
|
|
119
|
+
});
|
|
120
|
+
this.port.on('error', (err) => {
|
|
121
|
+
this.emit('error', err);
|
|
122
|
+
});
|
|
123
|
+
this.port.on('close', () => {
|
|
124
|
+
this.isOpen = false;
|
|
125
|
+
this.emit('close');
|
|
126
|
+
});
|
|
127
|
+
this.port.open((err) => {
|
|
128
|
+
if (err) {
|
|
129
|
+
reject(new Error(`Failed to open port: ${err.message}`));
|
|
130
|
+
}
|
|
131
|
+
else {
|
|
132
|
+
this.isOpen = true;
|
|
133
|
+
this.emit('open');
|
|
134
|
+
resolve();
|
|
135
|
+
}
|
|
136
|
+
});
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Close UART port
|
|
141
|
+
*/
|
|
142
|
+
async close() {
|
|
143
|
+
if (!this.port || !this.isOpen) {
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
return new Promise((resolve, reject) => {
|
|
147
|
+
this.port.close((err) => {
|
|
148
|
+
if (err) {
|
|
149
|
+
reject(new Error(`Failed to close port: ${err.message}`));
|
|
150
|
+
}
|
|
151
|
+
else {
|
|
152
|
+
this.isOpen = false;
|
|
153
|
+
this.port = null;
|
|
154
|
+
resolve();
|
|
155
|
+
}
|
|
156
|
+
});
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Write data to UART
|
|
161
|
+
*/
|
|
162
|
+
async write(data) {
|
|
163
|
+
if (!this.port || !this.isOpen) {
|
|
164
|
+
throw new Error('Port not open');
|
|
165
|
+
}
|
|
166
|
+
const buffer = typeof data === 'string' ? Buffer.from(data) : data;
|
|
167
|
+
return new Promise((resolve, reject) => {
|
|
168
|
+
this.port.write(buffer, (err) => {
|
|
169
|
+
if (err) {
|
|
170
|
+
reject(new Error(`Failed to write: ${err.message}`));
|
|
171
|
+
}
|
|
172
|
+
else {
|
|
173
|
+
this.port.drain((drainErr) => {
|
|
174
|
+
if (drainErr) {
|
|
175
|
+
reject(new Error(`Failed to drain: ${drainErr.message}`));
|
|
176
|
+
}
|
|
177
|
+
else {
|
|
178
|
+
resolve();
|
|
179
|
+
}
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
});
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Read data from UART (with timeout)
|
|
187
|
+
*/
|
|
188
|
+
async read(length, timeoutMs = 1000) {
|
|
189
|
+
if (!this.port || !this.isOpen) {
|
|
190
|
+
throw new Error('Port not open');
|
|
191
|
+
}
|
|
192
|
+
const chunks = [];
|
|
193
|
+
let totalRead = 0;
|
|
194
|
+
return new Promise((resolve, reject) => {
|
|
195
|
+
const timeoutId = setTimeout(() => {
|
|
196
|
+
cleanup();
|
|
197
|
+
resolve(Buffer.concat(chunks));
|
|
198
|
+
}, timeoutMs);
|
|
199
|
+
const onData = (data) => {
|
|
200
|
+
chunks.push(data);
|
|
201
|
+
totalRead += data.length;
|
|
202
|
+
if (totalRead >= length) {
|
|
203
|
+
cleanup();
|
|
204
|
+
resolve(Buffer.concat(chunks).subarray(0, length));
|
|
205
|
+
}
|
|
206
|
+
};
|
|
207
|
+
const cleanup = () => {
|
|
208
|
+
clearTimeout(timeoutId);
|
|
209
|
+
this.port.off('data', onData);
|
|
210
|
+
};
|
|
211
|
+
this.port.on('data', onData);
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* Read line (until newline)
|
|
216
|
+
*/
|
|
217
|
+
async readLine(timeoutMs = 1000) {
|
|
218
|
+
if (!this.port || !this.isOpen) {
|
|
219
|
+
throw new Error('Port not open');
|
|
220
|
+
}
|
|
221
|
+
const chunks = [];
|
|
222
|
+
return new Promise((resolve) => {
|
|
223
|
+
const timeoutId = setTimeout(() => {
|
|
224
|
+
cleanup();
|
|
225
|
+
resolve(Buffer.concat(chunks).toString('utf8'));
|
|
226
|
+
}, timeoutMs);
|
|
227
|
+
const onData = (data) => {
|
|
228
|
+
chunks.push(data);
|
|
229
|
+
const str = Buffer.concat(chunks).toString('utf8');
|
|
230
|
+
const newlineIndex = str.indexOf('\n');
|
|
231
|
+
if (newlineIndex !== -1) {
|
|
232
|
+
cleanup();
|
|
233
|
+
resolve(str.substring(0, newlineIndex + 1));
|
|
234
|
+
}
|
|
235
|
+
};
|
|
236
|
+
const cleanup = () => {
|
|
237
|
+
clearTimeout(timeoutId);
|
|
238
|
+
this.port.off('data', onData);
|
|
239
|
+
};
|
|
240
|
+
this.port.on('data', onData);
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
/**
|
|
244
|
+
* Write and wait for response
|
|
245
|
+
*/
|
|
246
|
+
async writeRead(data, responseLength, timeoutMs = 1000) {
|
|
247
|
+
await this.write(data);
|
|
248
|
+
return this.read(responseLength, timeoutMs);
|
|
249
|
+
}
|
|
250
|
+
/**
|
|
251
|
+
* Set baud rate
|
|
252
|
+
*/
|
|
253
|
+
async setBaudRate(baudRate) {
|
|
254
|
+
if (!this.port || !this.isOpen) {
|
|
255
|
+
throw new Error('Port not open');
|
|
256
|
+
}
|
|
257
|
+
return new Promise((resolve, reject) => {
|
|
258
|
+
this.port.update({ baudRate }, (err) => {
|
|
259
|
+
if (err) {
|
|
260
|
+
reject(new Error(`Failed to set baud rate: ${err.message}`));
|
|
261
|
+
}
|
|
262
|
+
else {
|
|
263
|
+
this.config.baudRate = baudRate;
|
|
264
|
+
resolve();
|
|
265
|
+
}
|
|
266
|
+
});
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
/**
|
|
270
|
+
* Flush input/output buffers
|
|
271
|
+
*/
|
|
272
|
+
async flush() {
|
|
273
|
+
if (!this.port || !this.isOpen) {
|
|
274
|
+
throw new Error('Port not open');
|
|
275
|
+
}
|
|
276
|
+
return new Promise((resolve, reject) => {
|
|
277
|
+
this.port.flush((err) => {
|
|
278
|
+
if (err) {
|
|
279
|
+
reject(new Error(`Failed to flush: ${err.message}`));
|
|
280
|
+
}
|
|
281
|
+
else {
|
|
282
|
+
resolve();
|
|
283
|
+
}
|
|
284
|
+
});
|
|
285
|
+
});
|
|
286
|
+
}
|
|
287
|
+
/**
|
|
288
|
+
* Set DTR signal
|
|
289
|
+
*/
|
|
290
|
+
async setDTR(value) {
|
|
291
|
+
if (!this.port || !this.isOpen) {
|
|
292
|
+
throw new Error('Port not open');
|
|
293
|
+
}
|
|
294
|
+
return new Promise((resolve, reject) => {
|
|
295
|
+
this.port.set({ dtr: value }, (err) => {
|
|
296
|
+
if (err) {
|
|
297
|
+
reject(new Error(`Failed to set DTR: ${err.message}`));
|
|
298
|
+
}
|
|
299
|
+
else {
|
|
300
|
+
resolve();
|
|
301
|
+
}
|
|
302
|
+
});
|
|
303
|
+
});
|
|
304
|
+
}
|
|
305
|
+
/**
|
|
306
|
+
* Set RTS signal
|
|
307
|
+
*/
|
|
308
|
+
async setRTS(value) {
|
|
309
|
+
if (!this.port || !this.isOpen) {
|
|
310
|
+
throw new Error('Port not open');
|
|
311
|
+
}
|
|
312
|
+
return new Promise((resolve, reject) => {
|
|
313
|
+
this.port.set({ rts: value }, (err) => {
|
|
314
|
+
if (err) {
|
|
315
|
+
reject(new Error(`Failed to set RTS: ${err.message}`));
|
|
316
|
+
}
|
|
317
|
+
else {
|
|
318
|
+
resolve();
|
|
319
|
+
}
|
|
320
|
+
});
|
|
321
|
+
});
|
|
322
|
+
}
|
|
323
|
+
/**
|
|
324
|
+
* Check if port is open
|
|
325
|
+
*/
|
|
326
|
+
isConnected() {
|
|
327
|
+
return this.isOpen;
|
|
328
|
+
}
|
|
329
|
+
/**
|
|
330
|
+
* Get current port path
|
|
331
|
+
*/
|
|
332
|
+
getPortPath() {
|
|
333
|
+
return this.portPath;
|
|
334
|
+
}
|
|
335
|
+
/**
|
|
336
|
+
* Get current configuration
|
|
337
|
+
*/
|
|
338
|
+
getConfig() {
|
|
339
|
+
return { ...this.config };
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
exports.CH347UART = CH347UART;
|
|
343
|
+
//# sourceMappingURL=uart.js.map
|
package/dist/usb.d.ts
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CH347 USB Communication Layer
|
|
3
|
+
*/
|
|
4
|
+
import * as usb from 'usb';
|
|
5
|
+
import { CH347DeviceInfo } from './types';
|
|
6
|
+
export declare class CH347USB {
|
|
7
|
+
private device;
|
|
8
|
+
private interface;
|
|
9
|
+
private epIn;
|
|
10
|
+
private epOut;
|
|
11
|
+
private isOpen;
|
|
12
|
+
/**
|
|
13
|
+
* Get the underlying USB device (for advanced operations)
|
|
14
|
+
*/
|
|
15
|
+
getDevice(): usb.Device | null;
|
|
16
|
+
/**
|
|
17
|
+
* List all connected CH347 devices
|
|
18
|
+
*/
|
|
19
|
+
static listDevices(): CH347DeviceInfo[];
|
|
20
|
+
/**
|
|
21
|
+
* Open connection to CH347 device
|
|
22
|
+
*/
|
|
23
|
+
open(deviceIndex?: number): Promise<void>;
|
|
24
|
+
/**
|
|
25
|
+
* Close connection
|
|
26
|
+
*/
|
|
27
|
+
close(): void;
|
|
28
|
+
/**
|
|
29
|
+
* Check if device is open
|
|
30
|
+
*/
|
|
31
|
+
isConnected(): boolean;
|
|
32
|
+
/**
|
|
33
|
+
* Send data to device
|
|
34
|
+
*/
|
|
35
|
+
write(data: Buffer): Promise<number>;
|
|
36
|
+
/**
|
|
37
|
+
* Read data from device
|
|
38
|
+
*/
|
|
39
|
+
read(length?: number, timeout?: number): Promise<Buffer>;
|
|
40
|
+
/**
|
|
41
|
+
* Write and then read response
|
|
42
|
+
*/
|
|
43
|
+
transfer(outData: Buffer, readLength?: number): Promise<Buffer>;
|
|
44
|
+
/**
|
|
45
|
+
* Bulk write for large data transfers
|
|
46
|
+
*/
|
|
47
|
+
bulkWrite(data: Buffer, chunkSize?: number): Promise<void>;
|
|
48
|
+
/**
|
|
49
|
+
* Bulk read for large data transfers
|
|
50
|
+
*/
|
|
51
|
+
bulkRead(totalLength: number, chunkSize?: number): Promise<Buffer>;
|
|
52
|
+
/**
|
|
53
|
+
* USB control transfer (for vendor-specific commands)
|
|
54
|
+
*/
|
|
55
|
+
controlTransfer(bmRequestType: number, bRequest: number, wValue: number, wIndex: number, dataOrLength: Buffer | number): Promise<Buffer | number>;
|
|
56
|
+
/**
|
|
57
|
+
* Get USB string descriptor
|
|
58
|
+
*/
|
|
59
|
+
getStringDescriptor(index: number): Promise<string>;
|
|
60
|
+
/**
|
|
61
|
+
* Get device descriptor info including serial number index
|
|
62
|
+
*/
|
|
63
|
+
getDeviceDescriptor(): usb.DeviceDescriptor | null;
|
|
64
|
+
/**
|
|
65
|
+
* Get the UART tty path for this CH347 device
|
|
66
|
+
* On Linux: scans /sys/class/tty for matching USB device
|
|
67
|
+
* On macOS: scans /dev for matching usbmodem device
|
|
68
|
+
*/
|
|
69
|
+
getUARTPath(): string | null;
|
|
70
|
+
/**
|
|
71
|
+
* Find TTY device on Linux by scanning sysfs
|
|
72
|
+
*/
|
|
73
|
+
private findLinuxTTY;
|
|
74
|
+
/**
|
|
75
|
+
* Find TTY device on macOS
|
|
76
|
+
* macOS names CDC ACM devices as /dev/tty.usbmodem* with location ID
|
|
77
|
+
*/
|
|
78
|
+
private findMacOSTTY;
|
|
79
|
+
}
|
|
80
|
+
//# sourceMappingURL=usb.d.ts.map
|