@jjlmoya/utils-hardware 1.25.0 → 1.26.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/package.json +1 -1
- package/src/category/index.ts +2 -1
- package/src/entries.ts +4 -1
- package/src/index.ts +1 -0
- package/src/tests/locale_completeness.test.ts +1 -1
- package/src/tests/tool_validation.test.ts +1 -1
- package/src/tool/webUsbSerialMonitor/bibliography.astro +15 -0
- package/src/tool/webUsbSerialMonitor/bibliography.ts +18 -0
- package/src/tool/webUsbSerialMonitor/component.astro +356 -0
- package/src/tool/webUsbSerialMonitor/entry.ts +30 -0
- package/src/tool/webUsbSerialMonitor/i18n/de.ts +241 -0
- package/src/tool/webUsbSerialMonitor/i18n/en.ts +241 -0
- package/src/tool/webUsbSerialMonitor/i18n/es.ts +241 -0
- package/src/tool/webUsbSerialMonitor/i18n/fr.ts +241 -0
- package/src/tool/webUsbSerialMonitor/i18n/id.ts +241 -0
- package/src/tool/webUsbSerialMonitor/i18n/it.ts +241 -0
- package/src/tool/webUsbSerialMonitor/i18n/ja.ts +241 -0
- package/src/tool/webUsbSerialMonitor/i18n/ko.ts +241 -0
- package/src/tool/webUsbSerialMonitor/i18n/nl.ts +241 -0
- package/src/tool/webUsbSerialMonitor/i18n/pl.ts +241 -0
- package/src/tool/webUsbSerialMonitor/i18n/pt.ts +241 -0
- package/src/tool/webUsbSerialMonitor/i18n/ru.ts +241 -0
- package/src/tool/webUsbSerialMonitor/i18n/sv.ts +241 -0
- package/src/tool/webUsbSerialMonitor/i18n/tr.ts +241 -0
- package/src/tool/webUsbSerialMonitor/i18n/zh.ts +241 -0
- package/src/tool/webUsbSerialMonitor/index.ts +12 -0
- package/src/tool/webUsbSerialMonitor/logic.ts +44 -0
- package/src/tool/webUsbSerialMonitor/seo.astro +16 -0
- package/src/tool/webUsbSerialMonitor/ui.ts +51 -0
- package/src/tool/webUsbSerialMonitor/web-usb-serial-monitor.css +415 -0
- package/src/tools.ts +2 -1
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
import type { WithContext, FAQPage, HowTo, SoftwareApplication } from 'schema-dts';
|
|
2
|
+
import type { ToolLocaleContent } from '../../../types';
|
|
3
|
+
import type { WebUsbSerialMonitorUI } from '../ui';
|
|
4
|
+
import { bibliography } from '../bibliography';
|
|
5
|
+
|
|
6
|
+
const slug = 'web-usb-serial-monitor';
|
|
7
|
+
const title = 'WebUSB 串口监视器';
|
|
8
|
+
const description = '从浏览器连接 USB 串口硬件,读取实时终端输出,发送命令,无需安装桌面终端即可调试 Arduino、ESP32、RP2040 和创客板。';
|
|
9
|
+
|
|
10
|
+
const faqData = [
|
|
11
|
+
{
|
|
12
|
+
question: '这个串口监视器支持 Arduino、ESP32 和 Raspberry Pi Pico 板吗?',
|
|
13
|
+
answer: '是的,当板卡提供 Web Serial 支持的 USB 串口接口,且浏览器基于 Chromium 时即可使用。常见的 Arduino、ESP32、RP2040、CH340、CP210x 和 FTDI 适配器在用户授予权限后通常可以正常工作。',
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
question: '为什么使用 Web Serial 却叫 WebUSB?',
|
|
17
|
+
answer: '大多数创客板通过 USB 连接,但浏览器终端访问由 Web Serial API 提供。WebUSB 是更低层的接口,不适合简单的 UART 风格终端。',
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
question: '网站可以在未经许可的情况下访问我的串口设备吗?',
|
|
21
|
+
answer: '不可以。浏览器要求用户点击并通过原生设备选择器选择设备后,网站才能打开串口。此工具不会存储终端日志或设备标识符。',
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
question: '我应该使用哪个浏览器进行 Web 串口终端连接?',
|
|
25
|
+
answer: '通过 HTTPS 或 localhost 使用 Chrome、Edge 或其他基于 Chromium 的浏览器。Firefox、Safari 和许多 iOS 浏览器不支持 Web Serial API。',
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
question: '我应该选择哪个波特率?',
|
|
29
|
+
answer: '选择固件中配置的波特率。Arduino 示例通常使用 9600 或 115200,而更快的日志、引导加载程序和高频传感器数据流可能使用 230400、460800 或 921600。',
|
|
30
|
+
},
|
|
31
|
+
];
|
|
32
|
+
|
|
33
|
+
const howToData = [
|
|
34
|
+
{
|
|
35
|
+
name: '连接 USB 串口设备',
|
|
36
|
+
text: '插入板卡或适配器,关闭可能已经占用该端口的其他串口终端。',
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
name: '选择波特率',
|
|
40
|
+
text: '选择与固件相同的波特率,例如许多 Arduino、ESP32 和 RP2040 示例使用 115200。',
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
name: '授予浏览器权限',
|
|
44
|
+
text: '点击连接,在浏览器选择器中选择串口设备,允许页面打开端口。',
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
name: '读取和发送终端数据',
|
|
48
|
+
text: '在终端中查看传入的日志,发送带有可选 CRLF 行尾的命令,并在需要时清除或暂停实时输出。',
|
|
49
|
+
},
|
|
50
|
+
];
|
|
51
|
+
|
|
52
|
+
const faqSchema: WithContext<FAQPage> = {
|
|
53
|
+
'@context': 'https://schema.org',
|
|
54
|
+
'@type': 'FAQPage',
|
|
55
|
+
mainEntity: faqData.map((item) => ({
|
|
56
|
+
'@type': 'Question',
|
|
57
|
+
name: item.question,
|
|
58
|
+
acceptedAnswer: { '@type': 'Answer', text: item.answer },
|
|
59
|
+
})),
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
const howToSchema: WithContext<HowTo> = {
|
|
63
|
+
'@context': 'https://schema.org',
|
|
64
|
+
'@type': 'HowTo',
|
|
65
|
+
name: title,
|
|
66
|
+
description,
|
|
67
|
+
step: howToData.map((step, i) => ({
|
|
68
|
+
'@type': 'HowToStep',
|
|
69
|
+
position: i + 1,
|
|
70
|
+
name: step.name,
|
|
71
|
+
text: step.text,
|
|
72
|
+
})),
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
const appSchema: WithContext<SoftwareApplication> = {
|
|
76
|
+
'@context': 'https://schema.org',
|
|
77
|
+
'@type': 'SoftwareApplication',
|
|
78
|
+
name: title,
|
|
79
|
+
description,
|
|
80
|
+
applicationCategory: 'DeveloperApplication',
|
|
81
|
+
operatingSystem: 'All',
|
|
82
|
+
offers: { '@type': 'Offer', price: '0', priceCurrency: 'USD' },
|
|
83
|
+
inLanguage: 'zh',
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
export const content: ToolLocaleContent<WebUsbSerialMonitorUI> = {
|
|
87
|
+
slug,
|
|
88
|
+
title,
|
|
89
|
+
description,
|
|
90
|
+
faq: faqData,
|
|
91
|
+
bibliography,
|
|
92
|
+
howTo: howToData,
|
|
93
|
+
schemas: [faqSchema, howToSchema, appSchema],
|
|
94
|
+
seo: [
|
|
95
|
+
{
|
|
96
|
+
type: 'title',
|
|
97
|
+
text: 'USB 创客硬件在线串口监视器',
|
|
98
|
+
level: 2,
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
type: 'paragraph',
|
|
102
|
+
html: '此浏览器串口监视器直接从 Chrome 或 Edge 打开 USB 串口,然后从微控制器、USB UART 桥接器、开发板、引导加载程序、测试夹具、传感器和实验室硬件流式传输文本。专为需要串口控制台但不想安装桌面 IDE 或终端应用程序时的快速诊断而设计。',
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
type: 'message',
|
|
106
|
+
title: '浏览器权限边界',
|
|
107
|
+
html: '页面无法静默枚举或打开您的串口设备。只有在您点击连接并在浏览器选择器中选择端口后,访问才会开始。终端数据保留在当前标签页中,除非您自行复制。',
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
type: 'stats',
|
|
111
|
+
items: [
|
|
112
|
+
{ value: '9600-921600', label: '常用波特率预设' },
|
|
113
|
+
{ value: 'CRLF', label: '可选命令结尾' },
|
|
114
|
+
{ value: '本地', label: '终端会话' },
|
|
115
|
+
],
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
type: 'title',
|
|
119
|
+
text: 'Web 串口终端的适用场景',
|
|
120
|
+
level: 3,
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
type: 'list',
|
|
124
|
+
items: [
|
|
125
|
+
'检查 Arduino、ESP32、ESP8266、RP2040、STM32 或自定义固件的启动消息。',
|
|
126
|
+
'通过 USB UART 适配器向调制解调器、GPS、LoRa、Wi-Fi、蓝牙或蜂窝模块发送 AT 命令。',
|
|
127
|
+
'从工厂测试夹具、教室实验室、机器人控制器或台式原型读取传感器输出。',
|
|
128
|
+
'验证 USB 串口桥接驱动程序、线缆、板卡电源和固件波特率是否协同工作。',
|
|
129
|
+
'在提交错误报告或请求硬件支持之前收集快速错误日志。',
|
|
130
|
+
],
|
|
131
|
+
},
|
|
132
|
+
{
|
|
133
|
+
type: 'comparative',
|
|
134
|
+
items: [
|
|
135
|
+
{
|
|
136
|
+
title: 'Web 串口监视器',
|
|
137
|
+
description: '最适合快速支持、课堂教学、现场诊断以及打开 URL 比安装 IDE 更快的创客工作流程。',
|
|
138
|
+
},
|
|
139
|
+
{
|
|
140
|
+
title: '桌面终端',
|
|
141
|
+
description: '更适合二进制协议、长时间捕获会话、脚本编写、硬件流控制、宏以及浏览器 API 被屏蔽的环境。',
|
|
142
|
+
},
|
|
143
|
+
],
|
|
144
|
+
},
|
|
145
|
+
{
|
|
146
|
+
type: 'title',
|
|
147
|
+
text: '波特率和行尾检查清单',
|
|
148
|
+
level: 3,
|
|
149
|
+
},
|
|
150
|
+
{
|
|
151
|
+
type: 'table',
|
|
152
|
+
headers: ['设置', '典型选择', '错误时会出现的问题'],
|
|
153
|
+
rows: [
|
|
154
|
+
['波特率', '许多现代板卡为 115200,旧示例为 9600。', '可读文本变成随机符号,或没有有用的日志出现。'],
|
|
155
|
+
['行尾', '许多命令解析器使用 CRLF,原始字符协议无需结尾。', '命令被忽略,因为固件正在等待终止符。'],
|
|
156
|
+
['独占端口访问', '关闭 Arduino 串口监视器、PuTTY、screen、minicom 或厂商工具。', '浏览器选择器打开端口,但读取或写入失败。'],
|
|
157
|
+
['安全上下文', 'HTTPS 或 localhost。', '即使在受支持的浏览器中,串口 API 也缺失。'],
|
|
158
|
+
],
|
|
159
|
+
},
|
|
160
|
+
{
|
|
161
|
+
type: 'diagnostic',
|
|
162
|
+
variant: 'warning',
|
|
163
|
+
title: '没有串口输出?',
|
|
164
|
+
html: '确认板卡已通电且 USB 线缆支持数据传输而不仅仅是充电。如果不知道固件波特率,请尝试 9600、57600 和 115200。连接后按复位键,因为许多板卡仅在启动时输出启动日志。关闭可能仍占用串口的其他软件,如果设备始终不出现,请安装 CH340、CP210x、FTDI 或板卡厂商的操作系统驱动程序。',
|
|
165
|
+
},
|
|
166
|
+
{
|
|
167
|
+
type: 'title',
|
|
168
|
+
text: '隐私、安全和限制',
|
|
169
|
+
level: 3,
|
|
170
|
+
},
|
|
171
|
+
{
|
|
172
|
+
type: 'proscons',
|
|
173
|
+
title: 'Web Serial 的优势和限制',
|
|
174
|
+
items: [
|
|
175
|
+
{
|
|
176
|
+
pro: '无需桌面安装即可进行基本串口文本诊断。',
|
|
177
|
+
con: '需要基于 Chromium 的浏览器和安全上下文。',
|
|
178
|
+
},
|
|
179
|
+
{
|
|
180
|
+
pro: '浏览器选择器限制仅访问您选择的特定端口。',
|
|
181
|
+
con: '不适用于二进制协议分析器或长时间无人值守的捕获。',
|
|
182
|
+
},
|
|
183
|
+
{
|
|
184
|
+
pro: '适用于文本日志、命令提示符和快速硬件检查。',
|
|
185
|
+
con: '某些企业策略、移动浏览器和操作系统会阻止 Web Serial。',
|
|
186
|
+
},
|
|
187
|
+
],
|
|
188
|
+
},
|
|
189
|
+
],
|
|
190
|
+
ui: {
|
|
191
|
+
unsupportedTitle: 'Web Serial 不可用',
|
|
192
|
+
unsupportedBody: '通过 HTTPS 或 localhost 使用 Chrome 或 Edge,并确保您的设备提供 USB 串口接口。',
|
|
193
|
+
secureContext: 'Web Serial 需要 HTTPS 或 localhost。请从安全来源重新加载此页面并重试。',
|
|
194
|
+
statusIdle: '选择波特率,然后连接 USB 串口设备',
|
|
195
|
+
statusPermission: '等待浏览器串口选择器',
|
|
196
|
+
statusOpening: '正在打开串口',
|
|
197
|
+
statusConnected: '串口已连接',
|
|
198
|
+
statusDisconnected: '串口已断开',
|
|
199
|
+
statusError: '串口连接失败',
|
|
200
|
+
connect: '连接串口',
|
|
201
|
+
disconnect: '断开连接',
|
|
202
|
+
send: '发送',
|
|
203
|
+
clear: '清除',
|
|
204
|
+
pause: '暂停',
|
|
205
|
+
resume: '继续',
|
|
206
|
+
baudRate: '波特率',
|
|
207
|
+
newline: '追加 CRLF',
|
|
208
|
+
inputPlaceholder: '输入命令,然后按回车键',
|
|
209
|
+
portFallback: '未选择端口',
|
|
210
|
+
portLabel: '端口标识',
|
|
211
|
+
connectedDeviceLabel: '已连接设备',
|
|
212
|
+
deviceNameFallback: 'USB 串口设备',
|
|
213
|
+
transportLabel: 'Web Serial 链路',
|
|
214
|
+
bytesLabel: '字节',
|
|
215
|
+
linesLabel: '行',
|
|
216
|
+
privacyTitle: '权限控制',
|
|
217
|
+
privacyBody: '浏览器仅公开您选择的串口设备。日志保留在此标签页中,除非您自行复制。',
|
|
218
|
+
emptyLog: '连接串口设备后,终端输出将显示在此处。',
|
|
219
|
+
copied: '已复制',
|
|
220
|
+
copyLog: '复制',
|
|
221
|
+
presetSlow: '9600',
|
|
222
|
+
presetArduino: '115200',
|
|
223
|
+
presetFast: '921600',
|
|
224
|
+
terminalLabel: '实时终端',
|
|
225
|
+
unknownUsbId: 'N/A',
|
|
226
|
+
logDirectionRx: 'rx',
|
|
227
|
+
logDirectionTx: 'tx',
|
|
228
|
+
logDirectionSys: 'sys',
|
|
229
|
+
vidPrefix: 'VID ',
|
|
230
|
+
pidSeparator: ' / PID ',
|
|
231
|
+
baudUnit: ' 波特',
|
|
232
|
+
vendorFtdi: 'FTDI USB Serial',
|
|
233
|
+
vendorSilabs: 'Silicon Labs CP210x',
|
|
234
|
+
vendorCh340: 'CH340 USB Serial',
|
|
235
|
+
vendorArduinoUsb: 'Arduino USB Serial',
|
|
236
|
+
vendorAdafruit: 'Adafruit USB Serial',
|
|
237
|
+
vendorRp2040: 'Raspberry Pi RP2040',
|
|
238
|
+
vendorEspressif: 'Espressif USB Serial',
|
|
239
|
+
vendorOpenSource: 'Open Source USB Serial',
|
|
240
|
+
},
|
|
241
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { webUsbSerialMonitor } from './entry';
|
|
2
|
+
import type { ToolDefinition } from '../../types';
|
|
3
|
+
|
|
4
|
+
export * from './entry';
|
|
5
|
+
|
|
6
|
+
export const WEB_USB_SERIAL_MONITOR_TOOL: ToolDefinition = {
|
|
7
|
+
entry: webUsbSerialMonitor,
|
|
8
|
+
Component: () => import('./component.astro'),
|
|
9
|
+
SEOComponent: () => import('./seo.astro'),
|
|
10
|
+
BibliographyComponent: () => import('./bibliography.astro'),
|
|
11
|
+
};
|
|
12
|
+
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
export interface SerialPortInfo {
|
|
2
|
+
usbVendorId?: number;
|
|
3
|
+
usbProductId?: number;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
export interface SerialLineInput {
|
|
7
|
+
text: string;
|
|
8
|
+
appendNewline: boolean;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface SerialPortLabelInput {
|
|
12
|
+
info: SerialPortInfo;
|
|
13
|
+
fallback: string;
|
|
14
|
+
unknownUsbId?: string;
|
|
15
|
+
vidPrefix?: string;
|
|
16
|
+
pidSeparator?: string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export const commonBaudRates = [9600, 19200, 38400, 57600, 115200, 230400, 460800, 921600];
|
|
20
|
+
|
|
21
|
+
export function formatUsbId(value: number | undefined, unknownUsbId = 'N/A'): string {
|
|
22
|
+
if (typeof value !== 'number') return unknownUsbId;
|
|
23
|
+
return `0x${value.toString(16).toUpperCase().padStart(4, '0')}`;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function buildSerialPortLabel(input: SerialPortLabelInput): string {
|
|
27
|
+
const unknownUsbId = input.unknownUsbId ?? 'N/A';
|
|
28
|
+
const vidPrefix = input.vidPrefix ?? 'VID ';
|
|
29
|
+
const pidSeparator = input.pidSeparator ?? ' / PID ';
|
|
30
|
+
const vendor = formatUsbId(input.info.usbVendorId, unknownUsbId);
|
|
31
|
+
const product = formatUsbId(input.info.usbProductId, unknownUsbId);
|
|
32
|
+
if (vendor === unknownUsbId && product === unknownUsbId) return input.fallback;
|
|
33
|
+
return `${vidPrefix}${vendor}${pidSeparator}${product}`;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function encodeSerialLine(input: SerialLineInput): string {
|
|
37
|
+
const normalized = input.text.replace(/\r?\n/g, '');
|
|
38
|
+
return input.appendNewline ? `${normalized}\r\n` : normalized;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export function decodeSerialChunk(bytes: Uint8Array): string {
|
|
42
|
+
return new TextDecoder().decode(bytes);
|
|
43
|
+
}
|
|
44
|
+
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
---
|
|
2
|
+
import { SEORenderer } from '@jjlmoya/utils-shared';
|
|
3
|
+
import { webUsbSerialMonitor } from './index';
|
|
4
|
+
import type { KnownLocale } from '../../types';
|
|
5
|
+
|
|
6
|
+
interface Props {
|
|
7
|
+
locale?: KnownLocale;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const { locale = 'en' } = Astro.props;
|
|
11
|
+
const content = await webUsbSerialMonitor.i18n[locale]?.();
|
|
12
|
+
if (!content) return null;
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
{content.seo?.length > 0 && <SEORenderer content={{ locale, sections: content.seo }} />}
|
|
16
|
+
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
export interface WebUsbSerialMonitorUI extends Record<string, string> {
|
|
2
|
+
unsupportedTitle: string;
|
|
3
|
+
unsupportedBody: string;
|
|
4
|
+
secureContext: string;
|
|
5
|
+
statusIdle: string;
|
|
6
|
+
statusPermission: string;
|
|
7
|
+
statusOpening: string;
|
|
8
|
+
statusConnected: string;
|
|
9
|
+
statusDisconnected: string;
|
|
10
|
+
statusError: string;
|
|
11
|
+
connect: string;
|
|
12
|
+
disconnect: string;
|
|
13
|
+
send: string;
|
|
14
|
+
clear: string;
|
|
15
|
+
pause: string;
|
|
16
|
+
resume: string;
|
|
17
|
+
baudRate: string;
|
|
18
|
+
newline: string;
|
|
19
|
+
inputPlaceholder: string;
|
|
20
|
+
portFallback: string;
|
|
21
|
+
portLabel: string;
|
|
22
|
+
connectedDeviceLabel: string;
|
|
23
|
+
deviceNameFallback: string;
|
|
24
|
+
transportLabel: string;
|
|
25
|
+
bytesLabel: string;
|
|
26
|
+
linesLabel: string;
|
|
27
|
+
privacyTitle: string;
|
|
28
|
+
privacyBody: string;
|
|
29
|
+
emptyLog: string;
|
|
30
|
+
copied: string;
|
|
31
|
+
copyLog: string;
|
|
32
|
+
presetSlow: string;
|
|
33
|
+
presetArduino: string;
|
|
34
|
+
presetFast: string;
|
|
35
|
+
terminalLabel: string;
|
|
36
|
+
unknownUsbId: string;
|
|
37
|
+
logDirectionRx: string;
|
|
38
|
+
logDirectionTx: string;
|
|
39
|
+
logDirectionSys: string;
|
|
40
|
+
vidPrefix: string;
|
|
41
|
+
pidSeparator: string;
|
|
42
|
+
baudUnit: string;
|
|
43
|
+
vendorFtdi: string;
|
|
44
|
+
vendorSilabs: string;
|
|
45
|
+
vendorCh340: string;
|
|
46
|
+
vendorArduinoUsb: string;
|
|
47
|
+
vendorAdafruit: string;
|
|
48
|
+
vendorRp2040: string;
|
|
49
|
+
vendorEspressif: string;
|
|
50
|
+
vendorOpenSource: string;
|
|
51
|
+
}
|