aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorjakob.stendahl <jakob.stendahl@infomedia.dk>2023-01-16 09:43:05 +0100
committerJakob Stendahl <jakob.stendahl@outlook.com>2023-01-16 09:44:19 +0100
commit1a2aa931a57f586c17cde95748d195f565619910 (patch)
tree1b7cc05647f57220d981227c0008f10e48cf7624
parentb7b331dd28ff0fb9763c20c8cc65c0c69cf460c8 (diff)
downloadmicrobit-gamepad-1a2aa931a57f586c17cde95748d195f565619910.tar.gz
microbit-gamepad-1a2aa931a57f586c17cde95748d195f565619910.zip
Add some error handling, add Uart service, add event queue for gatt events
-rw-r--r--package-lock.json4
-rw-r--r--package.json2
-rw-r--r--src/index.html2
-rw-r--r--src/js/main.js17
-rw-r--r--src/js/uBit.js161
5 files changed, 163 insertions, 23 deletions
diff --git a/package-lock.json b/package-lock.json
index eb14828..191c6d7 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "microbit-gamepad",
- "version": "1.0.2",
+ "version": "1.0.3",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "microbit-gamepad",
- "version": "1.0.2",
+ "version": "1.0.3",
"license": "MIT",
"dependencies": {
"@fortawesome/fontawesome-free": "^5.15.2"
diff --git a/package.json b/package.json
index e195b30..e51e650 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "microbit-gamepad",
- "version": "1.0.2",
+ "version": "1.0.3",
"description": "",
"license": "MIT",
"scripts": {
diff --git a/src/index.html b/src/index.html
index 525c5de..9272a78 100644
--- a/src/index.html
+++ b/src/index.html
@@ -40,7 +40,7 @@
<select name="layout" id="layout">
<option value="1">DPad + Buttons</option>
<option value="2">DPad + DPad</option>
- <option value="9">Joystick + Joystick (not micro:bit compatible)</option>
+ <option value="9">Joystick + Joystick (Needs more advanced micro:bit code)</option>
</select>
</label>
<label class="form-control form-cb" for="show-gamepad-alt-text">
diff --git a/src/js/main.js b/src/js/main.js
index 3a6f9e9..0b80e3a 100644
--- a/src/js/main.js
+++ b/src/js/main.js
@@ -115,11 +115,15 @@ document.querySelector("#btn_hide_settings").addEventListener("click", () => {
document.querySelector("#btn_disconnect").addEventListener("click", () => {
ubit.disconnect();
});
-document.getElementById("btn_connect").addEventListener("click", () => {
+document.getElementById("btn_connect").addEventListener("click", async () => {
if (!navigator.bluetooth) {
notif_alert("You need a bluetooth enabled browser for this app to work, try chrome.");
}
- ubit.searchDevice();
+ try {
+ await ubit.searchDevice();
+ } catch (e) {
+ notif_alert(`Could not connect to device: ${e}.`);
+ }
});
/* Handle gamepad events */
@@ -184,7 +188,14 @@ gamepad.onTouchEvent(e => {
}
}
if ((ubit.isConnected()) && (event_value != null)) {
- ubit.eventService.sendEvent(event_type, event_value);
+ ubit.sendEvent(event_type, event_value);
+ }
+
+ if ((e.id == "right") && e.hasOwnProperty("x")) {
+ ubit.sendUart(`x:${e.x}\n`);
+ }
+ if ((e.id == "left") && e.hasOwnProperty("y")) {
+ ubit.sendUart(`y:${e.y}\n`);
}
});
diff --git a/src/js/uBit.js b/src/js/uBit.js
index 0275ff9..9ed8342 100644
--- a/src/js/uBit.js
+++ b/src/js/uBit.js
@@ -1,3 +1,4 @@
+import { notif_alert, notif_warn, notif_info, notif_success } from './notification';
/*
* This code is written with a lot of help from these resources:
* https://github.com/antefact/microBit.js/blob/master/src/microBit.js
@@ -10,7 +11,7 @@ const UART_TX_CHARACTERISTIC_UUID = "6e400002-b5a3-f393-e0a9-e50e24dc
/* Used for writing UART data to micro bit */
const UART_RX_CHARACTERISTIC_UUID = "6e400003-b5a3-f393-e0a9-e50e24dcca9e";
/* The event service characteristic (which extends the uBit message bus over bluetooth) */
-const EVENT_SERVICE_CHARACTERISTIC_UUID = "e95d93af-251d-470a-a062-fa1922dfa9a8";
+const EVENT_SERVICE_UUID = "e95d93af-251d-470a-a062-fa1922dfa9a8";
/* This should be read once connected, as the ubit will advertise which events it wants to subscribe to */
const UBIT_REQUIREMENT_CHARACTERISTIC_UUID = "e95db84c-251d-470a-a062-fa1922dfa9a8";
/* The characteristic where we should write the events we wish to be informed of from the microbit */
@@ -43,50 +44,134 @@ export const MESEvents = {
}
class BluetoothService {
+ static gattEventQueue = [];
SERVICE_UUID = null;
+
+ static doGattEvent() {
+ if (BluetoothService.gattEventQueue <= 0) { return; }
+ BluetoothService.gattEventQueue.pop()();
+ }
}
class EventService extends BluetoothService {
- static SERVICE_UUID = EVENT_SERVICE_CHARACTERISTIC_UUID;
+ /* Implements methods for interacting with microbit EventService */
+ static SERVICE_UUID = EVENT_SERVICE_UUID;
service;
constructor(service, ubitEvent) {
super();
this.service = service;
this.ubitEvent = ubitEvent;
- console.log("EventService initialized.");
+ console.debug("EventService initialized.");
}
sendEvent(event_type, event_value) {
- this.ubitEvent.writeValue(
- new Uint16Array([event_type, event_value])
- );
+ BluetoothService.gattEventQueue.push(() => {
+ this.ubitEvent.writeValue(
+ new Uint16Array([event_type, event_value])
+ );
+ });
}
static async getService(gattServer) {
console.debug("Getting EventService");
let service = await gattServer.getPrimaryService(EventService.SERVICE_UUID);
- console.debug("Getting UBitevent characteristic");
+ console.debug("Getting ClientEvent characteristic");
let ubitEventCharacteristic = await service.getCharacteristic(CLIENTEVENT_CHARACTERISTIC_UUID);
return new EventService(service, ubitEventCharacteristic);
}
}
+class UartService extends BluetoothService {
+ /* Implements methods for interacting with microbit UartService */
+ static SERVICE_UUID = UART_SERVICE_UUID;
+ handlers = [];
+
+ constructor(service, uartTx, uartRx) {
+ super();
+ this.service = service;
+ this.uartTx = uartTx;
+ this.uartRx = uartRx;
+ console.debug("UartService initialized.");
+ }
+
+ async sendUart(str, isVolatile=true) {
+ let encoder = new TextEncoder();
+ try {
+ await this.uartRx.writeValue(
+ encoder.encode(str)
+ )
+ } catch (e) {
+ if (!isVolatile) {
+ console.error(e);
+ }
+ }
+ }
+
+ #onUartTx(e) {
+ for (let i = 0; i < this.handlers.length; i++) {
+ this.handlers[i]();
+ }
+ }
+
+ onUartTx(callback) {
+ this.handlers.push(callback);
+ }
+
+ static async getService(gattServer) {
+ console.debug("Getting UartService");
+ let service = await gattServer.getPrimaryService(UartService.SERVICE_UUID);
+
+ console.debug("Getting Uart characteristics");
+
+ let uartTxCharacteristic = await service.getCharacteristic(UART_TX_CHARACTERISTIC_UUID);
+ await uartTxCharacteristic.startNotifications();
+ await uartTxCharacteristic.addEventListener("characteristicvaluechanged", (e) => {
+ this.#onUartTx(e);
+ });
+
+ let uartRxCharacteristic = await service.getCharacteristic(UART_RX_CHARACTERISTIC_UUID);
+
+ return new UartService(service, uartTxCharacteristic, uartRxCharacteristic);
+ }
+
+
+}
+
export class uBitBLE {
eventService;
+ eventServiceAvailable = false;
+ uartService;
+ uartServiceAvailable = false;
+ uartTxHandlers = [];
device;
constructor() {
- this.onConnectCallback = function() {};
- this.onDisconnectCallback = function() {};
+ this.onConnectCallback = [];
+ this.onDisconnectCallback = [];
+ this.pushInterval = setInterval(BluetoothService.doGattEvent, 40);
+ }
+
+ #onDisconnect(e) {
+ console.debug("Device disconnected", e);
+ for (let i = 0; i < this.onDisconnectCallback.length; i++) {
+ this.onDisconnectCallback[i]();
+ }
+ }
+
+ #onConnect() {
+ console.debug("Device connected");
+ for (let i = 0; i < this.onConnectCallback.length; i++) {
+ this.onConnectCallback[i]();
+ }
}
onConnect(callbackFunction) {
- this.onConnectCallback = callbackFunction;
+ this.onConnectCallback.push(callbackFunction);
}
onDisconnect(callbackFunction) {
- this.onDisconnectCallback = callbackFunction;
+ this.onDisconnectCallback.push(callbackFunction);
}
isConnected() {
@@ -106,19 +191,63 @@ export class uBitBLE {
async searchDevice() {
this.device = await navigator.bluetooth.requestDevice({
filters: [{namePrefix: "BBC micro:bit"}],
- optionalServices: [EVENT_SERVICE_CHARACTERISTIC_UUID]
+ optionalServices: [EVENT_SERVICE_UUID, UART_SERVICE_UUID]
});
- this.device.addEventListener('gattserverdisconnected', this.onDisconnectCallback);
+ this.device.addEventListener('gattserverdisconnected', (e) => this.#onDisconnect(e));
console.log("Connected to new device", this.device.name, this.device.id);
console.debug("Connection to GATT server...");
const server = await this.device.gatt.connect()
- this.onConnectCallback();
+ this.#onConnect();
+
console.debug("Getting services...");
- const eventService = await EventService.getService(server);
- this.eventService = eventService;
+ try {
+ const eventService = await EventService.getService(server);
+ this.eventService = eventService;
+ this.eventServiceAvailable = true;
+ } catch (e) {
+ this.eventServiceAvailable = false;
+ console.debug("Could not get EventService");
+ notif_warn("Connected device's firmware does not support bluetooth EventService, gamepad will not work.");
+ }
+
+ try {
+ const uartService = await UartService.getService(server);
+ this.uartService = uartService;
+ for (let i = 0; i < this.uartTxHandlers.length; i++) {
+ this.uartService.onUartTx(this.uartTxHandlers[i]);
+ }
+ this.uartServiceAvailable = true;
+ } catch (e) {
+ this.uartServiceAvailable = false;
+ console.debug("Could not get UartService", e)
+ notif_info("Connected device's firmware does not support bluetooth UartService, joysticks won't work.");
+ }
+ }
+
+ sendEvent(event_type, event_value) {
+ if (this.isConnected() && this.eventServiceAvailable) {
+ this.eventService.sendEvent(event_type, event_value);
+ } else {
+ console.debug(`Could not send event {${event_type}, ${event_value}}, because: ${this.isConnected() ? "Device does not have EventService characteristic" : "No device connected"}.`);
+ }
+ }
+
+ sendUart(str) {
+ if (this.isConnected() && this.uartServiceAvailable) {
+ this.uartService.sendUart(str);
+ } else {
+ console.debug(`Could not send uart data, because: ${this.isConnected() ? "Device does not have UartService characteristic" : "No device connected"}.`);
+ }
+ }
+
+ onUartTx(callback) {
+ this.uartTxHandlers.push(callback);
+ if (this.uartServiceAvailable) {
+ this.uartService.onUartTx(callback);
+ }
}
}