## Bluetooth LE, Microcontrollers, and Badges Gordon Williams, @Espruino
## Bluetooth LE So what is it? - 2.4GHz Radio (like most WiFi/Bluetooth) - NOT Bluetooth - Low Power (Months/Years on a battery) - Simple (ish) - Average ~20M range
## How do you use it? - Laptops/Phones/tablets - Fitness bands/thermometers/toys/etc. - Make your own hardware?
## Linux... - Node.js packages called `noble` and `bleno` - Raspberry Pi 3 / Zero W - Any other Linux with USB BLE - Generally not suitable for > 1 week on battery
## Microcontrollers - Tiny all-in one computers (RAM, ROM and peripherals on-chip) - RAM and ROM usually measured in Kilobytes - Speed mostly under 100 MHz - Can be very low power (good sleep modes) - Bluetooth LE can also be on-chip 


## Espruino - JS interpreter for Microcontollers - Open Soure MPLv2 - Can run in 128kB Flash under 6kB RAM - Not 100% ES5, but RegEx, Classes, Promises, Arrow fns, Template Literals http://www.espruino.com/Features


## How do you program it? USB is only for charging  - Web Bluetooth
## Web Bluetooth Access Bluetooth LE from a Web Page * Mainly Chrome/Opera right now * Windows 10, Mac OS, Chromebook, Android 'just work' * Linux usually 'just works', but unsupported * WebBLE app available for iPhone/iPad * Packages for Web Bluetooth on Node.js * Espruino for Microcontrollers
## goo.gl/Xdiz83 https://gfwilliams.github.io/workshop-nodeconfeu2018/
# Step 1 ### Get Connected


## Set up your Badge Press button on LHS for Menu





## Hacking!
 
``` g.clear(); g.drawString("Hello World"); g.flip(); ```
```JavaScript Badge.patterns.green=()=>{ var n=0; return [()=>{ n+=50; if (n>1536)n=0; NC.ledTop([0,Math.max(255-Math.abs(n-1024),0),0]); NC.ledBottom([0,Math.max(255-Math.abs(n-1384),0),0]); NC.backlight([0,Math.max(255-Math.abs(n-640),0),0, 0,Math.max(255-Math.abs(n-512),0),0, 0,Math.max(255-Math.abs(n-384),0),0, 0,Math.max(255-Math.abs(n-256),0),0]); },50]; }; Badge.pattern("green"); // Use pattern now ```
```JavaScript require("Storage").write(".boot0",` Badge=global.Badge||{}; Badge.patterns=Badge.patterns||{}; // --------------------------------- Your code Badge.patterns.green=()=>{ // ... }; // --------------------------------- Badge.defaultPattern = "green"; `); ```
# ...
# Step 2 ### Advertising

* Burst of data sent every 100-1000(ish) ms, spread over 3 BT channels. * Max 31 bytes of payload (BT 4.2)
Length,Type,Data... ```Unformatted 0201050D095069786C2E6A732064373534 11079ECADC240EE5A9E093F3A3B50100406E 020105 2 bytes, 1 = flags, 5 0D095069786C2E6A732064373534 13 bytes, 9 = name, "Pixl.js d754" 11079ECADC240EE5A9E093F3A3B50100406E 17 bytes, 7 = 128 bit Service UUID 6e400001-b5a3-f393-e0a9-e50e24dcca9e ``` https://www.bluetooth.com/specifications/assigned-numbers/generic-access-profile
## UUIDs Explains what a device can do. Designed to save space (ish) * 16 bit UUIDs - assigned by Bluetooth (or $$$) * 32 bit UUIDs - assigned by Bluetooth (or $$$) * 128 bit UUIDs - Free! Randomly choose one.
## `6e400001-b5a3-f393-e0a9-e50e24dcca9e` Chosen by Nordic for 'Nordic UART Service' - which we'll cover later.
You can add data onto the end of a service. 

```JS // Bluetooth spec says data is 16 bits, 0.01/unit - so x100 var t = Math.round(hum.temperature*100); var h = Math.round(hum.humidity*100); // Set advertising data and name NRF.setAdvertising({ 0x2A6E : [t&255,t>>8], 0x2A6F : [h&255,h>>8], },{ name:"ENV", // change the name connectable:false // don't allow anyone to connect }); ```
Web Bluetooth can't read this... yet. But Espruino can: ```JS NRF.requestDevice({filters:[{name:"ENV"}]}).then(device => { console.log("Found device"); console.log(device); }).catch(err => { console.log("There was a problem!", err); }); ```
``` >NRF.requestDevice(... =Promise: { } Found device BluetoothDevice: { "id": "c0:03:88:c9:0d:ec random", "rssi": -29, "data": new Uint8Array([2, 1, 5, 5,... 78, 86]).buffer, "name": "ENV", "serviceData": { "2a6e": new Uint8Array([157, 9]).buffer, "2a6f": new Uint8Array([86, 17]).buffer } } ```
```JS var hum = { temperature : (new Uint16Array( device.serviceData["2a6e"]))[0]/100, humidity : (new Uint16Array( device.serviceData["2a6f"]))[0]/100 }; g.clear(); g.setFontVector(20); g.drawString(hum.temperature, 20,8); g.drawString(hum.humidity, 20,40); g.setFontBitmap(); g.drawString("Temperature ('C)", 0,0); g.drawString("Humidity (%)", 0,32); g.flip(); ```

# ...
# Step 3 ### Services
* Advertising is one-way, no guarantees * Also Unencrypted * For reliable/secure transmission you need a connection * Connection is based on Services/Characteristics * Characteristics can **notify** changes or be **read**/**written**
## Peripheral * Provides the 'services' * eg. Light bulb, Health Band/etc * Also known as the **server** ## Central * Usually **makes** the connection * eg. Phone/Laptop * Also known as the **client**
## Wireless Lightbulb Has a light, and battery. * **Service:** Light * **Characteristic:** Brightness (writable, readable) * **Characteristic:** Hue (writable, readable) * **Service:** Battery * **Characteristic:** Percent Charge (readable, notify)
## Serial port Has transmit, and receive: * **Service:** Nordic UART * `6e400001-b5a3-f393-e0a9-e50e24dcca9e` * **Characteristic:** TX (notify) * **Characteristic:** RX (writable)
## Control Espruino * JS REPL is available on Bluetooth LE... * JS commands + `"\n"` => `RX` Characteristic * Not 100% simple... * Finding device * Max packet size of 20 bytes * ...
## Web Bluetooth Security * Only available on HTTPS (we'll use GitHub pages) * Device Chooser only available in direct response to user input * No `document.onload`
To get started, use Puck.js library: ```
On!
Off!
```
# ...
# Step 4 ### Writing to Services
## Simple, Easy Also boring. Let's use Web Bluetooth directly. We need something to test on.



* **Service:** UUID 7b340000-105b-2b38-3a74-2932f884e90e - Badge Service * **Characteristic:** UUID 7b340001-105b-2b38-3a74-2932f884e90e - LEDs - 3 bytes (writable) * **Characteristic:** UUID 7b340002-105b-2b38-3a74-2932f884e90e - Vibraton motors - 2 bytes (writable)
```JS var options = { filters: [ { namePrefix: 'Pixl.js' }, ], optionalServices: [ "7b340000-105b-2b38-3a74-2932f884e90e" ] }; // Bring up the Web Bluetooth Device Chooser navigator.bluetooth.requestDevice(options).then( function(device) { return device.gatt.connect(); }).then(function(g) { ... ```
```JS ... }).then(function(g) { gatt = g; // Get our custom service return gatt.getPrimaryService("7b340000-105b-..."); }).then(function(service) { // Get the RGB LED characteristic return service.getCharacteristic("7b340001-105b-..."); }).then(function(characteristic) { ... ```
``` ... }).then(function(characteristic) { // Make a random color var rgb = new Uint8Array([ Math.random()*255, Math.random()*255, Math.random()*255]); // Write it to the characteristic return characteristic.writeValue(rgb); }).then(function(characteristic) { gatt.disconnect(); ```
* LEDs: `7b340001-105b-2b38-3a74-2932f884e90e` * 3 bytes (0..255), R, G, B * Vibrate: `7b340002-105b-2b38-3a74-2932f884e90e` * 2 bytes (0..255) for Left and Right Motors
## **Defining** a Service Not Web Bluetooth spec. On Espruino: ``` NRF.setServices({ "7b340000-105b-2b38-3a74-2932f884e90e" : { "7b340001-105b-2b38-3a74-2932f884e90e" : { writable : true, value : [0,0,0], maxLen : 3, onWrite : function(evt) { ... }, }, ... } }); ```
# ...
# Step 5 ### Reading from Services
### Write ``` return characteristic.writeValue(rgb); }).then(function() { ``` ### Read ``` return characteristic.readValue(rgb); }).then(function(value) { // value is a DataView ```
## Notifications ``` ... }).then(function(characteristic) { characteristic.addEventListener('characteristicvaluechanged', function(event) { var dataView = event.target.value; // a DataView ... }); return characteristic.startNotifications(); }).then(function() { ... ```

## **Defining** a Service Not Web Bluetooth spec. On Espruino: ``` NRF.setServices({ "7b340000-105b-2b38-3a74-2932f884e90e" : { "7b340003-105b-2b38-3a74-2932f884e90e" : { readable : true, notify : true, value : [0,0,0] } } ... NRF.updateServices({ "7b340000-105b-2b38-3a74-2932f884e90e" : { "7b340003-105b-2b38-3a74-2932f884e90e" : { value : [a,b,c], notify: true } } }); ```
# ...
# Wrap-up
* Advertising * Central / Peripheral * Services / Characteristics * Notifications
## Espruino * 'Central' uses the Web Bluetooth API * There's no standard for 'Peripheral' * Uses `NRF.setServices/setAdvertising/etc`
## What next? * Web Bluetooth Samples https://googlechrome.github.io/samples/web-bluetooth/ * Pixl.js/Espruino Tutorials http://www.espruino.com/Pixl.js#tutorials * Espruino Reference http://www.espruino.com/Reference * Espruino Forum http://forum.espruino.com/
# Fin. www.espruino.com / @Espruino