Contents
Introduction
As all we know, there is no support for accelerometer in Windows Phone 7 Emulator. If you are creating some accelerometer bases application, you have to deploy it to your Windows Phone powered device and then test it. Of course, there are projects which are using some hardware such as Wii or XBox 360 controller to emulate mobile device movement. But when you trying to use it occurs that they have one weak point - they needs hardware! Some day, when I had more free time, I decided to find some solution for this. After a couple of hours, I have found one and this is it... I want you to introduce WP7 Accelerometer Emulator project. Let's take a closer look at what it is.
Accelerometer force vectors. Source: The windows Phone Developer Blog
How It Works? (End User Perspective)
Solution consists of three projects which are connected each other:
MaciejGrabek.WP7Accelerometer
- This is the core library to use in your projects. It contains WP7AccelerometerProvider
class which provides all functionality given by real Accelerometer from Microsoft.Devices.Sensors
, i.e., Start
and Stop
methods and ReadingChanged
event which provides information about gravity for each axis. There is no IAccelerometer
interface to implement, so I simply copied names of all used items from accelerator class manifest.
MaciejGrabek.WP7AccelerometerEmulatorUI
- This project allows you to emulate position of phone by simple use of three scrolls for X, Y and Z rotation. It sends all required data to Proxy.
MaciejGrabek.WP7AccelerometerEmulatorProxy
- This proxy is used to receive data from UI and allows to read it from WP7AccelerometerProvider
.
If you are running your application on emulator, then WP7AccelerometerProvider
is trying to read data from Proxy - if something goes wrong on retrieving this data (for example, proxy is unavailable), it simply switches into random data mode. You can retry receiving data from proxy by simple use of Stop
and Start
methods. Another way is to restart application. Of course, if you will run the application on device it won't use proxy, but it will provide you hardware built in accelerometer data.
How To Use It?
It is very simple - like using real accelerometer.
When you create WP7 application, the first thing you need to do is add a reference to MaciejGrabek.WP7Accelerometer
library (or project). Then create an accelerometer object like below:
using MaciejGrabek.WP7Accelerometer;
var acc = new WP7AccelerometerProvider();
Now simply add handling to AccelerometerProviderReadingChanged
event and consume data provided by WP7AccelerometerReadingEventArgs
class:
acc.ReadingChanged += OnAccelerometerProviderReadingChanged;
private void OnAccelerometerProviderReadingChanged
(object sender, WP7AccelerometerReadingEventArgs args)
{
ProcessAccelerometerData(args.X, args.Y, args.Z, args.Timestamp);
}
Start WP7AccelerometerProvider
and be ready to get the data.
try
{
acc.Start();
}
catch (Exception exc)
{
txtblk.Text = exc.Message;
}
This is all you need to do in the application. Now let's see the control panel. To do this, you need to start MaciejGrabek.WP7AccelerometerEmulatorUI
and MaciejGrabek.WP7AccelerometerEmulatorProxy
(it should start automatically with MaciejGrabek.WP7AccelerometerEmulatorUI
, but if not be sure to do this manually - otherwise you will get random data). Run your WP7 application on emulator and on MaciejGrabek.WP7AccelerometerEmulatorUI
mark "Send data to emulator" checkbox and have fun! If you will leave unchecked "Send data to emulator" AccelerometerProvider
will simply read what is on proxy without any change on interface. Result is shown below:
As you can see, it is very simple and intuitive to use.
For the end of this part, let's compare Microsoft.Devices.Sensors.Accelerometer
and MaciejGrabek.WP7Accelerometer.WP7AccelerometerProvider
:
Accelerometer |
WP7AccelerometerProvider |
public SensorState State { get; }
|
public SensorState State { get; }
|
public event EventHandler ReadingChanged;
public DateTimeOffset Timestamp { get; }
public double X { get; }
public double Y { get; }
public double Z { get; }
|
public event EventHandler ReadingChanged;
public DateTimeOffset Timestamp { get; }
public double X { get; }
public double Y { get; }
public double Z { get; }
|
public void Dispose();
|
public void Dispose();
|
public void Start();
|
public void Start();
|
public void Stop();
|
public void Stop();
|
As you can see, both classes are very similar. There is no interface to implement (for example IAccelerometer
) so the only way was to create "similar" class. This is all that is important for the "end user" of this emulator. Now let's see how it really works. :)
Under the Hood
First of all, I should explain a little bit more about data flow in this project. As you can see below, the main communication is one way. It means that all that is important for UI project is to send data to the proxy. From other side, the WP7AccelerometerProvider
is responsible to read this data from proxy or from device (which has higher priority).
Proxy
As a proxy, I decided to use WCF service that allows to get data using simple "HTTP GET". Here is how service contract and its implementation look.
[ServiceContract(Namespace = "http://maciejgrabek.com/WP7AccelerometerEmulatorProxy")]
public interface IAccelerometerEmulatorProxy
{
[OperationContract]
[WebGet]
string GetData();
[OperationContract]
void SetData(float x, float y, float z);
}
[AspNetCompatibilityRequirements(RequirementsMode =
AspNetCompatibilityRequirementsMode.Allowed)]
public class AccelerometerEmulatorProxy : IAccelerometerEmulatorProxy
{
public string GetData()
{
AccelerometerVector v = HttpRuntime.Cache
[AccelerometerVector.CacheKey] as AccelerometerVector;
if (v != null)
{
return v.ToString();
}
else
{
return AccelerometerVector.Empty.ToString();
}
}
public void SetData(float x, float y, float z)
{
AccelerometerVector v = new AccelerometerVector()
{
X = x,
Y = y,
Z = z
};
HttpRuntime.Cache.Remove(AccelerometerVector.CacheKey);
HttpRuntime.Cache.Add(AccelerometerVector.CacheKey,
v,
null,
DateTime.MaxValue,
Cache.NoSlidingExpiration,
CacheItemPriority.Default,
delegate { });
}
}
The important thing is to use [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
and [WebGet]
to enable this kind of communication with WCF service.
As you can see, to store data from UI, I am using built in cache because it is quick and efficient. The whole logic is not any kind of rocket science, because I followed the "Keep It Simple" rule. :)
UI
On MaciejGrabek.WP7AccelerometerEmulatorUI
, there are some XNA classes based calculations, which are required to get proper values of gravity force base vectors. 3D graphic computations are quite a different topic so I will let it for XNA fans. After all operations data is sent to Proxy where it is stored and ready to transfer to Windows Phone Emulator. All these calculations and data transfer is executed 10 times per second. If you want to change, there is method called InitializeAndRunTimer
where you can change frequency of timer, but I thing that 10Hz is quite ok.
private void InitializeAndRunTimer()
{
_timer = new DispatcherTimer();
_timer.Interval = TimeSpan.FromMilliseconds(100);
_timer.Tick += delegate
{
PerformSingleStep();
};
_timer.Start();
}
Core
This is the time to see the core library which is responsible for reading data from "proper" source. Of course, I am thinking about MaciejGrabek.WP7Accelerometer
and WP7AccelerometerProvider
class. As I mentioned before, it is similar to real Accelerometer definition. The most important thing is to recognize current environment. You can use in this case Microsoft.Devices.Environment.DeviceType
property and Microsoft.Devices.DeviceType
enumerator. If it is equal to DeviceType.Device
, then we are using Accelerometer, otherwise (DeviceType.Emulator
) we are initializing WebClient
to get data from Proxy. As an example, we can use Stop()
method implementation:
[SecuritySafeCritical]
public void Stop()
{
if (Microsoft.Devices.Environment.DeviceType == DeviceType.Device)
{
_accelerometer.Stop();
}
else
{
this._timer.Stop();
}
}
When you are creating WP7AccelerometerProvider
instance, you can specify two parameters. First of them is dataUrl
with default value http://localhost:9876/AccelerometerEmulatorProxy.svc/GetData
and the second one is refreshRate
which is set to 100 (it means 10Hz). Of course, there is no sense to set this value less than 33, because current display refresh rate is 33 times per second so even if we will read data, it won't be visible for user.
When I was testing this application, I found some caching problem on emulator or web server side. Even if the data was changed on server, there was no way to get new version of it. That was the reason to use little trick:
if (!this._dataUrlError)
{
if (!this._client.IsBusy)
this._client.DownloadStringAsync(new Uri(String.Format("{0}?{1}",
this._dataUrl, this._rand.NextDouble())), null);
}
When we add some random item to URL, then we can be sure that our request will return not cached data. If there is some problem with data reading from proxy WP7AccelerometerProvider
is using Random generator to get some data. Only way to retry reading from proxy is to call Stop()
and Start()
methods on WP7AccelerometerProvider
instance or simply restarting application.
Where To Find It?
I have attached code with binaries, but I will modify and extend it in future. If you want to get current version, you can simply download MaciejGrabek.WP7AccelerometerEmulator and use it in your projects. I will put all updates of code on this place so if you are interested follow project site. There is, of course, some demo to see exactly how to use it and it is attached to the provided solution.
What Will Appear in Future Versions?
- Sometimes, you can see Gimbal Lock effect - I am currently trying to solve it
- Add mouse control for rotation
- Add movement sequences (you can record movement like shaking, rolling, etc., save it, and quick run every time you need)
- Add additional acceleration for movement (shake, etc.)
Fell free to comment!
Useful Links