Skip to content

Commit 4ae1af4

Browse files
committed
Fix BT reboot and CRC issues
1 parent 501b95b commit 4ae1af4

File tree

4 files changed

+140
-94
lines changed

4 files changed

+140
-94
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 && 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

+27-21
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

@@ -251,34 +252,39 @@ class Serial extends EventTarget {
251252
return false;
252253
}
253254

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`);
255+
console.log(`${this.logHead} Disconnecting from current protocol`, this._protocol);
256+
let result = false;
261257

262258
try {
263-
// Disconnect from the protocol
264-
const result = this._protocol.disconnect((success) => {
265-
if (success) {
266-
// Ensure our connection state is updated
267-
console.log(`${this.logHead} Disconnection successful`);
268-
} else {
269-
console.error(`${this.logHead} Disconnection failed`);
270-
}
271-
272-
// Call the callback with the result
273-
if (callback) callback(success);
274-
});
275-
276-
return result;
259+
// Handle case where we're already disconnected
260+
if (!this._protocol.connected) {
261+
console.log(`${this.logHead} Already disconnected, performing cleanup`);
262+
// Set result to true since we achieved the disconnected state
263+
result = true;
264+
if (callback) callback(result);
265+
} else {
266+
// Perform the actual disconnect
267+
this._protocol.disconnect((success) => {
268+
result = success;
269+
if (success) {
270+
console.log(`${this.logHead} Disconnection successful`);
271+
} else {
272+
console.error(`${this.logHead} Disconnection failed`);
273+
}
274+
// Call callback with disconnect result
275+
if (callback) callback(result);
276+
});
277+
// Early return to prevent callback from being called twice
278+
return result;
279+
}
277280
} catch (error) {
278281
console.error(`${this.logHead} Error during disconnect:`, error);
279282
if (callback) callback(false);
280283
return false;
281284
}
285+
286+
// This will only execute for the already disconnected case or if an error occurred
287+
return result;
282288
}
283289

284290
/**

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

+20-10
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,10 @@ class AutoDetect {
2626
constructor() {
2727
this.board = FC.CONFIG.boardName;
2828
this.targetAvailable = false;
29+
30+
// Store bound event handlers to make removal more reliable
31+
this.boundHandleConnect = this.handleConnect.bind(this);
32+
this.boundHandleDisconnect = this.handleDisconnect.bind(this);
2933
}
3034

3135
verifyBoard() {
@@ -51,11 +55,14 @@ class AutoDetect {
5155

5256
gui_log(i18n.getMessage("firmwareFlasherDetectBoardQuery"));
5357

54-
serial.addEventListener("connect", this.handleConnect.bind(this), { once: true });
55-
serial.addEventListener("disconnect", this.handleDisconnect.bind(this), { once: true });
56-
5758
if (port.startsWith("serial")) {
59+
serial.addEventListener("connect", this.boundHandleConnect, { once: true });
60+
serial.addEventListener("disconnect", this.boundHandleDisconnect, { once: true });
61+
62+
serial.selectProtocol("serial");
5863
serial.connect(port, { baudRate: 115200 });
64+
} else {
65+
gui_log(i18n.getMessage("serialPortOpenFail"));
5966
}
6067
}
6168

@@ -73,12 +80,6 @@ class AutoDetect {
7380
if (!this.targetAvailable) {
7481
gui_log(i18n.getMessage("firmwareFlasherBoardVerificationFail"));
7582
}
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));
8283
}
8384

8485
onFinishClose() {
@@ -109,8 +110,17 @@ class AutoDetect {
109110
);
110111
}
111112

112-
serial.disconnect(this.onClosed.bind(this));
113+
// Remove event listeners using stored references
114+
serial.removeEventListener("receive", readSerialAdapter);
115+
serial.removeEventListener("connect", this.boundHandleConnect);
116+
serial.removeEventListener("disconnect", this.boundHandleDisconnect);
117+
118+
// Clean up MSP listeners
119+
MSP.clearListeners();
113120
MSP.disconnect_cleanup();
121+
122+
// Disconnect without passing onClosed as a callback
123+
serial.disconnect();
114124
}
115125

116126
async getBoardInfo() {

0 commit comments

Comments
 (0)