Initial Node.js serial transport (#779)
* Initial Node.js serial transport * Minor doc fixes * Add serialport to lockfile * Typo fix * Fix link:
This commit is contained in:
@@ -0,0 +1,28 @@
|
|||||||
|
# @meshtastic/transport-node-serial
|
||||||
|
|
||||||
|
[](https://jsr.io/@meshtastic/transport-node-serial)
|
||||||
|
[](https://github.com/meshtastic/js/actions/workflows/ci.yml)
|
||||||
|
[](https://cla-assistant.io/meshtastic/meshtastic.js)
|
||||||
|
[](https://opencollective.com/meshtastic/)
|
||||||
|
[](https://vercel.com?utm_source=meshtastic&utm_campaign=oss)
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
`@meshtastic/transport-noden-node` Provides Serial transport (Node) for Meshtastic
|
||||||
|
devices. Installation instructions are available at
|
||||||
|
[JSR](https://jsr.io/@meshtastic/transport-node-serial)
|
||||||
|
[NPM](https://www.npmjs.com/package/@meshtastic/transport-node-serial)
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
```ts
|
||||||
|
import { MeshDevice } from "@meshtastic/core";
|
||||||
|
import { TransportNodeSerial } from "@meshtastic/transport-node-serial";
|
||||||
|
|
||||||
|
const transport = await TransportNodeSerial.create("/dev/cu.usbserial-0001");
|
||||||
|
const device = new MeshDevice(transport);
|
||||||
|
```
|
||||||
|
|
||||||
|
## Stats
|
||||||
|
|
||||||
|

|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export { TransportNodeSerial } from "./src/transport.ts";
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
{
|
||||||
|
"name": "@meshtastic/transport-node-serial",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"description": "NodeJS-specific serial transport layer for Meshtastic web applications.",
|
||||||
|
"exports": {
|
||||||
|
".": "./dist/mod.mjs"
|
||||||
|
},
|
||||||
|
"main": "./dist/mod.mjs",
|
||||||
|
"module": "./dist/mod.mjs",
|
||||||
|
"types": "./dist/mod.d.mts",
|
||||||
|
|
||||||
|
"license": "GPL-3.0-only",
|
||||||
|
"tsdown": {
|
||||||
|
"entry": "mod.ts",
|
||||||
|
"dts": true,
|
||||||
|
"format": ["esm"],
|
||||||
|
"splitting": false,
|
||||||
|
"clean": true
|
||||||
|
},
|
||||||
|
"files": [
|
||||||
|
"package.json",
|
||||||
|
"README.md",
|
||||||
|
"LICENSE",
|
||||||
|
"dist"
|
||||||
|
],
|
||||||
|
"scripts": {
|
||||||
|
"preinstall": "npx only-allow pnpm",
|
||||||
|
"prepack": "cp ../../LICENSE ./LICENSE",
|
||||||
|
"clean": "rm -rf dist LICENSE",
|
||||||
|
"build:npm": "tsdown",
|
||||||
|
"publish:npm": "pnpm clean && pnpm build:npm && pnpm publish --access public",
|
||||||
|
"prepare:jsr": "rm -rf dist && pnpm dlx pkg-to-jsr",
|
||||||
|
"publish:jsr": "pnpm run prepack && pnpm prepare:jsr && deno publish --allow-dirty --no-check"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@meshtastic/core": "workspace:*",
|
||||||
|
"serialport": "^13.0.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,87 @@
|
|||||||
|
import { Readable, Writable } from "node:stream";
|
||||||
|
import type { Types } from "@meshtastic/core";
|
||||||
|
import { Utils } from "@meshtastic/core";
|
||||||
|
import { SerialPort } from "serialport";
|
||||||
|
|
||||||
|
export class TransportNodeSerial implements Types.Transport {
|
||||||
|
private readonly _toDevice: WritableStream<Uint8Array>;
|
||||||
|
private readonly _fromDevice: ReadableStream<Types.DeviceOutput>;
|
||||||
|
private port: SerialPort | undefined;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates and connects a new TransportNode instance.
|
||||||
|
* @param path - Path to the serial device
|
||||||
|
* @param baudRate - The port number for the TCP connection (defaults to 4403).
|
||||||
|
* @returns A promise that resolves with a connected TransportNode instance.
|
||||||
|
*/
|
||||||
|
public static create(path: string, baudRate = 115200): Promise<TransportNodeSerial> {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const port = new SerialPort({
|
||||||
|
path,
|
||||||
|
baudRate,
|
||||||
|
autoOpen: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
const onError = (err: Error) => {
|
||||||
|
port.close();
|
||||||
|
reject(err);
|
||||||
|
};
|
||||||
|
|
||||||
|
port.once("error", onError);
|
||||||
|
port.on("open", () => {
|
||||||
|
port.removeListener("error", onError);
|
||||||
|
resolve(new TransportNodeSerial(port));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a new TransportNode.
|
||||||
|
* @param port - An active Node.js SerialPort connection.
|
||||||
|
*/
|
||||||
|
constructor(port: SerialPort) {
|
||||||
|
this.port = port;
|
||||||
|
this.port.on("error", (err) => {
|
||||||
|
console.error("Serial port connection error:", err);
|
||||||
|
});
|
||||||
|
|
||||||
|
const fromDeviceSource = Readable.toWeb(
|
||||||
|
port,
|
||||||
|
) as ReadableStream<Uint8Array>;
|
||||||
|
this._fromDevice = fromDeviceSource.pipeThrough(Utils.fromDeviceStream());
|
||||||
|
|
||||||
|
// Stream for data going FROM the application TO the Meshtastic device.
|
||||||
|
const toDeviceTransform = Utils.toDeviceStream;
|
||||||
|
this._toDevice = toDeviceTransform.writable;
|
||||||
|
|
||||||
|
// The readable end of the transform is then piped to the Node.js SerialPort connection.
|
||||||
|
// A similar assertion is needed here because `Writable.toWeb` also returns
|
||||||
|
// a generically typed stream (`WritableStream<any>`).
|
||||||
|
toDeviceTransform.readable
|
||||||
|
.pipeTo(Writable.toWeb(port) as WritableStream<Uint8Array>)
|
||||||
|
.catch((err) => {
|
||||||
|
console.error("Error piping data to serial port:", err);
|
||||||
|
this.port.close(err as Error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The WritableStream to send data to the Meshtastic device.
|
||||||
|
*/
|
||||||
|
public get toDevice(): WritableStream<Uint8Array> {
|
||||||
|
return this._toDevice;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The ReadableStream to receive data from the Meshtastic device.
|
||||||
|
*/
|
||||||
|
public get fromDevice(): ReadableStream<Types.DeviceOutput> {
|
||||||
|
return this._fromDevice;
|
||||||
|
}
|
||||||
|
|
||||||
|
disconnect() {
|
||||||
|
this.port.close();
|
||||||
|
this.port = undefined;
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
{
|
||||||
|
"extends": "../../tsconfig.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"module": "ESNext",
|
||||||
|
"target": "ES2020",
|
||||||
|
"declaration": true,
|
||||||
|
"outDir": "./dist",
|
||||||
|
"moduleResolution": "bundler",
|
||||||
|
"emitDeclarationOnly": false,
|
||||||
|
"esModuleInterop": true,
|
||||||
|
},
|
||||||
|
"include": ["src"]
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Generated
+9
@@ -61,6 +61,15 @@ importers:
|
|||||||
specifier: workspace:*
|
specifier: workspace:*
|
||||||
version: link:../core
|
version: link:../core
|
||||||
|
|
||||||
|
packages/transport-node-serial:
|
||||||
|
dependencies:
|
||||||
|
'@meshtastic/core':
|
||||||
|
specifier: workspace:*
|
||||||
|
version: link:../core
|
||||||
|
'serialport':
|
||||||
|
specifier: npm:serialport@^13.0.0
|
||||||
|
version: 13.0.0
|
||||||
|
|
||||||
packages/transport-web-bluetooth:
|
packages/transport-web-bluetooth:
|
||||||
dependencies:
|
dependencies:
|
||||||
'@meshtastic/core':
|
'@meshtastic/core':
|
||||||
|
|||||||
Reference in New Issue
Block a user