diff --git a/HISTORY.md b/HISTORY.md index 942a240b..69814dcd 100755 --- a/HISTORY.md +++ b/HISTORY.md @@ -10,6 +10,9 @@ * **Notable Fixes:** * fixed occasional invisible error messages +### 2021-06-25 +* extensions: added first rough experimental version of web-serial extension primitives + ### 2021-06-24 * extensions: tweaked loading unlisted script-extensions * byob, threads, store: removed unused code diff --git a/snap.html b/snap.html index 42a950dd..0b0034c9 100755 --- a/snap.html +++ b/snap.html @@ -19,7 +19,7 @@ - + diff --git a/src/extensions.js b/src/extensions.js index 0ffbee87..4f3b71c8 100644 --- a/src/extensions.js +++ b/src/extensions.js @@ -31,7 +31,7 @@ IDE_Morph, CamSnapshotDialogMorph, SoundRecorderDialogMorph, isSnapObject, nop, Color, Process, contains*/ -modules.extensions = '2021-June-24'; +modules.extensions = '2021-June-25'; // Global stuff @@ -716,6 +716,107 @@ SnapExtensions.primitives.set( } ); +// web serial (srl_): // +++ under construction + +SnapExtensions.primitives.set( + 'srl_open', + function () { + + var world = this.world(); // +++ change to IDE + + async function webSerialConnect() { // +++ instead of async make it Snap! thread friendly + // Prompt user to choose a serial port and open the one selected. + + var vendorIDs = [ + {usbVendorId: 0x0403}, // FTDI + {usbVendorId: 0x0d28}, // micro:bit, Calliope + {usbVendorId: 0x10c4}, // Silicon Laboratories, Inc. (CP210x) + {usbVendorId: 0x1a86}, // CH340 + {usbVendorId: 0x239a}, // AdaFruit + {usbVendorId: 0x2a03}, // Arduino + {usbVendorId: 0x2341}, // Arduino MKR Zero + {usbVendorId: 0x03eb}, // Atmel Corporation + {usbVendorId: 0x1366}, // SEGGER Calliope mini + {usbVendorId: 0x16c0}, // Teensy + ]; + world.webSerialPort = await navigator.serial.requestPort({filters: vendorIDs}).catch((e) => { console.log(e); }); // +++ make "await" Snap! thread friendly, change UI if possible to a non-blocking Morphic menu/dialog + if (!world.webSerialPort) {return; } // no serial port selected + await world.webSerialPort.open({ baudRate: 115200 }); // +++ make "await" Snap! thread friendly + world.webSerialReader = await world.webSerialPort.readable.getReader(); // +++ make "await" Snap! thread friendly + webSerialReadLoop(); + } + + async function webSerialReadLoop() { + try { + while (true) { // +++ should be improved, make thread-friendly + var {value, done} = await world.webSerialReader.read(); // +++ make "await" Snap! thread friendly + if (value) { + world.serialInputBuffers.push(Array.from(value)); + } + if (done) { // happens when world.webSerialReader.cancel() is called by disconnect + world.webSerialReader.releaseLock(); + return; + } + } + } catch (e) { // happens when board is unplugged + // console.log(e); + if (world.webSerialPort) await world.webSerialPort.close().catch(() => {}); // +++ make "await" Snap! thread friendly + world.webSerialPort = null; // +++ reformulate "world", use IDE reference + world.webSerialReader = null; + } + } + + world.serialInputBuffers = []; + webSerialConnect(); + } +); + +SnapExtensions.primitives.set( + 'srl_close', + function () { + var world = this.world(); // +++ change to IDE + + async function webSerialDisconnect() { // +++ make async thread friendly + if (world.webSerialReader) await world.webSerialReader.cancel(); // +++ make "await" Snap! thread friendly + if (world.webSerialPort) await world.webSerialPort.close().catch(() => {}); // +++ make "await" Snap! thread friendly + world.webSerialReader = null; + world.webSerialPort = null; + } + + webSerialDisconnect(); + } +); + +SnapExtensions.primitives.set( + 'srl_read', + function () { + var world = this.world(); // +++ change to IDE + + function webSerialRead() { // +++ do we need this function at all? Why not run this code directly? + if (world.serialInputBuffers && (world.serialInputBuffers.length > 0)) { + var result = [].concat.apply([], world.serialInputBuffers); + world.serialInputBuffers = []; + return new List(result); + } + return ''; + } + + return webSerialRead(); + } +); + +SnapExtensions.primitives.set( + 'srl_write(dta)', + function (data) { + var world = this.world(); // +++ change to IDE + + if (!world.webSerialPort || !world.webSerialPort.writable) return 0; // port not open + const w = world.webSerialPort.writable.getWriter(); + w.write(data.buffer); + w.releaseLock(); + } +); + // loading external scripts (src_) SnapExtensions.primitives.set(