In  my last post,  I tackled two APIs-- notifications and contacts.  Even though I wasn't able to fully explore the contacts, I managed to get things working without too much troubles.   I'm occasionally hitting some weird parsing issues in CFBuilder or underlying JavaScript errors I can't explain but "rearranging" my code will usually make it go away (more on this later).  I'll try to go back  and put in tickets for these after the fact, but I'm always reticent to shout "BUG!" in a crowded theater when I'm not 100% I'm doing it right.

Accelerometer

Well, let's get right to it.  Today I played with the accelerometer API which is incredibly simple in terms of the API's surface area, but rather deep in applications.  This one is a little tricky  because the raw data coming out of the API is very low-level and it takes a fair amount of massaging to do something with it.  PIctured on the right is the accelerometer chip that came with my Arduino starter kit.  I can only assume the accelerometers in our phone are about the same size, just without the gratuitous extra circuit board and pin headers.  

An accelerometer measures acceleration on 3 independent axes.  Hold your phone upright in front of you.

  • Sweeping left and right in a straight line registers on the X axis
  • Raising and lowering between the ceiling and floor registers on the Y axis.
  • Pushing and pulling the phone to your face and back away from your body registers on the Z axis.

Constant Forces

This first misnomer I had was that, at rest, my phone's accelerometer would measure zero on all axes.  This is not true though unless your are in outer space or in a free fall (weightless). If you lay your phone face up, flat on a table the X and Y axes will measure 0, but the Z axis will have a constant positive measurement.  This is gravity pulling down on your phone's Z axis.  Hold the phone face down above your head, and you will see a negative amount of the same force now pulling on the opposite end of the Z axis. The output from the accelerometer is output in small numbers below the X, Y, and Z boxes on the "Accelerometer API" page for you to play with.

I was sort of surprised to not see any obvious JavaScript libraries out there to help decipher the raw output of the accelerometer API after a preliminary Googling.  I spent several hours playing with the data trying out different techniques such as noise gates, rolling averages for smoothing, and primary axis detection.  In the end, the noise gate is the only thing I kept and I reverted the rest of my code, opting for a simpler approach.

There appears to be two immediate ways to use the data.  The raw output is useful to know what orientation the phone is in and it it's being rotated.  The second method is to compare each sample to its previous value and use the delta to decide if the phone is moving, how fast, and in what direction.

Keeping Watch

The accelerometer API only has 4 methods:

The only option available is the frequency of the polling interval in milliseconds.  I'm not sure why the whole "option" boilerplate even exists, but I assume it's just an artifact of the underlying PhoneGap libraries.

// Set polling interval to 200 ms
opt = cfclient.accelerometer.getOptions();
opt.frequency = 200;
cfclient.accelerometer.setOptions( opt );

 Getting the accelerometer data is asynchronous by nature.  You start "watching" the accelerometer , and then your callback function is executed once every n milliseconds based on your frequency.  You can stop "watching" the accelerometer at any time using the watchID you received originally.

accelerometerWatchId = cfclient.accelerometer.watch( onAccelerometer );

cfclient.accelerometer.clearWatch( accelerometerWatchId );

XYZ Example

The first example simply consists of 3 boxes  labeled X, Y, and Z.  They light up based on the delta values for each axis.  This means at a standstill, they are white, but when you quickly move your device along one or more axes, the appropriate box will light up with a darkness level matching the amount of force detected.  Blue is used for positive values, red for negative.  

You'll quickly find out it is almost impossible  to move your device on only a single axis unless you set it on a table, hold it parallel to an edge, and move along that edge.  Rapid, sudden motions will register higher since the delta, or difference between measurements, will be larger.  The small numbers under the boxes are the raw output from the API and are also fun to watch.  Try spinning slowly in your office chair while holding the phone in front of your face and see which axes change and which do not. 

Don't Be So Sensitive

There is a slider to adjust the sensitivity.  It acts as a noise gate, discarding any deltas that fall below the threshold.  Noise gates are common in audio applications.   I use one in combination with my guitar's distortion pedal to filter out background hiss.  The gate "opens" once the signal exceeds the threshold.  If you decrease sensitivity (raising the threshold of the gate) you will have to shake your phone harder before the movements trigger the light show.  

As simple as this example is, it was surprisingly fun to play with.  It was also a bit of a brain bender, since the raw data isn't very easy to use without stopping and working out how to read it.

Process Flow

Before I talk about the "fun" example, I'd like to take a minute to discuss the workflow.  Adobe has made  it clear that their vision for CFClient is more than  just cross-compiling CFML to JS and also includes integrated debugging and workflow tools.  Working on a project that requires a compilation step in order to run the code you just wrote is going to be more of a pain than working with CFML right off the bat.  Ram introduce a PhoneGap Shell app to allow you test your right off your local ColdFusion 11 server.  However, as documented in my first blog post I can't get that app to work and I never have heard back from Adobe (Anit, specifically) about it.  

This left me with no choice but to compile a full APK file every time.  I've probably done it 150-250 times now and it takes 20-30 seconds.

  1. Save the changes to my code in CF Builder 3
  2. Open the navigation page (mapped to F8 on my install to match Dreamweaver, HomeSite and MS SSMS)
  3. Right Click on the project and choose "Generate Phonegap Build"
  4. Wait for my CFClient code to be run, the generated JS and HTML files, etc are uploaded to the PhoneGap build server and compiled (This is the majority of the wait)
  5. Open the "Phonegap Status" view in Builder and repeatedly mash the refresh icon until "pending" changes to a "Download" button under the "Android Build Status" column
  6. Click the "Download" button and press "Enter" to save the APK file in the same directory as last time (with an incrementing number it)
  7. Make sure my phone is plugged into USB and clicking the "Install Application" button on GapDebug.
  8. Select the APK file I just downloaded and wait a couple seconds
  9. If the app is running, the installation process with close it
  10. Re-open the app on my phone
  11. GapDebug is nice enough to automatically reconnect the debugger automatically and pull up the last tab (usually console)
  12. Now I'm ready to begin using the new version of the app to test my changes

This process isn't TOO bad, but I'll be honest it has gotten a little tiresome.  I would have written a script or even a custom CommandBox command a while back to do all of it (or as much as possible) for me but I don't know any way to hook into the custom magic that CF Builder is doing behind the scenes.  I'm sure some of it is based on the PhoneGap CLI, but I can't find any information on how I would replicate it myself outside of CF Builder so I'm kind of stuck with the existing tooling.  While it's a pain, I can acknowledge I'm probably a bit spoiled by CFML engine's JIT and I also have no other mobile development tooling to compare it to.   I'd be interested in hearing how many clicks have to happen for your other mobile developers between a code change and testing it on a live device (or an emulator I suppose)

Get On The Ball

I wanted to do another example using the accelerometer API.  My first thought was one of those little apps where  you use your phone as a level to hang picture frames, etc.  Then I figured I would do a digital version of the wooden Labyrinth games-- just without any inside walls, or holes.  It's pretty hard to lose at this one :)  

I created a simple play surface consisting of a div of fixed height, and an image of a large steel ball that will be moved via absolute positioning inside of the div.  I suck at CSS, so I'll admit both of my examples took extra time for me to figure out positioning issues with divs, text, borders, etc.   The game initializes with te ball in the center of the box.   You hold your phone flat and tilt it from side to side to make the ball "roll" around.  I even hooked into the vibrate function from our notification example so you get a bit of haptic feedback any time the ball "touches" the sides of the box.  

 

I must say, this has been my favorite example thus far.  Even my kids played with it for a while before going to school in the morning.  If I were to improve it, here are some ideas:

  • Ball moves at a constant speed linearly proportional to the tilt of the phone.  To be more realistic, it should accelerate (speed up) when moving in the same direction for a while
  • Ball should have inertia that is acted upon by the tilting.  This means, a sudden change in tilt would not immediately reverse the direction of the ball until it had "slowed down"
  • Ball's kinetic energy should be redirected by the wall such that it "bounces" back based on the angle of reflection.
  • Add internal walls/collision detection
  • Add traps (holes) the ball can fall in

Are You Still Listening?

Since the CFClient Sampler is a single-page app, it means the HTML and JS are all loaded when the app is opened.  Earlier I showed How I added onPageLoad() convention whereby I could register a function to run any time a jQuery Mobile "page" was loaded (which consists of showing its <div> while hiding everything else).  I used this to defer loading the phone's contacts until the page was visited the first time. 

I also didn't want the accelerometer examples to always be running in the background either so I devised an onPageUnload() convention as well to help me manage the page lifecycle.  Here is the global listener that fires on EVERY page change and looks for load or unload functions to execute.

$(document).on("pagecontainershow", function(e, ui) {
    var page = $('body').pagecontainer('getActivePage');
    var pageID = page.prop('id');
    console.log( 'Showing page ' + pageID + '.' );

    // If we came from another page, see if it needs unloaded first
    if( ui.prevPage && ui.prevPage.length ) {
	var prevPage = $( ui.prevPage[0] );
	    
        if( prevPage.data( 'onPageUnLoad' ) ) {
        	console.log( 'Unloading page' + prevPage.prop('id') );
		prevPage.data( 'onPageUnLoad' )();
	}
    }
	
    if( page.data( 'onPageLoad' ) ) {
	page.data( 'onPageLoad' )();
    }
});

Then I registered load and unload functions for both of my accelerometer examples:

function startAccelerometer() {
	console.log('start watching accelerometer');
	accelerometerWatchId = cfclient.accelerometer.watch( onAccelerometer );
}

function stopAccelerometer() {
	console.log('stop watching accelerometer');
	cfclient.accelerometer.clearWatch( accelerometerWatchId );				
}

// When the Accelerometer page is loaded, start tracking
$( '##accelerometer' ).data( 'onPageLoad', function() {
	startAccelerometer();
});

// When the Accelerometer page is left, stop tracking
$( '##accelerometer' ).data( 'onPageUnLoad', function() {
	stopAccelerometer();
});

The end result is that the app starts listening to the accelerometer as soon as you load that page, but stops as soon as you navigate elsewhere.  I was fairly pleased with how this worked and a little surprised that the later versions of jQuery Mobile actually removed the in-built ability to bind load/unload methods to pages.

Now, you may be wondering why I bothered to create the startAccelerometer() and stopAccelerometer() functions in the first place when I could have just stuck their contents directly in the anonymous function like so:

$( '##accelerometer' ).data( 'onPageLoad', function() {
	accelerometerWatchId = cfclient.accelerometer.watch( onAccelerometer );
});

Unfortunately, this code would compile but would create JavaScript errors at run time.

cff_callback27 is not defined

Trying to read the compiled JavaScript is pretty hard so I didn't try to figure it out.  I just massaged the functions until they worked and made a note of the error.  

Fin

That's all for today. As always, check out the latest code on GitHub:

And the tagged release that has the version of the code discussed in this blog post here eternally stored here:


Want to read more in this series?  Pick one of the links below: