Let’s get physical
grandcentrix.net @grandcentrix
+Albrecht Noll @UhrArt
+Pascal Welsch @passsy
Agenda ● Physical Web ● Beacons (iBeacon vs. Eddystone) ● Google Nearby API ● More about beacons
Physical Web ● ●
●
Idea: Walk up and use anything. What? ○ Open standard that everyone can use ○ No proactive notifications ○ Open Source ○ BLE energy efficient & widespread ○ Built on web and urls Why? ○ Context is king ○ Bridge digital and real world
physical-web.org
Physical Web - technical overview ●
●
● ●
Broadcast only approach ○ BLE broadcast the URL in the advertising packet ○ Sender does not know receiver Format ○ 16 bit Service-UUID and 0xFEAA ○ 0x10 Service-Data ○ Url shorteners are recommended Client ○ lists meta information to URL: title, description, url, favicon. Ranking ○ Signal strength ○
Later ranking algorithms based on user preference, spamming etc.
Physical Web - Chrome opt-in
Physical Web - examples Chromecast setup CHF Payment via Beacon (Twint) Vending Machines Museums: Audio guides Conference: Spaces links at Google I/O 16 ● Drones ● … everything ● ● ● ● ●
Beacons Estimote 30$/unit (10$) ● ● ●
● ● ● ●
iBeacon & Eddystone Configuration with Estimote App 41 months battery life ○ at 950ms ○ Tx=-12dBm = 15m Tx_min = - 30 dBm 1.5m TX_max = + 4 dBm 70m Accelerometer Thermometer
Beacon Prototyping ● ● ●
Beacon Toy (Find & Simulate) Node.js npmjs.com/package/eddystone-beacon Mac App github.com/dermike/electron-slide-beaco
Beacon Provisioning ● Claim beacon with the beacon manufacturer app via bluetooth ● Adjust interval ● Change protocol if support for multiple protocols ○ ○ ○
iBeacon Eddystone AltBeacon
● Change IDs
iBeacon vs. Eddystone ● ● ● ● ● ● 1A
iBeacon developed by Apple (2014) BLE beacons private deployment use-case only searched for by a specific app ̴ 100 ms interval (fixed) ̴ 31 bytes FF
... 16 bytes UUID
Major 2 bytes
Minor 2 bytes
Tx
iBeacon vs. Eddystone ● ● ● ● ●
...
Beacon protocol by Google (2015) ̴ 1-2 s (custom) open standard / open source (github.com/google/eddystone) private deployment Eddystone UID e.g. Service UID: 0000feaa-0000-1000-8000-00805f9b34fb Service Data: 00.df.edd1ebeac04e5defa017.e360c7526c00
FEAA
00
Tx
Namespace ID 10 bytes
Instance ID 6 bytes
iBeacon vs. Eddystone ●
...
Eddystone-URL: A compressed URL that, once parsed and decompressed, is directly usable by the client. FEAA
10
Tx
18 bytes URL
10.df.03.74776974746572.00.7568726172742f https://
twitter
.com/
uhrart/
iBeacon vs. Eddystone ●
● ...
Eddystone-TLM: Beacon status data that is useful for beacon fleet maintenance, and powers Google Proximity Beacon API's diagnostics endpoint. No identifying data FEAA
20
...
BATT
TEMP
AD_COUNT...
iBeacon vs. Eddystone ●
●
...
Eddystone-EID: A time-varying beacon frame that can be resolved to a stable identifier by a linked resolver, such as Proximity Beacon API. ○ Time rotating 8 byte values ○ Registered via Web Service ○ Web service resolves EIDs Secure or Private use-cases
FEAA
30 EID[0] 8 bytes
EID[1] 8 bytes
EID[2] 8 bytes
...
Manual Bluetooth // Eddystone service uid (0xfeaa) private static ParcelUuid UID_SERVICE = ParcelUuid .fromString("0000feaa-0000-1000-8000-00805f9b34fb"); // namespace filter private static final byte[] NAMESPACE_FILTER = {}; /*0x0000d89bed6e130ee5cf1ba1000000000000*/; // namespace filter private static final byte[] NAMESPACE_FILTER_MASK = {}; /*0xff00ffffffffffffffffffff000000000000*/
Manual Bluetooth ScanFilter beaconfilter = new ScanFilter.Builder() .setServiceUuid(UID_SERVICE) .setServiceData(UID_SERVICE, NAMESPACE_FILTER, NAMESPACE_FILTER_MASK) .build(); List filters = Collections.singletonList(beaconfilter); ScanSettings settings = new ScanSettings.Builder() .setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY) .build();
Manual Bluetooth BluetoothManager manager = (BluetoothManager) getSystemService(BLUETOOTH_SERVICE); mScanner = manager.getAdapter().getBluetoothLeScanner(); mScanCallback = new ScanCallback() { @Override public void onScanResult(int callbackType, ScanResult result) { super.onScanResult(callbackType, result); byte[] data = result.getScanRecord() .getServiceData(UID_SERIVCE); } }; mScanner.startScan(filters, settings, mScanCallback);
Beacons Dashboard ● ● ●
● ●
Register beacons to Google API project Use Eddystone UUID Attach information ○ Key-value pairs ○ Messages ○ Notifications* (Not ready yet) Disable Beacons Decommission Beacons (Be careful!)
Google Nearby Connect devices which are close to each other No manual pairing between users required Google Play Services required and an internet connection
Nearby Messages
Google Play Services e37c2
- broadcast messages publish() - subscribe to broadcasted messages subscribe()
e37c2
Hello World e37c2
Hello World Hello World e37c2
- Internet connection required
Hello World
e37c2
Benefits of a server connection ● Messages are smaller for Bluetooth and sound transmission ● Security, data can only be read by your app ○
Server-side validation (device pairing code & app API token)
○
Everybody is able to read the pairing codes but not the actual message
Let’s implement Nearby Messages...
Google API client @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); ... mGoogleApiClient = new GoogleApiClient.Builder(this) .addApi(Nearby.MESSAGES_API) .addConnectionCallbacks(this) .enableAutoManage(this, this) .build(); }
Publish Nearby.Messages.publish(mGooglePlayClient, message, options) .setResultCallback { status: Status -> if (status.isSuccess) { Timber.v("Published successfully") Do not Make error } else { forget codes readable val code = status.statusCode val statusName = NearbyMessagesStatusCodes .getStatusCodeString(code) Timber.v("publish status: $statusName ($code)") }
}
// publish status: FORBIDDEN (2806)
Publish Message ● Message can be anything (plain text, JSON, Flatbuffers, ...) ● up to 100KB (recommended 4KB) ● Sending the same message twice doesn’t work when both messages are alive val message = Message("Hello Droidcon".toByteArray())
Subscribe
Called for every message
Nearby.Messages.subscribe(gClient, messageListener, options) .setResultCallback { status: Status -> if (status.isSuccess) { Timber.v("Subscribed to Nearby Messages") } else { val code = status.statusCode val statusName = NearbyMessagesStatusCodes .getStatusCodeString(code) Timber.v("subscribe status: $statusName ($code)") } }
Message Listener val messageListener = object : MessageListener() { override fun onFound(message: Message?) { Timber.d("found $message") }
}
override fun onLost(message: Message?) { Timber.d("lost $message") } onLost is optional, don’t forget it
Subscribe/Publish Options Nearby.Messages .subscribe(mGooglePlayClient, messageListener, options) Nearby.Messages .publish(mGooglePlayClient, message, options)
Options are created with builders. A lot of them...
Subscribe/Publish Options ● Discovery Method = {DEFAULT, SCAN, BROADCAST} ● Distance = {DEFAULT, EARSHOT} ● Time to Live = {seconds} ○