Browse Source

Initial Node.js serial transport (#779)

* Initial Node.js serial transport

* Minor doc fixes

* Add serialport to lockfile

* Typo fix

* Fix link:
pull/784/head
Henri Bergius 10 months ago
committed by GitHub
parent
commit
0b9ebade38
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 28
      packages/transport-node-serial/README.md
  2. 1
      packages/transport-node-serial/mod.ts
  3. 39
      packages/transport-node-serial/package.json
  4. 87
      packages/transport-node-serial/src/transport.ts
  5. 18
      packages/transport-node-serial/tsconfig.json
  6. 9
      pnpm-lock.yaml

28
packages/transport-node-serial/README.md

@ -0,0 +1,28 @@
# @meshtastic/transport-node-serial
[![JSR](https://jsr.io/badges/@meshtastic/transport-node-serial)](https://jsr.io/@meshtastic/transport-node-serial)
[![CI](https://img.shields.io/github/actions/workflow/status/meshtastic/js/ci.yml?branch=master&label=actions&logo=github&color=yellow)](https://github.com/meshtastic/js/actions/workflows/ci.yml)
[![CLA assistant](https://cla-assistant.io/readme/badge/meshtastic/meshtastic.js)](https://cla-assistant.io/meshtastic/meshtastic.js)
[![Fiscal Contributors](https://opencollective.com/meshtastic/tiers/badge.svg?label=Fiscal%20Contributors&color=deeppink)](https://opencollective.com/meshtastic/)
[![Vercel](https://img.shields.io/static/v1?label=Powered%20by&message=Vercel&style=flat&logo=vercel&color=000000)](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
![Alt](https://repobeats.axiom.co/api/embed/5330641586e92a2ec84676fedb98f6d4a7b25d69.svg "Repobeats analytics image")

1
packages/transport-node-serial/mod.ts

@ -0,0 +1 @@
export { TransportNodeSerial } from "./src/transport.ts";

39
packages/transport-node-serial/package.json

@ -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"
}
}

87
packages/transport-node-serial/src/transport.ts

@ -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();
}
}

18
packages/transport-node-serial/tsconfig.json

@ -0,0 +1,18 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"module": "ESNext",
"target": "ES2020",
"declaration": true,
"outDir": "./dist",
"moduleResolution": "bundler",
"emitDeclarationOnly": false,
"esModuleInterop": true,
},
"include": ["src"]
}

9
pnpm-lock.yaml

@ -61,6 +61,15 @@ importers:
specifier: workspace:*
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:
dependencies:
'@meshtastic/core':

Loading…
Cancel
Save