Implement WebSerial I/O
This commit is contained in:
parent
444ca860fa
commit
d85bf03011
130
webserial/src/serial/SerialConnection.svelte
Normal file
130
webserial/src/serial/SerialConnection.svelte
Normal file
@ -0,0 +1,130 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { onMount } from "svelte";
|
||||||
|
|
||||||
|
export let port: SerialPort;
|
||||||
|
export let fps: number = 50;
|
||||||
|
|
||||||
|
const FRAME_TIME = 1000 / fps; // milliseconds
|
||||||
|
|
||||||
|
let lines = [];
|
||||||
|
let linesResolve: (value: string | PromiseLike<string>) => void = null;
|
||||||
|
|
||||||
|
function readLine(): Promise<string> {
|
||||||
|
if (lines.length > 0) {
|
||||||
|
return Promise.resolve(lines.shift());
|
||||||
|
} else {
|
||||||
|
// lines is empty, register callbacks
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
linesResolve = resolve;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleLine = (line: string) => {
|
||||||
|
if (linesResolve !== null) {
|
||||||
|
linesResolve(line);
|
||||||
|
linesResolve = null;
|
||||||
|
} else {
|
||||||
|
lines.push(line);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let running = true;
|
||||||
|
async function readLoop() {
|
||||||
|
console.log("runner started");
|
||||||
|
await port.open({ baudRate: 500_000 });
|
||||||
|
let reader = port.readable.getReader();
|
||||||
|
let data = "";
|
||||||
|
try {
|
||||||
|
while (running) {
|
||||||
|
const { value, done } = await reader.read();
|
||||||
|
if (done) break;
|
||||||
|
|
||||||
|
const chunk = new TextDecoder().decode(value);
|
||||||
|
data += chunk;
|
||||||
|
|
||||||
|
const lines = data.split("\r\n");
|
||||||
|
data = lines.pop();
|
||||||
|
for (let line of lines) {
|
||||||
|
handleLine(line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
reader.releaseLock();
|
||||||
|
}
|
||||||
|
await port.close();
|
||||||
|
console.log("runner stopped");
|
||||||
|
}
|
||||||
|
let readLoopHandle: Promise<void> = null;
|
||||||
|
|
||||||
|
async function sync() {
|
||||||
|
while (running) {
|
||||||
|
const line = await readLine();
|
||||||
|
if (line === "Sync.") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let payload = new Uint8Array(512);
|
||||||
|
|
||||||
|
const sleep = (ms: number) =>
|
||||||
|
new Promise((resolve) => setTimeout(resolve, ms));
|
||||||
|
|
||||||
|
let lastLoopTime: number = null;
|
||||||
|
|
||||||
|
async function writeLoop() {
|
||||||
|
await sync();
|
||||||
|
while (running) {
|
||||||
|
const loopStart = performance.now();
|
||||||
|
|
||||||
|
// write out payload
|
||||||
|
let writer = port.writable.getWriter();
|
||||||
|
try {
|
||||||
|
await writer.write(payload);
|
||||||
|
} finally {
|
||||||
|
writer.releaseLock();
|
||||||
|
}
|
||||||
|
|
||||||
|
// read response
|
||||||
|
const response = await readLine();
|
||||||
|
if (response !== "Ack.") {
|
||||||
|
console.log(`received bad response: "${response}"`);
|
||||||
|
lastLoopTime = null;
|
||||||
|
await sync();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const loopTime = performance.now() - loopStart;
|
||||||
|
if (loopTime < FRAME_TIME) {
|
||||||
|
await sleep(FRAME_TIME - loopTime);
|
||||||
|
} else {
|
||||||
|
console.warn(`loop took too long (+${(loopTime - FRAME_TIME).toFixed(2)} ms)`);
|
||||||
|
}
|
||||||
|
lastLoopTime = loopTime;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let writeLoopHandle: Promise<void> = null;
|
||||||
|
|
||||||
|
onMount(() => {
|
||||||
|
console.log("mount");
|
||||||
|
readLoopHandle = readLoop();
|
||||||
|
writeLoopHandle = writeLoop();
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
console.log("unmount");
|
||||||
|
running = false;
|
||||||
|
if (linesResolve !== null) {
|
||||||
|
linesResolve("");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
{#if lastLoopTime !== null}
|
||||||
|
{lastLoopTime.toFixed(2).padStart(5)}
|
||||||
|
{:else}
|
||||||
|
syncing
|
||||||
|
{/if}
|
||||||
|
</p>
|
@ -1,5 +1,5 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import SerialReader from "./SerialReader.svelte";
|
import SerialConnection from "./SerialConnection.svelte";
|
||||||
|
|
||||||
let port: SerialPort = null;
|
let port: SerialPort = null;
|
||||||
|
|
||||||
@ -14,9 +14,9 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if navigator.serial}
|
{#if navigator.serial}
|
||||||
<p>serial baby</p>
|
<p>Serial available 🚀</p>
|
||||||
{#if port !== null}
|
{#if port !== null}
|
||||||
<SerialReader {port}/>
|
<SerialConnection {port}/>
|
||||||
<p><button on:click={disconnect}>disconnect</button></p>
|
<p><button on:click={disconnect}>disconnect</button></p>
|
||||||
{:else}
|
{:else}
|
||||||
<p><button on:click={connect}>connect</button></p>
|
<p><button on:click={connect}>connect</button></p>
|
||||||
|
@ -1,57 +0,0 @@
|
|||||||
<script lang="ts">
|
|
||||||
import { onMount } from "svelte";
|
|
||||||
|
|
||||||
export let port: SerialPort;
|
|
||||||
|
|
||||||
let lines = [];
|
|
||||||
|
|
||||||
const handleLine = (line: string) => {
|
|
||||||
lines.push(line);
|
|
||||||
if (lines.length > 10) lines.shift();
|
|
||||||
lines = lines;
|
|
||||||
}
|
|
||||||
|
|
||||||
let running = true;
|
|
||||||
async function runner() {
|
|
||||||
console.log("runner started");
|
|
||||||
await port.open({baudRate: 500_000});
|
|
||||||
let reader = port.readable.getReader();
|
|
||||||
let data = "";
|
|
||||||
try {
|
|
||||||
while (running) {
|
|
||||||
const { value, done } = await reader.read();
|
|
||||||
if (done) break;
|
|
||||||
|
|
||||||
const chunk = new TextDecoder().decode(value);
|
|
||||||
data += chunk;
|
|
||||||
|
|
||||||
const lines = data.split("\n");
|
|
||||||
data = lines.pop();
|
|
||||||
for (let line of lines) {
|
|
||||||
handleLine(line);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
reader.releaseLock();
|
|
||||||
}
|
|
||||||
await port.close();
|
|
||||||
console.log("runner stopped");
|
|
||||||
}
|
|
||||||
let handle: Promise<void> = null;
|
|
||||||
|
|
||||||
onMount(() => {
|
|
||||||
console.log("mount");
|
|
||||||
handle = runner();
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
console.log("unmount");
|
|
||||||
running = false;
|
|
||||||
};
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
{#each lines as line}
|
|
||||||
<div>{line}</div>
|
|
||||||
{/each}
|
|
||||||
</div>
|
|
Loading…
Reference in New Issue
Block a user