If you haven’t seen the video demo in my last post, I suggest you watch it first to get a better understanding. So in this post we are going to see how the WCF service, the .NET Compact Framework application running on HD2 and the Silverlight application running on the Windows Phone 7 emulator work together.
The Objective
We are going to write three applications, a simple WCF service hosted in a windows console application which implements two functions, one called UploadAccelData() and the other DownloadAccelData(). A .NET Compact Framework application for HD2 which calls the WCF services’ UploadAccelData() function periodically and passes it the x, y and z components of the acceleration vector. A silverlight application for Windows Phone 7 which calls into DownloadAccelData() periodically and gets the accelerometer data off it.
For those of you who want to jump straight into the code, you can download it from the following links,
Download ConsoleApp.zip – VS 2008 solution which contains two projects. One, the console application which hosts the WCF service and two the Compact Framework application which runs on HD2
Download WCFClient_WP7Emu.zip – Windows Phone 7 application which runs on the emulator
Creating the WCF service
I will be using Visual Studio 2008 for creating the service and the .net cf application. Open VS 2008 in administrator mode and create a windows console application in Visual C#. It is important that you start visual studio in administrator mode otherwise you might get an error while deploying the service depending on your access rights on the machine. Now lets add a WCF service which will be hosted by this console application. Right click on the project and select “Add –> New Item..”. In the next dialog select “WCF Service” and give a name to it. I call it “AccelerometerService”.
With this three files will be added to your project AccelerometerService.cs, IAccelerometerService.cs and App.Config. Open App.Config and change a few things in it, first change binding="wsHttpBinding" to binding="basicHttpBinding" since .net compact framework only supports basicHttpBinding. Next change the BaseAddress from,
<add baseAddress="http://localhost:8731/Design_Time_Addresses/ConsoleApp/AccelerometerService/" /> to
<add baseAddress="http://192.168.2.2:8731/ConsoleApp/AccelerometerService/" />
Again, it is important that you change “localhost” to the ip address of your machine. This is because we will be using this address from the compact framework application to refer to the service, so we need the address of the machine where the service is running. Using “localhost” from the compact framework application would mean a totally different thing. Here I use 192.168.2.2 which is the IP address assigned to my machine by the wireless router that I am using. Be sure to change this IP address depending on your machine setup.
Open Program.cs and add the following code in the Main() function,
static void Main(string[] args)
{
ServiceHost accelService = new ServiceHost(typeof(AccelerometerService));
try
{
accelService.Open();
Console.WriteLine("Accel service is running at:{0}", accelService.BaseAddresses[0].AbsoluteUri);
Console.WriteLine("Press <ENTER> to terminate the service..");
Console.ReadLine();
}
catch (Exception ex)
{
Console.WriteLine("An exception occured while creating the serivce. {0}", ex.Message);
accelService.Abort();
}
}
Here we are creating an instance of our AccelerometerService and calling Open() on it to start the service. The address where the service is running is output to the console and the service waits for any key to be pressed after which it will be terminated. Now build and run your project, you should see this console window,
Now to check our service, open internet explorer and browse to the base address that we entered above,
http://192.168.2.2:8731/ConsoleApp/AccelerometerService/
and you should see this,
If your browser is showing the same thing then great! We are progressing well.
Now lets implement our upload and download functions. Open IAccelerometerService.cs and add the following declarations to the interface,
[OperationContract]
void UploadAccelData(int x, int y, int z);
[OperationContract]
void DownloadAccelData(out int x, out int y, out int z);
To keep things simple I am passing only the x, y and z components of the vector, other information from the accelerometer like Roll and Pitch are ignored for simplicity, though adding those shouldn’t be rocket science. We will be implementing these functions in our AccelerometerService class. Open AccelerometerService.cs and add the following member variables to the AccelerometerService class,
static int _accelX;
static int _accelY;
static int _accelZ;
and add the following function definitions to the same class,
public void UploadAccelData(int x, int y, int z)
{
_accelX = x;
_accelY = y;
_accelZ = z;
Console.WriteLine("UploadAccelData: X:{0}, Y:{1}, Z:{2}", x, y, z);
}
public void DownloadAccelData(out int x, out int y, out int z)
{
x = _accelX;
y = _accelY;
z = _accelZ;
}
This completes our console app and the WCF service that it will be hosting.
Creating the Compact Framework Application for HD2
Now lets go ahead and add a smart device compact framework C# application to the same solution in VS 2008. Right click on the solution and select “Add –> New Project..”. In the Add New Project dialog select Smart Device under Visual C# and add a new project, WCFClient_HD2.
in the next screen select “Windows Mobile 6 Professional SDK” as the target platform and “.NET CF Version 3.5”, choose Device Application and press OK.
I have designed the form to show the x, y and z values from the accelerometer and a button to start and stop the sensor. Also, add a timer which fires every 100ms and is initially disabled.
Now to this project, we will add a reference to the WCF service. First make sure that the service is running, deploy the ConsoleApp project. Right click on WCFClient_HD2 and select “Add Web Reference..”. In the next dialog, enter the URL of the service and press Go.
If everything goes well, our AccelerometerService should be found and you will be presented with this dialog,
give a name to the web reference, in this case “AccelerometerService” and select “Add Reference” button. Now the functionalities offered by the AccelerometerService are available to our compact framework application. Next, we need to instantiate this service and use it to call the UploadAccelData() function.
Open Form1.cs and add a member variable to the Form1 class which refers to the WCF service,
WCFClient_HD2.AccelerometerService.AccelerometerService accelService =
new WCFClient_HD2.AccelerometerService.AccelerometerService();
I am going to go a bit off topic here and talk about the Accelerometer implementation on HD2. HTC does not provide an SDK for it’s devices (which is really sad) so the functionalities like Accelerometer, Proximity sensor and other custom features are not exposed to others for use. So some folks had to find out the library, reverse engineer the implementation and figure out how to use these features. The accelerometer driver is implemented in a dll called HTCSensorSDK.dll, this dll also implements a few other sensors as well. To use the accelerometer from a native application you’ll have to load this library [using LoadLibrary()], get pointers to the exported functions and call them. And from a managed application you’ll have to P/Invoke. Take a look at this post for more information on this.
Managed code to access the accelerometer on HD2 is available online and I am using the same code in this project here. Just go ahead and add HTCSensor.cs file to your project. This file implements HTCSensor class which you can use to read data from the accelerometer. The class implements StartSensor(), ReadDataFromSensor() and StopSensor() functions. Go back to Form1.cs and add two more variables to the class,
HTCSensor htcSensor = new HTCSensor();
HTCSensorData htcSensorData;
Create handlers for the Start Sensor button and uploadTimer,
private void button1_Click(object sender, EventArgs e)
{
Button btn = sender as Button;
if (btn.Text.Contains("Start"))
{
htcSensor.StartSensor();
uploadTimer.Enabled = true;
btn.Text = "Stop Sensor";
}
else
{
htcSensor.StopSensor();
uploadTimer.Enabled = false;
lblXAxis.Text = "???";
lblYAxis.Text = "???";
lblZAxis.Text = "???";
btn.Text = "Start Sensor";
}
}
and,
private void uploadTimer_Tick(object sender, EventArgs e)
{
htcSensor.ReadDataFromSensor(out htcSensorData);
lblXAxis.Text = htcSensorData.tiltX.ToString();
lblYAxis.Text = htcSensorData.tiltY.ToString();
lblZAxis.Text = htcSensorData.tiltZ.ToString();
}
The button handler controls the timer and starts and stops it. And whenever the timer fires, we read the data from the accelerometer and display it. Run this application on HD2, if you have one, and check if the values are being read from the sensor. I am guessing that this application should also work on HTC Touch Pro2 and HTC Diamond 2 as well, but I have not tested this on those devices. Now all that is left is hooking this with the WCF Service, so every time the timer function is called we call the UploadAccelData() function of the service and pass the x, y and z values to it. Add the following lines of code to the end of the uploadTimer_Tick() function,
try
{
accelService.UploadAccelData(htcSensorData.tiltX, true,
htcSensorData.tiltY, true,
htcSensorData.tiltZ, true);
}
catch (Exception ex)
{
Console.WriteLine("An exception occured while calling into the service.");
Console.WriteLine("{0}", ex.Message);
}
And that is all, our compact framework application is ready! Lets test whether the values are being passed to the service. Go to the solution properties and select multiple start-up projects and make sure that both the ConsoleApp and WCFClient_HD2 are set to start,
Press F5 to start both the console and the compact framework application. Make sure that you are deploying the compact framework application to the device and that the device is connected to the machine over Active Sync (Windows XP) or WMDC (Vista or Win 7). Once both applications are deployed press the “Start Sensor” button on the phone, the accelerometer data should be getting passed to the service and the console window should output the logs as follows,
Creating the Silverlight application for Windows Phone 7
Start Visual Studio 2010 Express for Windows Phone and create a new Silverlight Windows Phone project, WCFClient_WP7Emu.
Add text blocks and a button to Mainpage.xaml. The application UI looks like this,
Now lets add to this project a reference to the WCF Service. Deploy the WCF service from Visual Studio 2008 and make sure that it is running, right click on the silverlight project and select “Add Service Reference..”, and in the next dialog enter the URL of the service and press Go,
once the service is discovered enter a namespace “AccelerometerService” and press OK to add a reference to it.
Open Mainpage.xaml.cs and add the following member variables to the Mainpage class,
WCFClient_WP7Emu.AccelerometerService.AccelerometerServiceClient accelService;
DispatcherTimer dt;
Now in the Mainpage() constructor add the following lines of code after the InitializeComponent() call,
accelService = new WCFClient_WP7Emu.AccelerometerService.AccelerometerServiceClient();
accelService.DownloadAccelDataCompleted +=
new EventHandler<AccelerometerService.DownloadAccelDataCompletedEventArgs>(accelService_DownloadAccelDataCompleted);
dt = new DispatcherTimer();
dt.Interval = new TimeSpan(0, 0, 0, 0, 100);
dt.Tick += new EventHandler(dt_Tick);
lblXAxis.Text = "???";
lblYAxis.Text = "???";
lblZAxis.Text = "???";
Here we create an instance of the WCF service and register a handler for the DownloadAccelDataCompleted event. In silverlight for windows phone the web service api’s will be called asynchronously, and when the web service function returns, accelService_DownloadAccelDataCompleted() will be called. So we will be getting the accelerometer values in this function. Also, create a dispatch timer which fires every 100ms. Pressing TAB while creating the handlers for DownloadAccelDataCompleted and dt.Tick event will also create the function definitions for the handlers,
void accelService_DownloadAccelDataCompleted(object sender, AccelerometerService.DownloadAccelDataCompletedEventArgs e)
{
}
void dt_Tick(object sender, EventArgs e)
{
}
Add a handler for the button “Start reading data from web service”,
private void button1_Click(object sender, RoutedEventArgs e)
{
Button btn = sender as Button;
if (btn.Content.ToString().Contains("Start"))
{
dt.Start();
btn.Content = "Stop reading data from web service";
}
else
{
dt.Stop();
btn.Content = "Start reading data from web service";
lblXAxis.Text = "???";
lblYAxis.Text = "???";
lblZAxis.Text = "???";
}
}
So again, this handler just starts and stops the dispatch timer. Now fill in the handlers for dt.Tick and the DownloadAccelDataCompleted() functions as below,
void accelService_DownloadAccelDataCompleted(object sender, AccelerometerService.DownloadAccelDataCompletedEventArgs e)
{
lblXAxis.Text = e.Result.ToString();
lblYAxis.Text = e.y.ToString();
lblZAxis.Text = e.z.ToString();
}
void dt_Tick(object sender, EventArgs e)
{
accelService.DownloadAccelDataAsync();
}
So everytime the dispatch timer fires, we call the DownloadAccelData() function (the asynchronous version is created when we add a reference to the WCF service). And in the DownloadAccelDataCompleted() function we update the text blocks to display the accelerometer values.
And we are done, phew! Time to test’em all. Go to visual studio 2008 and start both the ConsoleApp and WCFClient_HD2 projects, once deployed press the “Start Sensor” button on the device. Now start the silverlight application from Visual Studio 2010 and press the “Start reading data from web service” button, and if you did everything right the accelerometer data should be read on the emulator and you should be seeing something like this,
The other application which I showed in the video demo where a marble moved around on the screen of the emulator can now be easily implemented using the accelerometer values. I will not be going into the details of it. If you want to know how to implement one take a look at my other post where I have the source code of the application attached, the same logic holds here as well.
It’s been a really long post, if you have an HD2 and happen to try out the source code do let me know how it goes. And if you have any suggestions on how to make this code better please do leave a comment!