8000 add · GyverLibs/Serial.js@dd5a4a4 · GitHub
[go: up one dir, main page]

Skip to content

Commit dd5a4a4

Browse files
committed
add
1 parent 0a7367d commit dd5a4a4

File tree

8 files changed

+253
-1
lines changed

8 files changed

+253
-1
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
node_modules
2+
package-lock.json

README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,4 @@
11
# Serial.js
2-
Web Serial API wrapper
2+
Web Serial API wrapper, [demo](https://gyverlibs.github.io/Serial.js/test/).
3+
4+
> npm i @alexgyver/serial

package.json

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
{
2+
"name": "@alexgyver/serial",
3+
"version": "1.0.0",
4+
"description": "Web Serial API wrapper",
5+
"main": "./serial.js",
6+
"module": "./serial.js",
7+
"types": "./serial.js",
8+
"scripts": {
9+
"build": "webpack --config ./webpack.config.js"
10+
},
11+
"repository": {
12+
"type": "git",
13+
"url": "git+https://github.com/GyverLibs/Serial.js.git"
14+
},
15+
"devDependencies": {
16+
"webpack": "^5.98.0",
17+
"webpack-cli": "^6.0.1"
18+
},
19+
"author": "AlexGyver <alex@alexgyver.ru>",
20+
"license": "MIT"
21+
}

serial.js

Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
export default class SerialJS {
2+
//#region handlers
3+
onbin = null;
4+
ontext = null;
5+
async onopen() { }
6+
async onclose() { }
7+
async onerror(e) { }
8+
async onportchange(selected) { }
9+
10+
//#region constructor
11+
constructor() {
12+
this._ok = 'serial' in navigator;
13+
if (this._ok) this._update().then(() => this.onportchange(this.selected()));
14+
else this._error('Browser is not supported');
15+
}
16+
17+
//#region methods
18+
opened() {
19+
return this._open;
20+
}
21+
selected() {
22+
return !!this._port;
23+
}
24+
getName() {
25+
if (!this._port) return 'None';
26+
27+
switch (this 9E88 ._port.getInfo().usbProductId) {
28+
case 0x55d3: return 'CH343';
29+
case 0x7584: return 'CH340S';
30+
case 0x7522: case 0x7523: return 'CH340';
31+
case 0x5512: case 0x5523: case 0x5584: return 'CH341';
32+
case 0x0402: case 0x0403: case 0x0404: case 0x0405: case 0x6001: case 0x0602: case 0x6010: return 'FT232';
33+
case 0x9500: case 0x0102: case 0x0501: case 0x80a9: case 0xea60: case 0xea61: case 0xea63: return 'CP210x';
34+
}
35+
return 'Unknown';
36+
}
37+
38+
async select() {
39+
if (!this._ok) return;
40+
41+
try {
42+
await this.close();
43+
let ports = await navigator.serial.getPorts();
44+
for (let p of ports) await p.forget();
45+
await navigator.serial.requestPort();
46+
await this._update();
47+
} catch (e) {
48+
this._port = null;
49+
this._error(e);
50+
}
51+
this.onportchange(this.selected());
52+
}
53+
async open(baud = 115200) {
54+
if (!this._ok) return;
55+
56+
try {
57+
await this.close();
58+
await this._update();
59+
if (!this.selected()) throw "No port";
60+
try {
61+
await this._port.open({ baudRate: baud });
62+
this._open = true;
63+
this.onopen();
64+
this.reader.reset();
65+
await this._readLoop();
66+
} finally {
67+
await this._port.close();
68+
this._open = false;
69+
this.onclose();
70+
}
71+
} catch (e) {
72+
this._error(e);
73+
}
74+
}
75+
async close() {
76+
if (!this._ok) return;
77+
if (!this._open) return;
78+
79+
this._close = true;
80+
if (this._reader) await this._reader.cancel();
81+
82+
while (this._open) await new Promise(r => setTimeout(r, 10));
83+
}
84+
85+
async sendBin(data) {
86+
if (!this._ok) return;
87+
if (!this.opened()) return;
88+
89+
try {
90+
let writer = this._port.writable.getWriter();
91+
await writer.write(data);
92+
writer.releaseLock();
93+
} catch (e) {
94+
this._error(e);
95+
}
96+
}
97+
async sendText(text) {
98+
await this.sendBin((new TextEncoder()).encode(text));
99+
}
100+
101+
reader = new SplitReader();
102+
103+
//#region private
104+
_port = null;
105+
_open = false;
106+
_close = false;
107+
_reader = null;
108+
109+
_error(e) {
110+
this.onerror('[SerialJS] ' + e);
111+
}
112+
async _update() {
113+
let ports = await navigator.serial.getPorts();
114+
this._port = ports.length ? ports[0] : null;
115+
}
116+
async _readLoop() {
117+
this._close = false;
118+
if (this._port.readable) this._reader = this._port.readable.getReader();
119+
while (this._port.readable && !this._close) {
120+
try {
121+
while (true) {
122+
let read = await this._reader.read();
123+
if (read.done) break;
124+
if (this.onbin) this.onbin(read.value);
125+
if (this.ontext) this.ontext(new TextDecoder().decode(read.value));
126+
if (this.reader.ontext) this.reader._write(new TextDecoder().decode(read.value));
127+
}
128+
} finally {
129+
this._reader.releaseLock();
130+
this._reader = null;
131+
}
132+
}
133+
}
< F987 /code>134+
}
135+
136+
class SplitReader {
137+
ontext = null;
138+
139+
setEOL(eol) {
140+
this._eol = eol;
141+
this.reset();
142+
}
143+
144+
reset() {
145+
this._buf = "";
146+
this._skip = true;
147+
}
148+
149+
_write(str) {
150+
this._buf += str;
151+
let t = this._buf.split(this._eol);
152+
if (t.length == 1) return;
153+
154+
if (t[t.length - 1].length) this._buf = t.pop();
155+
else this._buf = "", t.pop();
156+
157+
if (this._skip) t.shift(), this._skip = false;
158+
t.map(this.ontext);
159+
}
160+
161+
_buf = "";
162+
_skip = true;
163+
_eol = /\r?\n/;
164+
}

serial.min.js

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

test/index.html

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
4+
<head>
5+
<meta charset="UTF-8">
6+
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
7+
<script src="script.js" type="module" defer="defer"></script>
8+
</head>
9+
10+
<body>
11+
<button id="select_b">select</button>
12+
<button id="open_b">open</button>
13+
<button id="close_b">close</button>
14+
<button id="send_b">send</button>
15+
</body>
16+
17+
</html>

test/script.js

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import SerialJS from "https://gyverlibs.github.io/Serial.js/serial.min.js";
2+
3+
let i = 0;
4+
let ser = new SerialJS();
5+
6+
select_b.onclick = () => ser.select();
7+
open_b.onclick = () => ser.open();
8+
close_b.onclick = () => ser.close();
9+
send_b.onclick = () => ser.sendText('Hello ' + i++);
10+
11+
// split reader
12+
// ser.reader.setEOL(/\r?\n/); // default
13+
ser.reader.ontext = t => console.log(t);
14+
15+
// read raw
16+
// ser.onbin = b => console.log(b);
17+
// ser.ontext = t => console.log(t);
18+
19+
// state
20+
ser.onopen = () => {
21+
console.log('Opened', ser.getName());
22+
}
23+
ser.onclose = () => {
24+
console.log('Closed');
25+
}
26+
ser.onerror = e => {
27+
console.log(e);
28+
}
29+
ser.onportchange = () => {
30+
console.log('port change', ser.selected(), ser.getName());
31+
}

webpack.config.js

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
module.exports = {
2+
entry: './serial.js',
3+
output: {
4+
path: __dirname,
5+
filename: 'serial.min.js',
6+
library: {
7+
type: 'module'
8+
}
9+
},
10+
experiments: {
11+
outputModule: true
12+
},
13+
mode: "production",
14+
};

0 commit comments

Comments
 (0)
0