From caea7bb39d174095531cb86080c3552735139137 Mon Sep 17 00:00:00 2001 From: Alex McLean Date: Fri, 2 Dec 2022 10:45:01 +0000 Subject: [PATCH] Support sending CRC16 bytes with serial messages (#276) Optionally add a crc16 checksum. Optionally support sending names in name/value pairs as single characters --- packages/serial/serial.mjs | 78 +++++++++++++++++++++++++++++--------- 1 file changed, 60 insertions(+), 18 deletions(-) diff --git a/packages/serial/serial.mjs b/packages/serial/serial.mjs index 27f2bb58..e5e7c188 100644 --- a/packages/serial/serial.mjs +++ b/packages/serial/serial.mjs @@ -6,7 +6,7 @@ This program is free software: you can redistribute it and/or modify it under th import { Pattern, isPattern } from '@strudel.cycles/core'; -var serialWriter; +var writeMessage; var choosing = false; export async function getWriter(br = 38400) { @@ -14,18 +14,30 @@ export async function getWriter(br = 38400) { return; } choosing = true; - if (serialWriter) { - return serialWriter; + if (writeMessage) { + return writeMessage; } if ('serial' in navigator) { const port = await navigator.serial.requestPort(); await port.open({ baudRate: br }); - // eslint-disable-next-line no-undef - const textEncoder = new TextEncoderStream(); - const writableStreamClosed = textEncoder.readable.pipeTo(port.writable); - const writer = textEncoder.writable.getWriter(); - serialWriter = function (message) { - writer.write(message); + const encoder = new TextEncoder(); + const writer = port.writable.getWriter(); + writeMessage = function (message, chk) { + const encoded = encoder.encode(message) + if (!chk) { + writer.write(encoded); + } + else { + const bytes = new Uint8Array(4); + bytes[0] = 124; // | symbol + bytes[1] = (chk >> 8) & 0xFF; + bytes[2] = chk & 0xFF; + bytes[3] = 59; // semicolon + const withchk = new Uint8Array(encoded.length+4) + withchk.set(encoded); + withchk.set(bytes, encoded.length); + writer.write(withchk); + } }; } else { throw 'Webserial is not available in this browser.'; @@ -34,18 +46,41 @@ export async function getWriter(br = 38400) { const latency = 0.1; -Pattern.prototype.serial = function (...args) { - return this._withHap((hap) => { - if (!serialWriter) { - getWriter(...args); +// crc16 (CCITT-FALSE) https://gist.github.com/tijnkooijmans/10981093 +function crc16(data) { + const length = data.length + if (length == 0) { + return 0; + } + + var crc = 0xFFFF; + for (var i = 0; i < length; ++i) { + crc ^= data.charCodeAt(i) << 8; + for (var j = 0; j < 8; ++j) { + crc = (crc & 0x8000) > 0 ? (crc << 1) ^ 0x1021 : crc << 1; + } + } + + return crc & 0xffff; +} + +Pattern.prototype.serial = function (br=38400,sendcrc=false,singlecharids=false) { + return this.withHap((hap) => { + if (!writeMessage) { + getWriter(br); } const onTrigger = (time, hap, currentTime) => { var message = ''; + var chk = 0; if (typeof hap.value === 'object') { if ('action' in hap.value) { - message += hap.value['action'] + '('; + var action = hap.value['action']; + if (singlecharids) { + action = action.charAt(0); + } + message += action + '('; var first = true; - for (const [key, val] of Object.entries(hap.value)) { + for (var [key, val] of Object.entries(hap.value)) { if (key === 'action') { continue; } @@ -54,19 +89,26 @@ Pattern.prototype.serial = function (...args) { } else { message += ','; } - message += `${key}:${val}`; + if (singlecharids) { + key = key.charAt(0); + } + message += key + ':' + val; } message += ')'; + if (sendcrc) { + chk = crc16(message) + } } else { for (const [key, val] of Object.entries(hap.value)) { - message += `${key}:${val};`; + message += `${key}:${val}`; } } } else { message = hap.value; } const offset = (time - currentTime + latency) * 1000; - window.setTimeout(serialWriter, offset, message); + + window.setTimeout(function () {writeMessage(message, chk)}, offset); }; return hap.setContext({ ...hap.context, onTrigger, dominantTrigger: true }); });