Skip to content

Commit

Permalink
Add bluetooth support (#4024)
Browse files Browse the repository at this point in the history
* Add bluetooth support

* Add new files

* CLI now works over BT

* Fix MSP over bluetooth

* Cleanup

* More cleanup

* Fix bind for disconnect

* Rename port to device

* Reboot does not trigger event gattserverdisconnected

* Fix dual permission request

* Increase minimum MSP timeout for PWA

* Small refactor

* Reboot

* Do not crash when bluetooth flag is disabled

* Cleanup excessive logging

* Abstract navigator
  • Loading branch information
haslinghuis authored Jun 28, 2024
1 parent 91dab87 commit 5a3f06f
Show file tree
Hide file tree
Showing 9 changed files with 512 additions and 38 deletions.
15 changes: 14 additions & 1 deletion locales/en/messages.json
Original file line number Diff line number Diff line change
Expand Up @@ -84,9 +84,22 @@
"description": "Configure a Virtual Flight Controller without the need of a physical FC."
},
"portsSelectPermission": {
"message": "--- I can't find my device ---",
"message": "--- I can't find my USB device ---",
"description": "Option in the port selection dropdown to allow the user to give permissions to the system to access the device."
},
"portsSelectPermissionBluetooth": {
"message": "--- I can't find my Bluetooth device---",
"description": "Option in the port selection dropdown to allow the user to give permissions to the system to access a Bluetooth device."
},
"bluetoothConnected": {
"message": "Connected to Bluetooth device: $1"
},
"bluetoothConnectionType": {
"message": "Bluetooth connection type: $1"
},
"bluetoothConnectionError": {
"message": "Bluetooth error: $1"
},
"virtualMSPVersion": {
"message": "Virtual Firmware Version"
},
Expand Down
7 changes: 6 additions & 1 deletion src/components/port-picker/PortPicker.vue
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
/>
<PortsInput
:value="value"
:connected-bluetooth-devices="connectedBluetoothDevices"
:connected-serial-devices="connectedSerialDevices"
:connected-usb-devices="connectedUsbDevices"
:disabled="disabled"
Expand Down Expand Up @@ -44,14 +45,18 @@ export default {
autoConnect: true,
}),
},
connectedUsbDevices: {
connectedBluetoothDevices: {
type: Array,
default: () => [],
},
connectedSerialDevices: {
type: Array,
default: () => [],
},
connectedUsbDevices: {
type: Array,
default: () => [],
},
showVirtualOption: {
type: Boolean,
default: true,
Expand Down
16 changes: 16 additions & 0 deletions src/components/port-picker/PortsInput.vue
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,13 @@
>
{{ $t("portsSelectVirtual") }}
</option>
<option
v-for="connectedBluetoothDevice in connectedBluetoothDevices"
:key="connectedBluetoothDevice.path"
:value="connectedBluetoothDevice.path"
>
{{ connectedBluetoothDevice.displayName }}
</option>
<option
v-for="connectedSerialDevice in connectedSerialDevices"
:key="connectedSerialDevice.path"
Expand All @@ -47,6 +54,9 @@
<option value="requestpermission">
{{ $t("portsSelectPermission") }}
</option>
<option value="requestpermissionbluetooth">
{{ $t("portsSelectPermissionBluetooth") }}
</option>
</select>
</div>
<div id="auto-connect-and-baud">
Expand Down Expand Up @@ -114,6 +124,10 @@ export default {
type: Array,
default: () => [],
},
connectedBluetoothDevices: {
type: Array,
default: () => [],
},
disabled: {
type: Boolean,
default: false,
Expand Down Expand Up @@ -154,6 +168,8 @@ export default {
onChangePort(event) {
if (event.target.value === 'requestpermission') {
EventBus.$emit('ports-input:request-permission');
} else if (event.target.value === 'requestpermissionbluetooth') {
EventBus.$emit('ports-input:request-permission-bluetooth');
} else {
EventBus.$emit('ports-input:change', event.target.value);
}
Expand Down
12 changes: 6 additions & 6 deletions src/js/msp.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,8 @@
import GUI from "./gui.js";
import CONFIGURATOR from "./data_storage.js";
import serialNWJS from "./serial.js";
import serialWeb from "./webSerial.js";
import { isWeb } from "./utils/isWeb.js";
import { serialShim } from "./serial_shim.js";

const serial = serialShim();
let serial = serialShim();

const MSP = {
symbols: {
Expand Down Expand Up @@ -57,7 +54,7 @@ const MSP = {
packet_error: 0,
unsupported: 0,

MIN_TIMEOUT: 100,
MIN_TIMEOUT: 200,
MAX_TIMEOUT: 2000,
timeout: 200,

Expand Down Expand Up @@ -309,7 +306,10 @@ const MSP = {
return bufferOut;
},
send_message(code, data, callback_sent, callback_msp, doCallbackOnError) {
const connected = isWeb() ? serial.connected : serial.connectionId;
// Hack to make BT work
serial = serialShim();

const connected = serial.connected;

if (code === undefined || !connected || CONFIGURATOR.virtualMode) {
if (callback_msp) {
Expand Down
67 changes: 66 additions & 1 deletion src/js/port_handler.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,27 @@ import { get as getConfig } from "./ConfigStorage";
import { EventBus } from "../components/eventBus";
import serial from "./webSerial";
import usb from "./protocols/webusbdfu";
import BT from "./protocols/bluetooth";

const DEFAULT_PORT = 'noselection';
const DEFAULT_BAUDS = 115200;

const PortHandler = new function () {
this.currentSerialPorts = [];
this.currentUsbPorts = [];
this.currentBluetoothPorts = [];

this.portPicker = {
selectedPort: DEFAULT_PORT,
selectedBauds: DEFAULT_BAUDS,
portOverride: getConfig('portOverride', '/dev/rfcomm0').portOverride,
virtualMspVersion: "1.46.0",
autoConnect: getConfig('autoConnect', false).autoConnect,
};

this.portPickerDisabled = false;

this.bluetoothAvailable = false;
this.dfuAvailable = false;
this.portAvailable = false;
this.showAllSerialDevices = false;
Expand All @@ -27,14 +33,19 @@ const PortHandler = new function () {

PortHandler.initialize = function () {

EventBus.$on('ports-input:request-permission-bluetooth', this.askBluetoothPermissionPort.bind(this));
EventBus.$on('ports-input:request-permission', this.askSerialPermissionPort.bind(this));
EventBus.$on('ports-input:change', this.onChangeSelectedPort.bind(this));

BT.addEventListener("addedDevice", (event) => this.addedBluetoothDevice(event.detail));
BT.addEventListener("removedDevice", (event) => this.addedBluetoothDevice(event.detail));

serial.addEventListener("addedDevice", (event) => this.addedSerialDevice(event.detail));
serial.addEventListener("removedDevice", (event) => this.removedSerialDevice(event.detail));

usb.addEventListener("addedDevice", (event) => this.addedUsbDevice(event.detail));

this.addedBluetoothDevice();
this.addedSerialDevice();
this.addedUsbDevice();
};
Expand Down Expand Up @@ -73,6 +84,26 @@ PortHandler.removedSerialDevice = function (device) {
});
};

PortHandler.addedBluetoothDevice = function (device) {
this.updateCurrentBluetoothPortsList()
.then(() => {
const selectedPort = this.selectActivePort(device);
if (!device || selectedPort === device.path) {
// Send this event when the port handler auto selects a new device
EventBus.$emit('port-handler:auto-select-bluetooth-device', selectedPort);
}
});
};

PortHandler.removedBluetoothDevice = function (device) {
this.updateCurrentBluetoothPortsList()
.then(() => {
if (this.portPicker.selectedPort === device.path) {
this.selectActivePort();
}
});
};

PortHandler.addedUsbDevice = function (device) {
this.updateCurrentUsbPortsList()
.then(() => {
Expand Down Expand Up @@ -102,6 +133,15 @@ PortHandler.updateCurrentUsbPortsList = async function () {
this.currentUsbPorts = orderedPorts;
};

PortHandler.updateCurrentBluetoothPortsList = async function () {
if (BT.bluetooth) {
const ports = await BT.getDevices();
const orderedPorts = this.sortPorts(ports);
this.bluetoothAvailable = orderedPorts.length > 0;
this.currentBluetoothPorts = orderedPorts;
}
};

PortHandler.sortPorts = function(ports) {
return ports.sort(function(a, b) {
return a.path.localeCompare(b.path, window.navigator.language, {
Expand All @@ -111,6 +151,18 @@ PortHandler.sortPorts = function(ports) {
});
};

PortHandler.askBluetoothPermissionPort = function() {
if (BT.bluetooth) {
BT.requestPermissionDevice()
.then((port) => {
// When giving permission to a new device, the port is selected in the handleNewDevice method, but if the user
// selects a device that had already permission, or cancels the permission request, we need to select the port
// so do it here too
this.selectActivePort(port);
});
}
};

PortHandler.askSerialPermissionPort = function() {
serial.requestPermissionDevice(this.showAllSerialDevices)
.then((port) => {
Expand All @@ -136,6 +188,11 @@ PortHandler.selectActivePort = function(suggestedDevice) {
selectedPort = this.currentUsbPorts.find(device => device === usb.getConnectedPort());
}

// Return the same that is connected to bluetooth
if (BT.device) {
selectedPort = this.currentBluetoothPorts.find(device => device === BT.getConnectedPort());
}

// Return the suggested device (the new device that has been detected)
if (!selectedPort && suggestedDevice) {
selectedPort = suggestedDevice.path;
Expand All @@ -157,6 +214,14 @@ PortHandler.selectActivePort = function(suggestedDevice) {
}
}

// Return some bluetooth port that is recognized by the filter
if (!selectedPort) {
selectedPort = this.currentBluetoothPorts.find(device => deviceFilter.some(filter => device.displayName.includes(filter)));
if (selectedPort) {
selectedPort = selectedPort.path;
}
}

// Return the virtual port
if (!selectedPort && this.showVirtualMode) {
selectedPort = "virtual";
Expand All @@ -170,7 +235,7 @@ PortHandler.selectActivePort = function(suggestedDevice) {
// Return the default port if no other port was selected
this.portPicker.selectedPort = selectedPort || DEFAULT_PORT;

console.log(`Porthandler automatically selected device is '${this.portPicker.selectedPort}'`);
console.log(`[PORTHANDLER] automatically selected device is '${this.portPicker.selectedPort}'`);
return selectedPort;
};

Expand Down
Loading

0 comments on commit 5a3f06f

Please sign in to comment.