This is part 2 of a series. If you don’t yet have the Freescale board configured and tested with Windows 7, please refer to Part 1.
Now that we have the Freescale board working, I thought it would be fun to play with the accelerometer. In this post, I’ll show how to use the accelerometer on the board as a traditional 4 or 8 position joystick.
API Choices
There are three different ways you can use the accelerometer API. You can, of course, go directly against the COM API and generate you own wrappers and pinvokes. You can use the managed wrappers provided in the SDK, or you can use the Windows API Code Pack which includes support for the sensor API and a number of other Windows 7-specific enhancements.
In this case, I decided to use the code pack in concert with WPF 4.
Solution Structure
I intend to use the joystick functionality in another project, so I am wrapping that into a separate project. I also added just the sensor project from the Windows API Code Pack. You have a few choices there, including compiling the whole code pack as a separate assembly and simply referencing it. I decided to include only the bare minimum of assemblies required to support the example.
PeteBrown.Accelerometer is the reusable Accelerometer joystick class lib. The “Sensors” project is the Windows API Code Pack sensors project. The “Core” project is the required Windows API Code Pack core project
Sample Code
One great thing about the Windows API Code Pack is it includes a ton of example code. It just so happens that the accelerometer code is included in that.
Application Code
Typically I’m a binding fiend. I like to set up notification in my classes and just let binding handle all the dirty work. In this case, though, I wanted to go with a model that would work better in apps that used a type of GameLoop and instead of responding to events or binding, simply polled all input devices on an interval of their own design. For that reason, I’m using simple properties in this class, am not implementing INotifyPropertyChanged, and did not chose to raise any events.
If you’re a game programmer, or have that type of background, this decision probably won’t surprise you at all.
AccelerometerJoystick Class
The core class is the AccelerometerJoystick class.
I snagged some of the code right out of the Accelerometer sample from the code pack. One of the methods I borrowed and then messed with a bit was the Initialize method (called HookUpAccelerometer in the sample code). I changed it to only report X and Y, and to use an inline lambda event handler to cut down on code.
public void Initialize()
{
SensorList<Accelerometer3D> sl = SensorManager.GetSensorsByTypeId<Accelerometer3D>();
if (sl.Count > 0)
{
Accelerometer3D accel = sl[0];
accel.AutoUpdateDataReport = true;
accel.DataReportChanged += (s, e) =>
{
_rawXValue = accel.CurrentAcceleration[AccelerationAxis.X];
_rawYValue = accel.CurrentAcceleration[AccelerationAxis.Y];
};
}
}
The code finds the first accelerometer and then wires up the DataReportChanged event to catch changes in the X/Y values in the accelerometer. I don’t call Initialize from the constructor, as it may throw an exception. Instead, I have the using code call it explicitly. I may change that
I first tested by watching the raw values on a regular interval. The raw values are two simple properties on the same class:
private float _rawXValue;
public float RawXValue
{
get { return _rawXValue; }
}
private float _rawYValue;
public float RawYValue
{
get { return _rawYValue; }
}
Once I had the values set, and they made sense to me, I implemented properties to report the direction the accelerometer is pointing.
There were a couple different ways I could have gone here. I could have created a N, S, E, W enum, and had a single method that reported which direction the mouse was pointed. However, to support NW, NE, SW, SE, I’d have to add four more branches to that code and four more enum members.
I think it is more flexible to simply report true/false for each of the four cardinal directions, and let the calling code decide how to handle it from there. So, that’s the way I went.
public bool IsJoystickPointedNorth
{
get { return RoundValue(_rawYValue) > 0; }
}
public bool IsJoystickPointedSouth
{
get { return RoundValue(_rawYValue) < 0; }
}
public bool IsJoystickPointedWest
{
get { return RoundValue(_rawXValue) < 0; }
}
public bool IsJoystickPointedEast
{
get { return RoundValue(_rawXValue) > 0; }
}
private int RoundValue(float value)
{
return (int)Math.Round((double)value, 0);
// example making the joystick more sensitive
// return Math.Abs(value) > 0.25 ? 1 * Math.Sign(value) : 0;
}
A note on rounding: If you want to change the sensitivity, which you probably will, change the RoundValue method so that instead of using Math.Round, it does a check to see if the ABS of the value is, say, greater than 0.25 and return 1. That way you can have the joystick be a bit more sensitive to movement. Similarly, you can change it to return 1 only if the ABS of the value is greater than 0.75 for less sensitivity. Try it out to see how you prefer it.
MainWindow.xaml
The main window is pretty simple. I have a space in the middle where I report the raw X/Y values, and then four rectangles which light up (red in this case) when the joystick is pointed in that direction.
<Window x:Class="WpfAccelerometerApp.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<SolidColorBrush x:Key="ActiveDirectionBrush"
Color="Red" />
<SolidColorBrush x:Key="InactiveDirectionBrush"
Color="Gray" />
</Window.Resources>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="*" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Rectangle x:Name="North"
Grid.Row="0"
Grid.Column="1"
Margin="20"/>
<Rectangle x:Name="South"
Grid.Row="2"
Grid.Column="1"
Margin="20" />
<Rectangle x:Name="East"
Grid.Row="1"
Grid.Column="2"
Margin="20" />
<Rectangle x:Name="West"
Grid.Row="1"
Grid.Column="0"
Margin="20" />
<Grid Grid.Row="1" Grid.Column="1">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="2*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<TextBlock Text="X"
Grid.Row="0"
Grid.Column="0" />
<TextBlock Text="Y"
Grid.Row="1"
Grid.Column="0" />
<TextBlock x:Name="xValue"
Text="0"
Grid.Row="0"
Grid.Column="1" />
<TextBlock x:Name="yValue"
Text="0"
Grid.Row="1"
Grid.Column="1" />
</Grid>
</Grid>
</Window>
There’s nothing WPF-specific about this app other than the specific timer I use below, and the use of xaml in the window. If you code in Windows forms or another client technology, you should be able to port this pretty easily.
MainWindow.xaml.cs
Here’s the code-behind for the main window. First, I keep two private member variables, one for the joystick and one for the DispatcherTimer I use to poll the joystick (again, because I’m going with a gameloop-type approach rather than event-driven. In my other app, the joystick poll is actually one step in a big loop)
private AccelerometerJoystick _joystick = new AccelerometerJoystick();
private DispatcherTimer _timer = new DispatcherTimer();
The constructor simply does the usual initialization, and then wires up the loaded event. I tend to do significant work in Loaded events to avoid race conditions with controls not being ready yet, and to avoid exceptions in constructors. It’s not a bad habit to be in.
public MainWindow()
{
InitializeComponent();
Loaded += new RoutedEventHandler(MainWindow_Loaded);
}
The MainWindow_Loaded event handler is where I’m doing the dirty work. In here, I first initialize the joystick, then start the timer. The timer’s tick event is handled as an inline lanbda event handler. That handler polls the joystick, updates the display values and then sets the brush used by the rectangles based on whether or not the joystick reports that it is pointed in that direction.
void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
_joystick.Initialize();
_timer.Interval = TimeSpan.FromMilliseconds(PollingIntervalMilliseconds);
_timer.Start();
_timer.Tick += (s, ea) =>
{
// display position
xValue.Text = _joystick.RawXValue.ToString();
yValue.Text = _joystick.RawYValue.ToString();
Brush ActiveBrush = (Brush)this.Resources["ActiveDirectionBrush"];
Brush InactiveBrush = (Brush)this.Resources["InctiveDirectionBrush"];
if (_joystick.IsJoystickPointedSouth)
South.Fill = ActiveBrush;
else
South.Fill = InactiveBrush;
if (_joystick.IsJoystickPointedWest)
West.Fill = ActiveBrush;
else
West.Fill = InactiveBrush;
if (_joystick.IsJoystickPointedNorth)
North.Fill = ActiveBrush;
else
North.Fill = InactiveBrush;
if (_joystick.IsJoystickPointedEast)
East.Fill = ActiveBrush;
else
East.Fill = InactiveBrush;
};
}
Runtime
At runtime, the directions light up as expected, and the raw X and Y values are displayed in the middle. Now to use this in a real application :)
The source code and a video walkthrough of this will be available on windowsclient.net soon. I’ll update this post with the link once that happens.
[Update: Source and video here]