This project is meant to show how you can build application like Hexiwear. It the basics it is the similar application like Getting-Started app, just with more data that is reading from the sensor. This app is using Angular on front-end side. If you're familiar with web development, this should be easy.
Prerequisites
- Familiarity with Javascript, HTML, CSS
Our application uses angular for the front end. This keeps things simple when displaying data to user. We use angular binding for updating values on the screen based on sensor readouts. Following code from index.html shows an example of that.
<div flex="50">
<div style="margin-top: 10px">
<h3>Accelerometer</h3>
</div>
<div style="text-align: left;" layout="column" layout-align="center center">
<label flex="60">x: <label ng-show="hexiwear.motionData.acc.x == undefined" >---</label><label ng-show="hexiwear.motionData.acc.x != undefined" >{{hexiwear.motionData.acc.x}} g</label></label>
<label flex="60">y: <label ng-show="hexiwear.motionData.acc.y == undefined" >---</label><label ng-show="hexiwear.motionData.acc.y != undefined" >{{hexiwear.motionData.acc.y}} g</label></label>
<label flex="60">z: <label ng-show="hexiwear.motionData.acc.z == undefined" >---</label><label ng-show="hexiwear.motionData.acc.z != undefined" >{{hexiwear.motionData.acc.z}} g</label></label>
</div>
</div>
On Javascript side, our code is placed in two files. One is angular based app.js that is responsible for user events and displaying data. In hexiwear.js file we create a Hexiwear class like function that is responsible for bluetooth and all of the device's capabilities.
In Hexiwear class we first have to define constants for characteristic UUIDs, since we have to pass that when trying to read the data from it.
const MOTION_SERVICE
const ACCELEROMETER
const GYRO
const MAGNETOMETER
In this case we have a lot of characteristics, that is placed in different services. For example, in motion service we have accelerometer, gyrometer and magnometer characteristics.
After constants, we are defining function for setting variables and their default values.
Hexiwear.prototype.initialize()
Main method of the Hexiwear class is connect() method. First we define a variable filters which is an array of objects. Valid filters are:
- 'name': which will look for a device(s) that matches the name given.
- 'service': which can be an array of 16 bit or 128 bit uuids. These uuids represent services uuids that are advertised by the bluetooth device.
- 'namePrefix': which will look for a device(s) who's name matches the prefix given.
Once we set scanning options, we are passing them and calling navigator.bluetooth.requestDevice. In promise, we should get requested device, if it is available. Here we can set disconnection listener to let us know when the device is disconnected from the gateway, and we can use that to show some indicator of that event on the screen.
Since we are connected to the device, we can call getPrimaryService in promise function, with defined service uuid. Because we have a lot of services we are calling them in Promise.all.
Hexiwear.prototype.connect = function () {
var options = {filters: [{name: DEVICE_NAME}]};
return navigator.bluetooth.requestDevice(options)
.then(function (device) {
self.bluetoothDevice = device;
return device.gatt.connect();
})
.then(function (server) {
console.log("Discovering services");
self.dismiss();
self.server = server;
self.onSuccess('Connected with ' + server.name);
self.connected = true;
self.bluetoothDevice.on("gattserverdisconnected",
function (event) {
console.log("Device disconnected");
self.onError('Device disconnected');
self.disconnected = true;
self.disconnectIndicator();
});
return Promise.all([
server.getPrimaryService(DEVICE_INFORMATION_SERVICE)
.then(function (service) {
self.readDeviceInfo(service);
}),
When we get requested service, now we want to read characteristics and their values.
Hexiwear.prototype.readDeviceInfo = function (service) {
Promise.all([
service.getCharacteristic(MANUFACTURER_NAME)
.then(function (characteristic) {
characteristic.readValue()
.then(function (value) {
self.deviceInfoData.manufacturerName = decode(value);
self.updateUI();
});
}),
service.getCharacteristic(FIRMWARE_REVISION)
.then(function (characteristic) {
characteristic.readValue()
.then(function (value) {
self.deviceInfoData.firmware = decode(value);
self.updateUI();
});
})
])
.catch((error) => {
console.log('Reading device info data failed. Error: ' + JSON.stringify(error));
});
};
In functions for reading values, since we have service passed, we are using that to call service.getCharacteristic() with specific UUID, in order to get characteristic value. Then we can use that response, parse it and store in appropriate variable.
Since we want to refresh values on the screen from time to time, we have to define function for refreshing the data.
Hexiwear.prototype.refreshValues = function() {
if (self.motionService){
self.readMotion(self.motionService);
}
if (self.weatherService){
self.readWeather(self.weatherService);
}
if (self.healthService){
self.readHealth(self.healthService);
}
};
In app.js file, we first have to call angular related initializations, that is needed if you use angular.
var app;
(function(){
app = angular.module('hexiwear', ['ngMaterial', 'ngMdIcons'])
Also we have to define mainController that drives our controller. In this controller we are placing different functions for interacting with our html code. One of the functions is for creating popup for device info dialog, where we are passing current data from main controller object.
$scope.infoPopup = () => {
$mdDialog.show({
controller: 'infoController',
templateUrl: 'app/pages/infoPopup.html',
parent: angular.element(document.body),
clickOutsideToClose: true,
locals:{
mode: $scope.hexiwear.deviceInfoData.modeData,
manufacturer: $scope.hexiwear.deviceInfoData.manufacturerName,
firmware: $scope.hexiwear.deviceInfoData.firmware,
batteryLevel: $scope.hexiwear.deviceInfoData.batteryData,
deviceStatus: $scope.hexiwear.paired
}
});
};
Basically, most of the functions in this controller is related to UI stuff, for showing and dismissing indicators or for UI animations.
Since we are calling another controller for info dialog defined above, we have to initialize that controller also, where we are passing needed dependencies defined in locals object. This values is used just for displaying them on the screen.
app.controller('infoController', function($scope, $mdDialog, mode, manufacturer, firmware, batteryLevel, deviceStatus){
For starting the connection process, we are calling hexiwear.connect() function.
$scope.hexiwear.connect();
Comments