Skip to content

Commit 5225cbb

Browse files
committed
Fix BT reboot and CRC issues
1 parent 501b95b commit 5225cbb

File tree

4 files changed

+142
-92
lines changed

4 files changed

+142
-92
lines changed

src/js/protocols/WebBluetooth.js

+93-62
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,11 @@ class WebBluetooth extends EventTarget {
4646
this.bluetooth.addEventListener("gatserverdisconnected", (e) => this.handleRemovedDevice(e.target));
4747

4848
this.loadDevices();
49+
50+
// Properly bind all event handlers ONCE
51+
this.boundHandleDisconnect = this.handleDisconnect.bind(this);
52+
this.boundHandleNotification = this.handleNotification.bind(this);
53+
this.boundHandleReceiveBytes = this.handleReceiveBytes.bind(this);
4954
}
5055

5156
handleNewDevice(device) {
@@ -135,7 +140,8 @@ class WebBluetooth extends EventTarget {
135140

136141
console.log(`${this.logHead} Opening connection with ID: ${path}, Baud: ${options.baudRate}`);
137142

138-
this.device.addEventListener("gattserverdisconnected", this.handleDisconnect.bind(this));
143+
// Use bound method references
144+
this.device.addEventListener("gattserverdisconnected", this.boundHandleDisconnect);
139145

140146
try {
141147
console.log(`${this.logHead} Connecting to GATT Server`);
@@ -163,8 +169,9 @@ class WebBluetooth extends EventTarget {
163169
this.failed = 0;
164170
this.openRequested = false;
165171

166-
this.device.addEventListener("disconnect", this.handleDisconnect.bind(this));
167-
this.addEventListener("receive", this.handleReceiveBytes);
172+
// Use bound references here too
173+
this.device.addEventListener("disconnect", this.boundHandleDisconnect);
174+
this.addEventListener("receive", this.boundHandleReceiveBytes);
168175

169176
console.log(`${this.logHead} Connection opened with ID: ${this.connectionId}, Baud: ${options.baudRate}`);
170177

@@ -221,47 +228,69 @@ class WebBluetooth extends EventTarget {
221228
}
222229

223230
async getCharacteristics() {
224-
const characteristics = await this.service.getCharacteristics();
231+
try {
232+
const characteristics = await this.service.getCharacteristics();
225233

226-
characteristics.forEach((characteristic) => {
227-
// console.log("Characteristic: ", characteristic);
228-
if (characteristic.uuid == this.deviceDescription.writeCharacteristic) {
229-
this.writeCharacteristic = characteristic;
234+
if (!characteristics || characteristics.length === 0) {
235+
throw new Error("No characteristics found");
230236
}
231237

232-
if (characteristic.uuid == this.deviceDescription.readCharacteristic) {
233-
this.readCharacteristic = characteristic;
238+
// Reset characteristics
239+
this.writeCharacteristic = null;
240+
this.readCharacteristic = null;
241+
242+
for (const characteristic of characteristics) {
243+
if (characteristic.uuid === this.deviceDescription.writeCharacteristic) {
244+
this.writeCharacteristic = characteristic;
245+
}
246+
247+
if (characteristic.uuid === this.deviceDescription.readCharacteristic) {
248+
this.readCharacteristic = characteristic;
249+
}
250+
251+
if (this.writeCharacteristic && this.readCharacteristic) {
252+
break;
253+
}
234254
}
235-
return this.writeCharacteristic && this.readCharacteristic;
236-
});
237255

238-
if (!this.writeCharacteristic) {
239-
throw new Error(
240-
"Unexpected write characteristic found - should be",
241-
this.deviceDescription.writeCharacteristic,
242-
);
243-
}
256+
if (!this.writeCharacteristic) {
257+
throw new Error(`Write characteristic not found: ${this.deviceDescription.writeCharacteristic}`);
258+
}
244259

245-
if (!this.readCharacteristic) {
246-
throw new Error(
247-
"Unexpected read characteristic found - should be",
248-
this.deviceDescription.readCharacteristic,
249-
);
250-
}
260+
if (!this.readCharacteristic) {
261+
throw new Error(`Read characteristic not found: ${this.deviceDescription.readCharacteristic}`);
262+
}
251263

252-
this.readCharacteristic.addEventListener("characteristicvaluechanged", this.handleNotification.bind(this));
264+
// Use the bound method for the event listener
265+
this.readCharacteristic.addEventListener("characteristicvaluechanged", this.boundHandleNotification);
253266

254-
return await this.readCharacteristic.readValue();
267+
return await this.readCharacteristic.readValue();
268+
} catch (error) {
269+
console.error(`${this.logHead} Error getting characteristics:`, error);
270+
throw error;
271+
}
255272
}
256273

257274
handleNotification(event) {
258-
const buffer = new Uint8Array(event.target.value.byteLength);
275+
try {
276+
if (!event.target.value) {
277+
console.warn(`${this.logHead} Empty notification received`);
278+
return;
279+
}
259280

260-
for (let i = 0; i < event.target.value.byteLength; i++) {
261-
buffer[i] = event.target.value.getUint8(i);
262-
}
281+
const buffer = new Uint8Array(event.target.value.byteLength);
282+
283+
// Copy data with validation
284+
for (let i = 0; i < event.target.value.byteLength; i++) {
285+
buffer[i] = event.target.value.getUint8(i);
286+
}
263287

264-
this.dispatchEvent(new CustomEvent("receive", { detail: buffer }));
288+
if (buffer.length > 0) {
289+
this.dispatchEvent(new CustomEvent("receive", { detail: buffer }));
290+
}
291+
} catch (error) {
292+
console.error(`${this.logHead} Error handling notification:`, error);
293+
}
265294
}
266295

267296
startNotifications() {
@@ -287,48 +316,50 @@ class WebBluetooth extends EventTarget {
287316
return;
288317
}
289318

290-
const doCleanup = async () => {
291-
this.removeEventListener("receive", this.handleReceiveBytes);
319+
this.closeRequested = true; // Set this to prevent reentry
320+
321+
try {
322+
this.removeEventListener("receive", this.boundHandleReceiveBytes);
292323

293324
if (this.device) {
294-
this.device.removeEventListener("disconnect", this.handleDisconnect.bind(this));
295-
this.device.removeEventListener("gattserverdisconnected", this.handleDisconnect);
296-
this.readCharacteristic.removeEventListener(
297-
"characteristicvaluechanged",
298-
this.handleNotification.bind(this),
299-
);
300-
301-
if (this.device.gatt.connected) {
302-
this.device.gatt.disconnect();
325+
// Use the properly bound references
326+
this.device.removeEventListener("disconnect", this.boundHandleDisconnect);
327+
this.device.removeEventListener("gattserverdisconnected", this.boundHandleDisconnect);
328+
329+
if (this.readCharacteristic) {
330+
try {
331+
// Stop notifications first to avoid errors
332+
await this.readCharacteristic.stopNotifications();
333+
this.readCharacteristic.removeEventListener(
334+
"characteristicvaluechanged",
335+
this.boundHandleNotification,
336+
);
337+
} catch (err) {
338+
console.warn(`${this.logHead} Error stopping notifications:`, err);
339+
}
303340
}
304341

305-
this.writeCharacteristic = false;
306-
this.readCharacteristic = false;
307-
this.deviceDescription = false;
308-
this.device = null;
309-
}
310-
};
311-
312-
try {
313-
await doCleanup();
342+
// Safely disconnect GATT
343+
if (this.device.gatt?.connected) {
344+
await this.device.gatt.disconnect();
345+
}
314346

315-
console.log(
316-
`${this.logHead} Connection with ID: ${this.connectionId} closed, Sent: ${this.bytesSent} bytes, Received: ${this.bytesReceived} bytes`,
317-
);
347+
// Clear references
348+
this.writeCharacteristic = null;
349+
this.readCharacteristic = null;
350+
this.deviceDescription = null;
351+
}
318352

353+
console.log(`${this.logHead} Connection closed successfully`);
319354
this.connectionId = false;
320-
this.bitrate = 0;
355+
this.device = null;
321356
this.dispatchEvent(new CustomEvent("disconnect", { detail: true }));
322357
} catch (error) {
323-
console.error(error);
324-
console.error(
325-
`${this.logHead} Failed to close connection with ID: ${this.connectionId} closed, Sent: ${this.bytesSent} bytes, Received: ${this.bytesReceived} bytes`,
326-
);
358+
console.error(`${this.logHead} Error during disconnect:`, error);
327359
this.dispatchEvent(new CustomEvent("disconnect", { detail: false }));
328360
} finally {
329-
if (this.openCanceled) {
330-
this.openCanceled = false;
331-
}
361+
this.closeRequested = false;
362+
this.openCanceled = false;
332363
}
333364
}
334365

src/js/serial.js

+22-13
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,7 @@ class Serial extends EventTarget {
231231
this._protocol.connect(path, options);
232232
} else {
233233
console.error(`${this.logHead} Failed to disconnect before reconnecting`);
234+
return false;
234235
}
235236
});
236237

@@ -243,40 +244,48 @@ class Serial extends EventTarget {
243244

244245
/**
245246
* Disconnect from the current connection
247+
* @param {function} [callback] - Optional callback for backward compatibility
248+
* @returns {boolean|Promise<boolean>} - Returns boolean for sync calls, Promise for async calls
246249
*/
247250
disconnect(callback) {
251+
// Return immediately if no protocol is selected
248252
if (!this._protocol) {
249253
console.warn(`${this.logHead} No protocol selected, nothing to disconnect`);
250254
if (callback) callback(false);
251255
return false;
252256
}
253257

254-
if (!this._protocol.connected) {
255-
console.warn(`${this.logHead} Protocol not connected, nothing to disconnect`);
256-
if (callback) callback(false);
257-
return false;
258-
}
259-
260-
console.log(`${this.logHead} Disconnecting from current protocol`);
258+
console.log(`${this.logHead} Disconnecting from current protocol`, this._protocol);
261259

262260
try {
263-
// Disconnect from the protocol
261+
// Handle case where we're already disconnected
262+
if (!this._protocol.connected) {
263+
console.log(`${this.logHead} Already disconnected, performing cleanup`);
264+
if (callback) {
265+
callback(true);
266+
}
267+
return true;
268+
}
269+
270+
// Perform the actual disconnect
264271
const result = this._protocol.disconnect((success) => {
265272
if (success) {
266-
// Ensure our connection state is updated
267273
console.log(`${this.logHead} Disconnection successful`);
268274
} else {
269275
console.error(`${this.logHead} Disconnection failed`);
270276
}
271-
272-
// Call the callback with the result
273-
if (callback) callback(success);
277+
// Call callback with disconnect result
278+
if (callback) {
279+
callback(success);
280+
}
274281
});
275282

276283
return result;
277284
} catch (error) {
278285
console.error(`${this.logHead} Error during disconnect:`, error);
279-
if (callback) callback(false);
286+
if (callback) {
287+
callback(false);
288+
}
280289
return false;
281290
}
282291
}

src/js/tabs/firmware_flasher.js

-1
Original file line numberDiff line numberDiff line change
@@ -258,7 +258,6 @@ firmware_flasher.initialize = function (callback) {
258258
// extract osd protocols from general options and add to osdProtocols
259259
console.log(`${self.logHead} buildOptions`, FC.CONFIG.buildOptions);
260260
self.cloudBuildOptions = FC.CONFIG.buildOptions || [];
261-
console.log(`${self.logHead} generalOptions`, self.cloudBuildOptions);
262261
data.osdProtocols = data.generalOptions
263262
.filter((option) => option.group === "OSD")
264263
.map((option) => {

src/js/utils/AutoDetect.js

+27-16
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,19 @@ import { serial } from "../serial";
1818

1919
let mspHelper = null;
2020

21-
function readSerialAdapter(event) {
22-
MSP.read(event.detail);
23-
}
24-
2521
class AutoDetect {
2622
constructor() {
2723
this.board = FC.CONFIG.boardName;
2824
this.targetAvailable = false;
25+
26+
// Store bound event handlers to make removal more reliable
27+
this.boundHandleConnect = this.handleConnect.bind(this);
28+
this.boundHandleDisconnect = this.handleDisconnect.bind(this);
29+
this.boundReadSerialAdapter = this.readSerialAdapter.bind(this);
30+
}
31+
32+
readSerialAdapter(event) {
33+
MSP.read(event.detail);
2934
}
3035

3136
verifyBoard() {
@@ -51,11 +56,14 @@ class AutoDetect {
5156

5257
gui_log(i18n.getMessage("firmwareFlasherDetectBoardQuery"));
5358

54-
serial.addEventListener("connect", this.handleConnect.bind(this), { once: true });
55-
serial.addEventListener("disconnect", this.handleDisconnect.bind(this), { once: true });
56-
5759
if (port.startsWith("serial")) {
60+
serial.addEventListener("connect", this.boundHandleConnect, { once: true });
61+
serial.addEventListener("disconnect", this.boundHandleDisconnect, { once: true });
62+
63+
serial.selectProtocol("serial");
5864
serial.connect(port, { baudRate: 115200 });
65+
} else {
66+
gui_log(i18n.getMessage("serialPortOpenFail"));
5967
}
6068
}
6169

@@ -73,12 +81,6 @@ class AutoDetect {
7381
if (!this.targetAvailable) {
7482
gui_log(i18n.getMessage("firmwareFlasherBoardVerificationFail"));
7583
}
76-
77-
MSP.clearListeners();
78-
79-
serial.removeEventListener("receive", readSerialAdapter);
80-
serial.removeEventListener("connect", this.handleConnect.bind(this));
81-
serial.removeEventListener("disconnect", this.handleDisconnect.bind(this));
8284
}
8385

8486
onFinishClose() {
@@ -109,8 +111,17 @@ class AutoDetect {
109111
);
110112
}
111113

112-
serial.disconnect(this.onClosed.bind(this));
114+
// Remove event listeners using stored references
115+
serial.removeEventListener("receive", this.boundReadSerialAdapter);
116+
serial.removeEventListener("connect", this.boundHandleConnect);
117+
serial.removeEventListener("disconnect", this.boundHandleDisconnect);
118+
119+
// Clean up MSP listeners
120+
MSP.clearListeners();
113121
MSP.disconnect_cleanup();
122+
123+
// Disconnect without passing onClosed as a callback
124+
serial.disconnect();
114125
}
115126

116127
async getBoardInfo() {
@@ -171,8 +182,8 @@ class AutoDetect {
171182

172183
onConnect(openInfo) {
173184
if (openInfo) {
174-
serial.removeEventListener("receive", readSerialAdapter);
175-
serial.addEventListener("receive", readSerialAdapter);
185+
serial.removeEventListener("receive", this.boundReadSerialAdapter);
186+
serial.addEventListener("receive", this.boundReadSerialAdapter);
176187

177188
mspHelper = new MspHelper();
178189
MSP.listen(mspHelper.process_data.bind(mspHelper));

0 commit comments

Comments
 (0)