This project shows how to use the Super Ball which has the ST Sensor Tile. Sensor Tile has various sensors built in and connected via Bluetooth. This project uses Web Bluetooth to connect with Super Ball from browsers to read the sensor data. The application uses simple HTML, CSS & Javascript. It uses the Super Ball as Quarter Back Trainer with various features such as Hang Time, Speed etc.,
Prerequisites:
- Familiarity with Javascript, HTML, CSS
Starting from the HTML, we have one main index.html file, and four pages for displaying different application features - Hang Time, Velocity, Spiral Efficiency and Spin. Since we are using angular for this project, we can use one of its features for switching pages - angular routing. We are defining it in app.js file. This is an example for the hangtime page.
/* Angular routing setup */
.config(function ($stateProvider, $urlRouterProvider) {
$stateProvider
.state('hangtime', {
url: "/hangtime",
templateUrl: "app/pages/hangtime.html",
controller: "hangtimeController",
resolve:{
"check":function($location, $rootScope){
if(!$rootScope.isConnected){
$location.path('/');
}
}
}
})
Regarding particular html page, we are using angular binding to show and update the data on the screen in real time.
<div layout="row" layout-align="center start" flex="50" flex-xs="100" class="p-15">
<div class="time-score" flex="100">
<h3 style="color:white">Last Score</h3>
<h1 ng-show="lastTime !=undefined">{{lastTime}}s</h1>
<h1 ng-show="lastTime ==undefined"> </h1>
<h3 style="color:white; margin-top: 40px">Waiting...</h3>
</div>
</div>
Talking about Javascript code, we placed it in several files, each for every feature.
Two common Javascript files are app.js, used for defining routes, and home page functions, loading indicators etc. and quarterbackTrainer.js - for connecting to Bluetooth device and parsing the readout data.
In qurterbackTrainer class we are setting UUID constants for service and characteristics that we use.
/* Defining services and characteristics UUIDs */
var SENSOR_SERVICE = "00000000-0001-11e1-9ab4-0002a5d5c51b";
var ACC_GYRO_MAG = "00e00000-0001-11e1-ac36-0002a5d5c51b";
var BATTERY = "00020000-0001-11e1-ac36-0002a5d5c51b";
var SENSOR_FUSION = "00000100-0001-11e1-ac36-0002a5d5c51b";
For connections using web Bluetooth we have to set filters, and after that we are requesting device.
var options = {filters: [{name: DEVICE_NAME},{services: [SENSOR_SERVICE]}]};
if (navigator.bluetooth) {
return navigator.bluetooth.requestDevice(options)
/* Connecting to the device */
.then(function (device) {
self.bluetoothDevice = device;
return device.gatt.connect();
})
App's features are basically using data from two characteristics: movement, and sensor fusion. So we need to read those characteristics in Promise.all function.
return server.getPrimaryService(SENSOR_SERVICE)
.then(function (service) {
Promise.all([
/* Getting accelerometer/gyroscope/magnetometer characteristic */
service.getCharacteristic(ACC_GYRO_MAG)
.then(function (characteristic) {
return characteristic.startNotifications()
.then(function () {
self.agmEnabled = true;
characteristic.addEventListener('characteristicvaluechanged', function (value) {
/* Calling function for using motion data */
extractAccGyroMagData(self, parseResponse(value.target.value.buffer));
checkFreeFall();
checkSpinning();
checkMoving();
quarterbackTrainer.updateUI();
});
});
}),
We are subscribed to 'characteristicvaluechanged' event, and we are parsing readout values every time we get triggered.
In app.js, first we initialize angular components are registering to all modules.
var app;
(function(){
app = angular.module('quarterbackTrainer', ['ngMaterial', 'ngMdIcons','app.ht','app.spin','app.vel','app.sp','ui.router'])
This module is also used for loading saved scores for each feature. We use local storage to save and load those.
if (localStorage.getItem('htScoreboard') == null) {
$rootScope.htScoresArray = [];
} else {
$rootScope.htScoresArray = JSON.parse(localStorage["htScoreboard"]);
}
Talking about particular features, for example Hang Time, all related code is placed in hangtimeController.js. Here we define particular dependencies, methods and properties. How this feature works? Hang Time game is basically measuring time between two readouts where sensor shows that there is no movement, based on movement characteristic. We are checking that in 10 milliseconds period.
startMeasuring = setInterval(function () {
if (!$scope.quarterbackTrainer.moving) {
if ($scope.thrown) {
/* If not moving and the ball was thrown, taking end time here */
clearInterval(startMeasuring);
endTime = new Date();
/* Checking if there is enough movement for storing the score */
if ($scope.time > 1.2) {
/* Storing the score */
if ($scope.scores.length > 0) {
$scope.inTen = $scope.time > $scope.scores[$scope.scores.length - 1].score || $scope.scores.length < 10;
}
$scope.lastTime = $scope.time;
/* Storing the current score */
utilityService.saveResult($scope, 'htScoreboard', $scope.time);
$scope.$apply();
}
$scope.hangTimeStarted = false;
$scope.finished = true;
/* Starting the function again, for listening for new movement */
$scope.hangTimeGame();
}
} else {
/* If moving and not started yet, taking current time as a starting time */
$scope.thrown = true;
if (started == false) {
started = true;
startTime = quarterbackTrainer.agmTime;
}
var currentTime = quarterbackTrainer.agmTime;
var time = (currentTime - startTime) / 125;
$scope.time = Math.round(time * 10) / 10;
$scope.$apply();
}
}, 10);
When movement stops, we are storing and sorting the score in scoreboard. Like we mentioned that scoreboard is stored in local storage.
/* Storing score into local storage */
$scope.saveChanges = function () {
localStorage.htScoreboard = [];
localStorage.htScoreboard = JSON.stringify($scope.scores);
};
This data is than passed to UI using angular binding (in this case, on hangtime.html page).
So at the end, app is listening for user to click the bluetooth icon to start the process, and to select the feature that he wants to use.
Comments