Permalink
Browse files

updated Zephyr Heart Rate monitor sample to work with current API

  • Loading branch information...
1 parent 23e2089 commit 852a258170f73694013b7f8a796a52fa461ad9c8 @petele petele committed Oct 11, 2013
Showing with 199 additions and 133 deletions.
  1. +2 −2 zephyr_hxm/README.md
  2. +27 −3 zephyr_hxm/background.js
  3. +3 −4 zephyr_hxm/graph.html
  4. +7 −5 zephyr_hxm/index.html
  5. +155 −115 zephyr_hxm/main.js
  6. +5 −4 zephyr_hxm/manifest.json
View
@@ -6,8 +6,8 @@
Very simple Zephyr HXM heart rate monitor driver. This sample uses the bluetooth API to fetch heart rate data from a Zephyr HXM device
## Caveats:
-- The bluetooth API is only available on dev-channel Chrom(e|ium)OS
-- Resource clean-up isn't happening properly yet: you will likely have to disable/enable bluetooth between runs of the program or the connection will fail
+- The bluetooth API is only available on dev-channel
+
## APIs
View
@@ -1,8 +1,32 @@
+// Defines the profile used by the Zephyr heart rate monitor
+var HXM_PROFILE = {
+ uuid: '00001101-0000-1000-8000-00805f9b34fb',
+ name: 'Zephyr HXM Heart Rate Monitor'
+};
+
chrome.app.runtime.onLaunched.addListener(function() {
chrome.app.window.create('index.html', {
bounds: {
- width: 640,
- height: 480
- }
+ width: 600,
+ height: 350
+ },
+ singleton: true,
+ id: "bluetoothhxm"
+ }, function(win) {
+ // Add the profile to the list of profiles we support
+ chrome.bluetooth.addProfile(HXM_PROFILE, function(r) {
+ console.log("Profile added");
+ });
+
+ // Make the profile available in the main content window.
+ win.contentWindow.HXM_PROFILE = HXM_PROFILE;
});
});
+
+function removeProfile() {
+ console.log("Removing Zephyr HXM profile");
+ chrome.bluetooth.removeProfile(HXM_PROFILE, function(r) {
+ console.log("Profile removed");
+ });
+}
+
View
@@ -1,12 +1,11 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
-<html xmlns="http://www.w3.org/1999/xhtml">
+<!DOCTYPE html>
+<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8"/>
- <meta http-equiv="X-WebKit-CSP" content="script-src 'self' chrome-extension: http://code.highcharts.com http://ajax.googleapis.com">
<title>
Google Visualization API Sample
</title>
- <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js" type="text/javascript"></script>
+ <script src="http://ajax.googleapis.com/ajax/libs/jquery/2.0.3/jquery.min.js" type="text/javascript"></script>
<script src="http://code.highcharts.com/highcharts.js"></script>
<script src="http://code.highcharts.com/modules/exporting.js"></script>
</head>
View
@@ -1,10 +1,12 @@
<html>
+ <head>
+ </head>
<body>
- <iframe id="graph" src="graph.html" seamless="seamless" width="580px" height="420px"></iframe>
- <input type="button" id="close" value="Quit"></input>
- <!-- Uncomment for logging
- <pre id="log"></pre>
- -->
+ <iframe id="graph" src="graph.html" seamless="seamless" width="580px" height="300px"></iframe>
+ <input type="button" id="butQuit" value="Quit"></input>
+ <input type="button" id="butConnect" value="Connect"></input>
+ <input type="button" id="butDisconnect" value="Disconnect"></input>
+ <span id="hr"></span>
</body>
<script src="main.js"></script>
</html>
View
@@ -1,135 +1,175 @@
-function log(msg) {
- var msg_str = (typeof(msg) == 'object') ? JSON.stringify(msg) : msg;
- console.log(msg_str);
+var READ_INTERVAL = 1000;
- var l = document.getElementById('log');
- if (l) {
- l.innerText += msg_str + '\n';
- }
+var _socket = null;
+var _readIntervalId = null;
+
+function init() {
+ console.log("Starting Zephyr HXM demo...");
+
+ document.getElementById("butDisconnect")
+ .addEventListener("click", disconnect);
+ document.getElementById("butConnect")
+ .addEventListener("click", findAndConnect);
+ document.getElementById("butQuit").addEventListener("click", closeApp);
+
+ // Add the listener to deal with our initial connection
+ chrome.bluetooth.onConnection.addListener(onConnected);
+
+ // Check the paired Bluetooth devices then connect to the HXM
+ findAndConnect();
}
-function updateHeartRate(value) {
- document.getElementById('graph').contentWindow.postMessage(
- {heartrate: value}, '*');
+/*
+ HXM Specific Function
+*/
+
+function updateHeartRate(heartrate) {
+ document.getElementById("hr").innerText = heartrate;
+ document.getElementById("graph")
+ .contentWindow.postMessage({heartrate: heartrate}, "*");
}
-var kUUID = '00001101-0000-1000-8000-00805f9b34fb';
-var readIntervalId;
-var readInterval = function (socket) {
- return function() {
- chrome.bluetooth.read({socket: {id: socket.id}}, function(data) {
- if (chrome.apps.lastError) {
- log('Read error:');
- log(chrome.runtime.lastError);
- window.clearInterval(readIntervalId);
- } else {
- // Data parsing is based on the code in the openzephyr library:
- // http://code.google.com/p/zephyropen/source/browse/zephyropen/src/zephyropen/device/zephyr/ZephyrUtils.java
- if (data) {
- if ((data.byteLength % 60) != 0) {
- log('Payload is wrong size (' + data.byteLength +
- '). Discarding.');
- return;
- }
-
- var offset = 0;
- while (offset + 60 <= data.byteLength) {
- var data_view = new Uint8Array(data, offset);
- offset += 60;
-
- if (data_view[0] != 2) {
- log('Check failed data[0] = ' + data_view[0]);
- continue;
- }
-
- if (data_view[1] != 38) {
- log('Check failed data[1] = ' + data_view[1]);
- continue;
- }
-
- if (data_view[2] != 55) {
- log('Check failed data[2] = ' + data_view[2]);
- continue;
- }
-
- if (data_view[59] != 3) {
- log('Check failed data[59] = ' + data_view[59]);
- continue;
- }
-
- var heartrate = data_view[12];
- if (heartrate < 30 || heartrate > 240) {
- log('Heartrate out of range (' + heartrate + '). Discarding.');
- return;
- }
-
- updateHeartRate(heartrate);
- log('HR=' + heartrate);
- }
- }
- }
- });
- }
+function closeApp() {
+ console.log("Close app.");
+ disconnect();
+ window.close();
}
-function startReads(socket) {
- log('Starting reads');
- readIntervalId = window.setInterval(readInterval(socket), 1000);
+function findAndConnect() {
+ // Get's the device list, and passes that list to the connectToHXM
+ // function as a callback.
+ getDeviceList(connectToHXM);
}
-var socketId_;
-var connectCallback = function(socket) {
+function onConnected(socket) {
+ console.log("onConnected", socket);
if (socket) {
- log('Connected! Socket ID is: ' + socket.id + ' on service ' +
- socket.serviceUuid);
- startReads(socket);
- socketId_ = socket.id;
+ _socket = socket;
+ _readIntervalId = window.setInterval(function() {
+ // Reads the data from the socket and passes it to parseHXMData
+ chrome.bluetooth.read({socket: _socket}, parseHXMData);
+ }, READ_INTERVAL);
} else {
- log('Failed to connect.');
+ console.error("Failed to connect.");
}
-};
+}
+
+function parseHXMData(data) {
+
+ // Data parsing is based on the code in the openzephyr library:
+ // http://code.google.com/p/zephyropen/source/browse/zephyropen/src/zephyropen/device/zephyr/ZephyrUtils.java
+ if ((data) && ((data.byteLength % 60) === 0)) {
+ var offset = 0;
+ while (offset + 60 <= data.byteLength) {
+ var data_view = new Uint8Array(data, offset);
+ offset += 60;
+
+ if (data_view[0] != 2) {
+ console.log('Check failed data[0] = ' + data_view[0]);
+ continue;
+ }
+
+ if (data_view[1] != 38) {
+ console.log('Check failed data[1] = ' + data_view[1]);
+ continue;
+ }
-var connectToDevice = function(result) {
- if (chrome.runtime.lastError) {
- log('Error searching for a device to connect to.');
- return;
+ if (data_view[2] != 55) {
+ console.log('Check failed data[2] = ' + data_view[2]);
+ continue;
+ }
+
+ if (data_view[59] != 3) {
+ console.log('Check failed data[59] = ' + data_view[59]);
+ continue;
+ }
+
+ var heartrate = data_view[12];
+ if (heartrate < 30 || heartrate > 240) {
+ console.log('Heartrate out of range (' + heartrate + '). Discarding.');
+ return;
+ }
+
+ updateHeartRate(heartrate);
+ }
+ } else {
+ console.error("Error parsing data.", data, data.byteLength);
}
- if (result.length == 0) {
- log('No devices found to connect to.');
- return;
+}
+
+function connectToHXM(deviceList) {
+ console.log("connectToHXM", deviceList);
+ if (deviceList !== null) {
+ // Iterates through the device list looking for a device that starts with
+ // HXM as it's name then tries to connect to that device.
+ for (var i in deviceList) {
+ var device = deviceList[i];
+ if (device.name.indexOf("HXM") === 0) {
+ console.log("Connecting to HXM", device);
+ connect(device.address, HXM_PROFILE);
+ }
+ }
}
- for (var i in result) {
- var device = result[i];
- if (device.name.indexOf("HXM") === 0) {
- log('Connecting to device: ' + device.name + ' @ ' + device.address);
- chrome.bluetooth.connect(
- {device: {address: device.address}, profile: {uuid: kUUID}}, connectCallback);
+}
+
+
+/*
+ Generic BlueTooth
+*/
+
+function getDeviceList(callback) {
+ console.log("Searching for bluetooth devices...");
+ var deviceList = [];
+
+ // Get the BlueTooth adapter state and verify it's ready
+ chrome.bluetooth.getAdapterState(function(adapterState) {
+ if (adapterState.available && adapterState.powered) {
+
+ // Gets the list of paired devices and adds each one to the deviceList
+ // array, when done calls the callback with the list of devices.
+ chrome.bluetooth.getDevices({
+ deviceCallback: function(device) { deviceList.push(device); }
+ }, function () {
+ console.log("Devices found", deviceList);
+ if (callback) {
+ callback(deviceList);
+ } else {
+ console.error("No callback specified.");
+ }
+ });
+ } else {
+ // If the bluetooth adapter wasn't ready or is unavailble, return null
+ console.log("Bluetooth adapter not ready or unavailable.");
+ callback(null);
}
+ });
+}
+
+function connect(deviceAddress, profile) {
+ console.log("Connecting to device", deviceAddress, profile);
+ chrome.bluetooth.connect({
+ device: {address: deviceAddress},
+ profile: profile
+ }, function() {
+ if (chrome.runtime.lastError) {
+ console.error("Error on connection.", chrome.runtime.lastError.message);
+ }
+ });
+}
+
+function disconnect() {
+ console.log("End session.");
+ if (_readIntervalId !== null) {
+ console.log("Clearing interval.");
+ clearInterval(_readIntervalId);
}
-};
-
-log('Starting Zephyr HXM demo...');
-
-
-var kSimulate = false;
-if (kSimulate) {
- window.setInterval(function() {
- updateHeartRate(60 + Math.floor((Math.random()*10)+1));
- }, 1000);
-} else {
- var devicesFound = [];
- chrome.bluetooth.getDevices({
- profile: {uuid: kUUID},
- deviceCallback: function(device) { devicesFound.push(device); }
- }, function() {
- connectToDevice(devicesFound);
+ if (_socket !== null) {
+ console.log("Disconnecting socket.");
+ chrome.bluetooth.disconnect({socket: _socket}, function() {
+ console.log("Socket closed.");
+ _socket = null;
});
}
+}
-document.getElementById('close').addEventListener('click',
- function() {
- if (!kSimulate) {
- chrome.bluetooth.disconnect({socket: {id: socketId_}});
- }
- window.close();
- });
+init();
View
@@ -1,18 +1,19 @@
{
"name": "Heart Rate Monitor Integration Sample",
"description": "Demo of Zephyr HXM Heart Rate Monitor interaction",
- "version": "1.1",
+ "version": "1.2",
"manifest_version": 2,
- "minimum_chrome_version": "23",
+ "minimum_chrome_version": "30",
"app": {
"background": {
"scripts": ["background.js"]
}
},
"permissions": [
- "bluetooth"
+ {"bluetooth":[{"uuid": "00001101-0000-1000-8000-00805f9b34fb"}]}
],
"sandbox": {
"pages": ["graph.html"]
- }
+ },
+ "offline_enabled": true
}

0 comments on commit 852a258

Please sign in to comment.