Fun with Bluetooth @ Amsterdam University of Applied Sciences

Fun with Bluetooth @ Amsterdam University of Applied Sciences

Time for the browser to get physical. With WebBluetooth the browser can actually take control of all kinds of physical devices in the real world like lightbulbs, robots and even drones. This talk will teach you the basics that you need to get started and will show you some more advanced topics like building your own Bluetooth devices using just JavaScript.

De023a9aff4c7a5ede3a81e8c76f17b5?s=128

Niels Leenheer

April 11, 2018
Tweet

Transcript

  1. fun with bluetooth and other stuff

  2. why?

  3. progressive 
 web apps

  4. pwa’s are great !

  5. but...

  6. but...

  7. None
  8. None
  9. ethernet 
 & wifi

  10. wifi

  11. zigbee?


  12. ethernet

  13. runs a webserver use fetch or 
 xmlhttprequest

  14. [{ "id":"001788fffe100491", "internalipaddress":"192.168.2.23", "macaddress":"00:17:88:10:04:91", "name":"Philips Hue" }, { "id":"001788fffe09a168", "internalipaddress":"192.168.88.252"

    }, https://www.meethue.com/api/nupnp 1
  15. <root xmlns="urn:schemas-upnp-org:device-1-0"> <specVersion> <major>1</major> <minor>0</minor> </specVersion> <URLBase>http://192.168.2.23:80/</URLBase> <device> <deviceType>urn:schemas-upnp-org:device:Basic:1</deviceT <friendlyName>Philips

    hue (192.168.2.23)</friendlyName> <manufacturer>Royal Philips Electronics</manufacturer> http://192.168.2.23/description.xml 2
  16. http://192.168.2.23/api
 {"devicetype": "hue_pwa#pixel_xl"} 3 [{"success":{"username": "........"}}] User presses physical button

    on the hub *click* save to local storage
  17. http://192.168.2.23/api
 {"devicetype": "hue_pwa#pixel_xl"} 3 [{"success":{"username": "........"}}] User presses physical button

    on the hub *click* save to local storage
  18. 4 { "1": { "state": { "on": true, "bri": 144,

    "hue": 13088, "sat": 212, "xy": [0.5128,0.4147], "ct": 467, "alert": "none", http://192.168.2.23/api/......../lights
  19. http://192.168.2.23/api/......../lights/1/state
 { "hue": 50000, "on": true, "bri": 200 } 5

    [ {"success":{"/lights/1/state/bri":200}}, {"success":{"/lights/1/state/on":true}}, {"success":{"/lights/1/state/hue":50000}}
  20. *click* *click* *reload* *click*

  21. this does 
 not work !

  22. https only http only no certificate 
 for you

  23. = network use remote api’s ≠ access to local devices

  24. hue remote api Also allow control from 
 outside the

    local network
  25. We are currently preparing for release of this remote API

    which will initially be available to a limited number of partners. “ — Philips
  26. We are currently preparing for release of this remote API

    which will initially be available to a limited number of partners. “ — Philips two years ago very not yet
  27. fun with bluetooth problems local networks

  28. fun with bluetooth

  29. bluetooth

  30. bluetooth sucks

  31. classic bluetooth the reason everybody 
 hates bluetooth bluetooth low

    energy vs. control drones and other cool shit
  32. bluetooth low energy also known as BLE Bluetooth LE Bluetooth

    Smart Bluetooth 4
  33. playbulb sphere playbulb

  34. spherio bb-8

  35. parrot mini drone

  36. activity tracker

  37. the boring theoretical stuff

  38. central peripheral

  39. central

  40. generic attribute profile

  41. generic attribute profile ?

  42. generic attribute profile gatt, because gap was already taken

  43. central peripheral client server

  44. § i device information light multiple services per device

  45. i device information battery flight control

  46. i device information battery steering control

  47. i device information battery heart rate

  48. heart rate i device information battery

  49. i device information battery heart rate

  50. i device information manufacturer model number serial number hardware revision

    firmware revision software revision ... multiple characteristics 
 per service
  51. server service characteristic value array of objects object property value

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

    128 bit
  53. 0x180A 0000180A-0000-1000-8000-00805F9B34FB 16 bit 128 bit i device information

  54. 0x180F 0000180F-0000-1000-8000-00805F9B34FB 16 bit 128 bit battery

  55. not recommended any UUID outside of the range
 xxxxxxxx-0000-1000-8000-00805F9B34FB 16

    bit 128 bit light steering control flight c still, everybody does this
  56. i device information manufacturer model number serial number hardware revision

    firmware revision software revision ...
  57. i 0x180A 0x2A29 0x2A24 0x2A25 0x2A27 0x2A26 0x2A28 ... bad

    for readability, 
 good for saving bandwidth
  58. read write write without response notify each characteristic supports one

    or more of these
  59. every value is an array of bytes no fancy datatypes,

    just bytes
  60. pfew...

  61. None
  62. fun with bluetooth boring facts
 about

  63. fun with bluetooth

  64. web
 bluetooth api still not the fun part :-(

  65. connecting to a device

  66. 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 ]) ); }) 1 we tell the browser what 
 kind of device we want
  67. the user selects the actual device

  68. 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 ]) ); }) 2 connect to the server
  69. 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 ]) ); }) 3 get the service
  70. 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 ]) ); }) 4 get the characteristic
  71. writing data

  72. navigator.bluetooth.requestDevice({ ... }) .then(device => device.gatt.connect()) .then(server => server.getPrimaryService(0xff0f)) .then(service

    => service.getCharacteristic(0xfffc)) .then(c => { return c.writeValue( new Uint8Array([ 0x00, r, g, b ]) ); }) write some bytes
  73. reading data

  74. navigator.bluetooth.requestDevice({ ... }) .then(device => device.gatt.connect()) .then(server => server.getPrimaryService(0xff0f)) .then(service

    => service.getCharacteristic(0xfffc)) .then(c => c.readValue()) .then(value => { let r = value.getUint8(1); let g = value.getUint8(2); let b = value.getUint8(3); }) read some bytes
  75. get notified of changes

  76. navigator.bluetooth.requestDevice({ ... }) .then(device => device.gatt.connect()) .then(server => server.getPrimaryService(0xff0f)) .then(service

    => service.getCharacteristic(0xfffc)) .then(c => { c.addEventListener('characteristicvaluechanged', e => { let r = e.target.value.getUint8(1); let g = e.target.value.getUint8(2); let b = e.target.value.getUint8(3); }); c.startNotifications(); }) add event listener don't forget to start listening
  77. things you need to know: • the webbluetooth api •

    promises • typed arrays duh!
  78. browser support Chrome Opera Samsung Servo
 (soon)

  79. browser support Chrome Opera Samsung kinda works Servo
 (soon) WebBLE


    for iOS
  80. and... npm install node-web-bluetooth

  81. and... the puck.js

  82. custom characteristics. wtf!

  83. 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
  84. 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
  85. 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
  86. writing a value: 
 function(r, g, b, position) { let

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

    buffer = new Uint8Array([ 0x58, r, g, b, 0x01, position ]); ...
  88. 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 => {

  89. 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; }
  90. adafruit
 bluetooth 
 sniffer

  91. decompiling 
 the apk don't tell anyone!

  92. fun with usb

  93. None
  94. None
  95. None
  96. navigator.usb.requestDevice({ filters: [ { vendorId: 0x2341 } ] }) .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 ]) ); }) 1 we tell the browser what 
 kind of device we want
  97. navigator.usb.requestDevice({ filters: [ { vendorId: 0x2341 } ] }) .then(d

    => { device = d; device.open(); }) 2 connect to the server
  98. navigator.usb.requestDevice({ filters: [ { vendorId: 0x2341 } ] }) .then(d

    => { device = d; device.open(); }) .then(() => device.selectConfiguration(1)) 3 Select configuration
  99. navigator.usb.requestDevice({ filters: [ { vendorId: 0x2341 } ] }) .then(d

    => { device = d; device.open(); }) .then(() => device.selectConfiguration(1))
 .then(() => device.claimInterface(2)) 4 Claim the interface for us
  100. navigator.usb.requestDevice({ filters: [ { vendorId: 0x2341 } ] }) .then(d

    => { device = d; device.open(); }) .then(() => device.selectConfiguration(1))
 .then(() => device.claimInterface(2))
 .then(() => device.controlTransferOut({
 requestType: 'class',
 recipient: 'interface',
 request: 0x22,
 value: 0x01,
 index: 0x02
 })) 5
  101. writing data

  102. navigator.usb.requestDevice({...}) .then(d => { device = d; device.open(); }) .then(()

    => device.selectConfiguration(1))
 .then(() => device.claimInterface(2)) .then(() => device.controlTransferOut({...})) 
 
 .then(() => device.transferOut(4, data)) write some bytes
 to endpoint 4
  103. reading data

  104. navigator.usb.requestDevice({...}) .then(d => { device = d; device.open(); }) .then(()

    => device.selectConfiguration(1))
 .then(() => device.claimInterface(2)) .then(() => device.controlTransferOut({...})) 
 
 .then(() => device.transferIn(5, 64))
 .then(data => {
 console.log(data); }) read some 64 bytes 
 from endpoint 5
  105. demo finally the fun part

  106. warning experimental technology
 setting low expectations

  107. warning wifi interference
 lowering them even further

  108. None
  109. https:/ /bluetooth.rocks/lightbulb
 https:/ /github.com/NielsLeenheer/BluetoothBulb change the colour 
 of a

    lightbulb
  110. https:/ /bluetooth.rocks/pixel draw pixel art on 
 a led matrix

    display
  111. https:/ /bluetooth.rocks/racer https:/ /github.com/NielsLeenheer/BluetoothRacer control a lego racer 
 using

    a gamepad use css animations to 
 define a path
  112. https:/ /bluetooth.rocks/drone
 https:/ /github.com/poshaughnessy/web-bluetooth-parrot-drone control a drone 
 from your

    browser
  113. https:/ /bluetooth.rocks/printer print on a receipt printer

  114. https:/ /bluetooth.rocks/pulse
 https:/ /github.com/NielsLeenheer/BluetoothPulse find out your 
 current heartbeat

  115. physical web

  116. battery lasts years beacon really small

  117. broadcast the url
 of a web app easy to open

    from 
 the home screen
  118. coming soon automatic web bluetooth
 connection

  119. taking it one step further

  120. runs javascript physical web beacon supports 
 web bluetooth api

    has a button
  121. peripheral
 mode

  122. central
 mode

  123. None
  124. questions? @html5test