The Weekly Source Code 57 -Controlling an Eagletron TrackerPod with C# 4, ASP.NET MVC and jQuery
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.
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
- VIDEO: Hanselminutes on 9: Embodied Social Proxies (and Remote Video Heads!) with Microsoft Research
- YOUTUBE VIDEO: Embodied Social Proxy: Connecting Hub-and-Satellite Teams
- Virtual Camaraderie - A Persistent Video "Portal" for the Remote Worker
- Buy a TrackerCam
- Full Disclosure: I took a second just now and got an affiliate code for a TrackerCam because it was easy to use this camera. I bought the TrackerPod mount myself years and found it in my closet tonight. I don't know anyone at Eagletron.
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.
About Newsletter
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
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.
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
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.
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?
Comments are closed.
Only scott can get all these together in one blog post.
I really enjoyed it, Thanks!