From c0ccef87cd66e9de15e67b06f57a23228143bb63 Mon Sep 17 00:00:00 2001 From: Jakob Stendahl Date: Mon, 7 Jun 2021 22:37:16 +0200 Subject: :building_construction: Move away from web bluetooth library --- package-lock.json | 2 +- package.json | 3 +- src/js/hoverControlModule.js | 5 +- src/js/main.js | 91 +++++++++++----------------- src/js/uBit.js | 138 +++++++++++++++++++++++++++++++++++++++++++ src/scss/styles.scss | 7 +++ 6 files changed, 186 insertions(+), 60 deletions(-) create mode 100644 src/js/uBit.js diff --git a/package-lock.json b/package-lock.json index e9594a9..e864c21 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "hoverbit-ble-controller", - "version": "1.0.8-alpha.4", + "version": "1.1.1", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index ece8331..b903d4e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "hoverbit-ble-controller", - "version": "1.0.8-alpha.4", + "version": "1.1.0", "description": "", "license": "MIT", "scripts": { @@ -10,7 +10,6 @@ }, "dependencies": { "@fortawesome/fontawesome-free": "^5.15.2", - "microbit-web-bluetooth": "^0.6.0", "nipplejs": "^0.8.7" }, "devDependencies": { diff --git a/src/js/hoverControlModule.js b/src/js/hoverControlModule.js index 44c06fb..4058e8c 100644 --- a/src/js/hoverControlModule.js +++ b/src/js/hoverControlModule.js @@ -1,5 +1,7 @@ import { notif_alert, notif_warn, notif_info, notif_success } from './notification'; +let n = 0; + export default class hoverControlModule { #throttle = 0; #throttleAcc = 0; @@ -33,7 +35,8 @@ export default class hoverControlModule { console.log(`Unkown acc: ${item}`); } }); - document.querySelector(".acc-string pre").innerHTML = `T: ${this.#throttleAcc}, R: ${this.#rudderAcc}`; + document.querySelector(".acc-string pre").innerHTML = `T: ${this.#throttleAcc}, R: ${this.#rudderAcc}, ${n}`; + n++; } reset() { diff --git a/src/js/main.js b/src/js/main.js index f42f268..3d7f494 100644 --- a/src/js/main.js +++ b/src/js/main.js @@ -1,6 +1,6 @@ import nipplejs from 'nipplejs'; -import { requestMicrobit, getServices } from 'microbit-web-bluetooth'; import hoverControlModule from './hoverControlModule'; +import uBitBLE from "./uBit"; import { notif_alert, notif_warn, notif_info, notif_success } from './notification'; let sw = "service-worker.js"; @@ -32,10 +32,13 @@ document.getElementById("btn_ignore_landscape_warning").addEventListener("click" document.body.classList.add("ignore-landscape-warning"); }); +if (!navigator.bluetooth) { + alert("Bluetooth not enabled in your browser, this won't work..."); +} + /* Define and initialize things */ +let ubit = new uBitBLE(); let hoverControl = new hoverControlModule(); -let bluetoothDevice; -let bluetoothDeviceServices; let joystickLeft = nipplejs.create({ zone: document.querySelector(".joystick-left"), @@ -56,7 +59,7 @@ let joystickRight = nipplejs.create({ joystickLeft.on("move", (evt, data) => { let rudder = ((data.distance * 90) / 100); if (data.angle.degree > 90) { rudder = rudder * -1; } - hoverControl.setRudder(rudder); + hoverControl.setRudder(Math.round(rudder)); }); joystickLeft.on("end", (evt, data) => { hoverControl.setRudder(0); @@ -74,7 +77,7 @@ joystickRight.on("move", (evt, data) => { } } } - hoverControl.setThrottle(throttle); + hoverControl.setThrottle(Math.round(throttle)); }); joystickRight.on("end", (evt, data) => { hoverControl.setThrottle(0); @@ -90,45 +93,31 @@ document.getElementById("btn_disarm").addEventListener("click", () => { document.querySelector("#btn_disconnect").addEventListener("click", () => { hoverControl.reset(); - bluetoothDevice.gatt.disconnect(); + ubit.disconnect(); }); -let intervalConnectionChecker = setInterval(() => { - if (bluetoothDevice !== undefined && bluetoothDevice) { - if (bluetoothDevice.gatt.connected) { - document.body.classList.add("connected"); - } else { - document.body.classList.remove("connected"); - document.body.classList.remove("armed"); - } - } else if (bluetoothDevice !== undefined) { - bluetoothDevice.gatt.reconnect(); - } -}, 500); +document.getElementById("btn_connect").addEventListener("click", () => { + ubit.searchDevice(); +}); -let intervalSendCommands = setInterval(async() => { - if (bluetoothDevice !== undefined && bluetoothDevice) { - if (bluetoothDevice.gatt.connected && bluetoothDeviceServices.uartService) { - let command = - "T" + hoverControl.getThrottle().toString() + - "R" + hoverControl.getRudder().toString() + - "A" + (hoverControl.getArm() ? "1" : "0") + - "S0" + - ":"; - await bluetoothDeviceServices.uartService.sendText(command); - } - } -}, 70); +ubit.onConnect(() => { + document.body.classList.add("connected"); +}); + +ubit.onDisconnect(() => { + document.body.classList.remove("connected"); + document.body.classList.remove("armed"); +}); -function receiveText(event) { +ubit.onUartTx((text) => { /* Just make the ping symbol reappear. */ var elm = document.querySelector(".ping i"); var newone = elm.cloneNode(true); elm.parentNode.replaceChild(newone, elm); /* Actually handle received text. */ - if ((event.detail).indexOf(":") != -1) { - let parts = (event.detail).split(":"); + if ((text).indexOf(":") != -1) { + let parts = (text).split(":"); if (parts[0] == "B") { document.querySelector(".battery-status").innerHTML = parts[1] + "mV"; @@ -139,29 +128,19 @@ function receiveText(event) { } } else { notif_warn("Received weird data from MICRO:BIT..."); - console.log(`Received unknown: ${event.detail}`); + console.log(`Received unknown: ${text}`); } -} +}); -document.getElementById("btn_connect").onclick = async () => { - if (bluetoothDevice !== undefined && bluetoothDevice.gatt.connected) { - bluetoothDevice.disconnect(); +let intervalSendCommands = setInterval(async() => { + if (ubit.isConnected()) { + let command = + "T" + hoverControl.getThrottle().toString() + + "R" + hoverControl.getRudder().toString() + + "A" + (hoverControl.getArm() ? "1" : "0") + + "S0" + + ":"; + await ubit.sendUart(command); } +}, 100); - const device = await requestMicrobit(window.navigator.bluetooth); - bluetoothDevice = device; - - if (device) { - hoverControl.reset(); - const services = await getServices(device); - bluetoothDeviceServices = services; - - if (bluetoothDeviceServices.deviceInformationService) { - // logJson(await services.deviceInformationService.readDeviceInformation()); - } - - if (services.uartService) { - services.uartService.addEventListener("receiveText", receiveText); - } - } -} diff --git a/src/js/uBit.js b/src/js/uBit.js new file mode 100644 index 0000000..11cc7ac --- /dev/null +++ b/src/js/uBit.js @@ -0,0 +1,138 @@ +/* + * This code is written with a lot of help from these resources: + * https://github.com/antefact/microBit.js/blob/master/src/microBit.js + * https://gist.github.com/kotobuki/7c67f8b9361e08930da1a5cfcfb0653f + * https://lancaster-university.github.io/microbit-docs/resources/bluetooth/bluetooth_profile.html + */ +const UART_SERVICE_UUID = "6e400001-b5a3-f393-e0a9-e50e24dcca9e"; +const UART_TX_CHARACTERISTIC_UUID = "6e400002-b5a3-f393-e0a9-e50e24dcca9e"; +const UART_RX_CHARACTERISTIC_UUID = "6e400003-b5a3-f393-e0a9-e50e24dcca9e"; + +export default class uBitBLE { + + constructor() { + let device; + + this.onConnectCallback = function() {}; + this.onDisconnectCallback = function() {}; + this.onUartTxCallback = function() {}; + + this.characteristic = { + UART_RX: {}, + UART_TX: {} + } + } + + onConnect(callbackFunction) { + this.onConnectCallback = callbackFunction; + } + + onDisconnect(callbackFunction) { + this.onDisconnectCallback = callbackFunction; + } + + onUartTx(callbackFunction) { + this.onUartTxCallback = callbackFunction; + } + + sendUart(string) { + if (this.isConnected() && this.characteristic.UART_RX) { + let encoder = new TextEncoder(); + this.characteristic.UART_RX.writeValue( + encoder.encode(string) + ).catch(error => { + console.log(error); + }); + } + } + + isConnected() { + if (this.device) { + return this.device.gatt.connected; + } else { + return false; + } + } + + disconnect() { + if (this.isConnected()) { + this.device.gatt.disconnect(); + } + } + + searchDevice() { + navigator.bluetooth.requestDevice({ + filters: [{namePrefix: "BBC micro:bit"}], + optionalServices: [UART_SERVICE_UUID] + }) + .then(device => { + console.log('> Name: ' + device.name); + console.log('> Id: ' + device.id); + this.device = device; + + device.addEventListener('gattserverdisconnected', this.onDisconnectCallback); + + // Attempts to connect to remote GATT Server. + return device.gatt.connect(); + }) + .then(server => { + // Note that we could also get all services that match a specific UUID by + // passing it to getPrimaryServices(). + this.onConnectCallback(); + console.log('Getting Services...'); + return server.getPrimaryServices(); + }) + .then(services => { + console.log('Getting Characteristics...'); + let queue = Promise.resolve(); + services.forEach(service => { + queue = queue.then(_ => service.getCharacteristics().then(characteristics => { + console.log('> Service: ' + service.uuid); + characteristics.forEach(characteristic => { + console.log('>> Characteristic: ' + characteristic.uuid + ' ' + + getSupportedProperties(characteristic)); + switch (characteristic.uuid) { + case UART_RX_CHARACTERISTIC_UUID: + this.characteristic.UART_RX = characteristic; + break; + + case UART_TX_CHARACTERISTIC_UUID: + this.characteristic.UART_TX = characteristic; + this.characteristic.UART_TX.startNotifications(); + this.characteristic.UART_TX.addEventListener( + "characteristicvaluechanged", + (event) => { this.onUartTxCallback(eventByteArrayToString(event)) } + ); + break; + + default: + } + }); + })); + }); + return queue; + }) + .catch(error => { + console.log('Argh! ' + error); + }); + } + +} + +function getSupportedProperties(characteristic) { + let supportedProperties = []; + for (const p in characteristic.properties) { + if (characteristic.properties[p] === true) { + supportedProperties.push(p.toUpperCase()); + } + } + return '[' + supportedProperties.join(', ') + ']'; +} + +function eventByteArrayToString(event) { + let receivedData = []; + for (var i = 0; i < event.target.value.byteLength; i++) { + receivedData[i] = event.target.value.getUint8(i); + } + return String.fromCharCode.apply(null, receivedData); +} diff --git a/src/scss/styles.scss b/src/scss/styles.scss index e41b5fb..894ddf7 100644 --- a/src/scss/styles.scss +++ b/src/scss/styles.scss @@ -148,6 +148,13 @@ body { .connected & { display: block; } } +button:hover { + filter: brightness(80%); +} +button:active { + filter: brightness(70%); +} + .button-center { position: absolute; display: none; -- cgit v1.2.3