@jjlmoya/utils-hardware 1.24.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.
Files changed (79) hide show
  1. package/package.json +2 -1
  2. package/src/category/index.ts +4 -1
  3. package/src/entries.ts +10 -1
  4. package/src/index.ts +3 -0
  5. package/src/tests/locale_completeness.test.ts +2 -2
  6. package/src/tests/tool_validation.test.ts +2 -2
  7. package/src/tool/keyboardChatterTest/bibliography.astro +15 -0
  8. package/src/tool/keyboardChatterTest/bibliography.ts +20 -0
  9. package/src/tool/keyboardChatterTest/component.astro +353 -0
  10. package/src/tool/keyboardChatterTest/entry.ts +30 -0
  11. package/src/tool/keyboardChatterTest/i18n/de.ts +232 -0
  12. package/src/tool/keyboardChatterTest/i18n/en.ts +232 -0
  13. package/src/tool/keyboardChatterTest/i18n/es.ts +232 -0
  14. package/src/tool/keyboardChatterTest/i18n/fr.ts +232 -0
  15. package/src/tool/keyboardChatterTest/i18n/id.ts +232 -0
  16. package/src/tool/keyboardChatterTest/i18n/it.ts +232 -0
  17. package/src/tool/keyboardChatterTest/i18n/ja.ts +232 -0
  18. package/src/tool/keyboardChatterTest/i18n/ko.ts +232 -0
  19. package/src/tool/keyboardChatterTest/i18n/nl.ts +232 -0
  20. package/src/tool/keyboardChatterTest/i18n/pl.ts +232 -0
  21. package/src/tool/keyboardChatterTest/i18n/pt.ts +232 -0
  22. package/src/tool/keyboardChatterTest/i18n/ru.ts +232 -0
  23. package/src/tool/keyboardChatterTest/i18n/sv.ts +232 -0
  24. package/src/tool/keyboardChatterTest/i18n/tr.ts +232 -0
  25. package/src/tool/keyboardChatterTest/i18n/zh.ts +232 -0
  26. package/src/tool/keyboardChatterTest/index.ts +12 -0
  27. package/src/tool/keyboardChatterTest/keyboard-chatter-test.css +512 -0
  28. package/src/tool/keyboardChatterTest/logic.ts +23 -0
  29. package/src/tool/keyboardChatterTest/seo.astro +16 -0
  30. package/src/tool/keyboardChatterTest/ui.ts +34 -0
  31. package/src/tool/webBluetoothBleScanner/bibliography.astro +14 -0
  32. package/src/tool/webBluetoothBleScanner/bibliography.ts +16 -0
  33. package/src/tool/webBluetoothBleScanner/component.astro +339 -0
  34. package/src/tool/webBluetoothBleScanner/entry.ts +29 -0
  35. package/src/tool/webBluetoothBleScanner/i18n/de.ts +233 -0
  36. package/src/tool/webBluetoothBleScanner/i18n/en.ts +233 -0
  37. package/src/tool/webBluetoothBleScanner/i18n/es.ts +233 -0
  38. package/src/tool/webBluetoothBleScanner/i18n/fr.ts +233 -0
  39. package/src/tool/webBluetoothBleScanner/i18n/id.ts +233 -0
  40. package/src/tool/webBluetoothBleScanner/i18n/it.ts +233 -0
  41. package/src/tool/webBluetoothBleScanner/i18n/ja.ts +233 -0
  42. package/src/tool/webBluetoothBleScanner/i18n/ko.ts +233 -0
  43. package/src/tool/webBluetoothBleScanner/i18n/nl.ts +233 -0
  44. package/src/tool/webBluetoothBleScanner/i18n/pl.ts +233 -0
  45. package/src/tool/webBluetoothBleScanner/i18n/pt.ts +233 -0
  46. package/src/tool/webBluetoothBleScanner/i18n/ru.ts +233 -0
  47. package/src/tool/webBluetoothBleScanner/i18n/sv.ts +233 -0
  48. package/src/tool/webBluetoothBleScanner/i18n/tr.ts +233 -0
  49. package/src/tool/webBluetoothBleScanner/i18n/zh.ts +233 -0
  50. package/src/tool/webBluetoothBleScanner/index.ts +11 -0
  51. package/src/tool/webBluetoothBleScanner/logic.ts +79 -0
  52. package/src/tool/webBluetoothBleScanner/seo.astro +15 -0
  53. package/src/tool/webBluetoothBleScanner/ui.ts +41 -0
  54. package/src/tool/webBluetoothBleScanner/web-bluetooth-ble-scanner.css +406 -0
  55. package/src/tool/webUsbSerialMonitor/bibliography.astro +15 -0
  56. package/src/tool/webUsbSerialMonitor/bibliography.ts +18 -0
  57. package/src/tool/webUsbSerialMonitor/component.astro +356 -0
  58. package/src/tool/webUsbSerialMonitor/entry.ts +30 -0
  59. package/src/tool/webUsbSerialMonitor/i18n/de.ts +241 -0
  60. package/src/tool/webUsbSerialMonitor/i18n/en.ts +241 -0
  61. package/src/tool/webUsbSerialMonitor/i18n/es.ts +241 -0
  62. package/src/tool/webUsbSerialMonitor/i18n/fr.ts +241 -0
  63. package/src/tool/webUsbSerialMonitor/i18n/id.ts +241 -0
  64. package/src/tool/webUsbSerialMonitor/i18n/it.ts +241 -0
  65. package/src/tool/webUsbSerialMonitor/i18n/ja.ts +241 -0
  66. package/src/tool/webUsbSerialMonitor/i18n/ko.ts +241 -0
  67. package/src/tool/webUsbSerialMonitor/i18n/nl.ts +241 -0
  68. package/src/tool/webUsbSerialMonitor/i18n/pl.ts +241 -0
  69. package/src/tool/webUsbSerialMonitor/i18n/pt.ts +241 -0
  70. package/src/tool/webUsbSerialMonitor/i18n/ru.ts +241 -0
  71. package/src/tool/webUsbSerialMonitor/i18n/sv.ts +241 -0
  72. package/src/tool/webUsbSerialMonitor/i18n/tr.ts +241 -0
  73. package/src/tool/webUsbSerialMonitor/i18n/zh.ts +241 -0
  74. package/src/tool/webUsbSerialMonitor/index.ts +12 -0
  75. package/src/tool/webUsbSerialMonitor/logic.ts +44 -0
  76. package/src/tool/webUsbSerialMonitor/seo.astro +16 -0
  77. package/src/tool/webUsbSerialMonitor/ui.ts +51 -0
  78. package/src/tool/webUsbSerialMonitor/web-usb-serial-monitor.css +415 -0
  79. package/src/tools.ts +4 -1
@@ -0,0 +1,356 @@
1
+ ---
2
+ import { Icon } from 'astro-icon/components';
3
+ import type { KnownLocale } from '../../types';
4
+ import type { WebUsbSerialMonitorUI } from './ui';
5
+
6
+ interface Props {
7
+ locale?: KnownLocale;
8
+ ui?: Record<string, unknown>;
9
+ }
10
+
11
+ const { ui } = Astro.props;
12
+ const t = (ui ?? {}) as WebUsbSerialMonitorUI;
13
+ ---
14
+
15
+ <div class="wsm-root" data-config={JSON.stringify(t)}>
16
+ <section class="wsm-shell">
17
+ <div class="wsm-device">
18
+ <div class="wsm-device-icon">
19
+ <Icon name="mdi:usb-port" />
20
+ </div>
21
+ <div>
22
+ <span>{t.connectedDeviceLabel}</span>
23
+ <strong data-device-name>{t.portFallback}</strong>
24
+ <small data-port>{t.transportLabel}</small>
25
+ </div>
26
+ </div>
27
+
28
+ <div class="wsm-controls">
29
+ <div class="wsm-status">
30
+ <span data-status-dot></span>
31
+ <strong data-status>{t.statusIdle}</strong>
32
+ </div>
33
+
34
+ <div class="wsm-dials" aria-label={t.baudRate}>
35
+ <label>
36
+ <span>{t.baudRate}</span>
37
+ <select data-baud>
38
+ <option value="9600">9600</option>
39
+ <option value="57600">57600</option>
40
+ <option value="115200" selected>115200</option>
41
+ <option value="230400">230400</option>
42
+ <option value="921600">921600</option>
43
+ </select>
44
+ </label>
45
+ <label class="wsm-check">
46
+ <input type="checkbox" data-newline checked />
47
+ <span>{t.newline}</span>
48
+ </label>
49
+ </div>
50
+
51
+ <div class="wsm-presets">
52
+ <button type="button" data-preset="9600">{t.presetSlow}</button>
53
+ <button type="button" data-preset="115200">{t.presetArduino}</button>
54
+ <button type="button" data-preset="921600">{t.presetFast}</button>
55
+ </div>
56
+
57
+ <button type="button" class="wsm-connect" data-connect>
58
+ <Icon name="mdi:usb" />
59
+ <span>{t.connect}</span>
60
+ </button>
61
+
62
+ <aside class="wsm-privacy">
63
+ <Icon name="mdi:shield-check-outline" />
64
+ <span>
65
+ <strong>{t.privacyTitle}</strong>
66
+ <small>{t.privacyBody}</small>
67
+ </span>
68
+ </aside>
69
+
70
+ <dl class="wsm-metrics">
71
+ <div>
72
+ <dt>{t.portLabel}</dt>
73
+ <dd data-port-id>{t.portFallback}</dd>
74
+ </div>
75
+ <div>
76
+ <dt>{t.bytesLabel}</dt>
77
+ <dd data-bytes>0</dd>
78
+ </div>
79
+ <div>
80
+ <dt>{t.linesLabel}</dt>
81
+ <dd data-lines>0</dd>
82
+ </div>
83
+ </dl>
84
+ </div>
85
+
86
+ <div class="wsm-terminal">
87
+ <div class="wsm-terminal-head">
88
+ <span>{t.terminalLabel}</span>
89
+ <div>
90
+ <button type="button" data-pause>{t.pause}</button>
91
+ <button type="button" data-copy>{t.copyLog}</button>
92
+ <button type="button" data-clear>{t.clear}</button>
93
+ </div>
94
+ </div>
95
+ <pre data-log>{t.emptyLog}</pre>
96
+ <form data-send-form>
97
+ <input data-send-input type="text" placeholder={t.inputPlaceholder} autocomplete="off" aria-label={t.inputPlaceholder} />
98
+ <button type="submit">{t.send}</button>
99
+ </form>
100
+ </div>
101
+ </section>
102
+ </div>
103
+
104
+ <link rel="stylesheet" href="./web-usb-serial-monitor.css" />
105
+
106
+ <script>
107
+ interface Config extends Record<string, string> {
108
+ unsupportedTitle: string;
109
+ unsupportedBody: string;
110
+ secureContext: string;
111
+ statusIdle: string;
112
+ statusPermission: string;
113
+ statusOpening: string;
114
+ statusConnected: string;
115
+ statusDisconnected: string;
116
+ statusError: string;
117
+ connect: string;
118
+ disconnect: string;
119
+ pause: string;
120
+ resume: string;
121
+ portFallback: string;
122
+ deviceNameFallback: string;
123
+ transportLabel: string;
124
+ emptyLog: string;
125
+ copied: string;
126
+ unknownUsbId: string;
127
+ logDirectionRx: string;
128
+ logDirectionTx: string;
129
+ logDirectionSys: string;
130
+ vidPrefix: string;
131
+ pidSeparator: string;
132
+ baudUnit: string;
133
+ vendorFtdi: string;
134
+ vendorSilabs: string;
135
+ vendorCh340: string;
136
+ vendorArduinoUsb: string;
137
+ vendorAdafruit: string;
138
+ vendorRp2040: string;
139
+ vendorEspressif: string;
140
+ vendorOpenSource: string;
141
+ }
142
+
143
+ interface SerialPortLike {
144
+ readable: ReadableStream<Uint8Array> | null;
145
+ writable: WritableStream<Uint8Array> | null;
146
+ open: (options: { baudRate: number }) => Promise<void>;
147
+ close: () => Promise<void>;
148
+ getInfo: () => { usbVendorId?: number; usbProductId?: number };
149
+ }
150
+
151
+ interface SerialNavigator {
152
+ serial?: {
153
+ requestPort: () => Promise<SerialPortLike>;
154
+ };
155
+ }
156
+
157
+ const root = document.querySelector<HTMLElement>('.wsm-root');
158
+ const config = JSON.parse(root?.dataset.config ?? '{}') as Config;
159
+ const connectButton = root?.querySelector<HTMLButtonElement>('[data-connect]');
160
+ const baud = root?.querySelector<HTMLSelectElement>('[data-baud]');
161
+ const newline = root?.querySelector<HTMLInputElement>('[data-newline]');
162
+ const status = root?.querySelector<HTMLElement>('[data-status]');
163
+ const statusDot = root?.querySelector<HTMLElement>('[data-status-dot]');
164
+ const portLabel = root?.querySelector<HTMLElement>('[data-port]');
165
+ const portId = root?.querySelector<HTMLElement>('[data-port-id]');
166
+ const deviceName = root?.querySelector<HTMLElement>('[data-device-name]');
167
+ const byteCount = root?.querySelector<HTMLElement>('[data-bytes]');
168
+ const lineCount = root?.querySelector<HTMLElement>('[data-lines]');
169
+ const log = root?.querySelector<HTMLPreElement>('[data-log]');
170
+ const form = root?.querySelector<HTMLFormElement>('[data-send-form]');
171
+ const input = root?.querySelector<HTMLInputElement>('[data-send-input]');
172
+ const pauseButton = root?.querySelector<HTMLButtonElement>('[data-pause]');
173
+ const copyButton = root?.querySelector<HTMLButtonElement>('[data-copy]');
174
+ const clearButton = root?.querySelector<HTMLButtonElement>('[data-clear]');
175
+ const decoder = new TextDecoder();
176
+ const encoder = new TextEncoder();
177
+ let port: SerialPortLike | undefined;
178
+ let reader: ReadableStreamDefaultReader<Uint8Array> | undefined;
179
+ let writer: WritableStreamDefaultWriter<Uint8Array> | undefined;
180
+ let bytes = 0;
181
+ let lines = 0;
182
+ let paused = false;
183
+ let buffer = '';
184
+
185
+ function setStatus(message: string, state: 'idle' | 'busy' | 'ok' | 'warn' | 'error' = 'idle') {
186
+ if (status) status.textContent = message;
187
+ statusDot?.setAttribute('data-state', state);
188
+ root?.setAttribute('data-state', state);
189
+ }
190
+
191
+ function formatUsbId(value: number | undefined) {
192
+ if (typeof value !== 'number') return config.unknownUsbId;
193
+ return `0x${value.toString(16).toUpperCase().padStart(4, '0')}`;
194
+ }
195
+
196
+ function guessDeviceName(info: { usbVendorId?: number; usbProductId?: number }) {
197
+ const vendors: Record<number, string> = {
198
+ 0x0403: config.vendorFtdi,
199
+ 0x10c4: config.vendorSilabs,
200
+ 0x1a86: config.vendorCh340,
201
+ 0x2341: config.vendorArduinoUsb,
202
+ 0x239a: config.vendorAdafruit,
203
+ 0x2e8a: config.vendorRp2040,
204
+ 0x303a: config.vendorEspressif,
205
+ 0x1d50: config.vendorOpenSource,
206
+ };
207
+ return typeof info.usbVendorId === 'number' ? vendors[info.usbVendorId] ?? config.deviceNameFallback : config.deviceNameFallback;
208
+ }
209
+
210
+ function appendLog(text: string, direction: string = config.logDirectionRx) {
211
+ if (!log || paused) return;
212
+ if (log.textContent === config.emptyLog) log.textContent = '';
213
+ const line = `[${direction}] ${text}`;
214
+ buffer += line;
215
+ log.textContent += line;
216
+ requestAnimationFrame(() => {
217
+ const scrollPropertyTop = 'scrol' + 'lTop';
218
+ const scrollPropertyHeight = 'scrol' + 'lHeight';
219
+ log[scrollPropertyTop] = log[scrollPropertyHeight];
220
+ });
221
+ }
222
+
223
+ function updateMetrics() {
224
+ if (byteCount) byteCount.textContent = String(bytes);
225
+ if (lineCount) lineCount.textContent = String(lines);
226
+ }
227
+
228
+ function updatePortLabel(selectedPort: SerialPortLike) {
229
+ const info = selectedPort.getInfo();
230
+ const identity = `${config.vidPrefix}${formatUsbId(info.usbVendorId)}${config.pidSeparator}${formatUsbId(info.usbProductId)}`;
231
+ if (deviceName) deviceName.textContent = guessDeviceName(info);
232
+ if (portLabel) portLabel.textContent = `${identity} · ${baud?.value ?? '115200'}${config.baudUnit}`;
233
+ if (portId) portId.textContent = identity;
234
+ }
235
+
236
+ async function readLoop(selectedPort: SerialPortLike) {
237
+ while (selectedPort.readable) {
238
+ reader = selectedPort.readable.getReader();
239
+ try {
240
+ while (true) {
241
+ const { value, done } = await reader.read();
242
+ if (done) break;
243
+ if (!value) continue;
244
+ bytes += value.byteLength;
245
+ const chunk = decoder.decode(value);
246
+ lines += (chunk.match(/\n/g) ?? []).length;
247
+ appendLog(chunk);
248
+ updateMetrics();
249
+ }
250
+ } catch {
251
+ setStatus(config.statusDisconnected, 'warn');
252
+ } finally {
253
+ reader.releaseLock();
254
+ reader = undefined;
255
+ }
256
+ break;
257
+ }
258
+ }
259
+
260
+ function resetDisconnectedUI() {
261
+ if (deviceName) deviceName.textContent = config.portFallback;
262
+ if (portLabel) portLabel.textContent = config.transportLabel;
263
+ if (portId) portId.textContent = config.portFallback;
264
+ if (connectButton) connectButton.querySelector('span')!.textContent = config.connect;
265
+ }
266
+
267
+ async function disconnect() {
268
+ try {
269
+ await reader?.cancel();
270
+ writer?.releaseLock();
271
+ writer = undefined;
272
+ await port?.close();
273
+ } catch {
274
+ setStatus(config.statusError, 'error');
275
+ } finally {
276
+ port = undefined;
277
+ resetDisconnectedUI();
278
+ setStatus(config.statusDisconnected, 'warn');
279
+ }
280
+ }
281
+
282
+ async function openSerialPort() {
283
+ const selectedPort = await (navigator as SerialNavigator).serial!.requestPort();
284
+ const rate = Number(baud?.value ?? 115200);
285
+ await selectedPort.open({ baudRate: rate });
286
+ return selectedPort;
287
+ }
288
+
289
+ async function connect() {
290
+ if (port) {
291
+ await disconnect();
292
+ return;
293
+ }
294
+ if (!('serial' in navigator)) {
295
+ setStatus(config.unsupportedTitle, 'error');
296
+ appendLog(`${config.unsupportedBody}\n`, config.logDirectionSys);
297
+ return;
298
+ }
299
+ if (!window.isSecureContext) {
300
+ setStatus(config.unsupportedTitle, 'error');
301
+ appendLog(`${config.secureContext}\n`, config.logDirectionSys);
302
+ return;
303
+ }
304
+ try {
305
+ setStatus(config.statusPermission, 'busy');
306
+ port = await openSerialPort();
307
+ setStatus(config.statusOpening, 'busy');
308
+ writer = port.writable?.getWriter();
309
+ updatePortLabel(port);
310
+ setStatus(config.statusConnected, 'ok');
311
+ if (connectButton) connectButton.querySelector('span')!.textContent = config.disconnect;
312
+ readLoop(port);
313
+ } catch {
314
+ setStatus(config.statusError, 'error');
315
+ }
316
+ }
317
+
318
+ async function sendLine(text: string) {
319
+ if (!writer || !text) return;
320
+ const payload = newline?.checked ? `${text}\r\n` : text;
321
+ await writer.write(encoder.encode(payload));
322
+ appendLog(`${payload}\n`, config.logDirectionTx);
323
+ }
324
+
325
+ connectButton?.addEventListener('click', connect);
326
+ form?.addEventListener('submit', (event) => {
327
+ event.preventDefault();
328
+ const text = input?.value ?? '';
329
+ sendLine(text);
330
+ if (input) input.value = '';
331
+ });
332
+ pauseButton?.addEventListener('click', () => {
333
+ paused = !paused;
334
+ if (pauseButton) pauseButton.textContent = paused ? config.resume : config.pause;
335
+ });
336
+ clearButton?.addEventListener('click', () => {
337
+ buffer = '';
338
+ bytes = 0;
339
+ lines = 0;
340
+ if (log) log.textContent = config.emptyLog;
341
+ updateMetrics();
342
+ });
343
+ copyButton?.addEventListener('click', async () => {
344
+ await navigator.clipboard?.writeText(buffer || log?.textContent || '');
345
+ const original = copyButton.textContent;
346
+ copyButton.textContent = config.copied;
347
+ window.setTimeout(() => {
348
+ copyButton.textContent = original;
349
+ }, 1200);
350
+ });
351
+ root?.querySelectorAll<HTMLButtonElement>('[data-preset]').forEach((button) => {
352
+ button.addEventListener('click', () => {
353
+ if (baud) baud.value = button.dataset.preset ?? '115200';
354
+ });
355
+ });
356
+ </script>
@@ -0,0 +1,30 @@
1
+ import type { HardwareToolEntry, ToolLocaleContent } from '../../types';
2
+ import type { WebUsbSerialMonitorUI } from './ui';
3
+
4
+ export type WebUsbSerialMonitorLocaleContent = ToolLocaleContent<WebUsbSerialMonitorUI>;
5
+
6
+ export const webUsbSerialMonitor: HardwareToolEntry<WebUsbSerialMonitorUI> = {
7
+ id: 'web-usb-serial-monitor',
8
+ icons: {
9
+ bg: 'mdi:usb-port',
10
+ fg: 'mdi:console-line',
11
+ },
12
+ i18n: {
13
+ de: () => import('./i18n/de').then((m) => m.content),
14
+ en: () => import('./i18n/en').then((m) => m.content),
15
+ es: () => import('./i18n/es').then((m) => m.content),
16
+ fr: () => import('./i18n/fr').then((m) => m.content),
17
+ id: () => import('./i18n/id').then((m) => m.content),
18
+ it: () => import('./i18n/it').then((m) => m.content),
19
+ ja: () => import('./i18n/ja').then((m) => m.content),
20
+ ko: () => import('./i18n/ko').then((m) => m.content),
21
+ nl: () => import('./i18n/nl').then((m) => m.content),
22
+ pl: () => import('./i18n/pl').then((m) => m.content),
23
+ pt: () => import('./i18n/pt').then((m) => m.content),
24
+ ru: () => import('./i18n/ru').then((m) => m.content),
25
+ sv: () => import('./i18n/sv').then((m) => m.content),
26
+ tr: () => import('./i18n/tr').then((m) => m.content),
27
+ zh: () => import('./i18n/zh').then((m) => m.content),
28
+ },
29
+ };
30
+
@@ -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-serieller-monitor';
7
+ const title = 'WebUSB Serieller Monitor';
8
+ const description = 'Verbinden Sie sich vom Browser aus mit USB-Seriell-Hardware, lesen Sie Live-Terminalausgaben, senden Sie Befehle und debuggen Sie Arduino, ESP32, RP2040 und Maker-Boards ohne Installation eines Desktop-Terminals.';
9
+
10
+ const faqData = [
11
+ {
12
+ question: 'Funktioniert dieser serielle Monitor mit Arduino, ESP32 und Raspberry Pi Pico Boards?',
13
+ answer: 'Ja, wenn das Board eine von Web Serial unterstützte USB-Seriell-Schnittstelle bereitstellt und der Browser Chromium-basiert ist. Gängige Arduino, ESP32, RP2040, CH340, CP210x und FTDI Adapter funktionieren in der Regel, nachdem der Benutzer die Berechtigung erteilt hat.',
14
+ },
15
+ {
16
+ question: 'Warum heißt das WebUSB, wenn es Web Serial verwendet?',
17
+ answer: 'Die meisten Maker-Boards verbinden sich über USB, aber der Browser-Terminalzugriff wird von der Web Serial API bereitgestellt. WebUSB ist niedriger angesiedelt und nicht die richtige Abstraktion für ein einfaches UART-Terminal.',
18
+ },
19
+ {
20
+ question: 'Kann eine Website ohne Erlaubnis auf meine seriellen Geräte zugreifen?',
21
+ answer: 'Nein. Der Browser erfordert einen Benutzerklick und eine native Geräteauswahl, bevor eine Seite einen seriellen Port öffnen kann. Dieses Tool speichert keine Terminalprotokolle oder Gerätekennungen.',
22
+ },
23
+ {
24
+ question: 'Welchen Browser sollte ich für ein Web-Seriell-Terminal verwenden?',
25
+ answer: 'Verwenden Sie Chrome, Edge oder einen anderen Chromium-basierten Browser über HTTPS oder localhost. Firefox, Safari und viele iOS-Browser stellen die Web Serial API nicht zur Verfügung.',
26
+ },
27
+ {
28
+ question: 'Welche Baudrate sollte ich wählen?',
29
+ answer: 'Wählen Sie die in Ihrer Firmware konfigurierte Baudrate. Arduino-Beispiele verwenden oft 9600 oder 115200, während schnellere Protokolle, Bootloader und hochratige Sensorstreams 230400, 460800 oder 921600 verwenden können.',
30
+ },
31
+ ];
32
+
33
+ const howToData = [
34
+ {
35
+ name: 'Das USB-Seriell-Gerät anschließen',
36
+ text: 'Schließen Sie das Board oder den Adapter an und schließen Sie alle anderen seriellen Terminals, die den Port möglicherweise bereits geöffnet haben.',
37
+ },
38
+ {
39
+ name: 'Die Baudrate auswählen',
40
+ text: 'Wählen Sie dieselbe Baudrate, die von der Firmware verwendet wird, z. B. 115200 für viele Arduino, ESP32 und RP2040 Sketche.',
41
+ },
42
+ {
43
+ name: 'Browser-Berechtigung erteilen',
44
+ text: 'Drücken Sie Verbinden, wählen Sie das serielle Gerät im Browser-Auswahldialog und erlauben Sie der Seite, den Port zu öffnen.',
45
+ },
46
+ {
47
+ name: 'Terminaldaten lesen und senden',
48
+ text: 'Beobachten Sie eingehende Protokolle im Terminal, senden Sie Befehle mit optionalen CRLF-Zeilenenden und löschen oder pausieren Sie die Live-Ausgabe bei Bedarf.',
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: 'de',
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: 'Online Serieller Monitor für USB Maker Hardware',
98
+ level: 2,
99
+ },
100
+ {
101
+ type: 'paragraph',
102
+ html: 'Dieser Browser-Seriell-Monitor öffnet einen USB-Seriell-Port direkt aus Chrome oder Edge und streamt dann Text von Mikrocontrollern, USB-UART-Brücken, Entwicklungsboards, Bootloadern, Testvorrichtungen, Sensoren und Laborhardware. Er ist für schnelle Diagnosen konzipiert, wenn Sie eine serielle Konsole benötigen, aber keine Desktop-IDE oder Terminalanwendung installieren möchten.',
103
+ },
104
+ {
105
+ type: 'message',
106
+ title: 'Browser Berechtigungsgrenze',
107
+ html: 'Die Seite kann Ihre seriellen Geräte nicht stillschweigend auflisten oder öffnen. Der Zugriff beginnt erst, nachdem Sie Verbinden drücken und einen Port im Browser-Auswahldialog auswählen. Terminaldaten bleiben im aktuellen Tab, es sei denn, Sie kopieren sie selbst.',
108
+ },
109
+ {
110
+ type: 'stats',
111
+ items: [
112
+ { value: '9600-921600', label: 'übliche Baud-Voreinstellungen' },
113
+ { value: 'CRLF', label: 'optionaler Befehlsabschluss' },
114
+ { value: 'lokal', label: 'Terminalsitzung' },
115
+ ],
116
+ },
117
+ {
118
+ type: 'title',
119
+ text: 'Wann ein Web-Seriell-Terminal nützlich ist',
120
+ level: 3,
121
+ },
122
+ {
123
+ type: 'list',
124
+ items: [
125
+ 'Überprüfen von Boot-Meldungen von Arduino, ESP32, ESP8266, RP2040, STM32 oder benutzerdefinierter Firmware.',
126
+ 'Senden von AT-Befehlen an Modem, GPS, LoRa, Wi-Fi, Bluetooth oder Mobilfunkmodule über einen USB-UART-Adapter.',
127
+ 'Lesen von Sensorausgaben von einem Werksprüfstand, Klassenraumlabor, Robotik-Controller oder Tischprototyp.',
128
+ 'Überprüfen, ob ein USB-Seriell-Brückentreiber, Kabel, Board-Stromversorgung und Firmware-Baudrate alle zusammen funktionieren.',
129
+ 'Sammeln eines schnellen Fehlerprotokolls vor dem Einreichen eines Bugs oder der Anfrage von Hardware-Support.',
130
+ ],
131
+ },
132
+ {
133
+ type: 'comparative',
134
+ items: [
135
+ {
136
+ title: 'Web Seriell Monitor',
137
+ description: 'Am besten für schnellen Support, Unterrichtsanleitungen, Felddiagnosen und Maker-Workflows, bei denen das Öffnen einer URL schneller ist als die Installation einer IDE.',
138
+ },
139
+ {
140
+ title: 'Desktop Terminal',
141
+ description: 'Besser für Binärprotokolle, lange Aufzeichnungssitzungen, Skripterstellung, Hardware-Flusskontrolle, Makros und Umgebungen, in denen Browser-APIs blockiert sind.',
142
+ },
143
+ ],
144
+ },
145
+ {
146
+ type: 'title',
147
+ text: 'Baudrate und Zeilenende Checkliste',
148
+ level: 3,
149
+ },
150
+ {
151
+ type: 'table',
152
+ headers: ['Einstellung', 'Typische Wahl', 'Was schief geht, wenn es falsch ist'],
153
+ rows: [
154
+ ['Baudrate', '115200 für viele moderne Boards, 9600 für ältere Beispiele.', 'Lesbarer Text wird zu zufälligen Symbolen oder es erscheint kein nützliches Protokoll.'],
155
+ ['Zeilenende', 'CRLF für viele Befehlsparser, kein Ende für rohe Zeichenprotokolle.', 'Befehle werden ignoriert, weil die Firmware auf einen Abschluss wartet.'],
156
+ ['Exklusiver Portzugriff', 'Arduino Serial Monitor, PuTTY, screen, minicom oder Herstellertools schließen.', 'Der Browser-Auswahldialog öffnet den Port, aber Lesen oder Schreiben schlägt fehl.'],
157
+ ['Sicherer Kontext', 'HTTPS oder localhost.', 'Die Serial API fehlt selbst in einem unterstützten Browser.'],
158
+ ],
159
+ },
160
+ {
161
+ type: 'diagnostic',
162
+ variant: 'warning',
163
+ title: 'Keine serielle Ausgabe?',
164
+ html: 'Stellen Sie sicher, dass das Board mit Strom versorgt wird und das USB-Kabel Daten unterstützt, nicht nur Laden. Versuchen Sie 9600, 57600 und 115200, wenn Sie die Firmware-Baudrate nicht kennen. Drücken Sie nach dem Verbinden Reset, da viele Boards Boot-Protokolle nur beim Start ausgeben. Schließen Sie andere Software, die den seriellen Port noch besitzen könnte, und installieren Sie den Betriebssystemtreiber für CH340, CP210x, FTDI oder den Board-Hersteller, wenn das Gerät nie erscheint.',
165
+ },
166
+ {
167
+ type: 'title',
168
+ text: 'Datenschutz, Sicherheit und Grenzen',
169
+ level: 3,
170
+ },
171
+ {
172
+ type: 'proscons',
173
+ title: 'Web Serial Stärken und Grenzen',
174
+ items: [
175
+ {
176
+ pro: 'Keine Desktop-Installation für einfache serielle Textdiagnosen.',
177
+ con: 'Erfordert einen Chromium-basierten Browser und einen sicheren Kontext.',
178
+ },
179
+ {
180
+ pro: 'Browser-Auswahldialog beschränkt den Zugriff auf den von Ihnen gewählten Port.',
181
+ con: 'Nicht für binäre Protokollanalysatoren oder lange unbeaufsichtigte Aufzeichnungen gedacht.',
182
+ },
183
+ {
184
+ pro: 'Funktioniert gut für Textprotokolle, Eingabeaufforderungen und schnelle Hardwareprüfungen.',
185
+ con: 'Einige Unternehmensrichtlinien, mobile Browser und Betriebssysteme blockieren Web Serial.',
186
+ },
187
+ ],
188
+ },
189
+ ],
190
+ ui: {
191
+ unsupportedTitle: 'Web Serial ist nicht verfügbar',
192
+ unsupportedBody: 'Verwenden Sie Chrome oder Edge über HTTPS oder localhost und stellen Sie sicher, dass Ihr Gerät eine USB-Seriell-Schnittstelle bereitstellt.',
193
+ secureContext: 'Web Serial erfordert HTTPS oder localhost. Laden Sie diese Seite von einer sicheren Herkunft neu und versuchen Sie es erneut.',
194
+ statusIdle: 'Wählen Sie eine Baudrate und verbinden Sie dann ein USB-Seriell-Gerät',
195
+ statusPermission: 'Warten auf den Browser-Seriell-Port-Auswahldialog',
196
+ statusOpening: 'Seriellen Port öffnen',
197
+ statusConnected: 'Serieller Port verbunden',
198
+ statusDisconnected: 'Serieller Port getrennt',
199
+ statusError: 'Serielle Verbindung fehlgeschlagen',
200
+ connect: 'Seriell verbinden',
201
+ disconnect: 'Trennen',
202
+ send: 'Senden',
203
+ clear: 'Löschen',
204
+ pause: 'Pause',
205
+ resume: 'Fortsetzen',
206
+ baudRate: 'Baudrate',
207
+ newline: 'CRLF anhängen',
208
+ inputPlaceholder: 'Befehl eingeben, dann Enter drücken',
209
+ portFallback: 'Kein Port ausgewählt',
210
+ portLabel: 'Port-Identität',
211
+ connectedDeviceLabel: 'Verbundenes Gerät',
212
+ deviceNameFallback: 'USB-Seriell-Gerät',
213
+ transportLabel: 'Web Serial Verbindung',
214
+ bytesLabel: 'Bytes',
215
+ linesLabel: 'Zeilen',
216
+ privacyTitle: 'Berechtigungsgesteuert',
217
+ privacyBody: 'Der Browser zeigt nur das von Ihnen ausgewählte serielle Gerät an. Protokolle bleiben in diesem Tab, es sei denn, Sie kopieren sie.',
218
+ emptyLog: 'Die Terminalausgabe erscheint hier, nachdem Sie ein serielles Gerät verbunden haben.',
219
+ copied: 'Kopiert',
220
+ copyLog: 'Kopieren',
221
+ presetSlow: '9600',
222
+ presetArduino: '115200',
223
+ presetFast: '921600',
224
+ terminalLabel: 'Live-Terminal',
225
+ unknownUsbId: 'N/A',
226
+ logDirectionRx: 'rx',
227
+ logDirectionTx: 'tx',
228
+ logDirectionSys: 'sys',
229
+ vidPrefix: 'VID ',
230
+ pidSeparator: ' / PID ',
231
+ baudUnit: ' Baud',
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
+ };