Pedometer (Walking Motion)

2
197

Apedometer counts how many steps you take. It is a handy device for people who are interested in getting enough exercise and perhaps need a little motivation. Pedometers—especially good ones—are expensive. Thanks to the built-in accelerometer, the Pedometer app enables you to turn your phone into a pedometer without the need for a separate device.

Current Windows phones do not report accelerometer data while the screen is turned off!

Although this app runs while the phone is locked, phones do not report accelerometer data while the screen is off. Unfortunately, this means that no steps can be registered while the screen is off.You must keep the screen on (and therefore the phone unlocked) for the entire time you use this app.This app disables the screen time-out, so you don’t have to worry about your phone automatically locking in your pocket.You do, however, have to worry about accidentally bumping buttons. For the best results, the screen should face away from your body.

The Main Page

This app has a main page, a settings page, and an instructions page . The main page shows the current number of steps and converts that number to miles and kilometers, based on a stride-length setting customized on the settings page. It starts out in a “paused” state, shown in Figure 50.1, in which no steps are registered. This cuts down on the reporting of bogus steps. The idea is that users press the start button while the phone is close to their pocket, and then they slide the phone in carefully. (The step-detection algorithm can easily register two bogus steps with a little bit of jostling of the phone.)

This page also shows a welcome message urging the user to calibrate the pedometer, but it only shows it the first time the page is loaded (on the first run of the app).

Listing 50.1 contains the XAML for the main page, and Listing 50.2 contains its code-behind.

The app starts out in a paused state to avoid registering bogus steps.
FIGURE 50.1 The app starts out in a paused state to avoid registering bogus steps.

LISTING 50.1 MainPage.xaml—The User Interface for Pedometer’s Main Page

[code]

<phone:PhoneApplicationPage x:Class=”WindowsPhoneApp.MainPage”
xmlns=”http://schemas.microsoft.com/winfx/2006/xaml/presentation”
xmlns:x=”http://schemas.microsoft.com/winfx/2006/xaml”
xmlns:phone=”clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone”
xmlns:shell=”clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone”
xmlns:local=”clr-namespace:WindowsPhoneApp”
FontFamily=”{StaticResource PhoneFontFamilyNormal}”
FontSize=”{StaticResource PhoneFontSizeNormal}”
Foreground=”{StaticResource PhoneForegroundBrush}”
SupportedOrientations=”Portrait” shell:SystemTray.IsVisible=”True”>
<!– The application bar, with four buttons –>
<phone:PhoneApplicationPage.ApplicationBar>
<shell:ApplicationBar Opacity=”.5”
BackgroundColor=”{StaticResource PhoneAccentColor}”>
<shell:ApplicationBarIconButton Text=”start”
IconUri=”/Shared/Images/appbar.play.png”
Click=”StartPauseButton_Click”/>
<shell:ApplicationBarIconButton Text=”settings” Click=”SettingsButton_Click”
IconUri=”/Shared/Images/appbar.settings.png”/>
<shell:ApplicationBarIconButton Text=”instructions”
IconUri=”/Shared/Images/appbar.instructions.png”
Click=”InstructionsButton_Click”/>
<shell:ApplicationBarIconButton Text=”reset”
IconUri=”/Shared/Images/appbar.delete.png”
Click=”ResetButton_Click”/>
</shell:ApplicationBar>
</phone:PhoneApplicationPage.ApplicationBar>
<Grid>
<!– The accent-colored foot –>
<Rectangle Opacity=”.5” Fill=”{StaticResource PhoneAccentBrush}” Width=”277”
Height=”648” Margin=”0,12,12,0” VerticalAlignment=”Top”
HorizontalAlignment=”Right”>
<Rectangle.OpacityMask>
<ImageBrush ImageSource=”Images/foot.png”/>
</Rectangle.OpacityMask>
</Rectangle>
<StackPanel>
<!– PAUSED –>
<TextBlock x:Name=”PausedTextBlock” FontFamily=”Segoe WP Black”
HorizontalAlignment=”Center” Text=”PAUSED”
FontSize=”{StaticResource PhoneFontSizeExtraExtraLarge}”/>
<!– Steps –>
<TextBlock x:Name=”StepsTextBlock” Margin=”12,12,0,0” Opacity=”.5”
FontSize=”130”/>
<TextBlock Text=”steps” Margin=”18,-24” FontWeight=”Bold”
FontSize=”{StaticResource PhoneFontSizeMedium}”
Foreground=”{StaticResource PhoneSubtleBrush}”/>
<!– Miles –>
<TextBlock x:Name=”MilesTextBlock” Margin=”12,72,0,0” Opacity=”.5”
FontSize=”{StaticResource PhoneFontSizeExtraExtraLarge}”/>
<TextBlock Text=”miles” Margin=”18,-12” FontWeight=”Bold”
FontSize=”{StaticResource PhoneFontSizeMedium}”
Foreground=”{StaticResource PhoneSubtleBrush}”/>
<!– Kilometers –>
<TextBlock x:Name=”KilometersTextBlock” Margin=”12,72,0,0” Opacity=”.5”
FontSize=”{StaticResource PhoneFontSizeExtraExtraLarge}”/>
<TextBlock Text=”kilometers” Margin=”18,-12” FontWeight=”Bold”
FontSize=”{StaticResource PhoneFontSizeMedium}”
Foreground=”{StaticResource PhoneSubtleBrush}”/>
</StackPanel>
<!– The special one-time message –>
<Border x:Name=”FirstRunPanel” Visibility=”Collapsed” Width=”300”
Margin=”0,0,24,0” HorizontalAlignment=”Right” VerticalAlignment=”Center”
Background=”{StaticResource PhoneAccentBrush}” Padding=”24”>
<TextBlock TextWrapping=”Wrap” Text=”Welcome! Be sure to calibrate …”/>
</Border>
</Grid>
</phone:PhoneApplicationPage>

[/code]

Both the foot and the application bar are given the phone theme’s accent color, but with 50% opacity to make them more subtle.

LISTING 50.2 MainPage.xaml.cs—The Code-Behind for Pedometer’s Main Page

[code]

using System;
using System.ComponentModel;
using System.Windows;
using System.Windows.Navigation;
using Microsoft.Phone.Applications.Common; // For AccelerometerHelper
using Microsoft.Phone.Controls;
using Microsoft.Phone.Shell;
namespace WindowsPhoneApp
{
public partial class MainPage : PhoneApplicationPage
{
IApplicationBarIconButton startPauseButton;
IApplicationBarIconButton settingsButton;
IApplicationBarIconButton instructionsButton;
IApplicationBarIconButton resetButton;
bool pastPositiveThreshold = true;
public MainPage()
{
InitializeComponent();
this.startPauseButton = this.ApplicationBar.Buttons[0]
as IApplicationBarIconButton;
this.settingsButton = this.ApplicationBar.Buttons[1]
as IApplicationBarIconButton;
this.instructionsButton = this.ApplicationBar.Buttons[2]
as IApplicationBarIconButton;
this.resetButton = this.ApplicationBar.Buttons[3]
as IApplicationBarIconButton;
SoundEffects.Initialize();
// Use the accelerometer via Microsoft’s helper
AccelerometerHelper.Instance.ReadingChanged
+= Accelerometer_ReadingChanged;
// Allow the app to run when the phone is locked.
// Once disabled, you cannot re-enable the default behavior!
PhoneApplicationService.Current.ApplicationIdleDetectionMode =
IdleDetectionMode.Disabled;
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e);
RefreshDisplay();
// Show the welcome message if this is the first time ever
if (Settings.FirstRun.Value)
{
this.FirstRunPanel.Visibility = Visibility.Visible;
Settings.FirstRun.Value = false;
}
else
{
this.FirstRunPanel.Visibility = Visibility.Collapsed;
}
// While on this page, don’t allow the screen to auto-lock
PhoneApplicationService.Current.UserIdleDetectionMode =
IdleDetectionMode.Disabled;
}
protected override void OnNavigatedFrom(NavigationEventArgs e)
{
base.OnNavigatedTo(e);
// Restore the ability for the screen to auto-lock when on other pages
PhoneApplicationService.Current.UserIdleDetectionMode =
IdleDetectionMode.Enabled;
}
protected override void OnBackKeyPress(CancelEventArgs e)
{
base.OnBackKeyPress(e);
if (AccelerometerHelper.Instance.Active)
{
MessageBox.Show(“You must pause the pedometer before leaving. …”,
“Pedometer Still Collecting Data”, MessageBoxButton.OK);
e.Cancel = true;
}
}
// Process data coming from the accelerometer
void Accelerometer_ReadingChanged(object sender,
AccelerometerHelperReadingEventArgs e)
{
bool newStep = false;
double magnitude = e.OptimalyFilteredAcceleration.Magnitude;
if (!pastPositiveThreshold && magnitude > 1 + Settings.Threshold.Value)
{
newStep = true;
pastPositiveThreshold = true;
}
if (magnitude < 1 – Settings.Threshold.Value)
pastPositiveThreshold = false;
if (newStep)
{
// We only know about one leg, so count each new step as two
Settings.NumSteps.Value += 2;
this.Dispatcher.BeginInvoke(delegate()
{
RefreshDisplay();
if (Settings.PlaySound.Value)
SoundEffects.Ding.Play();
});
}
}
void RefreshDisplay()
{
this.StepsTextBlock.Text = Settings.NumSteps.Value.ToString(“N0”);
double totalInches = Settings.NumSteps.Value * Settings.Stride.Value;
this.MilesTextBlock.Text = (totalInches / 63360).ToString(“##0.####”);
this.KilometersTextBlock.Text =
(totalInches * 0.0000254).ToString(“##0.####”);
}
// Application bar handlers
void StartPauseButton_Click(object sender, EventArgs e)
{
if (AccelerometerHelper.Instance.Active)
{
// Stop the accelerometer with Microsoft’s helper
AccelerometerHelper.Instance.Active = false;
this.startPauseButton.Text = “start”;
this.startPauseButton.IconUri
= new Uri(“/Shared/Images/appbar.play.png”, UriKind.Relative);
this.PausedTextBlock.Text = “PAUSED”;
this.StepsTextBlock.Opacity = .5;
this.MilesTextBlock.Opacity = .5;
this.KilometersTextBlock.Opacity = .5;
this.settingsButton.IsEnabled = true;
this.instructionsButton.IsEnabled = true;
this.resetButton.IsEnabled = true;
}
else
{
// Start the accelerometer with Microsoft’s helper
AccelerometerHelper.Instance.Active = true;
this.startPauseButton.Text = “pause”;
this.startPauseButton.IconUri
= new Uri(“/Shared/Images/appbar.pause.png”, UriKind.Relative);
this.PausedTextBlock.Text = “”;
this.StepsTextBlock.Opacity = 1;
this.MilesTextBlock.Opacity = 1;
this.KilometersTextBlock.Opacity = 1;
this.settingsButton.IsEnabled = false;
this.instructionsButton.IsEnabled = false;
this.resetButton.IsEnabled = false;
}
}
void SettingsButton_Click(object sender, EventArgs e)
{
this.NavigationService.Navigate(
new Uri(“/SettingsPage.xaml”, UriKind.Relative));
}
void InstructionsButton_Click(object sender, EventArgs e)
{
this.NavigationService.Navigate(
new Uri(“/InstructionsPage.xaml”, UriKind.Relative));
}
void ResetButton_Click(object sender, EventArgs e)
{
if (MessageBox.Show(“Are you sure you want to clear your data?”, “Reset”,
MessageBoxButton.OKCancel) == MessageBoxResult.OK)
{
Settings.NumSteps.Value = 0;
RefreshDisplay();
}
}
}
}

[/code]

  • This listing uses the following settings defined in a separate Settings.cs file as follows:
    [code]
    public static class Settings
    {
    // Configurable settings
    public static readonly Setting<double> Threshold =
    new Setting<double>(“Threshold”, .43);
    public static readonly Setting<bool> PlaySound =
    new Setting<bool>(“PlaySound”, true);
    public static readonly Setting<double> Stride =
    new Setting<double>(“Stride”, 28);
    // Current state
    public static readonly Setting<int> NumSteps =
    new Setting<int>(“NumSteps”, 0);
    // A special flag that is set to false during the app’s first run
    public static readonly Setting<bool> FirstRun =
    new Setting<bool>(“FirstRun”, true);
    }
    [/code]
  • The walking detection, done inside Accelerometer_ReadingChanged, uses the magnitude of the acceleration vector reported by the AccelerometerHelper library (the square root of X2 + Y2 + Z2). Therefore, the direction of the motion doesn’t matter; just the amount of motion. A phone at rest has a magnitude of 1 (pointing toward the Earth), so this algorithm looks for a sufficiently smaller magnitude followed by a sufficiently larger magnitude. This represents the motion of the single leg holding the phone, so the detection of each step is actually counted as two steps, one per leg. The definition of sufficient is determined by the Threshold setting customized on the settings page.

The Settings Page

The settings page must be visited frequently in order to calibrate the pedometer just right.
FIGURE 50.2 The settings page must be visited frequently in order to calibrate the pedometer just right.

The settings page, shown in Figure 50.2, enables the user to change the values of the Threshold, PlaySound, and Stride settings used in the previous listing.

Users can try different sensitivity values (which map to Threshold) and then manually count their steps while Pedometer runs to see if the two counts match. To make this process even easier, this app supports playing a sound every time a step is detected. When the value is correct, the user will hear a sound every other step (when the leg carrying the phone makes a step). Once a good sensitivity value is found, the user can turn off the sound.

The best sensitivity value can vary based on where the user puts the phone, the depth of the pocket containing the phone, and other factors. Therefore, a sensitivity value that works best one day may not work well the next.

To give accurate values for miles and kilometers, users must enter their stride length at the bottom of the page. If users are unable to measure their stride length, they can multiply their height (in inches) by .413 for a woman or .415 for a man. The average stride length for a woman is 26.4 inches, and the average stride length for a man is 30 inches. This information is explained on this app’s instructions page.

Listing 50.3 contains the XAML for the settings page, and Listing 50.4 contains its codebehind.

LISTING 50.3 SettingsPage.xaml—The User Interface for Pedometer’s Settings Page

[code]

<phone:PhoneApplicationPage
x:Class=”WindowsPhoneApp.SettingsPage” x:Name=”Page”
xmlns=”http://schemas.microsoft.com/winfx/2006/xaml/presentation”
xmlns:x=”http://schemas.microsoft.com/winfx/2006/xaml”
xmlns:phone=”clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone”
xmlns:shell=”clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone”
xmlns:local=”clr-namespace:WindowsPhoneApp”
xmlns:toolkit=”clr-namespace:Microsoft.Phone.Controls;
➥assembly=Microsoft.Phone.Controls.Toolkit”
FontFamily=”{StaticResource PhoneFontFamilyNormal}”
FontSize=”{StaticResource PhoneFontSizeNormal}”
Foreground=”{StaticResource PhoneForegroundBrush}”
SupportedOrientations=”PortraitOrLandscape” shell:SystemTray.IsVisible=”True”>
<Grid Background=”Transparent”>
<Grid.RowDefinitions>
<RowDefinition Height=”Auto”/>
<RowDefinition Height=”*”/>
</Grid.RowDefinitions>
<!– The standard settings header –>
<StackPanel Grid.Row=”0” Style=”{StaticResource PhoneTitlePanelStyle}”>
<TextBlock Text=”SETTINGS” Style=”{StaticResource PhoneTextTitle0Style}”/>
<TextBlock Text=”pedometer”
Style=”{StaticResource PhoneTextTitle1Style}”/>
</StackPanel>
<ScrollViewer Grid.Row=”1”>
<StackPanel Margin=”{StaticResource PhoneMargin}”>
<TextBlock Text=”Tap &amp; hold …” TextWrapping=”Wrap” Margin=”12,0”/>
<!– The slider with three supporting text blocks –>
<Grid Height=”150”>
<TextBlock HorizontalAlignment=”Right” Margin=”0,12,12,0”
VerticalAlignment=”Center” Text=”report more steps”/>
<TextBlock HorizontalAlignment=”Left” Margin=”12,12,0,0”
VerticalAlignment=”Center” Text=”report fewer steps”/>
<TextBlock x:Name=”ThresholdTextBlock” Margin=”12,6,0,0”
Foreground=”{StaticResource PhoneSubtleBrush}”
HorizontalAlignment=”Left” VerticalAlignment=”Top”
FontSize=”{StaticResource PhoneFontSizeLarge}”/>
<Slider x:Name=”ThresholdSlider” Minimum=”.1” Maximum=”1”
LargeChange=”.001” IsDirectionReversed=”True”
Value=”{Binding Threshold, Mode=TwoWay, ElementName=Page}”/>
</Grid>
<!– reset –>
<Button Content=”reset sensitivity” Click=”ResetButton_Click”
local:Tilt.IsEnabled=”True”/>
<!– Play a sound toggle switch –>
<toolkit:ToggleSwitch x:Name=”PlaySoundToggleSwitch” Margin=”0,30,0,0”
Header=”Play a sound every other step”
IsChecked=”{Binding PlaySound, Mode=TwoWay, ElementName=Page}”/>
<!– Stride length –>
<TextBlock Style=”{StaticResource LabelStyle}”
Text=”Stride length (in inches)”/>
<TextBox x:Name=”StrideLengthTextBox” InputScope=”Number”
Text=”{Binding StrideLength, Mode=TwoWay, ElementName=Page}”/>
</StackPanel>
</ScrollViewer>
</Grid>
</phone:PhoneApplicationPage>

[/code]

LISTING 50.4 SettingsPage.xaml.cs—The Code-Behind for Pedometer’s Settings Page

[code]

using System.Windows;
using Microsoft.Phone.Controls;
namespace WindowsPhoneApp
{
public partial class SettingsPage : PhoneApplicationPage
{
public SettingsPage()
{
InitializeComponent();
this.ThresholdTextBlock.Text =
(Settings.Threshold.Value * 1000).ToString(“N0”);
}
// Simple property bound to the slider
public double Threshold
{
get { return Settings.Threshold.Value; }
set { this.ThresholdTextBlock.Text = (value * 1000).ToString(“N0”);
Settings.Threshold.Value = value; }
}
// Simple property bound to the toggle switch
public bool PlaySound
{
get { return Settings.PlaySound.Value; }
set { Settings.PlaySound.Value = value; }
}
// Simple property bound to the text box
public string StrideLength
{
get { return Settings.Stride.Value.ToString(); }
set
{
try { Settings.Stride.Value = int.Parse(value); }
catch { }
}
}
void ResetButton_Click(object sender, RoutedEventArgs e)
{
this.ThresholdSlider.Value = Settings.Threshold.DefaultValue;
}
}
}

[/code]

The Finished Product

Pedometer (Walking Motion)

 

 

2 COMMENTS

  1. Good Morning Sir
    Very Nice Work

    Do you have the code snippet for the “PhoneTitlePanelStyle” style?
    Thanks

  2. Hi,

    I was very surprised by reading your Settings.cs file. What is this “Setting” type (for instance in Setting )? And, can you specify where you’ve found this Window’s helper (I can’t find any documentation about Accelerometer.Helper on the Internet)?

    Thank you so much!

    Claire

Comments are closed.