I have a home music system that uses Evation’s irman to
provide remote control of song selection. That lets me listen upstairs and control
the music with a simple remote control.
I’m building a replacement to that system using C# (more on that in a few months),
and I can’t use the irman because then my main system wouldn’t work. It was either
order another irman, or get something different.
A couple of weeks ago, I ordered a Tira from
Home Electronics. Tira is like the irman, except that it connects via USB rather than
a COM port, and it supports both receiving and transmitting IR codes. That means you
could, if you wanted to, build your own macro program that would take a single IR
command and both control your computer and other IR-controllable devices. Home electronics
also makes the Ira-2, which does mostly
what the irman does.
The Tira showed up yesterday. You get the transceiver module hooked to a 6′ (ish)
usb cord, and a cute little baby CD with some software on it. Installation is easy
– plug in the cable, and point the new hardware wizard at the drivers. It installs
both a usb device and a virtual COM port, which makes it easier to control the device.
It also comes with a copy of Girder, which is
a program that lets you remotely control things on your computer. I installed it and
got Tira set up with it, but it was getting confusing results, and I wasn’t planning
on using Girder anyway, so I dove into the custom API. It’s a fairly typical C-Style
API with an accompanying DLL, so I dusted off my P/Invoke skills,
turned on some music, and got to work.
The interface is really straightforward. You need to init the library, tell it which
COM port to listen on, and then register a callback that will be called when their
is data available. I got those definitions in, fired it up, hit the button on my TiVo
remote, and hit a breakpoint in the callback. That took about 15 minutes total.
Encouraged by my success, I next worked on decoding the event data. The callback passes
as a parameter a pointer to a 13-byte string that identifies what button you pressed
(think of it as a digital fingerprint that identifies the key uniquely). I defined
that as an IntPtr since IIRC, the runtime doesn’t like marshalling strings in callbacks.
A bit of unsafe code let me copy this to a byte array, and we were off an running.
Start up the code, hit the remote button, and a nice 26 character string (the hex
values of the data) shows up in the console window. Keep doing it, and it works fine…
and then stops working. Hmm.
Add in some code to number the items. Try again. Each time, it writes out 35 items,
and then stops. Stop. Think a bit. There’s something familiar here, something about
delegates and p/Invoke. Ah… When you pass a delegate to an unmanaged function, the
runtime has no way of knowing what the unmanaged function does with it, so it assumes
that it doesn’t store it (the other assumption would mean that delegate never got
free’d). In this case, that assumption is wrong, and after a short bit of time (35
iterations in my case), the GC merrily collects the delegate, the tira thread dies
when it tries to call it, and things stop working.
The fix is easy – just store the delegate in a place where the GC can find it (a member
variable works well), and things a great. I haven’t gotten around to writing a nice
C# wrapper around it, but I’ll post when I get that done.
If you want to retransmit, you have to capture through a separate, more complicated
interface, since the 13-byte value doesn’t give the system enough info to reconstruct
the correct IR signal.