From dd1348fa497020729fdbeabd8748ea3eba9ec941 Mon Sep 17 00:00:00 2001 From: Matt Hillsdon Date: Thu, 22 Jan 2026 17:50:49 +0000 Subject: [PATCH] Wait for disconnect after partial flashing success This seems to help significantly with reconnect on Android Remove the retries for now - we might well reinstate but we need to do so with more care as it's led to very long retries in the case where there is no device due to app-level retries. --- lib/bluetooth-device-wrapper.ts | 41 ++++++++------------------------- lib/bluetooth.ts | 4 ++++ 2 files changed, 13 insertions(+), 32 deletions(-) diff --git a/lib/bluetooth-device-wrapper.ts b/lib/bluetooth-device-wrapper.ts index 7461db0..524fe5b 100644 --- a/lib/bluetooth-device-wrapper.ts +++ b/lib/bluetooth-device-wrapper.ts @@ -36,9 +36,8 @@ import { import { UARTService } from "./uart-service.js"; export const bondingTimeoutInMs = 40_000; -export const connectTimeoutInMs = 4_000; +export const connectTimeoutInMs = 10_000; export const scanningTimeoutInMs = 10_000; -const connectionMaxAttempts = 4; export const isAndroid = () => Capacitor.getPlatform() === "android"; @@ -83,7 +82,7 @@ export class BluetoothDeviceWrapper implements Logging { // or reconnection itself. private duringExplicitConnectDisconnect: number = 0; - private connected = false; + connected = false; private isReconnect = false; // Only updated after the full connection flow completes not during bond handling. @@ -153,11 +152,13 @@ export class BluetoothDeviceWrapper implements Logging { try { if (Capacitor.isNativePlatform()) { await this.connectHandlingBond(); + // We need this on Android for reconnecting after DFU. await BleClient.discoverServices(this.device.deviceId); } else { await this.connectInternal(); } await this.getBoardVersion(); + const events = this.currentEvents(); const services = await BleClient.getServices(this.device.deviceId); this.serviceIds = new Set(services.map((s) => s.uuid)); @@ -200,33 +201,10 @@ export class BluetoothDeviceWrapper implements Logging { private async connectInternal() { this.waitingForDisconnectEventCallbacks.length = 0; - - // Attempt multiple connection tries. - for (let i = 0; i < connectionMaxAttempts; i++) { - try { - // Fail immediately if disconnect occurs whilst connecting. - await this.raceDisconnectAndTimeout( - BleClient.connect(this.device.deviceId, this.handleDisconnectEvent), - { - actionName: "connect internal", - timeout: connectTimeoutInMs, - initiallyDisconnected: true, - }, - ); - this.connected = true; - return; - } catch (error) { - const attempts = i + 1; - if (attempts === connectionMaxAttempts) { - throw error; - } - const delayDuration = Math.pow(2, i) * 1000; // 1s, 2s, 4s - this.logging.log( - `Attempt ${attempts} failed, retrying in ${delayDuration}ms...`, - ); - await delay(delayDuration); - } - } + await BleClient.connect(this.device.deviceId, this.handleDisconnectEvent, { + timeout: connectTimeoutInMs, + }); + this.connected = true; } async disconnect(): Promise { @@ -433,10 +411,9 @@ export class BluetoothDeviceWrapper implements Logging { options: { actionName?: string; timeout?: number; - initiallyDisconnected?: boolean; } = {}, ): Promise { - if (!this.connected && !options.initiallyDisconnected) { + if (!this.connected) { throw new DisconnectError(); } const actionName = options.actionName ?? "action"; diff --git a/lib/bluetooth.ts b/lib/bluetooth.ts index 6f50838..3dcd10e 100644 --- a/lib/bluetooth.ts +++ b/lib/bluetooth.ts @@ -512,6 +512,10 @@ class MicrobitWebBluetoothConnectionImpl if (partialFlashResult === PartialFlashResult.AttemptFullFlash) { await fullFlash(connection, boardVersion, memoryMap, progress); + } else if (partialFlashResult === PartialFlashResult.Success) { + if (connection.connected) { + await connection.waitForDisconnect(10_000); + } } } catch (e) { this.error("Failed to flash", e);