tauri-plugin-serialplugin-api 2.18.0
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.spdx +20 -0
- package/README.md +1678 -0
- package/dist-js/auto-reconnect-manager.d.ts +69 -0
- package/dist-js/index.cjs +1114 -0
- package/dist-js/index.d.ts +372 -0
- package/dist-js/index.js +1112 -0
- package/dist-js/listener-manager.d.ts +34 -0
- package/package.json +55 -0
package/README.md
ADDED
|
@@ -0,0 +1,1678 @@
|
|
|
1
|
+
[](https://www.npmjs.com/package/tauri-plugin-serialplugin)
|
|
2
|
+
[](https://crates.io/crates/tauri-plugin-serialplugin)
|
|
3
|
+
[](https://docs.rs/tauri-plugin-serialplugin/latest/tauri_plugin_serialplugin/all.html)
|
|
4
|
+
[](https://github.com/s00d/tauri-plugin-serialplugin/issues)
|
|
5
|
+
[](https://github.com/s00d/tauri-plugin-serialplugin/stargazers)
|
|
6
|
+
[](https://www.donationalerts.com/r/s00d88)
|
|
7
|
+
|
|
8
|
+
# Tauri Plugin — SerialPort
|
|
9
|
+
|
|
10
|
+
A comprehensive plugin for Tauri applications to communicate with serial ports. This plugin provides a complete API for reading from and writing to serial devices, with support for various configuration options and control signals.
|
|
11
|
+
|
|
12
|
+
> **⚠️ Important Notice:** The JavaScript dependency has been renamed from `tauri-plugin-serialplugin` to `tauri-plugin-serialplugin-api`. Please update your `package.json` before updating to the latest version, following the same pattern used by other Tauri plugins.
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## Table of Contents
|
|
17
|
+
|
|
18
|
+
1. [Installation](#installation)
|
|
19
|
+
2. [Basic Usage](#basic-usage)
|
|
20
|
+
3. [TypeScript Support](#typescript-support)
|
|
21
|
+
4. [Rust Usage](#rust-usage)
|
|
22
|
+
5. [Permissions](#permissions)
|
|
23
|
+
6. [API Reference](#api-reference)
|
|
24
|
+
6.1. [Port Discovery](#port-discovery)
|
|
25
|
+
6.2. [Connection Management](#connection-management)
|
|
26
|
+
6.3. [Data Transfer](#data-transfer)
|
|
27
|
+
6.4. [Port Configuration](#port-configuration)
|
|
28
|
+
6.5. [Control Signals](#control-signals)
|
|
29
|
+
6.6. [Buffer Management](#buffer-management)
|
|
30
|
+
7. [Common Use Cases](#common-use-cases)
|
|
31
|
+
8. [Android Setup](#android-setup)
|
|
32
|
+
9. [Contributing](#contributing)
|
|
33
|
+
10. [Development Setup](#development-setup)
|
|
34
|
+
11. [Testing](#testing)
|
|
35
|
+
12. [Partners](#partners)
|
|
36
|
+
13. [License](#license)
|
|
37
|
+
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
## Installation
|
|
41
|
+
|
|
42
|
+
### Prerequisites
|
|
43
|
+
|
|
44
|
+
- **Rust** version 1.70 or higher
|
|
45
|
+
- **Tauri** 2.0 or higher
|
|
46
|
+
- **Node.js** and an npm-compatible package manager (npm, yarn, pnpm)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
## Installation
|
|
52
|
+
|
|
53
|
+
### Automatic Installation (Recommended)
|
|
54
|
+
|
|
55
|
+
Use the Tauri CLI to automatically install both the Rust and JavaScript parts of the plugin:
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
# npm
|
|
59
|
+
npm run tauri add serialplugin
|
|
60
|
+
|
|
61
|
+
# yarn
|
|
62
|
+
yarn run tauri add serialplugin
|
|
63
|
+
|
|
64
|
+
# pnpm
|
|
65
|
+
pnpm tauri add serialplugin
|
|
66
|
+
|
|
67
|
+
# deno
|
|
68
|
+
deno task tauri add serialplugin
|
|
69
|
+
|
|
70
|
+
# bun
|
|
71
|
+
bun tauri add serialplugin
|
|
72
|
+
|
|
73
|
+
# cargo
|
|
74
|
+
cargo tauri add serialplugin
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
### Manual Installation
|
|
78
|
+
|
|
79
|
+
#### Backend (Rust)
|
|
80
|
+
|
|
81
|
+
Add the plugin using cargo:
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
cd ./src-tauri
|
|
85
|
+
cargo add tauri-plugin-serialplugin
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
#### Frontend (JavaScript/TypeScript)
|
|
89
|
+
|
|
90
|
+
Install the JavaScript API:
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
npm install tauri-plugin-serialplugin-api
|
|
94
|
+
# or
|
|
95
|
+
pnpm add tauri-plugin-serialplugin-api
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
---
|
|
99
|
+
|
|
100
|
+
## Basic Usage
|
|
101
|
+
|
|
102
|
+
1. **Register the Plugin**
|
|
103
|
+
```rust
|
|
104
|
+
// src-tauri/src/main.rs
|
|
105
|
+
fn main() {
|
|
106
|
+
tauri::Builder::default()
|
|
107
|
+
.plugin(tauri_plugin_serialplugin::init())
|
|
108
|
+
.run(tauri::generate_context!())
|
|
109
|
+
.expect("error while running tauri application");
|
|
110
|
+
}
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
2. **Configure Permissions**
|
|
114
|
+
```jsonc
|
|
115
|
+
// src-tauri/capabilities/default.json
|
|
116
|
+
{
|
|
117
|
+
"$schema": "../gen/schemas/desktop-schema.json",
|
|
118
|
+
"identifier": "default",
|
|
119
|
+
"description": "Capability for the main window",
|
|
120
|
+
"windows": ["main"],
|
|
121
|
+
"permissions": [
|
|
122
|
+
"core:default",
|
|
123
|
+
"serialplugin:default"
|
|
124
|
+
]
|
|
125
|
+
}
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
3. **Basic Example**
|
|
129
|
+
```typescript
|
|
130
|
+
import { SerialPort } from "tauri-plugin-serialplugin-api";
|
|
131
|
+
|
|
132
|
+
// List available ports
|
|
133
|
+
const ports = await SerialPort.available_ports();
|
|
134
|
+
console.log("Available ports:", ports);
|
|
135
|
+
|
|
136
|
+
// Open a port
|
|
137
|
+
const port = new SerialPort({
|
|
138
|
+
path: "COM1",
|
|
139
|
+
baudRate: 9600
|
|
140
|
+
});
|
|
141
|
+
await port.open();
|
|
142
|
+
|
|
143
|
+
// Write data
|
|
144
|
+
await port.write("Hello, Serial Port!");
|
|
145
|
+
|
|
146
|
+
// Start port listening
|
|
147
|
+
const unsubscribe = await port.listen((data) => {
|
|
148
|
+
console.log("Received:", data);
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
// Stop listening when done
|
|
152
|
+
await port.cancelListen();
|
|
153
|
+
|
|
154
|
+
// Close port
|
|
155
|
+
await port.close();
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
4. **Error Handling Example**
|
|
159
|
+
```typescript
|
|
160
|
+
import { SerialPort } from "tauri-plugin-serialplugin-api";
|
|
161
|
+
|
|
162
|
+
async function handleSerialPort() {
|
|
163
|
+
let port: SerialPort | null = null;
|
|
164
|
+
|
|
165
|
+
try {
|
|
166
|
+
// List available ports
|
|
167
|
+
const ports = await SerialPort.available_ports();
|
|
168
|
+
if (Object.keys(ports).length === 0) {
|
|
169
|
+
throw new Error("No serial ports found");
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// Open port
|
|
173
|
+
port = new SerialPort({
|
|
174
|
+
path: "COM1",
|
|
175
|
+
baudRate: 9600
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
try {
|
|
179
|
+
await port.open();
|
|
180
|
+
} catch (error) {
|
|
181
|
+
throw new Error(`Failed to open port: ${error}`);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
try {
|
|
185
|
+
// Write data
|
|
186
|
+
await port.write("Test data");
|
|
187
|
+
} catch (error) {
|
|
188
|
+
throw new Error(`Failed to write data: ${error}`);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
try {
|
|
192
|
+
// Read data
|
|
193
|
+
const data = await port.read({ timeout: 1000 });
|
|
194
|
+
console.log("Received:", data);
|
|
195
|
+
} catch (error) {
|
|
196
|
+
throw new Error(`Failed to read data: ${error}`);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
try {
|
|
200
|
+
// Start listening
|
|
201
|
+
await port.startListening();
|
|
202
|
+
await port.listen((data) => {
|
|
203
|
+
console.log("Received:", data);
|
|
204
|
+
});
|
|
205
|
+
} catch (error) {
|
|
206
|
+
throw new Error(`Failed to start listening: ${error}`);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
try {
|
|
210
|
+
// Configure port settings
|
|
211
|
+
await port.setBaudRate(115200);
|
|
212
|
+
await port.setDataBits(DataBits.Eight);
|
|
213
|
+
await port.setFlowControl(FlowControl.None);
|
|
214
|
+
await port.setParity(Parity.None);
|
|
215
|
+
await port.setStopBits(StopBits.One);
|
|
216
|
+
await port.setTimeout(1000);
|
|
217
|
+
} catch (error) {
|
|
218
|
+
throw new Error(`Failed to configure port: ${error}`);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
} catch (error) {
|
|
222
|
+
// Handle all errors in one place
|
|
223
|
+
console.error("Serial port error:", error);
|
|
224
|
+
} finally {
|
|
225
|
+
// Clean up
|
|
226
|
+
if (port) {
|
|
227
|
+
try {
|
|
228
|
+
await port.cancelListen();
|
|
229
|
+
await port.close();
|
|
230
|
+
} catch (error) {
|
|
231
|
+
console.error("Error during cleanup:", error);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
// Usage
|
|
238
|
+
handleSerialPort();
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
---
|
|
242
|
+
|
|
243
|
+
## TypeScript Support
|
|
244
|
+
|
|
245
|
+
This plugin provides full TypeScript support with comprehensive type definitions. All methods, interfaces, and enums are properly typed for better development experience.
|
|
246
|
+
|
|
247
|
+
### Available Types
|
|
248
|
+
|
|
249
|
+
```typescript
|
|
250
|
+
import {
|
|
251
|
+
SerialPort,
|
|
252
|
+
DataBits,
|
|
253
|
+
FlowControl,
|
|
254
|
+
Parity,
|
|
255
|
+
StopBits,
|
|
256
|
+
ClearBuffer,
|
|
257
|
+
PortInfo,
|
|
258
|
+
SerialportOptions,
|
|
259
|
+
ReadOptions
|
|
260
|
+
} from "tauri-plugin-serialplugin-api";
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
### Type Definitions
|
|
264
|
+
|
|
265
|
+
- **`SerialPort`** - Main class for serial port operations
|
|
266
|
+
- **`DataBits`** - Enum: `Five`, `Six`, `Seven`, `Eight`
|
|
267
|
+
- **`FlowControl`** - Enum: `None`, `Software`, `Hardware`
|
|
268
|
+
- **`Parity`** - Enum: `None`, `Odd`, `Even`
|
|
269
|
+
- **`StopBits`** - Enum: `One`, `Two`
|
|
270
|
+
- **`ClearBuffer`** - Enum: `Input`, `Output`, `All`
|
|
271
|
+
- **`PortInfo`** - Interface for port information
|
|
272
|
+
- **`SerialportOptions`** - Interface for port configuration
|
|
273
|
+
- **`ReadOptions`** - Interface for read operation options
|
|
274
|
+
|
|
275
|
+
### Configuration Example with Types
|
|
276
|
+
|
|
277
|
+
```typescript
|
|
278
|
+
import { SerialPort, DataBits, FlowControl, Parity, StopBits } from "tauri-plugin-serialplugin-api";
|
|
279
|
+
|
|
280
|
+
const port = new SerialPort({
|
|
281
|
+
path: "/dev/ttyUSB0",
|
|
282
|
+
baudRate: 9600,
|
|
283
|
+
dataBits: DataBits.Eight, // Type-safe enum
|
|
284
|
+
flowControl: FlowControl.None, // Type-safe enum
|
|
285
|
+
parity: Parity.None, // Type-safe enum
|
|
286
|
+
stopBits: StopBits.One, // Type-safe enum
|
|
287
|
+
timeout: 1000,
|
|
288
|
+
size: 1024
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
// All configuration methods are fully typed
|
|
292
|
+
await port.setBaudRate(115200);
|
|
293
|
+
await port.setDataBits(DataBits.Eight);
|
|
294
|
+
await port.setFlowControl(FlowControl.None);
|
|
295
|
+
await port.setParity(Parity.None);
|
|
296
|
+
await port.setStopBits(StopBits.One);
|
|
297
|
+
await port.setTimeout(500);
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
### Control Signals with Types
|
|
301
|
+
|
|
302
|
+
```typescript
|
|
303
|
+
// Set control signals
|
|
304
|
+
await port.setRequestToSend(true);
|
|
305
|
+
await port.setDataTerminalReady(true);
|
|
306
|
+
|
|
307
|
+
// Alternative methods (writeRequestToSend and writeDataTerminalReady)
|
|
308
|
+
await port.writeRequestToSend(true);
|
|
309
|
+
await port.writeDataTerminalReady(true);
|
|
310
|
+
|
|
311
|
+
// Read control signals
|
|
312
|
+
const cts = await port.readClearToSend();
|
|
313
|
+
const dsr = await port.readDataSetReady();
|
|
314
|
+
const ri = await port.readRingIndicator();
|
|
315
|
+
const cd = await port.readCarrierDetect();
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
### Buffer Management with Types
|
|
319
|
+
|
|
320
|
+
```typescript
|
|
321
|
+
import { ClearBuffer } from "tauri-plugin-serialplugin-api";
|
|
322
|
+
|
|
323
|
+
// Check buffer status
|
|
324
|
+
const bytesToRead = await port.bytesToRead();
|
|
325
|
+
const bytesToWrite = await port.bytesToWrite();
|
|
326
|
+
|
|
327
|
+
// Clear buffers with type-safe enum
|
|
328
|
+
await port.clearBuffer(ClearBuffer.Input);
|
|
329
|
+
await port.clearBuffer(ClearBuffer.Output);
|
|
330
|
+
await port.clearBuffer(ClearBuffer.All);
|
|
331
|
+
|
|
332
|
+
// Break signal control
|
|
333
|
+
await port.setBreak();
|
|
334
|
+
await port.clearBreak();
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
---
|
|
338
|
+
|
|
339
|
+
## Rust Usage
|
|
340
|
+
|
|
341
|
+
This plugin can also be used directly from Rust code in your Tauri backend. For complete API documentation, see [docs.rs](https://docs.rs/tauri-plugin-serialplugin/).
|
|
342
|
+
|
|
343
|
+
Here's how to use it:
|
|
344
|
+
|
|
345
|
+
### Using Commands Directly
|
|
346
|
+
|
|
347
|
+
You can import and use the command functions directly from the plugin:
|
|
348
|
+
|
|
349
|
+
```rust
|
|
350
|
+
use tauri_plugin_serialplugin::commands::{
|
|
351
|
+
available_ports, open, write, read, close, set_baud_rate,
|
|
352
|
+
set_data_bits, set_flow_control, set_parity, set_stop_bits, set_timeout,
|
|
353
|
+
write_request_to_send, write_data_terminal_ready,
|
|
354
|
+
read_clear_to_send, read_data_set_ready,
|
|
355
|
+
bytes_to_read, bytes_to_write, clear_buffer,
|
|
356
|
+
set_break, clear_break
|
|
357
|
+
};
|
|
358
|
+
use tauri_plugin_serialplugin::state::{DataBits, FlowControl, Parity, StopBits, ClearBuffer};
|
|
359
|
+
use tauri::{AppHandle, State, Runtime};
|
|
360
|
+
use std::collections::HashMap;
|
|
361
|
+
|
|
362
|
+
#[tauri::command]
|
|
363
|
+
async fn rust_serial_example(
|
|
364
|
+
app: AppHandle<tauri::Wry>,
|
|
365
|
+
serial: State<'_, tauri_plugin_serialplugin::desktop_api::SerialPort<tauri::Wry>>
|
|
366
|
+
) -> Result<(), String> {
|
|
367
|
+
// Get available ports
|
|
368
|
+
let ports = available_ports(app.clone(), serial.clone())
|
|
369
|
+
.map_err(|e| format!("Failed to get ports: {}", e))?;
|
|
370
|
+
println!("Available ports: {:?}", ports);
|
|
371
|
+
|
|
372
|
+
// Open a serial port
|
|
373
|
+
let path = "COM1".to_string();
|
|
374
|
+
let baud_rate = 9600;
|
|
375
|
+
|
|
376
|
+
open(
|
|
377
|
+
app.clone(),
|
|
378
|
+
serial.clone(),
|
|
379
|
+
path.clone(),
|
|
380
|
+
baud_rate,
|
|
381
|
+
Some(DataBits::Eight),
|
|
382
|
+
Some(FlowControl::None),
|
|
383
|
+
Some(Parity::None),
|
|
384
|
+
Some(StopBits::One),
|
|
385
|
+
Some(1000u64) // timeout in milliseconds
|
|
386
|
+
).map_err(|e| format!("Failed to open port: {}", e))?;
|
|
387
|
+
|
|
388
|
+
// Write data
|
|
389
|
+
let data = "Hello from Rust!".to_string();
|
|
390
|
+
let bytes_written = write(app.clone(), serial.clone(), path.clone(), data)
|
|
391
|
+
.map_err(|e| format!("Failed to write: {}", e))?;
|
|
392
|
+
println!("Wrote {} bytes", bytes_written);
|
|
393
|
+
|
|
394
|
+
// Read data
|
|
395
|
+
let received_data = read(
|
|
396
|
+
app.clone(),
|
|
397
|
+
serial.clone(),
|
|
398
|
+
path.clone(),
|
|
399
|
+
Some(1000u64), // timeout
|
|
400
|
+
Some(1024usize) // max bytes to read
|
|
401
|
+
).map_err(|e| format!("Failed to read: {}", e))?;
|
|
402
|
+
println!("Received: {}", received_data);
|
|
403
|
+
|
|
404
|
+
// Configure port settings
|
|
405
|
+
set_baud_rate(app.clone(), serial.clone(), path.clone(), 115200)
|
|
406
|
+
.map_err(|e| format!("Failed to set baud rate: {}", e))?;
|
|
407
|
+
|
|
408
|
+
set_data_bits(app.clone(), serial.clone(), path.clone(), DataBits::Eight)
|
|
409
|
+
.map_err(|e| format!("Failed to set data bits: {}", e))?;
|
|
410
|
+
|
|
411
|
+
set_flow_control(app.clone(), serial.clone(), path.clone(), FlowControl::None)
|
|
412
|
+
.map_err(|e| format!("Failed to set flow control: {}", e))?;
|
|
413
|
+
|
|
414
|
+
set_parity(app.clone(), serial.clone(), path.clone(), Parity::None)
|
|
415
|
+
.map_err(|e| format!("Failed to set parity: {}", e))?;
|
|
416
|
+
|
|
417
|
+
set_stop_bits(app.clone(), serial.clone(), path.clone(), StopBits::One)
|
|
418
|
+
.map_err(|e| format!("Failed to set stop bits: {}", e))?;
|
|
419
|
+
|
|
420
|
+
// Set timeout
|
|
421
|
+
set_timeout(app.clone(), serial.clone(), path.clone(), 1000u64)
|
|
422
|
+
.map_err(|e| format!("Failed to set timeout: {}", e))?;
|
|
423
|
+
|
|
424
|
+
// Control signals
|
|
425
|
+
write_request_to_send(app.clone(), serial.clone(), path.clone(), true)
|
|
426
|
+
.map_err(|e| format!("Failed to set RTS: {}", e))?;
|
|
427
|
+
|
|
428
|
+
write_data_terminal_ready(app.clone(), serial.clone(), path.clone(), true)
|
|
429
|
+
.map_err(|e| format!("Failed to set DTR: {}", e))?;
|
|
430
|
+
|
|
431
|
+
// Read control signals
|
|
432
|
+
let cts = read_clear_to_send(app.clone(), serial.clone(), path.clone())
|
|
433
|
+
.map_err(|e| format!("Failed to read CTS: {}", e))?;
|
|
434
|
+
println!("CTS: {}", cts);
|
|
435
|
+
|
|
436
|
+
let dsr = read_data_set_ready(app.clone(), serial.clone(), path.clone())
|
|
437
|
+
.map_err(|e| format!("Failed to read DSR: {}", e))?;
|
|
438
|
+
println!("DSR: {}", dsr);
|
|
439
|
+
|
|
440
|
+
// Buffer management
|
|
441
|
+
let bytes_to_read = bytes_to_read(app.clone(), serial.clone(), path.clone())
|
|
442
|
+
.map_err(|e| format!("Failed to get bytes to read: {}", e))?;
|
|
443
|
+
println!("Bytes available to read: {}", bytes_to_read);
|
|
444
|
+
|
|
445
|
+
let bytes_to_write = bytes_to_write(app.clone(), serial.clone(), path.clone())
|
|
446
|
+
.map_err(|e| format!("Failed to get bytes to write: {}", e))?;
|
|
447
|
+
println!("Bytes waiting to write: {}", bytes_to_write);
|
|
448
|
+
|
|
449
|
+
// Clear buffers
|
|
450
|
+
clear_buffer(app.clone(), serial.clone(), path.clone(), ClearBuffer::All)
|
|
451
|
+
.map_err(|e| format!("Failed to clear buffer: {}", e))?;
|
|
452
|
+
|
|
453
|
+
// Break signal
|
|
454
|
+
set_break(app.clone(), serial.clone(), path.clone())
|
|
455
|
+
.map_err(|e| format!("Failed to set break: {}", e))?;
|
|
456
|
+
|
|
457
|
+
clear_break(app.clone(), serial.clone(), path.clone())
|
|
458
|
+
.map_err(|e| format!("Failed to clear break: {}", e))?;
|
|
459
|
+
|
|
460
|
+
// Close the port
|
|
461
|
+
close(app, serial, path)
|
|
462
|
+
.map_err(|e| format!("Failed to close port: {}", e))?;
|
|
463
|
+
|
|
464
|
+
Ok(())
|
|
465
|
+
}
|
|
466
|
+
```
|
|
467
|
+
|
|
468
|
+
### Advanced Rust Example with Error Handling
|
|
469
|
+
|
|
470
|
+
```rust
|
|
471
|
+
use tauri_plugin_serialplugin::commands::{
|
|
472
|
+
available_ports, open, write, read, close, force_close, managed_ports, start_listening
|
|
473
|
+
};
|
|
474
|
+
use tauri_plugin_serialplugin::state::{DataBits, FlowControl, Parity, StopBits};
|
|
475
|
+
use tauri::{AppHandle, State};
|
|
476
|
+
use std::collections::HashMap;
|
|
477
|
+
|
|
478
|
+
#[tauri::command]
|
|
479
|
+
async fn advanced_serial_example(
|
|
480
|
+
app: AppHandle<tauri::Wry>,
|
|
481
|
+
serial: State<'_, tauri_plugin_serialplugin::desktop_api::SerialPort<tauri::Wry>>
|
|
482
|
+
) -> Result<(), String> {
|
|
483
|
+
// Get available ports with error handling
|
|
484
|
+
let ports = match available_ports(app.clone(), serial.clone()) {
|
|
485
|
+
Ok(ports) => ports,
|
|
486
|
+
Err(e) => {
|
|
487
|
+
eprintln!("Failed to get available ports: {}", e);
|
|
488
|
+
return Err("No serial ports available".to_string());
|
|
489
|
+
}
|
|
490
|
+
};
|
|
491
|
+
|
|
492
|
+
if ports.is_empty() {
|
|
493
|
+
return Err("No serial ports found".to_string());
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
// Use the first available port
|
|
497
|
+
let port_path = ports.keys().next().unwrap().clone();
|
|
498
|
+
println!("Using port: {}", port_path);
|
|
499
|
+
|
|
500
|
+
// Open port with full configuration
|
|
501
|
+
let open_result = open(
|
|
502
|
+
app.clone(),
|
|
503
|
+
serial.clone(),
|
|
504
|
+
port_path.clone(),
|
|
505
|
+
9600u32, // baud rate
|
|
506
|
+
Some(DataBits::Eight),
|
|
507
|
+
Some(FlowControl::None),
|
|
508
|
+
Some(Parity::None),
|
|
509
|
+
Some(StopBits::One),
|
|
510
|
+
Some(5000u64) // 5 second timeout
|
|
511
|
+
);
|
|
512
|
+
|
|
513
|
+
match open_result {
|
|
514
|
+
Ok(_) => println!("Port opened successfully"),
|
|
515
|
+
Err(e) => {
|
|
516
|
+
eprintln!("Failed to open port: {}", e);
|
|
517
|
+
return Err(format!("Failed to open port {}: {}", port_path, e));
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
// Start listening for data
|
|
522
|
+
match start_listening(
|
|
523
|
+
app.clone(),
|
|
524
|
+
serial.clone(),
|
|
525
|
+
port_path.clone(),
|
|
526
|
+
Some(1000u64), // timeout
|
|
527
|
+
Some(1024usize) // max bytes
|
|
528
|
+
) {
|
|
529
|
+
Ok(_) => println!("Started listening"),
|
|
530
|
+
Err(e) => {
|
|
531
|
+
eprintln!("Failed to start listening: {}", e);
|
|
532
|
+
// Continue anyway, we can still read manually
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
// Send a command and read response
|
|
537
|
+
let command = "AT\r\n".to_string();
|
|
538
|
+
match write(app.clone(), serial.clone(), port_path.clone(), command) {
|
|
539
|
+
Ok(bytes) => println!("Sent {} bytes", bytes),
|
|
540
|
+
Err(e) => {
|
|
541
|
+
eprintln!("Failed to write command: {}", e);
|
|
542
|
+
return Err(format!("Write failed: {}", e));
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
// Read response with timeout
|
|
547
|
+
match read(
|
|
548
|
+
app.clone(),
|
|
549
|
+
serial.clone(),
|
|
550
|
+
port_path.clone(),
|
|
551
|
+
Some(2000u64), // 2 second timeout
|
|
552
|
+
Some(512usize) // max 512 bytes
|
|
553
|
+
) {
|
|
554
|
+
Ok(response) => println!("Response: {}", response),
|
|
555
|
+
Err(e) => {
|
|
556
|
+
eprintln!("Failed to read response: {}", e);
|
|
557
|
+
return Err(format!("Read failed: {}", e));
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
// Get managed ports
|
|
562
|
+
let managed_ports = match managed_ports(app.clone(), serial.clone()) {
|
|
563
|
+
Ok(ports) => ports,
|
|
564
|
+
Err(e) => {
|
|
565
|
+
eprintln!("Failed to get managed ports: {}", e);
|
|
566
|
+
Vec::new()
|
|
567
|
+
}
|
|
568
|
+
};
|
|
569
|
+
println!("Managed ports: {:?}", managed_ports);
|
|
570
|
+
|
|
571
|
+
// Clean up
|
|
572
|
+
let cleanup_result = close(app.clone(), serial.clone(), port_path.clone());
|
|
573
|
+
match cleanup_result {
|
|
574
|
+
Ok(_) => println!("Port closed successfully"),
|
|
575
|
+
Err(e) => {
|
|
576
|
+
eprintln!("Failed to close port: {}", e);
|
|
577
|
+
// Try force close
|
|
578
|
+
if let Err(e2) = force_close(app, serial, port_path) {
|
|
579
|
+
eprintln!("Failed to force close port: {}", e2);
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
Ok(())
|
|
585
|
+
}
|
|
586
|
+
```
|
|
587
|
+
|
|
588
|
+
### Binary Data Handling in Rust
|
|
589
|
+
|
|
590
|
+
```rust
|
|
591
|
+
use tauri_plugin_serialplugin::commands::{open, write_binary, read_binary, close};
|
|
592
|
+
use tauri_plugin_serialplugin::state::{DataBits, FlowControl, Parity, StopBits};
|
|
593
|
+
use tauri::{AppHandle, State};
|
|
594
|
+
|
|
595
|
+
#[tauri::command]
|
|
596
|
+
async fn binary_data_example(
|
|
597
|
+
app: AppHandle<tauri::Wry>,
|
|
598
|
+
serial: State<'_, tauri_plugin_serialplugin::desktop_api::SerialPort<tauri::Wry>>
|
|
599
|
+
) -> Result<(), String> {
|
|
600
|
+
let port_path = "COM1".to_string();
|
|
601
|
+
|
|
602
|
+
// Open port
|
|
603
|
+
open(
|
|
604
|
+
app.clone(),
|
|
605
|
+
serial.clone(),
|
|
606
|
+
port_path.clone(),
|
|
607
|
+
115200u32,
|
|
608
|
+
Some(DataBits::Eight),
|
|
609
|
+
Some(FlowControl::None),
|
|
610
|
+
Some(Parity::None),
|
|
611
|
+
Some(StopBits::One),
|
|
612
|
+
Some(1000u64)
|
|
613
|
+
).map_err(|e| format!("Failed to open port: {}", e))?;
|
|
614
|
+
|
|
615
|
+
// Write binary data
|
|
616
|
+
let binary_data = vec![0x48, 0x65, 0x6C, 0x6C, 0x6F]; // "Hello" in ASCII
|
|
617
|
+
let bytes_written = write_binary(app.clone(), serial.clone(), port_path.clone(), binary_data)
|
|
618
|
+
.map_err(|e| format!("Failed to write binary data: {}", e))?;
|
|
619
|
+
println!("Wrote {} bytes of binary data", bytes_written);
|
|
620
|
+
|
|
621
|
+
// Read binary data
|
|
622
|
+
let received_data = read_binary(
|
|
623
|
+
app.clone(),
|
|
624
|
+
serial.clone(),
|
|
625
|
+
port_path.clone(),
|
|
626
|
+
Some(1000u64), // timeout
|
|
627
|
+
Some(256usize) // max bytes
|
|
628
|
+
).map_err(|e| format!("Failed to read binary data: {}", e))?;
|
|
629
|
+
|
|
630
|
+
println!("Received {} bytes: {:?}", received_data.len(), received_data);
|
|
631
|
+
|
|
632
|
+
// Close port
|
|
633
|
+
close(app, serial, port_path)
|
|
634
|
+
.map_err(|e| format!("Failed to close port: {}", e))?;
|
|
635
|
+
|
|
636
|
+
Ok(())
|
|
637
|
+
}
|
|
638
|
+
```
|
|
639
|
+
|
|
640
|
+
### Using Commands vs Direct API
|
|
641
|
+
|
|
642
|
+
You have two ways to use the plugin in Rust:
|
|
643
|
+
|
|
644
|
+
#### Option 1: Using Commands (Recommended)
|
|
645
|
+
|
|
646
|
+
Import and use the command functions directly. These functions are documented in the [docs.rs documentation](https://docs.rs/tauri-plugin-serialplugin/):
|
|
647
|
+
|
|
648
|
+
```rust
|
|
649
|
+
use tauri_plugin_serialplugin::commands::{available_ports, open, write, read, close};
|
|
650
|
+
use tauri::{AppHandle, State};
|
|
651
|
+
|
|
652
|
+
#[tauri::command]
|
|
653
|
+
async fn my_serial_function(
|
|
654
|
+
app: AppHandle<tauri::Wry>,
|
|
655
|
+
serial: State<'_, tauri_plugin_serialplugin::desktop_api::SerialPort<tauri::Wry>>
|
|
656
|
+
) -> Result<(), String> {
|
|
657
|
+
// Use command functions
|
|
658
|
+
let ports = available_ports(app.clone(), serial.clone())?;
|
|
659
|
+
open(app.clone(), serial.clone(), "COM1".to_string(), 9600, None, None, None, None, None)?;
|
|
660
|
+
// ... rest of your code
|
|
661
|
+
}
|
|
662
|
+
```
|
|
663
|
+
|
|
664
|
+
#### Option 2: Using Direct API
|
|
665
|
+
|
|
666
|
+
Use the SerialPort methods directly:
|
|
667
|
+
|
|
668
|
+
```rust
|
|
669
|
+
use tauri::State;
|
|
670
|
+
use tauri_plugin_serialplugin::desktop_api::SerialPort;
|
|
671
|
+
|
|
672
|
+
#[tauri::command]
|
|
673
|
+
async fn my_serial_function(
|
|
674
|
+
serial: State<'_, SerialPort<tauri::Wry>>
|
|
675
|
+
) -> Result<(), String> {
|
|
676
|
+
// Use serial methods directly
|
|
677
|
+
let ports = serial.available_ports()?;
|
|
678
|
+
// ... rest of your code
|
|
679
|
+
}
|
|
680
|
+
```
|
|
681
|
+
|
|
682
|
+
### Available Rust Types
|
|
683
|
+
|
|
684
|
+
The plugin provides the following Rust types for configuration:
|
|
685
|
+
|
|
686
|
+
```rust
|
|
687
|
+
use tauri_plugin_serialplugin::state::{
|
|
688
|
+
DataBits, // Five, Six, Seven, Eight
|
|
689
|
+
FlowControl, // None, Software, Hardware
|
|
690
|
+
Parity, // None, Odd, Even
|
|
691
|
+
StopBits, // One, Two
|
|
692
|
+
ClearBuffer // Input, Output, All
|
|
693
|
+
};
|
|
694
|
+
```
|
|
695
|
+
|
|
696
|
+
### Complete Command Functions Reference
|
|
697
|
+
|
|
698
|
+
Here are all the available command functions you can import and use. For detailed documentation with examples, see the [docs.rs documentation](https://docs.rs/tauri-plugin-serialplugin/):
|
|
699
|
+
|
|
700
|
+
```rust
|
|
701
|
+
use tauri_plugin_serialplugin::commands::{
|
|
702
|
+
// Port discovery
|
|
703
|
+
available_ports, // Get list of available ports
|
|
704
|
+
available_ports_direct, // Get ports using platform-specific commands
|
|
705
|
+
managed_ports, // Get list of currently managed ports
|
|
706
|
+
|
|
707
|
+
// Connection management
|
|
708
|
+
open, // Open a serial port
|
|
709
|
+
close, // Close a serial port
|
|
710
|
+
close_all, // Close all open ports
|
|
711
|
+
force_close, // Force close a port
|
|
712
|
+
|
|
713
|
+
// Data transfer
|
|
714
|
+
write, // Write string data
|
|
715
|
+
write_binary, // Write binary data
|
|
716
|
+
read, // Read string data
|
|
717
|
+
read_binary, // Read binary data
|
|
718
|
+
|
|
719
|
+
// Listening
|
|
720
|
+
start_listening, // Start listening for data
|
|
721
|
+
stop_listening, // Stop listening
|
|
722
|
+
cancel_read, // Cancel read operations
|
|
723
|
+
|
|
724
|
+
// Port configuration
|
|
725
|
+
set_baud_rate, // Set baud rate
|
|
726
|
+
set_data_bits, // Set data bits
|
|
727
|
+
set_flow_control, // Set flow control
|
|
728
|
+
set_parity, // Set parity
|
|
729
|
+
set_stop_bits, // Set stop bits
|
|
730
|
+
set_timeout, // Set timeout
|
|
731
|
+
|
|
732
|
+
// Control signals
|
|
733
|
+
write_request_to_send, // Set RTS signal
|
|
734
|
+
write_data_terminal_ready, // Set DTR signal
|
|
735
|
+
read_clear_to_send, // Read CTS signal
|
|
736
|
+
read_data_set_ready, // Read DSR signal
|
|
737
|
+
read_ring_indicator, // Read RI signal
|
|
738
|
+
read_carrier_detect, // Read CD signal
|
|
739
|
+
|
|
740
|
+
// Buffer management
|
|
741
|
+
bytes_to_read, // Get bytes available to read
|
|
742
|
+
bytes_to_write, // Get bytes waiting to write
|
|
743
|
+
clear_buffer, // Clear buffers
|
|
744
|
+
|
|
745
|
+
// Break signal
|
|
746
|
+
set_break, // Start break signal
|
|
747
|
+
clear_break, // Stop break signal
|
|
748
|
+
};
|
|
749
|
+
```
|
|
750
|
+
|
|
751
|
+
### Command Function Signatures
|
|
752
|
+
|
|
753
|
+
All command functions follow this pattern:
|
|
754
|
+
|
|
755
|
+
```rust
|
|
756
|
+
pub fn function_name<R: Runtime>(
|
|
757
|
+
app: AppHandle<R>,
|
|
758
|
+
serial: State<'_, SerialPort<R>>,
|
|
759
|
+
// ... additional parameters specific to the function
|
|
760
|
+
) -> Result<ReturnType, Error>
|
|
761
|
+
```
|
|
762
|
+
|
|
763
|
+
For example:
|
|
764
|
+
```rust
|
|
765
|
+
// Open port
|
|
766
|
+
pub fn open<R: Runtime>(
|
|
767
|
+
app: AppHandle<R>,
|
|
768
|
+
serial: State<'_, SerialPort<R>>,
|
|
769
|
+
path: String,
|
|
770
|
+
baud_rate: u32,
|
|
771
|
+
data_bits: Option<DataBits>,
|
|
772
|
+
flow_control: Option<FlowControl>,
|
|
773
|
+
parity: Option<Parity>,
|
|
774
|
+
stop_bits: Option<StopBits>,
|
|
775
|
+
timeout: Option<u64>,
|
|
776
|
+
) -> Result<(), Error>
|
|
777
|
+
|
|
778
|
+
// Write data
|
|
779
|
+
pub fn write<R: Runtime>(
|
|
780
|
+
app: AppHandle<R>,
|
|
781
|
+
serial: State<'_, SerialPort<R>>,
|
|
782
|
+
path: String,
|
|
783
|
+
value: String,
|
|
784
|
+
) -> Result<usize, Error>
|
|
785
|
+
```
|
|
786
|
+
|
|
787
|
+
### Error Messages
|
|
788
|
+
|
|
789
|
+
#### Port Discovery
|
|
790
|
+
- "Failed to lock serialports mutex" - Error acquiring mutex lock when listing ports
|
|
791
|
+
- "Invalid response format" - Invalid response format from plugin
|
|
792
|
+
- "Plugin error: {error}" - Plugin execution error
|
|
793
|
+
|
|
794
|
+
#### Port Management
|
|
795
|
+
- "Failed to acquire lock: {error}" - Error acquiring mutex lock
|
|
796
|
+
- "Port '{path}' not found" - Port does not exist
|
|
797
|
+
- "Serial port {path} is not open!" - Port is not open
|
|
798
|
+
- "Failed to open serial port: {error}" - Error opening port
|
|
799
|
+
- "Failed to clone serial port: {error}" - Error cloning port
|
|
800
|
+
- "Failed to set short timeout: {error}" - Error setting timeout
|
|
801
|
+
- "Failed to stop existing listener: {error}" - Error stopping existing listener
|
|
802
|
+
- "Failed to join thread: {error}" - Error waiting for thread completion
|
|
803
|
+
- "Failed to cancel serial port data reading: {error}" - Error canceling data reading
|
|
804
|
+
|
|
805
|
+
#### Data Operations
|
|
806
|
+
- "Failed to write data: {error}" - Error writing data
|
|
807
|
+
- "Failed to write binary data: {error}" - Error writing binary data
|
|
808
|
+
- "Failed to read data: {error}" - Error reading data
|
|
809
|
+
- "no data received within {timeout} ms" - Read timeout
|
|
810
|
+
- "Failed to set timeout: {error}" - Error setting timeout
|
|
811
|
+
|
|
812
|
+
#### Port Configuration
|
|
813
|
+
- "Failed to set baud rate: {error}" - Error setting baud rate
|
|
814
|
+
- "Failed to set data bits: {error}" - Error setting data bits
|
|
815
|
+
- "Failed to set flow control: {error}" - Error setting flow control
|
|
816
|
+
- "Failed to set parity: {error}" - Error setting parity
|
|
817
|
+
- "Failed to set stop bits: {error}" - Error setting stop bits
|
|
818
|
+
|
|
819
|
+
#### Control Signals
|
|
820
|
+
- "Failed to set RTS: {error}" - Error setting RTS signal
|
|
821
|
+
- "Failed to set DTR: {error}" - Error setting DTR signal
|
|
822
|
+
- "Failed to read CTS: {error}" - Error reading CTS signal
|
|
823
|
+
- "Failed to read DSR: {error}" - Error reading DSR signal
|
|
824
|
+
- "Failed to read RI: {error}" - Error reading RI signal
|
|
825
|
+
- "Failed to read CD: {error}" - Error reading CD signal
|
|
826
|
+
- "Failed to set break: {error}" - Error setting break signal
|
|
827
|
+
- "Failed to clear break: {error}" - Error clearing break signal
|
|
828
|
+
|
|
829
|
+
#### Buffer Management
|
|
830
|
+
- "Failed to clear buffer: {error}" - Error clearing buffer
|
|
831
|
+
- "Failed to get bytes to read: {error}" - Error getting bytes available to read
|
|
832
|
+
- "Failed to get bytes to write: {error}" - Error getting bytes waiting to write
|
|
833
|
+
|
|
834
|
+
---
|
|
835
|
+
|
|
836
|
+
## Permissions
|
|
837
|
+
|
|
838
|
+
Below is a list of all permissions the plugin supports. Granting or denying them allows fine-grained control over what your application can do with serial ports.
|
|
839
|
+
|
|
840
|
+
| Permission | Description |
|
|
841
|
+
|---------------------------------------------|-------------------------------------------------------------------------------|
|
|
842
|
+
| `serialplugin:allow-available-ports` | Allows listing of available serial ports |
|
|
843
|
+
| `serialplugin:deny-available-ports` | Denies listing of available serial ports |
|
|
844
|
+
| `serialplugin:allow-cancel-read` | Allows canceling of read operations |
|
|
845
|
+
| `serialplugin:deny-cancel-read` | Denies canceling of read operations |
|
|
846
|
+
| `serialplugin:allow-close` | Allows closing of serial ports |
|
|
847
|
+
| `serialplugin:deny-close` | Denies closing of serial ports |
|
|
848
|
+
| `serialplugin:allow-close-all` | Allows closing of all open serial ports |
|
|
849
|
+
| `serialplugin:deny-close-all` | Denies closing of all open serial ports |
|
|
850
|
+
| `serialplugin:allow-force-close` | Allows forcefully closing of serial ports |
|
|
851
|
+
| `serialplugin:deny-force-close` | Denies forcefully closing of serial ports |
|
|
852
|
+
| `serialplugin:allow-open` | Allows opening of serial ports |
|
|
853
|
+
| `serialplugin:deny-open` | Denies opening of serial ports |
|
|
854
|
+
| `serialplugin:allow-read` | Allows reading data from serial ports |
|
|
855
|
+
| `serialplugin:deny-read` | Denies reading data from serial ports |
|
|
856
|
+
| `serialplugin:allow-read-binary` | Allows reading binary data from serial ports |
|
|
857
|
+
| `serialplugin:deny-read-binary` | Denies reading binary data from serial ports |
|
|
858
|
+
| `serialplugin:allow-write` | Allows writing data to serial ports |
|
|
859
|
+
| `serialplugin:deny-write` | Denies writing data to serial ports |
|
|
860
|
+
| `serialplugin:allow-write-binary` | Allows writing binary data to serial ports |
|
|
861
|
+
| `serialplugin:deny-write-binary` | Denies writing binary data to serial ports |
|
|
862
|
+
| `serialplugin:allow-available-ports-direct` | Enables the `available_ports_direct` command without any pre-configured scope |
|
|
863
|
+
| `serialplugin:deny-available-ports-direct` | Denies the `available_ports_direct` command without any pre-configured scope |
|
|
864
|
+
| `serialplugin:allow-set-baud-rate` | Allows changing the baud rate of serial ports |
|
|
865
|
+
| `serialplugin:deny-set-baud-rate` | Denies changing the baud rate of serial ports |
|
|
866
|
+
| `serialplugin:allow-set-data-bits` | Allows changing the data bits configuration |
|
|
867
|
+
| `serialplugin:deny-set-data-bits` | Denies changing the data bits configuration |
|
|
868
|
+
| `serialplugin:allow-set-flow-control` | Allows changing the flow control mode |
|
|
869
|
+
| `serialplugin:deny-set-flow-control` | Denies changing the flow control mode |
|
|
870
|
+
| `serialplugin:allow-set-parity` | Allows changing the parity checking mode |
|
|
871
|
+
| `serialplugin:deny-set-parity` | Denies changing the parity checking mode |
|
|
872
|
+
| `serialplugin:allow-set-stop-bits` | Allows changing the stop bits configuration |
|
|
873
|
+
| `serialplugin:deny-set-stop-bits` | Denies changing the stop bits configuration |
|
|
874
|
+
| `serialplugin:allow-set-timeout` | Allows changing the timeout duration |
|
|
875
|
+
| `serialplugin:deny-set-timeout` | Denies changing the timeout duration |
|
|
876
|
+
| `serialplugin:allow-write-rts` | Allows setting the RTS (Request To Send) control signal |
|
|
877
|
+
| `serialplugin:deny-write-rts` | Denies setting the RTS control signal |
|
|
878
|
+
| `serialplugin:allow-write-dtr` | Allows setting the DTR (Data Terminal Ready) control signal |
|
|
879
|
+
| `serialplugin:deny-write-dtr` | Denies setting the DTR control signal |
|
|
880
|
+
| `serialplugin:allow-read-cts` | Allows reading the CTS (Clear To Send) control signal state |
|
|
881
|
+
| `serialplugin:deny-read-cts` | Denies reading the CTS control signal state |
|
|
882
|
+
| `serialplugin:allow-read-dsr` | Allows reading the DSR (Data Set Ready) control signal state |
|
|
883
|
+
| `serialplugin:deny-read-dsr` | Denies reading the DSR control signal state |
|
|
884
|
+
| `serialplugin:allow-read-ri` | Allows reading the RI (Ring Indicator) control signal state |
|
|
885
|
+
| `serialplugin:deny-read-ri` | Denies reading the RI control signal state |
|
|
886
|
+
| `serialplugin:allow-read-cd` | Allows reading the CD (Carrier Detect) control signal state |
|
|
887
|
+
| `serialplugin:deny-read-cd` | Denies reading the CD control signal state |
|
|
888
|
+
| `serialplugin:allow-bytes-to-read` | Allows checking the number of bytes available to read |
|
|
889
|
+
| `serialplugin:deny-bytes-to-read` | Denies checking the number of bytes available to read |
|
|
890
|
+
| `serialplugin:allow-bytes-to-write` | Allows checking the number of bytes waiting to be written |
|
|
891
|
+
| `serialplugin:deny-bytes-to-write` | Denies checking the number of bytes waiting to be written |
|
|
892
|
+
| `serialplugin:allow-clear-buffer` | Allows clearing input/output buffers |
|
|
893
|
+
| `serialplugin:deny-clear-buffer` | Denies clearing input/output buffers |
|
|
894
|
+
| `serialplugin:allow-set-break` | Allows starting break signal transmission |
|
|
895
|
+
| `serialplugin:deny-set-break` | Denies starting break signal transmission |
|
|
896
|
+
| `serialplugin:allow-clear-break` | Allows stopping break signal transmission |
|
|
897
|
+
| `serialplugin:deny-clear-break` | Denies stopping break signal transmission |
|
|
898
|
+
| `serialplugin:allow-start-listening` | Allows starting automatic port monitoring and data listening |
|
|
899
|
+
| `serialplugin:deny-start-listening` | Denies starting automatic port monitoring and data listening |
|
|
900
|
+
| `serialplugin:allow-stop-listening` | Allows stopping automatic port monitoring and data listening |
|
|
901
|
+
| `serialplugin:deny-stop-listening` | Denies stopping automatic port monitoring and data listening |
|
|
902
|
+
|
|
903
|
+
### Granting All Permissions (Example)
|
|
904
|
+
|
|
905
|
+
```jsonc
|
|
906
|
+
"permissions": [
|
|
907
|
+
"core:default",
|
|
908
|
+
"serialplugin:default",
|
|
909
|
+
"serialplugin:allow-available-ports",
|
|
910
|
+
"serialplugin:allow-cancel-read",
|
|
911
|
+
"serialplugin:allow-close",
|
|
912
|
+
"serialplugin:allow-close-all",
|
|
913
|
+
"serialplugin:allow-force-close",
|
|
914
|
+
"serialplugin:allow-open",
|
|
915
|
+
"serialplugin:allow-read",
|
|
916
|
+
"serialplugin:allow-write",
|
|
917
|
+
"serialplugin:allow-write-binary",
|
|
918
|
+
"serialplugin:allow-available-ports-direct",
|
|
919
|
+
"serialplugin:allow-set-baud-rate",
|
|
920
|
+
"serialplugin:allow-set-data-bits",
|
|
921
|
+
"serialplugin:allow-set-flow-control",
|
|
922
|
+
"serialplugin:allow-set-parity",
|
|
923
|
+
"serialplugin:allow-set-stop-bits",
|
|
924
|
+
"serialplugin:allow-set-timeout",
|
|
925
|
+
"serialplugin:allow-write-rts",
|
|
926
|
+
"serialplugin:allow-write-dtr",
|
|
927
|
+
"serialplugin:allow-read-cts",
|
|
928
|
+
"serialplugin:allow-read-dsr",
|
|
929
|
+
"serialplugin:allow-read-ri",
|
|
930
|
+
"serialplugin:allow-read-cd",
|
|
931
|
+
"serialplugin:allow-bytes-to-read",
|
|
932
|
+
"serialplugin:allow-bytes-to-write",
|
|
933
|
+
"serialplugin:allow-clear-buffer",
|
|
934
|
+
"serialplugin:allow-set-break",
|
|
935
|
+
"serialplugin:allow-clear-break",
|
|
936
|
+
"serialplugin:allow-start-listening",
|
|
937
|
+
"serialplugin:allow-stop-listening"
|
|
938
|
+
]
|
|
939
|
+
```
|
|
940
|
+
|
|
941
|
+
---
|
|
942
|
+
|
|
943
|
+
## API Reference
|
|
944
|
+
|
|
945
|
+
### Port Discovery
|
|
946
|
+
|
|
947
|
+
```typescript
|
|
948
|
+
class SerialPort {
|
|
949
|
+
/**
|
|
950
|
+
* Lists all available serial ports on the system
|
|
951
|
+
* @returns {Promise<{[key: string]: PortInfo}>} Map of port names to port information
|
|
952
|
+
* @example
|
|
953
|
+
* const ports = await SerialPort.available_ports();
|
|
954
|
+
* console.log(ports);
|
|
955
|
+
*/
|
|
956
|
+
static async available_ports(): Promise<{ [key: string]: PortInfo }>;
|
|
957
|
+
|
|
958
|
+
/**
|
|
959
|
+
* Lists ports using platform-specific commands for enhanced detection
|
|
960
|
+
* @returns {Promise<{[key: string]: PortInfo}>} Map of port names to port information
|
|
961
|
+
* @example
|
|
962
|
+
* const ports = await SerialPort.available_ports_direct();
|
|
963
|
+
*/
|
|
964
|
+
static async available_ports_direct(): Promise<{ [key: string]: PortInfo }>;
|
|
965
|
+
|
|
966
|
+
/**
|
|
967
|
+
* @description Lists all managed serial ports (ports that are currently open and managed by the application).
|
|
968
|
+
* @returns {Promise<string[]>} A promise that resolves to an array of port paths (names).
|
|
969
|
+
*/
|
|
970
|
+
static async managed_ports(): Promise<string[]>;
|
|
971
|
+
}
|
|
972
|
+
```
|
|
973
|
+
|
|
974
|
+
### Connection Management
|
|
975
|
+
|
|
976
|
+
```typescript
|
|
977
|
+
class SerialPort {
|
|
978
|
+
/**
|
|
979
|
+
* Opens the serial port with specified configuration
|
|
980
|
+
* @returns {Promise<void>}
|
|
981
|
+
* @throws {Error} If port is already open or invalid configuration
|
|
982
|
+
* @example
|
|
983
|
+
* const port = new SerialPort({ path: "COM1", baudRate: 9600 });
|
|
984
|
+
* await port.open();
|
|
985
|
+
*/
|
|
986
|
+
async open(): Promise<void>;
|
|
987
|
+
|
|
988
|
+
/**
|
|
989
|
+
* Closes the serial port connection
|
|
990
|
+
* @returns {Promise<void>}
|
|
991
|
+
* @throws {Error} If port is not open
|
|
992
|
+
* @example
|
|
993
|
+
* await port.close();
|
|
994
|
+
*/
|
|
995
|
+
async close(): Promise<void>;
|
|
996
|
+
|
|
997
|
+
/**
|
|
998
|
+
* Starts listening for data on the serial port
|
|
999
|
+
* @returns {Promise<void>} A promise that resolves when listening starts
|
|
1000
|
+
* @throws {Error} If starting listener fails or port is not open
|
|
1001
|
+
* @example
|
|
1002
|
+
* await port.startListening();
|
|
1003
|
+
*
|
|
1004
|
+
* // Listen for data events
|
|
1005
|
+
* port.listen((data) => {
|
|
1006
|
+
* console.log("Data received:", data);
|
|
1007
|
+
* });
|
|
1008
|
+
*/
|
|
1009
|
+
async startListening(): Promise<void>;
|
|
1010
|
+
|
|
1011
|
+
/**
|
|
1012
|
+
* Stops listening for data on the serial port
|
|
1013
|
+
* @returns {Promise<void>} A promise that resolves when listening stops
|
|
1014
|
+
* @throws {Error} If stopping listener fails or port is not open
|
|
1015
|
+
* @example
|
|
1016
|
+
* await port.stopListening();
|
|
1017
|
+
*/
|
|
1018
|
+
async stopListening(): Promise<void>;
|
|
1019
|
+
|
|
1020
|
+
/**
|
|
1021
|
+
* Forces a serial port to close regardless of its state
|
|
1022
|
+
* @param {string} path Port path to force close
|
|
1023
|
+
* @returns {Promise<void>}
|
|
1024
|
+
* @example
|
|
1025
|
+
* await SerialPort.forceClose("COM1");
|
|
1026
|
+
*/
|
|
1027
|
+
static async forceClose(path: string): Promise<void>;
|
|
1028
|
+
|
|
1029
|
+
/**
|
|
1030
|
+
* Closes all open serial port connections
|
|
1031
|
+
* @returns {Promise<void>}
|
|
1032
|
+
* @example
|
|
1033
|
+
* await SerialPort.closeAll();
|
|
1034
|
+
*/
|
|
1035
|
+
static async closeAll(): Promise<void>;
|
|
1036
|
+
}
|
|
1037
|
+
```
|
|
1038
|
+
|
|
1039
|
+
### Data Transfer
|
|
1040
|
+
|
|
1041
|
+
```typescript
|
|
1042
|
+
class SerialPort {
|
|
1043
|
+
/**
|
|
1044
|
+
* Writes string data to the serial port
|
|
1045
|
+
* @param {string} data Data to write
|
|
1046
|
+
* @returns {Promise<number>} Number of bytes written
|
|
1047
|
+
* @throws {Error} If write fails or port is not open
|
|
1048
|
+
* @example
|
|
1049
|
+
* const bytesWritten = await port.write("Hello");
|
|
1050
|
+
*/
|
|
1051
|
+
async write(data: string): Promise<number>;
|
|
1052
|
+
|
|
1053
|
+
/**
|
|
1054
|
+
* Reads data from the serial port
|
|
1055
|
+
* @param {ReadOptions} [options] Read options
|
|
1056
|
+
* @returns {Promise<string>} A promise that resolves to a string
|
|
1057
|
+
*/
|
|
1058
|
+
async read(options?: ReadOptions): Promise<string>;
|
|
1059
|
+
|
|
1060
|
+
/**
|
|
1061
|
+
* Reads binary data from the serial port
|
|
1062
|
+
* @param {ReadOptions} [options] Read options
|
|
1063
|
+
* @returns {Promise<Uint8Array>} A promise that resolves with binary data
|
|
1064
|
+
*/
|
|
1065
|
+
async readBinary(options?: ReadOptions): Promise<Uint8Array>;
|
|
1066
|
+
|
|
1067
|
+
/**
|
|
1068
|
+
* Writes binary data to the serial port
|
|
1069
|
+
* @param {Uint8Array | number[]} data Binary data to write
|
|
1070
|
+
* @returns {Promise<number>} Number of bytes written
|
|
1071
|
+
* @throws {Error} If write fails or port is not open
|
|
1072
|
+
* @example
|
|
1073
|
+
* const data = new Uint8Array([0x01, 0x02, 0x03]);
|
|
1074
|
+
* const bytesWritten = await port.writeBinary(data);
|
|
1075
|
+
*/
|
|
1076
|
+
async writeBinary(data: Uint8Array | number[]): Promise<number>;
|
|
1077
|
+
|
|
1078
|
+
/**
|
|
1079
|
+
* Sets up a listener for incoming data
|
|
1080
|
+
* @param {(data: string | Uint8Array) => void} callback Function to handle received data
|
|
1081
|
+
* @param {boolean} [decode=true] Whether to decode data as string (true) or return raw bytes (false)
|
|
1082
|
+
* @returns {Promise<UnlistenFn>} A promise that resolves to an unlisten function
|
|
1083
|
+
* @example
|
|
1084
|
+
* const unsubscribe = await port.listen((data) => {
|
|
1085
|
+
* console.log("Received:", data);
|
|
1086
|
+
* });
|
|
1087
|
+
*
|
|
1088
|
+
* // Later, to stop listening:
|
|
1089
|
+
* unsubscribe();
|
|
1090
|
+
*/
|
|
1091
|
+
async listen(callback: (data: string | Uint8Array) => void, decode?: boolean): Promise<UnlistenFn>;
|
|
1092
|
+
|
|
1093
|
+
/**
|
|
1094
|
+
* Cancels listening for serial port data (does not affect disconnect listeners)
|
|
1095
|
+
* @returns {Promise<void>} A promise that resolves when listening is cancelled
|
|
1096
|
+
* @example
|
|
1097
|
+
* await port.cancelListen();
|
|
1098
|
+
*/
|
|
1099
|
+
async cancelListen(): Promise<void>;
|
|
1100
|
+
}
|
|
1101
|
+
```
|
|
1102
|
+
|
|
1103
|
+
### Port Configuration
|
|
1104
|
+
|
|
1105
|
+
```typescript
|
|
1106
|
+
class SerialPort {
|
|
1107
|
+
/**
|
|
1108
|
+
* Sets the baud rate
|
|
1109
|
+
* @param {number} baudRate Speed in bits per second
|
|
1110
|
+
* @returns {Promise<void>}
|
|
1111
|
+
* @example
|
|
1112
|
+
* await port.setBaudRate(115200);
|
|
1113
|
+
*/
|
|
1114
|
+
async setBaudRate(baudRate: number): Promise<void>;
|
|
1115
|
+
|
|
1116
|
+
/**
|
|
1117
|
+
* Sets the number of data bits
|
|
1118
|
+
* @param {DataBits} dataBits Number of bits per character (5-8)
|
|
1119
|
+
* @returns {Promise<void>}
|
|
1120
|
+
* @example
|
|
1121
|
+
* await port.setDataBits(DataBits.Eight);
|
|
1122
|
+
*/
|
|
1123
|
+
async setDataBits(dataBits: DataBits): Promise<void>;
|
|
1124
|
+
|
|
1125
|
+
/**
|
|
1126
|
+
* Sets the flow control mode
|
|
1127
|
+
* @param {FlowControl} flowControl Flow control setting
|
|
1128
|
+
* @returns {Promise<void>}
|
|
1129
|
+
* @example
|
|
1130
|
+
* await port.setFlowControl(FlowControl.Hardware);
|
|
1131
|
+
*/
|
|
1132
|
+
async setFlowControl(flowControl: FlowControl): Promise<void>;
|
|
1133
|
+
|
|
1134
|
+
/**
|
|
1135
|
+
* Sets the parity checking mode
|
|
1136
|
+
* @param {Parity} parity Parity checking mode
|
|
1137
|
+
* @returns {Promise<void>}
|
|
1138
|
+
* @example
|
|
1139
|
+
* await port.setParity(Parity.None);
|
|
1140
|
+
*/
|
|
1141
|
+
async setParity(parity: Parity): Promise<void>;
|
|
1142
|
+
|
|
1143
|
+
/**
|
|
1144
|
+
* Sets the number of stop bits
|
|
1145
|
+
* @param {StopBits} stopBits Number of stop bits
|
|
1146
|
+
* @returns {Promise<void>}
|
|
1147
|
+
* @example
|
|
1148
|
+
* await port.setStopBits(StopBits.One);
|
|
1149
|
+
*/
|
|
1150
|
+
async setStopBits(stopBits: StopBits): Promise<void>;
|
|
1151
|
+
|
|
1152
|
+
/**
|
|
1153
|
+
* Sets the timeout for read operations
|
|
1154
|
+
* @param {number} timeout Timeout value in milliseconds
|
|
1155
|
+
* @returns {Promise<void>}
|
|
1156
|
+
* @example
|
|
1157
|
+
* await port.setTimeout(1000);
|
|
1158
|
+
*/
|
|
1159
|
+
async setTimeout(timeout: number): Promise<void>;
|
|
1160
|
+
}
|
|
1161
|
+
```
|
|
1162
|
+
|
|
1163
|
+
### Control Signals
|
|
1164
|
+
|
|
1165
|
+
```typescript
|
|
1166
|
+
class SerialPort {
|
|
1167
|
+
/**
|
|
1168
|
+
* Sets the RTS (Request to Send) signal
|
|
1169
|
+
* @param {boolean} level Signal level (true = high, false = low)
|
|
1170
|
+
* @returns {Promise<void>}
|
|
1171
|
+
* @example
|
|
1172
|
+
* await port.writeRequestToSend(true);
|
|
1173
|
+
*/
|
|
1174
|
+
async writeRequestToSend(level: boolean): Promise<void>;
|
|
1175
|
+
|
|
1176
|
+
/**
|
|
1177
|
+
* Sets the DTR (Data Terminal Ready) signal
|
|
1178
|
+
* @param {boolean} level Signal level (true = high, false = low)
|
|
1179
|
+
* @returns {Promise<void>}
|
|
1180
|
+
* @example
|
|
1181
|
+
* await port.writeDataTerminalReady(true);
|
|
1182
|
+
*/
|
|
1183
|
+
async writeDataTerminalReady(level: boolean): Promise<void>;
|
|
1184
|
+
|
|
1185
|
+
/**
|
|
1186
|
+
* Alternative method to set RTS signal
|
|
1187
|
+
* @param {boolean} value Signal level (true = high, false = low)
|
|
1188
|
+
* @returns {Promise<void>}
|
|
1189
|
+
* @example
|
|
1190
|
+
* await port.setRequestToSend(true);
|
|
1191
|
+
*/
|
|
1192
|
+
async setRequestToSend(value: boolean): Promise<void>;
|
|
1193
|
+
|
|
1194
|
+
/**
|
|
1195
|
+
* Alternative method to set DTR signal
|
|
1196
|
+
* @param {boolean} value Signal level (true = high, false = low)
|
|
1197
|
+
* @returns {Promise<void>}
|
|
1198
|
+
* @example
|
|
1199
|
+
* await port.setDataTerminalReady(true);
|
|
1200
|
+
*/
|
|
1201
|
+
async setDataTerminalReady(value: boolean): Promise<void>;
|
|
1202
|
+
|
|
1203
|
+
/**
|
|
1204
|
+
* Reads the CTS (Clear to Send) signal state
|
|
1205
|
+
* @returns {Promise<boolean>} Signal state
|
|
1206
|
+
* @example
|
|
1207
|
+
* const cts = await port.readClearToSend();
|
|
1208
|
+
*/
|
|
1209
|
+
async readClearToSend(): Promise<boolean>;
|
|
1210
|
+
|
|
1211
|
+
/**
|
|
1212
|
+
* Reads the DSR (Data Set Ready) signal state
|
|
1213
|
+
* @returns {Promise<boolean>} Signal state
|
|
1214
|
+
* @example
|
|
1215
|
+
* const dsr = await port.readDataSetReady();
|
|
1216
|
+
*/
|
|
1217
|
+
async readDataSetReady(): Promise<boolean>;
|
|
1218
|
+
|
|
1219
|
+
/**
|
|
1220
|
+
* Reads the RI (Ring Indicator) signal state
|
|
1221
|
+
* @returns {Promise<boolean>} Signal state
|
|
1222
|
+
* @example
|
|
1223
|
+
* const ri = await port.readRingIndicator();
|
|
1224
|
+
*/
|
|
1225
|
+
async readRingIndicator(): Promise<boolean>;
|
|
1226
|
+
|
|
1227
|
+
/**
|
|
1228
|
+
* Reads the CD (Carrier Detect) signal state
|
|
1229
|
+
* @returns {Promise<boolean>} Signal state
|
|
1230
|
+
* @example
|
|
1231
|
+
* const cd = await port.readCarrierDetect();
|
|
1232
|
+
*/
|
|
1233
|
+
async readCarrierDetect(): Promise<boolean>;
|
|
1234
|
+
}
|
|
1235
|
+
```
|
|
1236
|
+
|
|
1237
|
+
### Buffer Management
|
|
1238
|
+
|
|
1239
|
+
```typescript
|
|
1240
|
+
class SerialPort {
|
|
1241
|
+
/**
|
|
1242
|
+
* Gets number of bytes available to read
|
|
1243
|
+
* @returns {Promise<number>} Number of bytes in read buffer
|
|
1244
|
+
* @example
|
|
1245
|
+
* const available = await port.bytesToRead();
|
|
1246
|
+
*/
|
|
1247
|
+
async bytesToRead(): Promise<number>;
|
|
1248
|
+
|
|
1249
|
+
/**
|
|
1250
|
+
* Gets number of bytes waiting to be written
|
|
1251
|
+
* @returns {Promise<number>} Number of bytes in write buffer
|
|
1252
|
+
* @example
|
|
1253
|
+
* const pending = await port.bytesToWrite();
|
|
1254
|
+
*/
|
|
1255
|
+
async bytesToWrite(): Promise<number>;
|
|
1256
|
+
|
|
1257
|
+
/**
|
|
1258
|
+
* Clears the specified buffer
|
|
1259
|
+
* @param {ClearBuffer} buffer Buffer to clear
|
|
1260
|
+
* @returns {Promise<void>}
|
|
1261
|
+
* @example
|
|
1262
|
+
* await port.clearBuffer(ClearBuffer.Input);
|
|
1263
|
+
*/
|
|
1264
|
+
async clearBuffer(buffer: ClearBuffer): Promise<void>;
|
|
1265
|
+
|
|
1266
|
+
/**
|
|
1267
|
+
* Sets the break signal
|
|
1268
|
+
* @returns {Promise<void>}
|
|
1269
|
+
* @example
|
|
1270
|
+
* await port.setBreak();
|
|
1271
|
+
*/
|
|
1272
|
+
async setBreak(): Promise<void>;
|
|
1273
|
+
|
|
1274
|
+
/**
|
|
1275
|
+
* Clears the break signal
|
|
1276
|
+
* @returns {Promise<void>}
|
|
1277
|
+
* @example
|
|
1278
|
+
* await port.clearBreak();
|
|
1279
|
+
*/
|
|
1280
|
+
async clearBreak(): Promise<void>;
|
|
1281
|
+
}
|
|
1282
|
+
```
|
|
1283
|
+
|
|
1284
|
+
### Auto-Reconnect Management
|
|
1285
|
+
|
|
1286
|
+
```typescript
|
|
1287
|
+
class SerialPort {
|
|
1288
|
+
/**
|
|
1289
|
+
* Enables auto-reconnect functionality
|
|
1290
|
+
* @param {Object} options Auto-reconnect configuration options
|
|
1291
|
+
* @param {number} [options.interval=5000] Reconnection interval in milliseconds
|
|
1292
|
+
* @param {number | null} [options.maxAttempts=10] Maximum number of reconnection attempts (null for infinite)
|
|
1293
|
+
* @param {Function} [options.onReconnect] Callback function called on each reconnection attempt
|
|
1294
|
+
* @returns {Promise<void>}
|
|
1295
|
+
* @example
|
|
1296
|
+
* await port.enableAutoReconnect({
|
|
1297
|
+
* interval: 3000,
|
|
1298
|
+
* maxAttempts: 5,
|
|
1299
|
+
* onReconnect: (success, attempt) => {
|
|
1300
|
+
* console.log(`Reconnect attempt ${attempt}: ${success ? 'success' : 'failed'}`);
|
|
1301
|
+
* }
|
|
1302
|
+
* });
|
|
1303
|
+
*/
|
|
1304
|
+
async enableAutoReconnect(options?: {
|
|
1305
|
+
interval?: number;
|
|
1306
|
+
maxAttempts?: number | null;
|
|
1307
|
+
onReconnect?: (success: boolean, attempt: number) => void;
|
|
1308
|
+
}): Promise<void>;
|
|
1309
|
+
|
|
1310
|
+
/**
|
|
1311
|
+
* Disables auto-reconnect functionality
|
|
1312
|
+
* @returns {Promise<void>}
|
|
1313
|
+
* @example
|
|
1314
|
+
* await port.disableAutoReconnect();
|
|
1315
|
+
*/
|
|
1316
|
+
async disableAutoReconnect(): Promise<void>;
|
|
1317
|
+
|
|
1318
|
+
/**
|
|
1319
|
+
* Gets auto-reconnect status and configuration
|
|
1320
|
+
* @returns {Object} Auto-reconnect information
|
|
1321
|
+
* @example
|
|
1322
|
+
* const info = port.getAutoReconnectInfo();
|
|
1323
|
+
* console.log('Auto-reconnect enabled:', info.enabled);
|
|
1324
|
+
* console.log('Current attempts:', info.currentAttempts);
|
|
1325
|
+
*/
|
|
1326
|
+
getAutoReconnectInfo(): {
|
|
1327
|
+
enabled: boolean;
|
|
1328
|
+
interval: number;
|
|
1329
|
+
maxAttempts: number | null;
|
|
1330
|
+
currentAttempts: number;
|
|
1331
|
+
hasCallback: boolean;
|
|
1332
|
+
};
|
|
1333
|
+
|
|
1334
|
+
/**
|
|
1335
|
+
* Manually triggers a reconnection attempt
|
|
1336
|
+
* @returns {Promise<boolean>} A promise that resolves to true if reconnection was successful
|
|
1337
|
+
* @example
|
|
1338
|
+
* const success = await port.manualReconnect();
|
|
1339
|
+
* if (success) {
|
|
1340
|
+
* console.log('Manual reconnection successful');
|
|
1341
|
+
* }
|
|
1342
|
+
*/
|
|
1343
|
+
async manualReconnect(): Promise<boolean>;
|
|
1344
|
+
}
|
|
1345
|
+
```
|
|
1346
|
+
|
|
1347
|
+
## Common Use Cases
|
|
1348
|
+
|
|
1349
|
+
### Reading Sensor Data
|
|
1350
|
+
|
|
1351
|
+
```typescript
|
|
1352
|
+
const port = new SerialPort({
|
|
1353
|
+
path: "COM1",
|
|
1354
|
+
baudRate: 9600
|
|
1355
|
+
});
|
|
1356
|
+
|
|
1357
|
+
await port.open();
|
|
1358
|
+
await port.listen((data) => {
|
|
1359
|
+
const sensorValue = parseFloat(data);
|
|
1360
|
+
console.log("Sensor reading:", sensorValue);
|
|
1361
|
+
});
|
|
1362
|
+
```
|
|
1363
|
+
|
|
1364
|
+
### Binary Protocol Communication
|
|
1365
|
+
|
|
1366
|
+
```typescript
|
|
1367
|
+
const port = new SerialPort({
|
|
1368
|
+
path: "COM1",
|
|
1369
|
+
baudRate: 115200
|
|
1370
|
+
});
|
|
1371
|
+
|
|
1372
|
+
await port.open();
|
|
1373
|
+
|
|
1374
|
+
// Send command
|
|
1375
|
+
const command = new Uint8Array([0x02, 0x01, 0x03]);
|
|
1376
|
+
await port.writeBinary(command);
|
|
1377
|
+
|
|
1378
|
+
// Read response (raw bytes)
|
|
1379
|
+
await port.listen((data) => {
|
|
1380
|
+
const response = data instanceof Uint8Array ? data : new Uint8Array();
|
|
1381
|
+
console.log("Response:", response);
|
|
1382
|
+
}, false);
|
|
1383
|
+
```
|
|
1384
|
+
|
|
1385
|
+
### Modbus Communication
|
|
1386
|
+
|
|
1387
|
+
```typescript
|
|
1388
|
+
const port = new SerialPort({
|
|
1389
|
+
path: "COM1",
|
|
1390
|
+
baudRate: 9600,
|
|
1391
|
+
dataBits: DataBits.Eight,
|
|
1392
|
+
stopBits: StopBits.One,
|
|
1393
|
+
parity: Parity.None
|
|
1394
|
+
});
|
|
1395
|
+
|
|
1396
|
+
await port.open();
|
|
1397
|
+
|
|
1398
|
+
function createModbusRequest(address: number, length: number): Uint8Array {
|
|
1399
|
+
return new Uint8Array([
|
|
1400
|
+
0x01, // Device ID
|
|
1401
|
+
0x03, // Function code: Read Holding Registers
|
|
1402
|
+
address >> 8, address & 0xFF,
|
|
1403
|
+
length >> 8, length & 0xFF
|
|
1404
|
+
]);
|
|
1405
|
+
}
|
|
1406
|
+
|
|
1407
|
+
// Send Modbus request
|
|
1408
|
+
const request = createModbusRequest(0x1000, 10);
|
|
1409
|
+
await port.writeBinary(request);
|
|
1410
|
+
```
|
|
1411
|
+
|
|
1412
|
+
### Auto-Reconnect for Reliable Communication
|
|
1413
|
+
|
|
1414
|
+
```typescript
|
|
1415
|
+
const port = new SerialPort({
|
|
1416
|
+
path: "COM1",
|
|
1417
|
+
baudRate: 9600
|
|
1418
|
+
});
|
|
1419
|
+
|
|
1420
|
+
await port.open();
|
|
1421
|
+
|
|
1422
|
+
// Enable auto-reconnect with custom settings
|
|
1423
|
+
await port.enableAutoReconnect({
|
|
1424
|
+
interval: 3000, // Try to reconnect every 3 seconds
|
|
1425
|
+
maxAttempts: 5, // Maximum 5 attempts
|
|
1426
|
+
onReconnect: (success, attempt) => {
|
|
1427
|
+
if (success) {
|
|
1428
|
+
console.log(`Reconnected successfully on attempt ${attempt}`);
|
|
1429
|
+
} else {
|
|
1430
|
+
console.log(`Reconnection attempt ${attempt} failed`);
|
|
1431
|
+
}
|
|
1432
|
+
}
|
|
1433
|
+
});
|
|
1434
|
+
|
|
1435
|
+
// Set up data listener
|
|
1436
|
+
const unsubscribe = await port.listen((data) => {
|
|
1437
|
+
console.log("Received data:", data);
|
|
1438
|
+
});
|
|
1439
|
+
|
|
1440
|
+
// The port will automatically reconnect if disconnected
|
|
1441
|
+
// You can also manually trigger reconnection
|
|
1442
|
+
const success = await port.manualReconnect();
|
|
1443
|
+
if (success) {
|
|
1444
|
+
console.log("Manual reconnection successful");
|
|
1445
|
+
}
|
|
1446
|
+
|
|
1447
|
+
// Check auto-reconnect status
|
|
1448
|
+
const info = port.getAutoReconnectInfo();
|
|
1449
|
+
console.log("Auto-reconnect enabled:", info.enabled);
|
|
1450
|
+
console.log("Current attempts:", info.currentAttempts);
|
|
1451
|
+
|
|
1452
|
+
// Disable auto-reconnect when no longer needed
|
|
1453
|
+
await port.disableAutoReconnect();
|
|
1454
|
+
```
|
|
1455
|
+
|
|
1456
|
+
---
|
|
1457
|
+
|
|
1458
|
+
## Android Setup
|
|
1459
|
+
|
|
1460
|
+
To use this plugin on Android, you need to add the JitPack repository to your project's `build.gradle.kts` file located at `/src-tauri/gen/android/build.gradle.kts`. Below is an example of how to configure it:
|
|
1461
|
+
|
|
1462
|
+
```kotlin
|
|
1463
|
+
buildscript {
|
|
1464
|
+
repositories {
|
|
1465
|
+
// ...
|
|
1466
|
+
maven { url = uri("https://jitpack.io") }
|
|
1467
|
+
}
|
|
1468
|
+
// ...
|
|
1469
|
+
}
|
|
1470
|
+
|
|
1471
|
+
allprojects {
|
|
1472
|
+
repositories {
|
|
1473
|
+
// ...
|
|
1474
|
+
maven { url = uri("https://jitpack.io") }
|
|
1475
|
+
}
|
|
1476
|
+
}
|
|
1477
|
+
```
|
|
1478
|
+
|
|
1479
|
+
---
|
|
1480
|
+
|
|
1481
|
+
## Contributing
|
|
1482
|
+
|
|
1483
|
+
Pull requests are welcome! Please read our contributing guidelines before you start.
|
|
1484
|
+
|
|
1485
|
+
---
|
|
1486
|
+
|
|
1487
|
+
## Development Setup
|
|
1488
|
+
|
|
1489
|
+
```bash
|
|
1490
|
+
git clone https://github.com/s00d/tauri-plugin-serialplugin.git
|
|
1491
|
+
cd tauri-plugin-serialplugin
|
|
1492
|
+
|
|
1493
|
+
pnpm i
|
|
1494
|
+
pnpm run build
|
|
1495
|
+
pnpm run playground
|
|
1496
|
+
```
|
|
1497
|
+
|
|
1498
|
+
## Testing
|
|
1499
|
+
|
|
1500
|
+
For testing applications without physical hardware, you can use a mock implementation of the serial port. The mock port emulates all functions of a real port and allows testing the application without physical devices.
|
|
1501
|
+
|
|
1502
|
+
### Using Mock Port
|
|
1503
|
+
|
|
1504
|
+
```rust
|
|
1505
|
+
use tauri_plugin_serialplugin::tests::mock::MockSerialPort;
|
|
1506
|
+
|
|
1507
|
+
// Create a mock port
|
|
1508
|
+
let mock_port = MockSerialPort::new();
|
|
1509
|
+
|
|
1510
|
+
// Configure port settings
|
|
1511
|
+
mock_port.set_baud_rate(9600).unwrap();
|
|
1512
|
+
mock_port.set_data_bits(serialport::DataBits::Eight).unwrap();
|
|
1513
|
+
mock_port.set_flow_control(serialport::FlowControl::None).unwrap();
|
|
1514
|
+
mock_port.set_parity(serialport::Parity::None).unwrap();
|
|
1515
|
+
mock_port.set_stop_bits(serialport::StopBits::One).unwrap();
|
|
1516
|
+
|
|
1517
|
+
// Write data
|
|
1518
|
+
mock_port.write("Test data".as_bytes()).unwrap();
|
|
1519
|
+
|
|
1520
|
+
// Read data
|
|
1521
|
+
let mut buffer = [0u8; 1024];
|
|
1522
|
+
let bytes_read = mock_port.read(&mut buffer).unwrap();
|
|
1523
|
+
let data = String::from_utf8_lossy(&buffer[..bytes_read]);
|
|
1524
|
+
assert_eq!(data, "Test data");
|
|
1525
|
+
```
|
|
1526
|
+
|
|
1527
|
+
### Mock Port Features
|
|
1528
|
+
|
|
1529
|
+
- Complete emulation of all real port functions
|
|
1530
|
+
- Built-in buffer for data storage
|
|
1531
|
+
- Control signal emulation (RTS, DTR, CTS, DSR)
|
|
1532
|
+
- Support for parallel operation testing
|
|
1533
|
+
- No additional software required
|
|
1534
|
+
- Works on all platforms
|
|
1535
|
+
|
|
1536
|
+
### Application Testing Example
|
|
1537
|
+
|
|
1538
|
+
```rust
|
|
1539
|
+
#[test]
|
|
1540
|
+
fn test_serial_communication() {
|
|
1541
|
+
let app = create_test_app();
|
|
1542
|
+
let serial_port = SerialPort::new(app.handle().clone());
|
|
1543
|
+
app.manage(serial_port);
|
|
1544
|
+
|
|
1545
|
+
// Open mock port
|
|
1546
|
+
app.state::<SerialPort<MockRuntime>>().open(
|
|
1547
|
+
"COM1".to_string(),
|
|
1548
|
+
9600,
|
|
1549
|
+
Some(DataBits::Eight),
|
|
1550
|
+
Some(FlowControl::None),
|
|
1551
|
+
Some(Parity::None),
|
|
1552
|
+
Some(StopBits::One),
|
|
1553
|
+
Some(1000),
|
|
1554
|
+
).unwrap();
|
|
1555
|
+
|
|
1556
|
+
// Test write and read operations
|
|
1557
|
+
app.state::<SerialPort<MockRuntime>>().write(
|
|
1558
|
+
"COM1".to_string(),
|
|
1559
|
+
"Test data".to_string(),
|
|
1560
|
+
).unwrap();
|
|
1561
|
+
|
|
1562
|
+
let data = app.state::<SerialPort<MockRuntime>>().read(
|
|
1563
|
+
"COM1".to_string(),
|
|
1564
|
+
Some(1000),
|
|
1565
|
+
Some(1024),
|
|
1566
|
+
).unwrap();
|
|
1567
|
+
assert_eq!(data, "Test data");
|
|
1568
|
+
|
|
1569
|
+
// Test port settings
|
|
1570
|
+
app.state::<SerialPort<MockRuntime>>().set_baud_rate(
|
|
1571
|
+
"COM1".to_string(),
|
|
1572
|
+
115200,
|
|
1573
|
+
).unwrap();
|
|
1574
|
+
|
|
1575
|
+
// Close port
|
|
1576
|
+
app.state::<SerialPort<MockRuntime>>().close("COM1".to_string()).unwrap();
|
|
1577
|
+
}
|
|
1578
|
+
```
|
|
1579
|
+
|
|
1580
|
+
### Implementing Your Own Mock Port
|
|
1581
|
+
|
|
1582
|
+
You can implement your own mock port by implementing the `SerialPort` trait. Here's a basic example of how to create a custom mock port:
|
|
1583
|
+
|
|
1584
|
+
```rust
|
|
1585
|
+
use std::io::{self, Read, Write};
|
|
1586
|
+
use serialport::{self, SerialPort};
|
|
1587
|
+
use std::time::Duration;
|
|
1588
|
+
|
|
1589
|
+
struct CustomMockPort {
|
|
1590
|
+
buffer: Vec<u8>,
|
|
1591
|
+
baud_rate: u32,
|
|
1592
|
+
data_bits: serialport::DataBits,
|
|
1593
|
+
flow_control: serialport::FlowControl,
|
|
1594
|
+
parity: serialport::Parity,
|
|
1595
|
+
stop_bits: serialport::StopBits,
|
|
1596
|
+
timeout: Duration,
|
|
1597
|
+
}
|
|
1598
|
+
|
|
1599
|
+
impl CustomMockPort {
|
|
1600
|
+
fn new() -> Self {
|
|
1601
|
+
Self {
|
|
1602
|
+
buffer: Vec::new(),
|
|
1603
|
+
baud_rate: 9600,
|
|
1604
|
+
data_bits: serialport::DataBits::Eight,
|
|
1605
|
+
flow_control: serialport::FlowControl::None,
|
|
1606
|
+
parity: serialport::Parity::None,
|
|
1607
|
+
stop_bits: serialport::StopBits::One,
|
|
1608
|
+
timeout: Duration::from_millis(1000),
|
|
1609
|
+
}
|
|
1610
|
+
}
|
|
1611
|
+
}
|
|
1612
|
+
|
|
1613
|
+
// Implement Read trait for reading data
|
|
1614
|
+
impl Read for CustomMockPort {
|
|
1615
|
+
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
|
1616
|
+
let len = std::cmp::min(buf.len(), self.buffer.len());
|
|
1617
|
+
if len > 0 {
|
|
1618
|
+
buf[..len].copy_from_slice(&self.buffer[..len]);
|
|
1619
|
+
self.buffer.drain(..len);
|
|
1620
|
+
}
|
|
1621
|
+
Ok(len)
|
|
1622
|
+
}
|
|
1623
|
+
}
|
|
1624
|
+
|
|
1625
|
+
// Implement Write trait for writing data
|
|
1626
|
+
impl Write for CustomMockPort {
|
|
1627
|
+
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
|
1628
|
+
self.buffer.extend_from_slice(buf);
|
|
1629
|
+
Ok(buf.len())
|
|
1630
|
+
}
|
|
1631
|
+
|
|
1632
|
+
fn flush(&mut self) -> io::Result<()> {
|
|
1633
|
+
Ok(())
|
|
1634
|
+
}
|
|
1635
|
+
}
|
|
1636
|
+
|
|
1637
|
+
// Implement SerialPort trait for port configuration
|
|
1638
|
+
impl SerialPort for CustomMockPort {
|
|
1639
|
+
fn name(&self) -> Option<String> {
|
|
1640
|
+
Some("CUSTOM_PORT".to_string())
|
|
1641
|
+
}
|
|
1642
|
+
|
|
1643
|
+
fn baud_rate(&self) -> serialport::Result<u32> {
|
|
1644
|
+
Ok(self.baud_rate)
|
|
1645
|
+
}
|
|
1646
|
+
|
|
1647
|
+
fn data_bits(&self) -> serialport::Result<serialport::DataBits> {
|
|
1648
|
+
Ok(self.data_bits)
|
|
1649
|
+
}
|
|
1650
|
+
|
|
1651
|
+
// ... implement other required methods ...
|
|
1652
|
+
}
|
|
1653
|
+
```
|
|
1654
|
+
|
|
1655
|
+
For a complete implementation example, see the mock port implementation in the plugin's test directory:
|
|
1656
|
+
[`src/tests/mock.rs`](https://github.com/s00d/tauri-plugin-serialplugin/blob/main/src/tests/mock.rs)
|
|
1657
|
+
|
|
1658
|
+
The example includes:
|
|
1659
|
+
- Full implementation of all required traits
|
|
1660
|
+
- Buffer management for read/write operations
|
|
1661
|
+
- Control signal emulation
|
|
1662
|
+
- Port configuration handling
|
|
1663
|
+
- Error handling
|
|
1664
|
+
- Thread safety considerations
|
|
1665
|
+
|
|
1666
|
+
You can use this implementation as a reference when creating your own mock port with custom behavior for specific testing scenarios.
|
|
1667
|
+
|
|
1668
|
+
---
|
|
1669
|
+
|
|
1670
|
+
## Partners
|
|
1671
|
+
|
|
1672
|
+
If you find this plugin valuable and would like to support further development, feel free to donate via [DonationAlerts](https://www.donationalerts.com/r/s00d88). Any contribution is greatly appreciated!
|
|
1673
|
+
|
|
1674
|
+
---
|
|
1675
|
+
|
|
1676
|
+
## License
|
|
1677
|
+
|
|
1678
|
+
This code is dual-licensed under MIT or Apache-2.0, where applicable, © 2019–2025 Tauri Programme within The Commons Conservancy.
|