beacons, raspberry pi & node.js
TRANSCRIPT
BeaconsRaspberryPiNodejs
Uniting technologies to Giving a URL to All Objects
Jeff Prestes
About me…
@jeffprestesgithub.com/jeffprestesslideshare.net/jeffprestes
Developer & EntrepreneurJava, PHP, JavaScript, Mobile Developer
Football (Soccer?) fan,father and IoT Researcher
BeaconsRaspberryPiNodejs
Uniting technologies to Giving a URL to All Objects
Jeff Prestes
To be more fun…first install
Physical Web clients
@jeffprestes#physicalweb
iOS – Physical Webhttps://itunes.apple.com/ca/app/physical-web/id927653608?mt=8
Android – Physical Webhttps://play.google.com/store/apps/details?id=physical_web.org.physicalweb
InstallingNodejs
on a Raspberry Pi
@jeffprestes#physicalweb
$ curl -sLS https://apt.adafruit.com/add | sudo bash
$ sudo apt-get install node
$ sudo apt-get autoremove
@jeffprestes#physicalweb
Source: https://learn.adafruit.com/node-embedded-development/installing-node-dot-js
Demo
@jeffprestes#physicalweb
Advertising a URL
https://github.com/jeffprestes/node-eddystone-url
@jeffprestes#physicalweb
$ sudo apt-get install bluetooth bluez libbluetooth-dev libudev-dev
$ git clone https://github.com/jeffprestes/node-eddystone-url
$ cd node-eddystone-url
$ npm install eddystone-beacon –-save
@jeffprestes#physicalweb
var Beacon = require('./node_modules/eddystone-beacon/lib/beacon');
beacon = new Beacon();
var options = { txPowerLevel: -22, //override TX Power Level, default value is -21, tlmCount: 2, // 2 TLM frames tlmPeriod: 10 // every 10 advertisements};
var myPayPalMe = 'http://bit.ly/1Q3Iani';var myLinkedIn = 'http://bit.ly/1SLiO3n';var myGitHub = 'http://bit.ly/1Ld89Mg';
beacon.advertiseUrl(myGitHub, [options]);
@jeffprestes#physicalweb
$ sudo node index.js
@jeffprestes#physicalweb
Demo
@jeffprestes#physicalweb
EddystoneProtocol
@jeffprestes#physicalweb
eddystone-tlmeddystone-uideddystone-url
@jeffprestes#physicalweb
eddystone-tlmeddystone-uideddystone-url
@jeffprestes#physicalweb
eddystone-tlmeddystone-uideddystone-url
@jeffprestes#physicalweb
eddystone-tlmeddystone-uideddystone-url
@jeffprestes#physicalweb
https://github.com/google/eddystone/tree/master/eddystone-url
@jeffprestes#physicalweb
var Beacon = require('./node_modules/eddystone-beacon/lib/beacon');
beacon = new Beacon();
var options = { txPowerLevel: -22, //override TX Power Level, default value is -21, tlmCount: 2, // 2 TLM frames tlmPeriod: 10 // every 10 advertisements};
var myPayPalMe = 'http://bit.ly/1Q3Iani';var myLinkedIn = 'http://bit.ly/1SLiO3n';var myGitHub = 'http://bit.ly/1Ld89Mg';
beacon.advertiseUrl(myGitHub, [options]);
@jeffprestes#physicalweb
@jeffprestes
Physical WebExpand the internet
#physicalweb
http://physical-web.org
@jeffprestes
AppendixCreate your own client
#physicalweb
How to detect a Beacon using Eddystone formatand how to parse his data
Code for Android
MainActivity.java
protected void onResume() { super.onResume(); BluetoothManager btManager = (BluetoothManager) getSystemService(BLUETOOTH_SERVICE); BluetoothAdapter btAdapter = btManager != null ? btManager.getAdapter() : null; if (btAdapter == null) { finish(); return; }
if (checkIfUserHasOptedIn()) { ensureBluetoothIsEnabled(btAdapter); showNearbyBeaconsFragment();
} else { // Show the oob activity // Webview to: https://google.github.io/physical-web/mobile/android/getting-started.html Intent intent = new Intent(this, OobActivity.class); startActivity(intent); }}
@jeffprestes#physicalweb
NearbyBeaconsFragment.java
public void onResume() { super.onResume(); getActivity().getActionBar().setTitle(R.string.title_nearby_beacons); getActivity().getActionBar().setDisplayHomeAsUpEnabled(false); getListView().setVisibility(View.INVISIBLE); mDiscoveryServiceConnection.connect(true);}
public synchronized void connect(boolean requestCachedPwos) { if (mDiscoveryService != null) { return; }
mRequestCachedPwos = requestCachedPwos; Intent intent = new Intent(getActivity(), PwoDiscoveryService.class); getActivity().startService(intent); getActivity().bindService(intent, this, Context.BIND_AUTO_CREATE);}
@jeffprestes#physicalweb
PwoDiscoveryService.java
/** * This is a service that scans for nearby Physical Web Objects. * It is created by MainActivity. * It finds nearby ble beacons, and stores a count of them. * It also listens for screen on/off events * and start/stops the scanning accordingly. * It also silently issues a notification informing the user of nearby beacons. * As beacons are found and lost, the notification is updated to reflect * the current number of nearby beacons. */
public class PwoDiscoveryService extends Serviceprivate void initialize() { mNotificationManager = NotificationManagerCompat.from(this); mPwoDiscoverers = new ArrayList<>(); mPwoDiscoverers.add(new BlePwoDiscoverer(this)); for (PwoDiscoverer pwoDiscoverer : mPwoDiscoverers) { pwoDiscoverer.setCallback(this); } ...}
@jeffprestes#physicalweb
BlePwoDiscoverer.java
public class BlePwoDiscoverer extends PwoDiscoverer implements BluetoothAdapter.LeScanCallback {
mBluetoothAdapter.startLeScan(this);
@jeffprestes#physicalweb
mScanFilterUuids = new ParcelUuid[]{URIBEACON_SERVICE_UUID, EDDYSTONE_URL_SERVICE_UUID};
@Override public void onLeScan(final BluetoothDevice device, final int rssi, final byte[] scanBytes) { if (!leScanMatches(ScanRecord.parseFromBytes(scanBytes))) { return; }
UriBeacon uriBeacon = UriBeacon.parseFromBytes(scanBytes); if (uriBeacon == null) { return; }
String url = uriBeacon.getUriString(); if (!URLUtil.isNetworkUrl(url)) { return; }
PwoMetadata pwoMetadata = createPwoMetadata(url); pwoMetadata.setBleMetadata(device.getAddress(), rssi, uriBeacon.getTxPowerLevel()); pwoMetadata.bleMetadata.updateRegionInfo(); reportPwo(pwoMetadata); }
BlePwoDiscoverer.java
@jeffprestes#physicalweb
/** * Parse scan record bytes to Eddystone * The format is defined in Eddystone specification. * * @param scanRecordBytes The scan record of Bluetooth LE advertisement and/or scan response. */public static UriBeacon parseFromBytes(byte[] scanRecordBytes) { byte[] serviceData = parseServiceDataFromBytes(scanRecordBytes);
...
if (serviceData != null && serviceData.length >= 2) { int currentPos = 0; byte txPowerLevel = serviceData[currentPos++]; byte flags = (byte) (serviceData[currentPos] >> 4); serviceData[currentPos] = (byte) (serviceData[currentPos] & 0xFF); String uri = decodeUri(serviceData, currentPos);
return new UriBeacon(flags, txPowerLevel, uri); } return null;}
UriBeacon.java
@jeffprestes#physicalweb
private static String decodeUri(byte[] serviceData, int offset) { if (serviceData.length == offset) { return NO_URI; } StringBuilder uriBuilder = new StringBuilder(); if (offset < serviceData.length) { byte b = serviceData[offset++]; String scheme = URI_SCHEMES.get(b); if (scheme != null) { uriBuilder.append(scheme); if (URLUtil.isNetworkUrl(scheme)) { return decodeUrl(serviceData, offset, uriBuilder); } else if ("urn:uuid:".equals(scheme)) { return decodeUrnUuid(serviceData, offset, uriBuilder); } } Log.w(TAG, "decodeUri unknown Uri scheme code=" + b); } return null;}
UriBeacon.java
@jeffprestes#physicalweb
private static String decodeUri(byte[] serviceData, int offset) { if (serviceData.length == offset) { return NO_URI; } StringBuilder uriBuilder = new StringBuilder(); if (offset < serviceData.length) { byte b = serviceData[offset++]; String scheme = URI_SCHEMES.get(b); if (scheme != null) { uriBuilder.append(scheme); if (URLUtil.isNetworkUrl(scheme)) { return decodeUrl(serviceData, offset, uriBuilder); } else if ("urn:uuid:".equals(scheme)) { return decodeUrnUuid(serviceData, offset, uriBuilder); } } Log.w(TAG, "decodeUri unknown Uri scheme code=" + b); } return null;}
UriBeacon.java
@jeffprestes#physicalweb
@jeffprestes
#physicalweb
/** * URI Scheme maps a byte code into the scheme and an optional scheme specific prefix. */ private static final SparseArray<String> URI_SCHEMES = new SparseArray<String>() {{ put((byte) 0, "http://www."); put((byte) 1, "https://www."); put((byte) 2, "http://"); put((byte) 3, "https://"); put((byte) 4, "urn:uuid:"); // RFC 2141 and RFC 4122}; }}; /** * Expansion strings for "http" and "https" schemes. These contain strings appearing anywhere in a * URL. Restricted to Generic TLDs. <p/> Note: this is a scheme specific encoding. */ private static final SparseArray<String> URL_CODES = new SparseArray<String>() {{ put((byte) 0, ".com/"); put((byte) 1, ".org/"); put((byte) 2, ".edu/"); put((byte) 3, ".net/"); put((byte) 4, ".info/"); put((byte) 5, ".biz/"); put((byte) 6, ".gov/"); put((byte) 7, ".com"); put((byte) 8, ".org"); put((byte) 9, ".edu"); put((byte) 10, ".net"); put((byte) 11, ".info"); put((byte) 12, ".biz"); put((byte) 13, ".gov"); }};
UriBeacon.java