fun with bluetooth

why?

progressive 
 web apps

pwa’s are great !

but...

but...

bluetooth

bluetooth sucks

classic bluetooth the reason everybody 
 hates bluetooth bluetooth low energy vs. control drones and other cool shit

bluetooth low energy also known as BLE Bluetooth LE Bluetooth Smart Bluetooth 4

bluetooth low energy also known as BLE Bluetooth LE Bluetooth Smart Bluetooth 4 and 5

10 million


 bluetooth devices 
 shipping every day

mobile phone

computer

glucose monitor somebody's hand

activity tracker

playbulb sphere playbulb

spherio bb-8

parrot mini drone

fi dget spinner

the boring theoretical stuff

central peripheral

central

generic attribute profile

generic attribute profile ?

generic attribute profile gatt, because gap was already taken

client server

central peripheral

server service characteristic value array of objects object property value

services and characteristics are identified by uuid’s 16 bit or 128 bit

read write write without response notify each characteristic supports
one or more of these

every value is an array of bytes no fancy datatypes, just bytes

pfew...

fun

with

bluetooth boring facts 
 about

fun with bluetooth

 web


 bluetooth
api still not the fun part :-(

connecting to a device

navigator.bluetooth.requestDevice({ filters: [
{ namePrefix: 'PLAYBULB' }
], optionalServices: [ 0xff0f ] }) .then(device => device.gatt.connect()) .then(server => server.getPrimaryService(0xff0f)) .then(service => service.getCharacteristic(0xfffc)) .then(characteristic => { return characteristic.writeValue( new Uint8Array([ 0x00, r, g, b ]) ); }) we tell the browser what 
 kind of device we want

the user selects
the actual device

navigator.bluetooth.requestDevice({ filters: [
{ namePrefix: 'PLAYBULB' }
], optionalServices: [ 0xff0f ] }) .then(device => { 
 .... 
 }) .then(device => device.gatt.connect()) .then(server => server.getPrimaryService(0xff0f)) .then(service => service.getCharacteristic(0xfffc)) .then(characteristic => { return characteristic.writeValue( new Uint8Array([ 0x00, r, g, b ]) ); }) promises are so 2017

let device = await navigator.bluetooth.requestDevice({ filters: [
{ namePrefix: 'PLAYBULB' }
], optionalServices: [ 0xff0f ] }); .then(device => device.gatt.connect()) .then(server => server.getPrimaryService(0xff0f)) .then(service => service.getCharacteristic(0xfffc)) .then(characteristic => { return characteristic.writeValue( new Uint8Array([ 0x00, r, g, b ]) ); })

let device = await navigator.bluetooth.requestDevice({ filters: [
{ namePrefix: 'PLAYBULB' }
], optionalServices: [ 0xff0f ] }); let server = await device.gatt.connect()); let service = await server.getPrimaryService(0xff0f)); let characteristic = await service.getCharacteristic(0xfffc)); .then(characteristic => { return characteristic.writeValue( new Uint8Array([ 0x00, r, g, b ]) ); }) get the characteristic get the service connect to the server

writing data

let device = await navigator.bluetooth.requestDevice({ ... }); let server = await device.gatt.connect()); let service = await server.getPrimaryService(0xff0f)); let characteristic = await service.getCharacteristic(0xfffc)); 
 characteristic.writeValue( new Uint8Array([ 0x00, r, g, b ]) ); write some bytes

reading data

let device = await navigator.bluetooth.requestDevice({ ... }); let server = await device.gatt.connect()); let service = await server.getPrimaryService(0xff0f)); let characteristic = await service.getCharacteristic(0xfffc)); 
 let value = await characteristic.readValue(); let r = value.getUint8(1);
let g = value.getUint8(2); let b = value.getUint8(3); read some bytes

get notified of changes

let device = await navigator.bluetooth.requestDevice({ ... }); let server = await device.gatt.connect()); let service = await server.getPrimaryService(0xff0f)); let characteristic = await service.getCharacteristic(0xfffc)); characteristic.addEventListener( 
 'characteristicvaluechanged', e => { let r = e.target.value.getUint8(1);
let g = e.target.value.getUint8(2); let b = e.target.value.getUint8(3); } ); characteristic.startNotifications(); add event listener don't forget to start listening

things you need to know: • the webbluetooth api • promises (or async await) • typed arrays duh!

custom
characteristics . wtf!

writing a value: function(r, g, b) { return new Uint8Array([ 0x00, r, g, b ]); } reading a value: function(buffer) { return { 
 r: buffer.getUint8(1), 
 g: buffer.getUint8(2), 
 b: buffer.getUint8(3) 
 } } writing to and reading 
 from the same characteristic

writing a value: function(r, g, b) { return new Uint8Array([
0x01, g, 0x01, 0x00, 0x01, 
 b, 0x01, r, 0x01, 0x00

 ]); } reading the current 
 color is not possible

writing a value: 
 function(r, g, b) { var buffer = new Uint8Array([ 
 0xaa, 0x0a, 0xfc, 0x3a, 0x86, 0x01, 0x0d, 
 0x06, 0x01, r, g, b, 0x00, 0x00, 
 (Math.random() * 1000) & 0xff, 0x55, 0x0d 
 ]); for (var i = 1; i < buffer.length - 2; i++) { buffer[15] += buffer[i]; } return buffer; } reading the current 
 color is not possible

writing a value: 
 function(r, g, b, position) { let buffer = new Uint8Array([
0x07, 0x02, position + 1, r, g, b
]); return buffer; }

writing a value: 
 function(r, g, b, position) { let buffer = new Uint8Array([
0x58, r, g, b, 0x01, position ]); ...

writing a value: 
 function(r, g, b, position) { let buffer = new Uint8Array([
0x58, r, g, b, 0x01, position ]); let payload = new Uint8Array(buffer.length + 4); payload[0] = payload.length - 2; payload[1] = payload.length - 2 >>> 8; payload.set(buffer, 2); let checksum = payload.reduce((a, b) => a + b, 0); payload[payload.length - 2] = checksum; payload[payload.length - 1] = checksum >>> 8; let extra = payload.filter(value => { 
 value === 0x01 || value === 0x02 || value == 0x03 


        message[m] = 0x03; 
        message[m + 1] = 0x05; 
        m += 2; 
    } 
    else if (payload[i] === 0x03) { 
        message[m] = 0x03; 
        message[m + 1] = 0x06; 
        m += 2; 
    } 
    else { 
        message[m] = payload[i]; 
        m++; 
    } 
} 
message[0] = 0x01; 
message[message.length - 1] = 0x02; 
return message; 

}

adafruit 
 bluetooth 
 sniffer

log all 
 bluetooth 
 packets 
 on your phone and use adb to 
 transfer the log

use wireshark to 
 look at the data 


decompiling


 the apk don't tell anyone!

demofinally the fun part

warning experimental technology 
 setting low expectations

warning wifi interference 
 lowering them even further

fun with bluetooth !

questions? @html5test