Skip to content

Commit d0599ae

Browse files
authored
Add DFU devices to the port handler (#3991)
* Add DFU devices to the port handler The firmware flasher ignores it, at this moment. Needs a future PR to use it in place of the actual code that asks permission each time. * Remove the DFU word in the displayName of the USB devices some devices contain it in the productName, so better to remove it to not duplicate the name.
1 parent 617790b commit d0599ae

File tree

7 files changed

+148
-51
lines changed

7 files changed

+148
-51
lines changed

src/components/port-picker/PortPicker.vue

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@
1212
/>
1313
<PortsInput
1414
:value="value"
15-
:connected-devices="connectedDevices"
15+
:connected-serial-devices="connectedSerialDevices"
16+
:connected-usb-devices="connectedUsbDevices"
1617
:disabled="disabled"
1718
:show-virtual-option="showVirtualOption"
1819
:show-manual-option="showManualOption"
@@ -43,7 +44,11 @@ export default {
4344
autoConnect: true,
4445
}),
4546
},
46-
connectedDevices: {
47+
connectedUsbDevices: {
48+
type: Array,
49+
default: () => [],
50+
},
51+
connectedSerialDevices: {
4752
type: Array,
4853
default: () => [],
4954
},

src/components/port-picker/PortsInput.vue

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -32,11 +32,18 @@
3232
{{ $t("portsSelectVirtual") }}
3333
</option>
3434
<option
35-
v-for="connectedDevice in connectedDevices"
36-
:key="connectedDevice.path"
37-
:value="connectedDevice.path"
35+
v-for="connectedSerialDevice in connectedSerialDevices"
36+
:key="connectedSerialDevice.path"
37+
:value="connectedSerialDevice.path"
3838
>
39-
{{ connectedDevice.displayName }}
39+
{{ connectedSerialDevice.displayName }}
40+
</option>
41+
<option
42+
v-for="connectedUsbDevice in connectedUsbDevices"
43+
:key="connectedUsbDevice.path"
44+
:value="connectedUsbDevice.path"
45+
>
46+
{{ connectedUsbDevice.displayName }}
4047
</option>
4148
<option value="requestpermission">
4249
{{ $t("portsSelectPermission") }}
@@ -100,7 +107,11 @@ export default {
100107
autoConnect: true,
101108
}),
102109
},
103-
connectedDevices: {
110+
connectedSerialDevices: {
111+
type: Array,
112+
default: () => [],
113+
},
114+
connectedUsbDevices: {
104115
type: Array,
105116
default: () => [],
106117
},

src/index.html

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,8 @@
2828
></betaflight-logo>
2929
<port-picker
3030
v-model="PortHandler.portPicker"
31-
:connected-devices="PortHandler.currentPorts"
31+
:connected-serial-devices="PortHandler.currentSerialPorts"
32+
:connected-usb-devices="PortHandler.currentUsbPorts"
3233
:show-virtual-option="PortHandler.showVirtualMode"
3334
:show-manual-option="PortHandler.showManualMode"
3435
:disabled="PortHandler.portPickerDisabled"

src/js/port_handler.js

Lines changed: 71 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,18 @@ import { get as getConfig } from "./ConfigStorage";
55
import { isWeb } from "./utils/isWeb";
66
import { usbDevices } from "./usb_devices";
77
import { serialShim } from "./serial_shim.js";
8+
import { usbShim } from "./usb_shim.js";
89
import { EventBus } from "../components/eventBus";
910

1011
const serial = serialShim();
12+
const usb = usbShim();
1113

1214
const DEFAULT_PORT = 'noselection';
1315
const DEFAULT_BAUDS = 115200;
1416

1517
const PortHandler = new function () {
16-
this.currentPorts = [];
18+
this.currentSerialPorts = [];
19+
this.currentUsbPorts = [];
1720
this.portPicker = {
1821
selectedPort: DEFAULT_PORT,
1922
selectedBauds: DEFAULT_BAUDS,
@@ -38,13 +41,10 @@ PortHandler.initialize = function () {
3841
serial.addEventListener("addedDevice", (event) => this.addedSerialDevice(event.detail));
3942
serial.addEventListener("removedDevice", (event) => this.removedSerialDevice(event.detail));
4043

41-
if (!this.portAvailable) {
42-
this.check_usb_devices();
43-
}
44+
usb.addEventListener("addedDevice", (event) => this.addedUsbDevice(event.detail));
4445

45-
if (!this.dfuAvailable) {
46-
this.addedSerialDevice();
47-
}
46+
this.addedUsbDevice();
47+
this.addedSerialDevice();
4848
};
4949

5050
PortHandler.setShowVirtualMode = function (showVirtualMode) {
@@ -58,34 +58,55 @@ PortHandler.setShowManualMode = function (showManualMode) {
5858
};
5959

6060
PortHandler.addedSerialDevice = function (device) {
61-
this.updateCurrentPortsList()
61+
this.updateCurrentSerialPortsList()
6262
.then(() => {
6363
const selectedPort = this.selectActivePort(device);
6464
if (!device || selectedPort === device.path) {
6565
// Send this event when the port handler auto selects a new device
66-
EventBus.$emit('port-handler:auto-select-device', selectedPort);
66+
EventBus.$emit('port-handler:auto-select-serial-device', selectedPort);
6767
}
6868
});
6969
};
7070

7171
PortHandler.removedSerialDevice = function (device) {
72-
this.updateCurrentPortsList()
72+
this.updateCurrentSerialPortsList()
7373
.then(() => {
7474
if (this.portPicker.selectedPort === device.path) {
7575
this.selectActivePort();
7676
}
7777
});
7878
};
7979

80+
PortHandler.addedUsbDevice = function (device) {
81+
this.updateCurrentUsbPortsList()
82+
.then(() => {
83+
const selectedPort = this.selectActivePort(device);
84+
if (!device || selectedPort === device.path) {
85+
// Send this event when the port handler auto selects a new device
86+
EventBus.$emit('port-handler:auto-select-usb-device', selectedPort);
87+
}
88+
});
89+
};
90+
8091
PortHandler.onChangeSelectedPort = function(port) {
8192
this.portPicker.selectedPort = port;
8293
};
8394

84-
PortHandler.updateCurrentPortsList = function () {
95+
PortHandler.updateCurrentSerialPortsList = function () {
8596
return serial.getDevices()
8697
.then((ports) => {
87-
ports = this.sortPorts(ports);
88-
this.currentPorts = ports;
98+
const orderedPorts = this.sortPorts(ports);
99+
this.portAvailable = orderedPorts.length > 0;
100+
this.currentSerialPorts = orderedPorts;
101+
});
102+
};
103+
104+
PortHandler.updateCurrentUsbPortsList = function () {
105+
return usb.getDevices()
106+
.then((ports) => {
107+
const orderedPorts = this.sortPorts(ports);
108+
this.dfuAvailable = orderedPorts.length > 0;
109+
this.currentUsbPorts = orderedPorts;
89110
});
90111
};
91112

@@ -110,43 +131,54 @@ PortHandler.askSerialPermissionPort = function() {
110131

111132
PortHandler.selectActivePort = function(suggestedDevice) {
112133

113-
// Return the same that is connected
134+
const deviceFilter = ['AT32', 'CP210', 'SPR', 'STM'];
135+
let selectedPort;
136+
137+
// Return the same that is connected to serial
114138
if (serial.connected) {
115-
return serial.getConnectedPort();
139+
selectedPort = serial.getConnectedPort();
116140
}
117141

118-
let selectedPort;
119-
const deviceFilter = ['AT32', 'CP210', 'SPR', 'STM'];
142+
// Return the same that is connected to usb (dfu mode)
143+
if (usb.usbDevice) {
144+
selectedPort = usb.getConnectedPort();
145+
}
120146

121-
if (suggestedDevice) {
147+
// Return the suggested device (the new device that has been detected)
148+
if (!selectedPort && suggestedDevice) {
122149
selectedPort = suggestedDevice.path;
123-
this.portAvailable = true;
124-
} else {
125-
for (let port of this.currentPorts) {
126-
const portName = port.displayName;
127-
const pathSelect = port.path;
128-
const deviceRecognized = deviceFilter.some(device => portName.includes(device));
129-
const legacyDeviceRecognized = portName.includes('usb');
130-
if (deviceRecognized || legacyDeviceRecognized) {
131-
selectedPort = pathSelect;
132-
this.portAvailable = true;
133-
console.log(`Porthandler detected device ${portName} on port: ${pathSelect}`);
134-
break;
135-
}
150+
}
151+
152+
// Return some serial port that is recognized by the filter
153+
if (!selectedPort) {
154+
selectedPort = this.currentUsbPorts.find(device => deviceFilter.some(filter => device.displayName.includes(filter)));
155+
if (selectedPort) {
156+
selectedPort = selectedPort.path;
136157
}
158+
}
137159

138-
if (!selectedPort) {
139-
this.portAvailable = false;
140-
if (this.showVirtualMode) {
141-
selectedPort = "virtual";
142-
} else if (this.showManualMode) {
143-
selectedPort = "manual";
144-
}
160+
// Return some usb port that is recognized by the filter
161+
if (!selectedPort) {
162+
selectedPort = this.currentSerialPorts.find(device => deviceFilter.some(filter => device.displayName.includes(filter)));
163+
if (selectedPort) {
164+
selectedPort = selectedPort.path;
145165
}
146166
}
147167

168+
// Return the virtual port
169+
if (!selectedPort && this.showVirtualMode) {
170+
selectedPort = "virtual";
171+
}
172+
173+
// Return the manual port
174+
if (!selectedPort && this.showManualMode) {
175+
selectedPort = "manual";
176+
}
177+
178+
// Return the default port if no other port was selected
148179
this.portPicker.selectedPort = selectedPort || DEFAULT_PORT;
149-
console.log(`Porthandler default device is '${this.portPicker.selectedPort}'`);
180+
181+
console.log(`Porthandler automatically selected device is '${this.portPicker.selectedPort}'`);
150182
return selectedPort;
151183
};
152184

src/js/protocols/webusbdfu.js

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,9 @@ import { i18n } from "../localization";
1818
import { gui_log } from "../gui_log";
1919
import { usbDevices } from "../usb_devices";
2020

21-
class WEBUSBDFU_protocol {
21+
class WEBUSBDFU_protocol extends EventTarget {
2222
constructor() {
23+
super();
2324
this.callback = null;
2425
this.hex = null;
2526
this.verify_hex = [];
@@ -70,6 +71,53 @@ class WEBUSBDFU_protocol {
7071
this.chipInfo = null; // information about chip's memory
7172
this.flash_layout = { 'start_address': 0, 'total_size': 0, 'sectors': [] };
7273
this.transferSize = 2048; // Default USB DFU transfer size for F3,F4 and F7
74+
75+
navigator.usb.addEventListener("connect", e => this.handleNewDevice(e.device));
76+
navigator.usb.addEventListener("disconnect", e => this.handleNewDevice(e.device));
77+
}
78+
handleNewDevice(device) {
79+
const added = this.createPort(device);
80+
this.dispatchEvent(new CustomEvent("addedDevice", { detail: added }));
81+
82+
return added;
83+
}
84+
handleRemovedDevice(device) {
85+
const removed = this.createPort(device);
86+
this.dispatchEvent(new CustomEvent("removedDevice", { detail: removed }));
87+
}
88+
createPort(port) {
89+
return {
90+
path: `usb_${port.serialNumber}`,
91+
displayName: `Betaflight ${port.productName}`,
92+
vendorId: port.manufacturerName,
93+
productId: port.productName,
94+
port: port,
95+
};
96+
}
97+
async getDevices() {
98+
const ports = await navigator.usb.getDevices(usbDevices);
99+
const customPorts = ports.map(function (port) {
100+
return this.createPort(port);
101+
}, this);
102+
103+
return customPorts;
104+
}
105+
async requestPermission() {
106+
let newPermissionPort = null;
107+
try {
108+
const userSelectedPort = await navigator.usb.requestDevice(usbDevices);
109+
console.info("User selected USB device from permissions:", userSelectedPort);
110+
console.log(`WebUSB Version: ${userSelectedPort.deviceVersionMajor}.${userSelectedPort.deviceVersionMinor}.${userSelectedPort.deviceVersionSubminor}`);
111+
112+
newPermissionPort = this.handleNewDevice(userSelectedPort);
113+
} catch (error) {
114+
console.error("User didn't select any USB device when requesting permission:", error);
115+
}
116+
return newPermissionPort;
117+
118+
}
119+
getConnectedPort() {
120+
return this.usbDevice ? `usb_${this.usbDevice.serialNumber}` : null;
73121
}
74122
connect(hex, options, callback) {
75123
this.hex = hex;

src/js/serial_backend.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ export function initializeSerialBackend() {
6767

6868
$("div.connect_controls a.connect").on('click', connectDisconnect);
6969

70-
EventBus.$on('port-handler:auto-select-device', function(device) {
70+
EventBus.$on('port-handler:auto-select-serial-device', function(device) {
7171
if (!GUI.connected_to && !GUI.connecting_to
7272
&& ((PortHandler.portPicker.autoConnect && !["manual", "virtual"].includes(device))
7373
|| Date.now() - rebootTimestamp < REBOOT_CONNECT_MAX_TIME_MS)) {

src/js/webSerial.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -105,9 +105,9 @@ class WebSerial extends EventTarget {
105105
if (!newPermissionPort) {
106106
newPermissionPort = this.handleNewDevice(userSelectedPort);
107107
}
108-
console.info("User selected device from permissions:", newPermissionPort.path);
108+
console.info(`${this.logHead}User selected SERIAL device from permissions:`, newPermissionPort.path);
109109
} catch (error) {
110-
console.error("User didn't select any device when requesting permission:", error);
110+
console.error(`${this.logHead}User didn't select any SERIAL device when requesting permission:`, error);
111111
}
112112
return newPermissionPort;
113113
}

0 commit comments

Comments
 (0)