This post is about introducing you Windows Phones Accelerometer (I have taken following contents from Charles Petzold's book "Programming Windows Phone 7 - Special Excerpt 2". The contents are Copyright of Microsoft and is used only for informational purpose). You can get more detailed information about using Accelerometer from above mentioned book, complete book is still to be released later this year.
Windows Phones contain an accelerometer — a small hardware device that essentially measures force, which elementary physics tells us is proportional to acceleration. When the phone is held still, the accelerometer responds to the force of gravity, so the accelerometer can tell your application the direction of the Earth relative to the phone. It is convenient to represent the accelerometer output as a vector in three-dimensional space. Vectors are commonly written in boldface, so the acceleration vector can be symbolized as (x, y, z). XNA defines a three-dimensional vector type; Silverlight does not.
While a three-dimensional point (x, y, z) indicates a particular location in space, the vector (x, y, z) encapsulates instead a direction and a magnitude. Obviously the point and the vector are related: The direction of the vector (x, y, z) is the direction from the point (0, 0, 0) to the point(x, y, z). But the vector (x, y, z) is definitely not the line from (0, 0, 0) to (x, y, z). It’s only the direction of that line. The magnitude of the vector (x, y, z) is calculable from the three-dimensional form of the Pythagorean Theorem:
where:
For working with the accelerometer, you can imagine the phone as defining a threedimensional coordinate system. No matter how the phone is oriented, the positive Y axis points from the bottom of the phone (with the buttons) to the top, the positive X axis points from left to right.
When the phone is still, the accelerometer vector points towards the Earth. The magnitude is 1, meaning 1 g, which is the force of gravity on the earth's surface. When holding your phone in the upright position, the acceleration vector is (0, –1, 0), that is, straight down.
So lets start, To use the accelerometer, you’ll need a reference to the
You create an instance of the Accelerometer class, set an event handler for the
ReadingChanging event, and call Start. And then it gets a little tricky. Let’s take a look at a project named
This is a program that will display the accelerometer vector throughout its lifetime, so it creates the Accelerometer class in its constructor and calls Start:
The documentation warns that calling Start might raise an exception, so the program protects itself against that eventuality. These user-interface objects are not thread safe; they are not built to be accessed simultaneously from multiple threads. For this reason, Silverlight will not allow you to access a user-interface object from a non-UI thread.
This means that the
a non-UI thread on a queue where they are later executed by the UI thread.
The Dispatcher class defines a method named
The verbose version requires a delegate and a method defined in accordance with that delegate. The delegate (and method) should have no return value, but as many arguments as you need to do the job, in this case setting a string to the Text property of a TextBlock:
Yes! The
brace following the method body. The empty parentheses following the delegate keyword are not required.
The Windows Phone 7 emulator doesn’t contain any actual accelerometer, so it always reports a value of (0, 0, –1), which indicates the phone is lying on a flat surface:
Windows Phones contain an accelerometer — a small hardware device that essentially measures force, which elementary physics tells us is proportional to acceleration. When the phone is held still, the accelerometer responds to the force of gravity, so the accelerometer can tell your application the direction of the Earth relative to the phone. It is convenient to represent the accelerometer output as a vector in three-dimensional space. Vectors are commonly written in boldface, so the acceleration vector can be symbolized as (x, y, z). XNA defines a three-dimensional vector type; Silverlight does not.
While a three-dimensional point (x, y, z) indicates a particular location in space, the vector (x, y, z) encapsulates instead a direction and a magnitude. Obviously the point and the vector are related: The direction of the vector (x, y, z) is the direction from the point (0, 0, 0) to the point(x, y, z). But the vector (x, y, z) is definitely not the line from (0, 0, 0) to (x, y, z). It’s only the direction of that line. The magnitude of the vector (x, y, z) is calculable from the three-dimensional form of the Pythagorean Theorem:
Magnitude = Math.sqrt( Math.pow(x,2), Math.pow(y,2), Math.pow(z,2) )
where:
Math.sqrt() function returns the square root of a number
Math.pow() function returns a number raised to a power
For working with the accelerometer, you can imagine the phone as defining a threedimensional coordinate system. No matter how the phone is oriented, the positive Y axis points from the bottom of the phone (with the buttons) to the top, the positive X axis points from left to right.
When the phone is still, the accelerometer vector points towards the Earth. The magnitude is 1, meaning 1 g, which is the force of gravity on the earth's surface. When holding your phone in the upright position, the acceleration vector is (0, –1, 0), that is, straight down.
So lets start, To use the accelerometer, you’ll need a reference to the
Microsoft.Devices.Sensors
library, and a using directive for the Microsoft.Devices.Sensors
namespace. In WMAppManifest.xml
, you need<Capability Name="ID_CAP_SENSORS">
You create an instance of the Accelerometer class, set an event handler for the
ReadingChanging event, and call Start. And then it gets a little tricky. Let’s take a look at a project named
SilverlightAccelerometer
project that simply displays the current reading in its content grid. A centered TextBlock
is defined in the XAML.<Grid x:Name="ContentGrid" Grid.Row="1"> <TextBlock Name="txtblk" HorizontalAlignment="Center" VerticalAlignment="Center" /> </Grid>
This is a program that will display the accelerometer vector throughout its lifetime, so it creates the Accelerometer class in its constructor and calls Start:
public MainPage() { InitializeComponent(); Accelerometer acc = new Accelerometer(); acc.ReadingChanged += OnAccelerometerReadingChanged; try { acc.Start(); } catch (Exception exc) { txtblk.Text = exc.Message; } }
The documentation warns that calling Start might raise an exception, so the program protects itself against that eventuality. These user-interface objects are not thread safe; they are not built to be accessed simultaneously from multiple threads. For this reason, Silverlight will not allow you to access a user-interface object from a non-UI thread.
This means that the
OnAccelerometerReadingChanged
method cannot directly access the TextBlock element to set a new value to its Text property. Fortunately, there’s a solution involving a class named Dispatcher defined in the System.Windows.Threading
namespace. Through the Dispatcher
class, you can post jobs froma non-UI thread on a queue where they are later executed by the UI thread.
The Dispatcher class defines a method named
CheckAccess
that returns true if you can access a particular user interface object from the current thread. (The CheckAccess
method is also duplicated by DependencyObject
itself.) If an object can’t be accessed from the current thread, then Dispatcher provides two versions of a method named Invoke
that you use to post the job to the UI thread. The verbose version requires a delegate and a method defined in accordance with that delegate. The delegate (and method) should have no return value, but as many arguments as you need to do the job, in this case setting a string to the Text property of a TextBlock:
delegate void SetTextBlockTextDelegate(TextBlock txtblk, string text); void SetTextBlockText(TextBlock txtblk, string text) { txtblk.Text = text; }The
OnAccelerometerReadingChanged
is responsible for calling SetTextBlockText
. It first makes use of CheckAccess
to see if it can just call the SetTextBlockText
method directly. If not, then the handler calls the BeginInvoke
method. The first argument is an instantiation of the delegate with the SetTextBlockText
method; this is followed by all the arguments that SetTextBlockText
requires:void OnAccelerometerReadingChanged(object sender, AccelerometerReadingEventArgs args) { string str = String.Format("X = {0:F2}\n" + "Y = {1:F2}\n" + "Z = {2:F2}\n\n" + "Magnitude = {3:F2}\n\n" + "{4}", args.X, args.Y, args.Z, Math.Sqrt(args.X * args.X + args.Y * args.Y + args.Z * args.Z), args.Timestamp); if (txtblk.CheckAccess()) { SetTextBlockText(txtblk, str); } else { txtblk.Dispatcher.BeginInvoke(new SetTextBlockTextDelegate(SetTextBlockText), txtblk, str); } }This is not too bad, but the need for the code to jump across threads has necessitated an additional method and a delegate. Is there a way to do the whole job right in the event handler?
Yes! The
BeginInvoke
method has an overload that accepts an Action delegate, which defines a method that has no return value and no arguments. You can create an anonymous method right in the BeginInvoke
call. The complete code following the creation of the string object looks like this:if (txtblk.CheckAccess()) { txtblk.Text = str; } else { txtblk.Dispatcher.BeginInvoke(delegate() { txtblk.Text = str; }); }The anonymous method begins with the keyword delegate and concludes with the curly
brace following the method body. The empty parentheses following the delegate keyword are not required.
The Windows Phone 7 emulator doesn’t contain any actual accelerometer, so it always reports a value of (0, 0, –1), which indicates the phone is lying on a flat surface: