Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion lib/device.ts
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,6 @@ export interface FlashDataSource {
}

export interface ConnectOptions {
serial?: boolean;
// Name filter used for Web Bluetooth
name?: string;
}
Expand Down
59 changes: 52 additions & 7 deletions lib/usb.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 => {
Expand Down Expand Up @@ -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);
},
Expand All @@ -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) {
Expand Down Expand Up @@ -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();
}
}
}
}
Expand Down Expand Up @@ -381,13 +404,15 @@ export class MicrobitWebUSBConnection
this.setStatus(ConnectionStatus.NO_AUTHORIZED_DEVICE);
}

private async connectInternal(options: ConnectOptions): Promise<void> {
private async connectInternal(
options: InternalConnectOptions,
): Promise<void> {
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);
Expand All @@ -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) =>
Expand Down
31 changes: 31 additions & 0 deletions src/demo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
ConnectionStatus,
ConnectionStatusEvent,
DeviceConnection,
SerialDataEvent,
} from "../lib/device";
import { MicrobitWebBluetoothConnection } from "../lib/bluetooth";
import { AccelerometerDataEvent } from "../lib/accelerometer";
Expand All @@ -30,6 +31,10 @@ document.querySelector<HTMLDivElement>("#app")!.innerHTML = `
<p class="status"></p>
<label><div>File to flash</div><input type="file"/></label>
<button class="flash">Flash</button>
<div class="serial-controls">
<button class="serial-listen">Listen to serial</button>
<button class="serial-stop">Stop serial data</button>
</div>
<div class="acc-data-controls">
<button class="acc-data-get">Get accelerometer data</button>
<button class="acc-data-listen">Listen to accelerometer data</button>
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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);
});