Mobile Accelerometer Programming with Javascript and PhoneGap 3.0.0





5.00/5 (4 votes)
In this tutorial, we’ll build a hardware-aware application that uses Javascript to work with the device accelerometer.
As the Android and iOS platforms continue to diverge, there is growing interest in libraries that allow developers to build cross platform applications. One of the most prominent of these libraries is phone gap. In this tutorial, we’ll build a hardware-aware application that uses Javascript to work with the device accelerometer.
If you haven’t yet downloaded the latest version of the PhoneGap framework it’s amazingly easy. Assuming you have NodeJS installed installation is takes a single line. Once you have PhoneGap installed we can build your application.
Step by Step
1) Create the phone gap application by typing the following in to your command line:
$ phonegap create accelerate $ cd accelerate $ phonegap local plugin add https://git-wip- us.apache.org/repos/asf/cordova-plugin-device-motion.git
Note that the application will be created in what ever folder your command line is pointed to. Be sure to navigate to your destination folder first! The first two lines of command line instruction are fairly self-explanatory. They create the PhoneGap application called accelerate and navigate in to the folder just created.
The latest version of PhoneGap uses a remote plug-in architecture, so we have to manually obtain and install the accelerometer plug-in using the final line of command line code.
2) Open the accelerate folder and note the application structure includes a www folder. This is where we’re going to do our work.
The PhoneGap create command actually builds a small sample application. You can see it’s splash screen if you load the index.html page in to a browser. If you prefer you can actually run the whole application in a mobile device.
To run the sample app on an Android mobile device, connect your device to your computer via the USB to miniUSB cable. Issue the following commands with the command line path pointed at your project folder:
$ phonegap build android $ phonegap run android
Running the sample app on an IOS device is actually more complex as the device has to be properly provisioned through XCode. To provision iOS devices for testing you must be registered with the Apple Developer program.
We’re actually going to delete the CSS folder, icon.png, the img folder and the js folder from the application. You can open index.html in your favorite text editor. Once you have the file open, remove the code relating to the sample application. You should be left with something like this:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="format-detection" content="telephone=no" />
<meta name="viewport" content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width, height=device-height, target-densitydpi=device-dpi" />
<script type="text/javascript" src="phonegap.js"></script>
<title>Accelerate</title>
</head>
<body>
</body>
</html>
As you can see, there are a few meta tags in the head that deal with screen size normalization. The balance of the code is standard HTML. We’ll first set up an initialization routine so that we know that the device and PhoneGap library are both ready before we run any code.
We’re
going to be adding the following <script>
element to the <head>
of
the document right before the <title>
.
<script> window.onload = function() { //init(); document.addEventListener("deviceready", init, false); } function init() { alert("Ready!"); } </script>
The
init()
function will be our entry point into the application. You’ll notice
that the anonymous function associated with the window.onload
event attaches a
“deviceready
” listener to the application. This event will tell us when the
device and PhoneGap library are ready to interact with the user. For
convenience I also comment out a call to init()
in case I want to do browser
testing. Since the “deviceready
” event is part of the PhoneGap library, it
would not function in a browser.
This is a good point to stop and test. Again, with your Android device connected and your command line pointed at your application folder issue the following command:
$ phonegap run android
If
you get the expected alert box and everything else is correct, it’s time to
create our UI for the application. (You might want to comment out the alert
box in the init()
function at this point.) We’re going to create a fairly
simple UI that shows us the results generated by the accelerometer in the
device. We’re going to add the following HTML inside the body element:
<footer>
<table id="footerTable">
<tr>
<td>X: <span id="xOut"></span></td>
<td>Y: <span id="yOut"></span></td>
<td>Z: <span id="zOut"></span></td>
</tr>
</table>
</footer>
Add a style element in the document head to contain the CSS:
<style> body { margin: 0px; } #footerTable { width: 100%; } footer { position:absolute; bottom:0; width:100%; height:40px; background:#ccc; } </style>
In the PhoneGap library the acceleration object has three methods. They are:
getCurrentAcceleration()
watchAcceleration()
clearWatch()
The
first method, getCurrentAcceleration()
, will get a snapshot of the
accelerometer readings when the function is implemented. watchAcceleration()
will return the readings of the accelerometer until a clearWatch()
command is
issued. For the purposes of our application we’re going to use
watchAcceleration()
so we can view the acceleration over a period of time.
The
watchAcceleration()
method returns an ID value that we can later use to stop
the accelerometer. This is important because we don’t want the accelerometer
using valuable resources when it’s no longer needed.
Edit your code so it appears as follows:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="format-detection" content="telephone=no" />
<meta name="viewport" content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width, height=device-height, target-densitydpi=device-dpi" />
<style>
body {
margin: 0px;
}
#footerTable {
width: 100%;
}
footer {
position:absolute;
bottom:0;
width:100%;
height:40px;
background:#ccc;
}
</style>
<script type="text/javascript" src="phonegap.js"></script>
<script>
var watch = 0;
window.onload = function()
{
//init();
document.addEventListener("deviceready", init, false);
}
function init()
{
watch = navigator.accelerometer.watchAcceleration(success, failure, {frequency: 100});
}
function success(accel)
{
document.getElementById("xOut").innerHTML = accel.x;
document.getElementById("yOut").innerHTML = accel.y;
document.getElementById("zOut").innerHTML = accel.z;
}
function failure()
{
alert("Error");
}
</script>
<title>Hello World</title>
</head>
<body>
<footer>
<table id="footerTable">
<tr>
<td>X: <span id="xOut">0</span></td>
<td>Y: <span id="yOut">0</span></td>
<td>Z: <span id="zOut">0</span></td>
</tr>
</table>
</footer>
</body>
</html>
We’ve
added in the foundational code to make the accelerometer work. You’ll notice
that the init()
function contains a very important line of code:
watch = navigator.accelerometer.watchAcceleration(success, failure, {frequency: 100});
This
line essentially configures the accelerometer and turns it on. The three
arguments in the watchAcceleration()
method provide a call back if the
accelerometer is successful at obtaining device movement information, a failure
callback if the accelerometer fails and a frequency object which sets the
frequency with which the accelerometer is sampled in miliseconds. The watch
variable which is declared globally at the beginning of the script is used to
capture the watch ID.
The
success()
and failure()
call backs should appear fairly straight-forward. In
the success()
call back an acceleration object is passed in to the function and
it contains properties which contain the readings for the x, y and z axis. The
failure()
callback simply outputs an error message at this point.
This would be a good time to test your application again and see if everything is working correctly up to this point. Again, to build and run your app issue the following command:
$ phonegap run android
Assuming everything is working correctly you should see the X Y and Z readouts at the bottom of your screen update as you move the device. Move the device slowly and notice how X, Y and Z react when you move the device along different axis.
Now we’re going to make our application a little more interesting. Let’s use the accelerometer to move an object around the screen. We’ll add the object itself through the HTML and then use the accelerometer readings to determine the rate and direction of motion. To get ready, let’s add a canvas to our application and place an object on it. To add the canvas, we’re going to add a canvas element to the first line of the body.
<canvas id="myCanvas" ></canvas>
We’re also going to normalize the html and body’s width, height and margin css properties just in case. Add the following selector and rules to your CSS.
html, body
{
width: 100%;
height: 100%;
margin: 0px;
}
I’m
using an image called “crosshair.png’ for this application. We’re going to
load it to the canvas dynamically in the init()
event. You’ll want to set up
cnv, for the canvas context, and target, for the image as globals at the top of
your script.
Revise
your init()
function to be coded as follows:
function init()
{
var iW = window.innerWidth;
var iH = window.innerHeight;
cnv = document.getElementById('myCanvas').getContext("2d");
cnv.canvas.width = iW;
cnv.canvas.height = iH-40;
target = new Image();
target.src = "crosshair.png";
target.onload = function()
{
cnv.drawImage(target, (iW-(target.width))/2, (iH-
(target.height))/2);
}
watch = navigator.accelerometer.watchAcceleration(success,
failure, {frequency: 100});
}
There’s quite a bit going on here, so let’s break the important code down. The iW and iH variables simply contain the screen size. Obviously mobile devices vary in their specifications, so we want to be sure to create a canvas that’s flexible. The cnv variable is used to reference the context object on which we’ll do most of our work. It’s helpful to think of the canvas as an invisible layer wrapped around the canvas.
We use the context object, cnv, to set the canvas’ width and height properties. We subtract 40 from the height to allow for the output bar we’ve already created at the bottom of the display. We initialize the target variable as an Image object and provide it’s source image to the src property.
We want to make sure the image has loaded before we try to display it, so we setup an anonymous function that is called when the image object called target loads. At that point we draw the image and do some simple math to put it I the center of the screen. Finally we configure the accelerometer.
This is another excellent time to test your application on your device.
Now,
we’ll make the image move as the user tilts the device and the accelerometer
reacts. Most of this work will be done in the success()
callback function,
however, I made a few changes to the init()
function to facilitate a smoother
running application. Here’s the modified init()
function:
function init()
{
var iW = window.innerWidth;
var iH = window.innerHeight;
canvas= document.getElementById('myCanvas');
cnv = canvas.getContext("2d");
cnv.canvas.width = iW;
cnv.canvas.height = iH-40;
target = new Image();
target.src = "crosshair.png";
xPos = (iW-target.width)/2;
yPos = (iH-target.height)/2;
target.onload = function()
{
cnv.drawImage(target, xPos, yPos);
}
watch = navigator.accelerometer.watchAcceleration(success,
failure, {frequency: 25});
}
Once
you’ve modified your init()
add canvas to your list of global declarations at
the beginning of the script. You’ll also notice that xPos
and yPos
variables
are set during the init()
process to capture the initial position of the
image. You’ll need to add these to your list of globals as well. Finally, I
reduced the frequency value to 25 so we’re going to get results from the
accelerator every 25 milliseconds. Now on to the success()
callback function:
function success(accel)
{
document.getElementById("xOut").innerHTML = accel.x;
document.getElementById("yOut").innerHTML = accel.y;
document.getElementById("zOut").innerHTML = accel.z;
cnv.clearRect(0, 0, canvas.width, canvas.height);
xPos += -1*(accel.x * 1.5);
yPos += (accel.y * 1.5);
cnv.drawImage(target, xPos, yPos);
}
After
updating the display bar at the bottom of the screen, the clearRect()
method is
fired on the canvas object. This clears the canvas prior to redrawing the
image in the updated position. The clearRect()
method requires a starting
point and width and height you’d like to clear. We’re clearing the entire
canvas so we provide it’s width and height as arguments.
Next
we update the xPos
and yPos
before redrawing. xPos
has to be negated so that
the image moves in the direction the user is tilting the device. Both the xPos
and yPos
are multiplied by 1.5 to move the image slightly faster. Finally
drawImage()
is called which draws the image at the new and updated position.
Test your application one more time and it should move as the device is tiled along each axis. To challenge yourself create some additional code that prevents the image from falling off the edge of the screen.
As you can see, PhoneGap makes it easy to access device hardware, like the accelerometer, and include it in your Javascript based mobile applications.
Special thanks to Adobe evangelist Raymond Camden (www.raymondcamden.com) for his generous assistance.