Tak jsem potřeboval, respektive chtěl vyzkoušet, využít JavaScript Serial API pro komunikaci s Arduinem. Toto API podporují pouze Chrome, Edge a Opera (viz https://developer.mozilla.org/en-US/docs/Web/API/Serial). Jelikož mám Chrome, tak nic nebránilo tomu, abych to zkusil.
Důležité upozornění: Soubor HTML je nutno otevřít buď lokálně nebo ze zabezpečeného HTTPS, jinak je Serial API prohlížečem blokováno!

https://www.ctvrtky.info/wp-content/uploads/2025/05/Serial-API.html
Funguje to moc pěkně, tak zkusím v budoucnu i obousměrnou komunikaci. Například pro nastavení zařízení vypadá taková stránka mnohem lépe, než prostý příkazový řádek. Nehledě na to, že lze pomocí HTML + CSS + JS vytvořit již dostatečně robustní a designově hezkou aplikaci.
Sketch do Arduina
void setup() { Serial.begin(9600); sendJSON(); } void loop() { } void sendJSON() { while (Serial.available() <= 0) { String out = "{\"time\":"; out += millis(); out += "}"; Serial.println(out); delay(1000); } }
HTML + JS
<button hidden>open</button><br> <textarea cols="40" rows="5" hidden></textarea> <script> button = document.querySelector('button'); textOut = document.querySelector('textarea'); textOut.value = ""; // Třída pro ošetření čtení řádků oddělených \n class LineBreakTransformer { constructor() { this.chunks = ""; } transform(chunk, controller) { this.chunks += chunk; const lines = this.chunks.split("\n"); this.chunks = lines.pop(); lines.forEach((line) => controller.enqueue(line)); } flush(controller) { controller.enqueue(this.chunks); } } if('serial' in navigator) { button.hidden = false; button.addEventListener('click', async () => { try { // Výzva uživatele k vybrání portu const port = await navigator.serial.requestPort(); // Otevření portu await port.open({ baudRate: 9600 }); const textDecoder = new TextDecoderStream(); const readableStreamClosed = port.readable.pipeTo(textDecoder.writable); const reader = textDecoder.readable.pipeThrough(new TransformStream(new LineBreakTransformer())).getReader(); textOut.hidden = false; // Čekání na příchozí data z portu while(true) { const { value, done } = await reader.read(); if(done) { reader.releaseLock(); break; } // Hodnota je string try { var o = JSON.parse(value); if(o && typeof o === "object") { var d = new Date(); textOut.value += d.toLocaleTimeString() + ": " + o.time + '\r\n'; textOut.scrollTop = textOut.scrollHeight; } } catch (e) {} } const textEncoder = new TextEncoderStream(); const writableStreamClosed = textEncoder.readable.pipeTo(port.writable); reader.cancel(); await readableStreamClosed.catch(() => { // Ignorace chyby }); writer.close(); await writableStreamClosed; await port.close(); } catch (e) { // Uživatel nevybral žádný port } }); }else{ textOut.hidden = false; textOut.value = "Tento prohlížeč nepodporuje Serial API, nebo je soubor otevřen z nezabezpečené URL!" } function tryParseJSONObject(jsonString) { try { var o = JSON.parse(jsonString); if(o && typeof o === "object") { return o; } } catch (e) {} return false; }; </script>