Browse Source
* Initial Node.js serial transport * Minor doc fixes * Add serialport to lockfile * Typo fix * Fix link:pull/784/head
committed by
GitHub
6 changed files with 182 additions and 0 deletions
@ -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"] |
|||
} |
|||
|
|||
|
|||
|
|||
|
|||
|
|||
Loading…
Reference in new issue