Scott Hanselman

The Weekly Source Code 57 -Controlling an Eagletron TrackerPod with C# 4, ASP.NET MVC and jQuery

November 22, '10 Comments [12] Posted in Channel9 | Hardware | Open Source | Remote Work | Source Code | VS2010
Sponsored By

LifeCam mounted to an Eagletron TrackerPod I have a 42" HDTV in Seattle that's hooked up all the time as an "Embodied Social Proxy." It's a portal between the Microsoft Redmond campus and my house here in Oregon. I've blogged about Embodied Social Proxies before as well as shot video of one for Channel 9. It's called the "HanselCart" around work, although recently it's stopped being a cart and now it's a whole office that folks in Building 42 can drop by and see me, er, the Virtual Me.

One of the things that hasn't been 100% smooth is that while the LifeCam Cinema HD 720p is a nice camera, I can't MOVE it. I have to ask folks to move it for me, which is a slight irritant.

I'm getting ready to head up to Seattle for a meeting. While I was packing I found this TrackerPod motorized WebCam pan/tilt/zoom in my junk closet. I must have purchased it a long time ago and forgotten. I drilled a hole into the metal base of the LifeCam Cinema HD and superglued it while half-threading it on the TrackerPod's standard tripod-style male screw.

It's late, but I figured it I was going up to Seattle tomorrow, maybe I could hack something together quickly with this device and take it with me. There's a Custom Programming API page on the TrackerPod site with a TrackerPod COM Client.

This is what I built in action:

Here's how I did it in 40 minutes. First, I made a new ASP.NET MVC 3 web project, keeping the default template. This is quick and dirty, right?

Yes, I used a <TABLE>, sue me.

image

Here's the complete Razor View. I knew that I'd want a bunch of buttons to move the camera, and I assumed I would use jQuery to make an AJAX to the server side running ASP.NET MVC. I'm using the latest jQuery 1.4.4 and I'm getting it from the updated Microsoft cookieless CDN (Content Delivery Network.)

Rather than making a complex switch statement for the different buttons or different event handlers, I decided to use arbitrary HTML5 data attributes. Each INPUT Button has attributes like data-xvalue and data-yvalue.

There's one Click() handler hooked up to all Buttons. It gets the values of those data attributes, then POSTs the data to the Move method of the Home Controller.

@{
View.Title = "Home Page";
}
<script src="•http://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.4.4.min.js"
type="text/javascript"></script>
<table border="0">
<tr>
<td></td><td>
<input type="button" value=" up " name="up"
data-xvalue="0" data-yvalue="-10" data-method="0" />
</td><td></td>
</tr>
<tr>
<td>
<input type="button" value="left" name="left"
data-xvalue="10" data-yvalue="0" data-method="0" />
</td>
<td>
<input type="button" value="home" name="home"
data-xvalue="0" data-yvalue="0" data-method="1" />
</td>
<td>
<input type="button" value="right" name="right"
data-xvalue="-10" data-yvalue="0"
data-method="0" />
</td>
</tr>
<tr>
<td>
</td>
<td>
<input type="button" value="down" name="down"
data-xvalue="0" data-yvalue="10" data-method="0" />
</td>
<td>
</td>
</tr>
</table>
<script type="text/javascript">
//<![CDATA[
$(document).ready(function () {
$('input').click(function (event) {
var target = event.target;
x = $(target).data('xvalue');
y = $(target).data('yvalue');
m = $(target).data('method');
$.post("/Home/Move", { x: x, y: y, method: m });
}
);
});
//]]>
</script>

In the Home Controller, there's a method called Move(int x, int y, int method) where method is the way to move the camera - relative is 0 or absolute is 1. That's part of the camera's calling convention.

using TRACKERPOD_DUAL_COMLib;

namespace TrackerPodWeb.Controllers
{
public class HomeController : Controller
{
private dynamic cam = MvcApplication.myCameraInstance;

[HttpPost]
public void Move(int x, int y, int method)
{
cam.x = x;
cam.y = y;
cam.move_method = method;
cam.move();
}

public ActionResult Index()
{
return View();
}
}
}

See that dynamic object? That was the part that blew me away. I'm so used to COM Interop being a freaking nightmare from .NET that I spent most of the time messing with COM Interfaces and Type Libraries and exceptions when I realized that C# 4 was supposed to fix all that.

Like I've been saying about Razor - "stop thinking about syntax and just use it." - the same applies to COM interop in .NET 4 (Remember that the Visual Basic guys have have this nice experience for years...that's why VB is such a popular business automation language.)

Just use the dynamic keyword and start calling COM methods. Seriously, it just worked. I was copy/pasting code from the TrackerCam's VB6 (yes Visual Basic 6) samples into C#4 and other than a few semicolons, it was working directly!

Here's my Web Application's startup code:

public static dynamic myCameraInstance { get; set; }

protected void Application_Start()
{
//snip the MVC init stuff...
myCameraInstance = new TrackerPod();
myCameraInstance.app_name = "hanselcam";
myCameraInstance.initialize();
}

Here I hang on to the COM object for the camera as a poor man's singleton for use elsewhere. I should probably put guard-code around this to make sure it doesn't disappear or something but it's working so far. It should be a proper singleton I think.

Then I use that instance in my HomeController and call the COM methods in Move(). ASP.NET MVC takes care of the binding from jQuery to the Action Method, and .NET 4, C# and the DLR take care of the call into the COM TrackerCam stuff.

HTML5+jQuery -> ASP.NET MVC -> C# 4 dynamic keyword -> DLR COM Binder -> COM Library = It just works.

There's some HTML5 attributes, five lines of JS here and basically four lines of COM interop on my Move() method.

Now I'll be able to control my Seattle WebCam from Oregon. I may make it so I can control it from the Office Communicator Lync chat client or something. It'd also be nice if someone wrapped up the TrackerPod as a nice C# library and put it on CodePlex.

I'll add that to my ToDo list, or perhaps you will, Dear Reader. ;)

Related Links

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.

facebook twitter subscribe
About   Newsletter
Sponsored By
Hosting By
Dedicated Windows Server Hosting by ORCS Web
Monday, November 22, 2010 9:59:05 AM UTC
Educational, simple, entertaining and useful.
Only scott can get all these together in one blog post.
I really enjoyed it, Thanks!
Mohamed
Monday, November 22, 2010 10:17:34 AM UTC
I think you should connect a kinect to it so that you can control or the camera people from your end;-).
Monday, November 22, 2010 4:02:53 PM UTC
Scott,

I think it would be cool if you could just hold a Windows Phone in your hand and control the camera from there, don't you?  Would be pretty easy to port this to windows phone, if you setup the MVC web app exposing some REST based APIs on your home computer, have the Windows Phone call those and then have your home computer do what its currently doing just using those REST based method calls instead of buttons!  Easy cheezy!

Of course this would be a Deploy your own App type of setup and would never go up to the Marketplace but I think it would be damn cool
Monday, November 22, 2010 7:52:06 PM UTC
Why did you have to waste time , you could have just brought a logitech sphere/orbit or any other security hd camera.....
did obush
Monday, November 22, 2010 8:18:59 PM UTC
It's great to see that there's now a cookieless CDN domain for serving jQuery from, but the actual number of bytes served for those assets is still 25% more than the exact same assets served from Edgecast or Google (as described on the Downloading jQuery page). I briefly dug into it before posting this, and it appears that the gzip compression level is incredibly poor. Recompressing the HTTP response with a higher gzip compression level eliminates that tremendous 25% difference, reducing the size from 34k to 27k.

Fire up Fiddler and check it out yourself here:

http://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.4.4.min.js
vs

http://ajax.googleapis.com/ajax/libs/jquery/1.4.4/jquery.min.js

Hopefully that will be fixed soon.
Tuesday, November 23, 2010 2:40:08 AM UTC
I am going to buy a TrackerPod^_^, thanks Scott for sharing the interest topic:)
Tuesday, November 23, 2010 8:30:13 AM UTC
Hi,

that was a great blog post, I was really enjoying it and thanks for the tip with the "dynamic" keyword. I`ll definitly check this out, cause I have to work with COM interop a lot recently and it`s a pain in the ass.^^

Best regards,
Ben
Tuesday, November 23, 2010 2:58:08 PM UTC
XNA project and use your xbox controller? Either way, "blah, blah, blah = It just works." is what i like to see. And it seems we've been seeing a lot more of that lately from you guys! Keep up the good work. BTW, your DBA on the latest this dev's life is the DBA from my friends company:) small world.
pat
Tuesday, November 30, 2010 6:21:38 PM UTC
"public static dynamic myCameraInstance { get; set; }"

Seeing the static and dynamic keywords both applied to the same thing makes me chuckle. In this situation they are not opposites, but it seems like they should be.
Friday, December 03, 2010 5:19:04 PM UTC
Fantastic idea

I love the Windows phone as a remote also
Friday, December 10, 2010 12:03:29 PM UTC
Thanks Scott - this might save my career :)

This + Skype's automation API looks like a solid combo.

I see TrackerPod's claims to have a built in web server that does what you built. Is that new? Or not up to snuff?

Also, do you have a solution for screensavers? Do you just turn off the screensaver? In my case, I'm "virtually available" for a large portion of the day, but not all day... and not at night. I'm struggling to keep that remote machine awake when I need it without having it awake 24x7 and burning pixels.

Thoughts?
Tuesday, January 04, 2011 10:37:11 PM UTC
Kelly - I turn off the screensaver and turn off the power policy. TrackerPod does have a webserver. I think it's lame so I did this instead.
Comments are closed.

Disclaimer: The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.