Random geekery since 2005.

Husband, father, programmer, speaker, writer, blogger, podcaster, collector, traveler, golfer.

31 Days of Mango | Day #6: Motion

Written in

by

Day6-MotionDay5-GyroscopeDay4-CompassDay3-RemindersDay2-DeviceStatus

This article is Day #6 in a series called the 31 Days of Mango.

Today, we are going to take all of the information we learned over the past two days, and make it much easier to manage by using the Motion class.  The Motion class is a combination of the data we receive from the Accelerometer, the Compass, and the Gyroscope on a Windows Phone.

If you would like to download this application to your Windows Phone, it is available in the Windows Phone Marketplace for free:

DownloadIcon

Pitch, Yaw, and Roll

200px-Aptch 200px-Ayaw 200px-Aileron_roll
PITCH YAW ROLL

As you can see from the animated images above, pitch, yaw, and roll are three important pieces of data in aviation, but they apply to our Windows Phone devices as well.  When we looked at the Accelerometer and Gyroscope, we were able to gather data about the device on specific axes: X, Y, and Z.  Pitch, yaw, and roll actually represent the rotation around those same axes.  This is done using a bunch of math that the Motion class actually performs for us automatically.  (For more information on the principal axes of aircraft, check out Wikipedia.)

Using the Motion Class

In our previous examples of the Gyroscope and Compass, we had to detect the sensors on the user’s device before proceeding.  We don’t need to make the individual checks when using the Motion class, but we do need to make sure that Motion itself is supported.

Before we dive into the specific code for the Motion class, we should come up with a compelling interface.  For our example, we are going to place a XAML star on our screen, like the image below:

image

By using the Yaw value from the Motion class, we will be able to manipulate the orientation of the star.  As the user rotates their phone, the star will actually appear to remain stationary in relation to the user.  Here’s a quick video illustration of this effect at work:

Here is the XAML we need to accomplish this user interface:

<phone:PhoneApplicationPage
   x:Class="Day6_Motion.MainPage"
   xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation&quot;
   xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml&quot;
   xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
   xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
   xmlns:d="http://schemas.microsoft.com/expression/blend/2008&quot;
   xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006&quot;
    xmlns:es="clr-namespace:Microsoft.Expression.Shapes;assembly=Microsoft.Expression.Drawing"
   mc:Ignorable="d" d:DesignWidth="480" d:DesignHeight="768"
   FontFamily="{StaticResource PhoneFontFamilyNormal}"
   FontSize="{StaticResource PhoneFontSizeNormal}"
   Foreground="{StaticResource PhoneForegroundBrush}"
   SupportedOrientations="Portrait" Orientation="Portrait"
   shell:SystemTray.IsVisible="True">

    <Grid x:Name="LayoutRoot" Background="Transparent">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>

        <StackPanel x:Name="TitlePanel" Grid.Row="0" Margin="12,17,0,28">
            <TextBlock x:Name="ApplicationTitle" Text="31 DAYS OF MANGO" Style="{StaticResource PhoneTextNormalStyle}"/>
            <TextBlock x:Name="PageTitle" Text="motion" Margin="9,-7,0,0" Style="{StaticResource PhoneTextTitle1Style}"/>
        </StackPanel>

        <Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
            <es:RegularPolygon x:Name="Star" InnerRadius="0.47211" Margin="100,175,100,175" PointCount="5" Stretch="Fill" Stroke="White" UseLayoutRounding="False" StrokeThickness="6">
                <es:RegularPolygon.Fill>
                    <SolidColorBrush Color="{StaticResource PhoneAccentColor}"/>
                </es:RegularPolygon.Fill>
                <es:RegularPolygon.RenderTransform>
                    <RotateTransform CenterX="100" CenterY="128"></RotateTransform>
                </es:RegularPolygon.RenderTransform>
            </es:RegularPolygon>

            <TextBlock x:Name="yawValue" Text="YAW = 34.567" FontSize="40" Width="400" Height="100" TextAlignment="Center" Margin="28,503,28,4" />
        </Grid>
    </Grid>
</phone:PhoneApplicationPage>
 

You should notice that our Star is created using a new assembly specific to Expression Blend.  We don’t need to use Blend in order to use the controls it contains, however.  You will need to add a reference to the Microsoft.Expression.Drawing assembly, and also make a XML namespace reference at the top of your XAML page.  If you haven’t played with it before, try changing up the PointCount property of the RegularPolygon element.  You can create stars with as many as points as you want (though you won’t notice much of a difference after 60ish.)

You can also see two other changes that I’ve made to the star.  The first is the Fill color.  I’ve actually defined a SolidColorBrush that will use the user’s selected Theme color.  This was covered in Day #5 of the 31 Days of Windows Phone, so make sure to read it if you’d like to use this in your application.

To get started making this star start to move, we’re going to be using very similar concepts to those we used on the individual sensors.  First, we do our Motion detection:

using System;
using System.Windows.Media;
using Microsoft.Phone.Controls;
using Microsoft.Devices.Sensors;
using Microsoft.Xna.Framework;

namespace Day6_Motion
{
    public partial class MainPage : PhoneApplicationPage
    {
        public MainPage()
        {
            InitializeComponent();

            if (Motion.IsSupported)
            {
                //DO SOMETHING
            }
        }
    }
}
 

In our “DO SOMETHING” comment, we need to initialize our Motion object, and create an event handler to listen for the data that will be created.

using System;
using System.Windows.Media;
using Microsoft.Phone.Controls;
using Microsoft.Devices.Sensors;
using Microsoft.Xna.Framework;

namespace Day6_Motion
{
    public partial class MainPage : PhoneApplicationPage
    {
        Motion motion;
        
        public MainPage()
        {
            InitializeComponent();

            if (Motion.IsSupported)
            {
                motion = new Motion();
                motion.TimeBetweenUpdates = TimeSpan.FromMilliseconds(20);
                motion.CurrentValueChanged += new EventHandler<SensorReadingEventArgs<MotionReading>>(motion_CurrentValueChanged);
                motion.Start();
            }
        }

        void motion_CurrentValueChanged(object sender, SensorReadingEventArgs<MotionReading> e)
        {
            //MAKE THIS THREAD SAFE.
        }
    }
}
 

Finally, we need to make sure that we practice some thread safety.  We can’t read the sensor data from the UI thread, so we need to pitch that processing to a separate thread using the Dispatcher.BeginInvoke() method.  You will see that added below, along with our math to manipulate the star.  The code below should be the only version of the app that you actually need to copy and paste into your own. 

using System;
using System.Windows.Media;
using Microsoft.Phone.Controls;
using Microsoft.Devices.Sensors;
using Microsoft.Xna.Framework;

namespace Day6_Motion
{
    public partial class MainPage : PhoneApplicationPage
    {
        Motion motion;
        
        public MainPage()
        {
            InitializeComponent();

            if (Motion.IsSupported)
            {
                motion = new Motion();
                motion.TimeBetweenUpdates = TimeSpan.FromMilliseconds(20);
                motion.CurrentValueChanged += new EventHandler<SensorReadingEventArgs<MotionReading>>(motion_CurrentValueChanged);
                motion.Start();
            }
        }

        void motion_CurrentValueChanged(object sender, SensorReadingEventArgs<MotionReading> e)
        {
            Dispatcher.BeginInvoke(() => UpdateUI(e.SensorReading));
        }

        private void UpdateUI(MotionReading e)
        {
            ((RotateTransform)Star.RenderTransform).Angle = MathHelper.ToDegrees(e.Attitude.Yaw);
            yawValue.Text = "YAW = " + e.Attitude.Yaw.ToString();
        }
    }
}
 

You can see that we apply our Yaw value to the RotateTransform angle of our star, forcing it to rotate as the sensors detect the motion of the phone.  Give it a shot on a device (assuming you have one available to you) and you’ll probably notice a significant difference in behavior between a phone that has a gyroscope and a phone that doesn’t.  As I mentioned yesterday, a gyroscope provides a much more smooth and accurate feed of data, and as such, the star moves with grace when a gyroscope is present.

What do you mean you don’t have a phone?

I’m not suggesting that you run out today and buy one (though you are certainly welcome to).  If you live in the midwest United States, send me an email.  I have phones that I can loan out for 10 days.  If you don’t live there, email me anyways, and I can connect you with the Developer Evangelist in your area of the world.  We all have a few phones available to loan.

Some other interesting data…

In my code example above, I really only utilized the Yaw data from the Motion class.  I want to make sure you’re aware of all of the values that are available to you from the Motion class, however.  In the downloadable Windows Phone solution below, I’ve also included variables to grab all of the available data from the Motion class.  Here’s what that looks like:

float pitch = e.Attitude.Pitch;
float yaw = e.Attitude.Yaw;
float roll = e.Attitude.Roll;

float accelerometerX = e.DeviceAcceleration.X;
float accelerometerY = e.DeviceAcceleration.Y;
float accelerometerZ = e.DeviceAcceleration.Z;

float gyroscopeX = e.DeviceRotationRate.X;
float gyroscopeY = e.DeviceRotationRate.Y;
float gyroscopeZ = e.DeviceRotationRate.Z;

float gravityX = e.Gravity.X;
float gravityY = e.Gravity.Y;
float gravityZ = e.Gravity.Z;

DateTimeOffset timestamp = e.Timestamp;
 

You can see that we still have access to the data from the individual sensors, as well as a TimeStamp value, so that we know exactly when the data occurred.

Summary

So that’s the Motion class.  It should be the primary way that you interact with the Gyroscope, Compass, and Accelerometer, and should make much of the math and processing much easier for you.  For those of you struggling to come up with an interesting way to use this data, here’s a great example:

One great use of the accelerometer that I’ve seen was in a cycling application. The primary focus of the application was to track where a user was riding their bicycle. As a safety feature, you could enter an emergency phone number. If the application recognized an uncomfortably fast stop, followed by very little movement, it would automatically text the emergency phone number with the GPS coordinates of the device. Following that, it would prompt the phone to dial 911.  This is an amazing use of both location and accelerometer data that will ultimately help to keep cyclists safer. Your app can use it too.

If you would like to download the code solution that we created in this article, click the Download Code button below:

download

Tomorrow, we’re going to shift gears to another useful API: the camera.  We now have the ability to gather the raw data that the phone captures, and we’ll talk all about how we get it, and what we can use it for.  See you then!

toolsbutton

Tags

13 responses to “31 Days of Mango | Day #6: Motion”

  1. Mike Ward Avatar

    Your pitch and yaw images are reversed…

    1. jeffblankenburg Avatar
      jeffblankenburg

      You’re right! Thanks! It’s been fixed!

  2. […] Der Originalartikel befindet sich hier: Day #6: Motion. […]

  3. […] No trackbacks yet. « 31 Days of Mango | Day #6: Motion […]

  4. […] 31 Days of Mango | Day #6: Motion […]

  5. […] 31 Days of Mango | Day #6: Motion […]

  6. […] Англоязычная версия статьи досупна здесь. […]

  7. […] Este artículo es una traducción de Day 6: Motion, puedes encontrar aquí la versión original en i… […]

  8. […] Эта статья День#6 из серии статей 31 День c Mango. Версия на английском языке доступна по ссылке. […]

  9. Ander Avatar
    Ander

    How are related the yaw from motion sensor and heading from magnetometer?

  10. Ander Avatar
    Ander

    Nevermind, I see that yaw is same as true heading, but they rotate the opposite way.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

Create a website or blog at WordPress.com

%d bloggers like this: