Introduction
The Windows Phone 7 emulator that gets installed when you download and install the developer tools does not have any way to access an accelerometer. Although you can add a reference to Microsoft.Devices.Sensors, you will not get any values during application execution. Since I have a Freescale badge that comes with an accelerometer sensor (among others) that works with the Sensor and Location platform in Windows 7, I decided to create a small Windows application that could ‘send’ accelerometer values to the emulator.
The Windows application that connects to the accelerometer (AccelerometerWCF solution)
In the Windows application, first thing I had to do was add a reference to the Microsoft.WindowsAPICodePack.Sensors.dll contained in the Windows API Code Pack for Windows 7 (a beautiful library that lets you access all Windows 7 goodness from managed code). This way I was able to hook to the accelerometer, and receive data reports at regular intervals. Let’s see some code on how.
1: ServiceHost svc;
2: Accelerometer3D acc;
3: public MainWindow()
4: {
5: InitializeComponent();
6: this.Loaded += new RoutedEventHandler(MainWindow_Loaded);
7: }
8:
9: void MainWindow_Loaded(object sender, RoutedEventArgs e)
10: {
11: SensorList<Accelerometer3D> sl = SensorManager.GetSensorsByTypeId<Accelerometer3D>();
12: acc = sl[0];
13: acc.AutoUpdateDataReport = true;
14: acc.DataReportChanged +=new DataReportChangedEventHandler(acc_DataReportChanged);
15: svc = new ServiceHost(typeof(Accelerometer3DService), new Uri("http://localhost:10000/Accelerometer3DService/"));
16: svc.Open();
17: }
18:
19: void acc_DataReportChanged(Sensor sender, EventArgs e)
20: {
21: Accelerometer3D acc = sender as Accelerometer3D;
22: Report.Accelerometer3D = new CustomAccelerometer3DReport()
23: {
24: AxisX_G = acc.CurrentAcceleration[AccelerationAxis.X],
25: AxisY_G = acc.CurrentAcceleration[AccelerationAxis.Y],
26: AxisZ_G = acc.CurrentAcceleration[AccelerationAxis.Z]
27: };
28: }
At the beginning, we declare an Accelerometer object. On MainWindow_Loaded we get the accelerometer sensor list (accelerometers that are attached to the current computer), and we assign the accelerometer object to the first element in the list, since we know that we have only one accelerometer attached to the computer. Then, we set the AutoUpdateDataReport to true, so the accelerometer sends a data report regularly, and then we hook to the DataReportChanged event (forget all the “svc” related lines for now).
In the event handler, we get a reference to the sender, and then set the X,Y and Z values to a static class (Report) variable of type CustomAccelerometer3DReport which will be reported to the emulator. Unsurprisingly, the class definition is the following
1: [DataContract]
2: public class CustomAccelerometer3DReport
3: {
4: [DataMember]
5: public float AxisX_G { get; set; }
6: [DataMember]
7: public float AxisY_G { get; set; }
8: [DataMember]
9: public float AxisZ_G { get; set; }
10: }
So, the question is, how to report the data to the emulator? The only way to “connect” this to the emulator is using web services, and the preferred option for that is WCF, since in this way we can use “Add Service Reference” in the Windows Phone project to create proxy objects for the service.
In the first code segment, you can see that we are creating a new ServiceHost object. This ServiceHost object is then initialized with a reference to the type of the service’s implementation and a communication Uri. As we can see in the app.config, the service is configured to use basicHttpBinding, since this is supported by Silverlight in Windows Phone 7. Implementation of the service is rather simple, too.
1: [ServiceBehavior(InstanceContextMode=InstanceContextMode.Single)]
2: public class Accelerometer3DService : IAccelerometer3DService
3: {
4: public CustomAccelerometer3DReport GetReport()
5: {
6: return Report.Accelerometer3D;
7: }
8: }
The most important thing to notice here is that the service is instantiated as a Singleton, so that only one instance of a service object is active, at a given point in time. The GetReport method returns a copy of the Report.Accelerometed3D property, an object that contains the X,Y and Z values. We’re ready!
The Windows Phone 7 Silverlight application (AccelerometerTest solution)
I created a very simple Windows Phone 7 Silverlight application that basically has 3 textboxes, which get updated at a regular time interval with the accelerometer’s values. I also used “Add Service Reference” to create the proxy objects to have the Windows Phone 7 application properly communicate with the Windows application hosting the service. Because I cannot “add an event” to the web service, I decided to create a timer (using the StoryBoard object), which gets updated every 200 milliseconds. At each update, it updates the 3 textblocks on the screen with the X, Y and Z values coming from the accelerometer. Full source code, so you can reuse it in your projects:
1: Accelerometer3DServiceClient client;
2: Storyboard timer;
3:
4: // Constructor
5: public MainPage()
6: {
7: InitializeComponent();
8: this.Loaded += new RoutedEventHandler(MainPage_Loaded);
9: }
10:
11: void MainPage_Loaded(object sender, RoutedEventArgs e)
12: {
13: client = new Accelerometer3DServiceClient();
14: client.GetReportCompleted += new EventHandler<GetReportCompletedEventArgs>(client_GetReportCompleted);
15:
16: timer = new Storyboard();
17: timer.Duration = TimeSpan.FromMilliseconds(200);
18:
19: timer.Completed += new EventHandler(timer_Completed);
20: timer.Begin();
21: }
22:
23: void client_GetReportCompleted(object sender, GetReportCompletedEventArgs e)
24: {
25: textBlock1.Text = e.Result.AxisX_G.ToString();
26: textBlock2.Text = e.Result.AxisY_G.ToString();
27: textBlock3.Text = e.Result.AxisZ_G.ToString();
28: }
29:
30: void timer_Completed(object sender, EventArgs e)
31: {
32: client.GetReportAsync();
33: timer.Begin();
34: }
Some important things to notice: First of all, I decided to use StoryBoard over DispatcherTimer for various reasons, some of them you can check here. I set the StoryBoard to a 200 milliseconds interval, and I “Begin” it again each time it “Completes”. Moreover, each time the Completed event is fired, I download (asynchronously) the accelerometer values from the web service, which then are shown to the three textblocks on the User Interface. Finally, for the sake of the argument, check a screenshot of the applications and the accelerometer running:
Download full source code here: http://cid-10e568adbb498dc8.office.live.com/self.aspx/Windows%20Phone%20Source%20Code/EmulatingAccelerometerWP7.zip