From 2d6cd813f9678ff5ecd1b065be9d6456cdfcb5c0 Mon Sep 17 00:00:00 2001 From: aronmal Date: Sun, 25 Sep 2022 14:45:22 +0200 Subject: [PATCH] Improved Bluetooth sketches and docs --- Arduino/Bluetooth.ino | 66 +++++++++++ README.md | 15 ++- frontend/src/Bluetooth.tsx | 220 ++++++++++++++++++++++--------------- 3 files changed, 208 insertions(+), 93 deletions(-) create mode 100644 Arduino/Bluetooth.ino diff --git a/Arduino/Bluetooth.ino b/Arduino/Bluetooth.ino new file mode 100644 index 0000000..12f028b --- /dev/null +++ b/Arduino/Bluetooth.ino @@ -0,0 +1,66 @@ +#include + +unit8_t uvindexvalue = 0x0; +#define UUID16_SVC_ENVIRONMENTAL_SENSING 0x181A +#define UUID16_CHR_UV_INDEX 0x2A76 + +BLEService enviromental_sensing_service = BLEService(UUID16_SVC_ENVIRONMENTAL_SENSING); +BLECharacteristic uv_index_characteristic = BLECharacteristic(UUID16_CHR_UV_INDEX); + +void setup() +{ + Serial.beginn(115200); + delay(500); + Serial.println("Start!"); + + Bluefruit.begin(); + Bluefruit.setName("Palm"); + + setupESService(); + startAvd(); +} + +void loop() +{ + uvindexvalue = random(0, 11); + + Serial.print("UV Index: "); + Serial.println(uvindexvalue); + + if (uv_index_characteristic.indicate(&uvindexvalue, sizeof(uvindexvalue))) + { + Serial.print("Updated UV Index: "); + Serial.println(uvindexvalue); + } + else + { + Serial.print("UV Index Indicate not set"); + } + + delay(1000); + +} + +void startAvd(void) +{ + Bluefruit.Advertising.addService(environmental_sensing_service); + + Bluefruit.Advertising.addFlags(BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE); + Bluefruit.Advertising.addTxPower(); + Bluefruit.Advertising.addName(); + Bluefruit.Advertising.restartOnDisconnect(true); + Bluefruit.Advertising.setInterval(32, 244); + Bluefruit.Advertising.setFastTimeout(30); + Bluefruit.Advertising.start(0); +} + +void setupESService(void) { + environmental_sensing_service.begin(); + + uv_index_characteristic.setProperties(CHR_PROPS_INDICATE); + uv_index_characteristic.setPermission(SECMODE_OPEN, SECMODE_NO_ACCESS); + uv_index_characteristic.setFixedLen(1); + uv_index_characteristic.begin(); + + uv_index_characteristic.write(&uvindexvalue, sizeof(uvindexvalue)); +} \ No newline at end of file diff --git a/README.md b/README.md index f9e85ae..9d8b856 100644 --- a/README.md +++ b/README.md @@ -7,8 +7,13 @@ Download [Android SDK Platform-Tools](https://developer.android.com/studio/relea Commands: ``` -./adb pair 10.1.0.125:38407 -./adb connect 10.1.0.125:39099 +./platform-tools/adb pair 10.1.0.125:38407 +./platform-tools/adb connect 10.1.0.125:39099 +``` + +And debug Chrome: +``` +chrome://inspect/#devices ``` Chrome flags to be enabled: @@ -23,5 +28,7 @@ chrome://bluetooth-internals/#devices ``` Other resources: -- [GATT Characteristics](https://btprodspecificationrefs.blob.core.windows.net/assigned-values/16-bit%20UUID%20Numbers%20Document.pdf) -- [Using Web BLE](https://youtu.be/TsXUcAKi790) \ No newline at end of file +- [Android Wireless Debugging](https://youtu.be/gyVZdZtIxnw?t=49) Tutorial +- [GATT Characteristics](https://btprodspecificationrefs.blob.core.windows.net/assigned-values/16-bit%20UUID%20Numbers%20Document.pdf) Document +- [Using Web BLE](https://youtu.be/TsXUcAKi790) Tutorial +- [Adafruit Feather nRF52 Bluefruit LE](https://www.berrybase.de/adafruit-feather-nrf52-bluefruit-le) Dev. Boards \ No newline at end of file diff --git a/frontend/src/Bluetooth.tsx b/frontend/src/Bluetooth.tsx index f4f2ce6..c9017d8 100644 --- a/frontend/src/Bluetooth.tsx +++ b/frontend/src/Bluetooth.tsx @@ -1,108 +1,150 @@ +import { useState } from "react" + function Bluetooth() { - const connectToDevice = async () => { - if (!navigator.bluetooth) - console.log('Web Bluetooth is not available!'); - navigator.bluetooth - .requestDevice({ - filters: [ - { namePrefix: "Chromecast Remote" } - ], - optionalServices: ["battery_service"], - }) - .then(device => { - console.log(device); - // console.log(device.id, device.name, device.gatt); - // Set up event listener for when device gets disconnected. - device.addEventListener('gattserverdisconnected', onDisconnected); + const [startDisabled, setStartDisabled] = useState(true) + const [stopDisabled, setStopDisabled] = useState(true) - console.log(1) - // Attempts to connect to remote GATT Server. - const gatt = device.gatt - if (!gatt) - throw new Error('no gatt'); - return gatt.connect(); + const deviceName = 'Chromecast Remote' + // ble UV Index + const bleService = 'environmental_sensing' + const bleCharacteristic = 'uv_index' + + // ble Battery percentage + // const bleService = 'battery_service' + // const bleCharacteristic = 'battery_level' + + // ble Manufacturer Name + // const bleService = 'device_information' + // const bleCharacteristic = 'manufacturer_name_string' + let bluetoothDeviceDetected: BluetoothDevice + let gattCharacteristic: BluetoothRemoteGATTCharacteristic + + function isWebBluetoothEnabled() { + if (!navigator.bluetooth) { + alert('Web Bluetooth API is not available in this browser!') + return false + } + return true + } + function getDeviceInfo() { + const options = { + // acceptAllDevices: true, + filters: [ + { name: deviceName } + ], + // optionalServices: ['battery_service'], + } + + console.log('Requesting Bluetooth Device...') + + return navigator.bluetooth + .requestDevice(options) + .then(device => { + bluetoothDeviceDetected = device + console.log('> Name: ' + device.name) + device.addEventListener('gattserverdisconnected', onDisconnected) }) + .catch(error => console.log('Argh! ' + error)) + } + function read() { + if (!isWebBluetoothEnabled()) + return + return getDeviceInfo() + .then(connectGatt) + .then(_ => { + console.log('Reading UV Index...') + return gattCharacteristic.readValue() + }) + .catch(error => console.log('Waiting to start reading: ' + error)) + } + function connectGatt() { + if (bluetoothDeviceDetected.gatt && bluetoothDeviceDetected.gatt.connected && gattCharacteristic) + return Promise.resolve() + if (!bluetoothDeviceDetected || !bluetoothDeviceDetected.gatt) + return Promise.reject() + return bluetoothDeviceDetected.gatt.connect() .then(server => { - console.log(2) - console.log(server) - // Getting Battery Service… - return server.getPrimaryService('battery_service'); + console.log('Getting GATT Service...') + return server.getPrimaryService(bleService) }) .then(service => { - console.log(3) - // Getting Battery Level Characteristic… - return service.getCharacteristic('battery_level'); - // return service.getCharacteristic(0x2a19); + console.log('Getting GATT Characteristic...') + return service.getCharacteristic(bleCharacteristic) }) .then(characteristic => { - console.log(4) - // Reading Battery Level… - return characteristic.readValue(); + gattCharacteristic = characteristic + characteristic.addEventListener('characteristicvaluechanged', handleChangedValue) + + setStartDisabled(false) + setStopDisabled(true) }) - .then(value => { - console.log(5) - console.log(`Battery percentage is ${value.getUint8(0)}`); - }) - .catch(error => { console.log(error); }); } - const connectToDevice2 = async () => { - if (!navigator.bluetooth) - console.log('Web Bluetooth is not available!'); - let device = await navigator.bluetooth - navigator.bluetooth - .requestDevice({ - filters: [ - { namePrefix: "Chromecast Remote" } - ], - optionalServices: ["device_information"], - }) - .then(device => { - // Set up event listener for when device gets disconnected. - device.addEventListener('gattserverdisconnected', onDisconnected); + function handleChangedValue(event: Event) { + const characteristic = event.target as BluetoothRemoteGATTCharacteristic + if (!characteristic.value) { + console.log('Characteristic undefined!') + return + } + const value = characteristic.value.getUint8(0) + const now = new Date() + // Output the UV Index + console.log(`> ${now.getHours()}:${now.getMinutes()}:${now.getSeconds()} UV Index is ${value}`) - console.log(1) - // Attempts to connect to remote GATT Server. - const gatt = device.gatt - if (!gatt) - throw new Error('no gatt'); - return gatt.connect(); - }) - .then(server => { - console.log(2) - console.log(server) - // Getting Battery Service… - return server.getPrimaryService('device_information'); - }) - .then(service => { - console.log(3) - // Getting Battery Level Characteristic… - return service.getCharacteristic('manufacturer_name_string'); - }) - .then(characteristic => { - console.log(4) - // Reading Battery Level… - return characteristic.readValue(); - }) - .then(value => { - console.log(5) - let decoder = new TextDecoder('utf-8'); - console.log(decoder.decode(value)); - }) - .catch(error => { console.log(error); }); + // Output the Battery percentage + // console.log(`> ${now.getHours()}:${now.getMinutes()}:${now.getSeconds()} Battery percentage is ${value}`) + + // Output the Manufacturer Name + // let decoder = new TextDecoder('utf-8') + // console.log(`> ${now.getHours()}:${now.getMinutes()}:${now.getSeconds()} Manufacturer Name is ${decoder.decode(characteristic.value)}`) } - - const onDisconnected = (event: any) => { - // alert("Device Disconnected"); - // console.log(event); - const device = event.target; - console.log(`Device "${device.name}" is disconnected.`); + function start() { + if (!isWebBluetoothEnabled()) + return + gattCharacteristic.startNotifications() + .then(_ => { + console.log('Start reading...') + setStartDisabled(true) + setStopDisabled(false) + }) + .catch(error => console.log('[ERROR] Start: ' + error)) + } + function stop() { + if (!isWebBluetoothEnabled()) + return + gattCharacteristic.stopNotifications() + .then(_ => { + console.log('Stop reading...') + setStartDisabled(false) + setStopDisabled(true) + }) + .catch(error => console.log('[ERROR] Stop: ' + error)) + } + function onDisconnected(event: Event) { + alert("Device Disconnected") + // console.log(event) + const device = event.target as BluetoothDevice + console.log(`Device "${device.name}" is disconnected.`) } - - return ( <> - + + + )