diff --git a/lib/device.ts b/lib/device.ts index 0714cf3..c256831 100644 --- a/lib/device.ts +++ b/lib/device.ts @@ -116,7 +116,6 @@ export interface FlashDataSource { } export interface ConnectOptions { - serial?: boolean; // Name filter used for Web Bluetooth name?: string; } diff --git a/lib/usb.ts b/lib/usb.ts index 767ab7a..74dbc5f 100644 --- a/lib/usb.ts +++ b/lib/usb.ts @@ -26,6 +26,10 @@ import { } from "./device.js"; import { TypedEventTarget } from "./events.js"; +interface InternalConnectOptions extends ConnectOptions { + serial?: boolean; +} + // Temporary workaround for ChromeOS 105 bug. // See https://bugs.chromium.org/p/chromium/issues/detail?id=1363712&q=usb&can=2 export const isChromeOS105 = (): boolean => { @@ -120,7 +124,9 @@ export class MicrobitWebUSBConnection setTimeout(() => { if (this.status === ConnectionStatus.CONNECTED) { this.unloading = false; - this.startSerialInternal(); + if (this.addedListeners.serialdata) { + this.startSerialInternal(); + } } }, assumePageIsStayingOpenDelay); }, @@ -130,11 +136,26 @@ export class MicrobitWebUSBConnection private logging: Logging; + private _addEventListener = this.addEventListener; + private _removeEventListener = this.removeEventListener; + + private addedListeners = { + serialdata: false, + }; + constructor( options: MicrobitWebUSBConnectionOptions = { logging: new NullLogging() }, ) { super(); this.logging = options.logging; + this.addEventListener = (type, ...args) => { + this._addEventListener(type, ...args); + this.startNotifications(type); + }; + this.removeEventListener = (type, ...args) => { + this.stopNotifications(type); + this._removeEventListener(type, ...args); + }; } private log(v: any) { @@ -250,10 +271,12 @@ export class MicrobitWebUSBConnection } else { // This might not strictly be "reinstating". We should make this // behaviour configurable when pulling out a library. - this.log("Reinstating serial after flash"); - if (this.connection.daplink) { - await this.connection.daplink.connect(); - await this.startSerialInternal(); + if (this.addedListeners.serialdata) { + this.log("Reinstating serial after flash"); + if (this.connection.daplink) { + await this.connection.daplink.connect(); + await this.startSerialInternal(); + } } } } @@ -381,13 +404,15 @@ export class MicrobitWebUSBConnection this.setStatus(ConnectionStatus.NO_AUTHORIZED_DEVICE); } - private async connectInternal(options: ConnectOptions): Promise { + private async connectInternal( + options: InternalConnectOptions, + ): Promise { if (!this.connection) { const device = await this.chooseDevice(); this.connection = new DAPWrapper(device, this.logging); } await withTimeout(this.connection.reconnectAsync(), 10_000); - if (options.serial === undefined || options.serial) { + if (this.addedListeners.serialdata && options.serial !== false) { this.startSerialInternal(); } this.setStatus(ConnectionStatus.CONNECTED); @@ -404,6 +429,26 @@ export class MicrobitWebUSBConnection this.dispatchTypedEvent("afterrequestdevice", new AfterRequestDevice()); return this.device; } + + private async startNotifications(type: string) { + switch (type as keyof DeviceConnectionEventMap) { + case "serialdata": { + this.startSerialInternal(); + this.addedListeners.serialdata = true; + break; + } + } + } + + private async stopNotifications(type: string) { + switch (type as keyof DeviceConnectionEventMap) { + case "serialdata": { + this.stopSerialInternal(); + this.addedListeners.serialdata = false; + break; + } + } + } } const genericErrorSuggestingReconnect = (e: any) => diff --git a/src/demo.ts b/src/demo.ts index 98257d0..f42640b 100644 --- a/src/demo.ts +++ b/src/demo.ts @@ -11,6 +11,7 @@ import { ConnectionStatus, ConnectionStatusEvent, DeviceConnection, + SerialDataEvent, } from "../lib/device"; import { MicrobitWebBluetoothConnection } from "../lib/bluetooth"; import { AccelerometerDataEvent } from "../lib/accelerometer"; @@ -30,6 +31,10 @@ document.querySelector("#app")!.innerHTML = `

+
+ + +
@@ -73,6 +78,12 @@ const accPeriodInput = document.querySelector( const accPeriodSet = document.querySelector( "#flash > .acc-period-controls > .acc-period-set", )!; +const serialListen = document.querySelector( + "#flash > .serial-controls > .serial-listen", +)!; +const serialStop = document.querySelector( + "#flash > .serial-controls > .serial-stop", +)!; const displayStatus = (status: ConnectionStatus) => { statusParagraph.textContent = status.toString(); @@ -212,3 +223,23 @@ accPeriodSet.addEventListener("click", async () => { ); } }); + +let data = ""; +const serialDataListener = (event: SerialDataEvent) => { + for (const char of event.data) { + if (char === "\n") { + console.log(data); + data = ""; + } else { + data += char; + } + } +}; + +serialListen.addEventListener("click", async () => { + connection.addEventListener("serialdata", serialDataListener); +}); + +serialStop.addEventListener("click", async () => { + connection.removeEventListener("serialdata", serialDataListener); +});