I haven’t been able to dedicate as much time to TuneControl as I’d like, but I managed to work on it here and there over the past few weeks. My focus has been on developing the firmware, and this is a new approach for me. Usually I build the hardware first.
It’s early days yet, but there has been some progress.
RTOS Selection
Instead of writing to the bare metal, I decided to go with a full-featured RTOS. This saves considerable effort that would otherwise be spent integrating a network stack.
I was originally looking at Contiki, but after playing with it a bit, I decided it’s not the platform I want. The primary issue is that it uses protothreads for multitasking. Protothreads obfuscate the flow of control; I prefer true preemptive multitasking if I can get away with it.
Enter NuttX.
NuttX strives for compliance with various standards, including POSIX. It features true preemptive multitasking, BSD sockets, IPv4 and IPv6 stacks, POSIX threads, and more. It’s a bit heavier than many alternatives, but not horribly so.
As a first step in the process of evaluating it, I patched the NuttX TAP driver both to work correctly under Linux, and to support bridge devices. This patch has since been merged to master, and should be in the latest NuttX release. The bridge support allows me to put a simluated NuttX instance directly on the network so it can interact with iTunes.
NuttX does have a learning curve, but not a terrible one if you’re familiar with the Linux kernel configuration system. It was fairly simple to set it up with a telnet server and the NuttX shell. From there I was able to start writing real code.
I might put a “getting started” guide together at some point; it needs one.
Multicast DNS
One thing that NuttX lacks is support for Multicast DNS (also known as Bonjour). This is a requirement for Apple’s DACP protocol; the device has to publish TXT records that iTunes will use in the pairing process.
I started with Apple’s own mDNSResponder, but gave up quickly. The code is a mess, and porting would be much more work than it’s worth. Avahi was also ruled out due to the size and complexity of the codebase.
In the end, my research led me to tinysvcmdns, which is a little BSD-licensed mDNS service publisher. It lacks the code required to actually make multicast DNS queries, but it’ll publish records just fine. The code is also reasonably clean, which is a bonus.
The only changes I had to make to get it working on NuttX involved
replacing a missing pthread_set_detachstate
API call with an
alternative, and swapping out the IP multicast socket setup code with
a call to a function from NuttX’s network utilities.
It was surprisingly easy.
A Fake Web Server
When iTunes pairs with the device, it will first query mDNS for the
service configuration. Then it makes an HTTP request back to the
device (on the port specified via mDNS) to pair with it. This is a
simple process; as long as the web server returns a 200
status code,
the pairing will succeed.
I took a look at both of the web servers included with NuttX, but neither were what I was looking for. Instead I implemented a “fake” server; no matter what request you send it, it will send a response appropriate for a successful pairing. The request can even be invalid; it doesn’t care, because it mostly ignores it.
I eventually want to replace this with an actual web server, but that’s not required for the device to work.
DACP Client
Apple’s DACP protocol allows you to remotely control iTunes (and the AppleTV, though I haven’t yet tested that theory). You can use it to perform basic player functions (play, pause, skip tracks, etc.), as well as to retrieve album art, select and manage playlists, and more. This looks like an ideal mechanism for a wireless device designed specifically to control iTunes.
If you want to see what it’s capable of, simply download the Apple Remote app to your iPhone. This is the protocol it speaks.
Unfortunately, at least as far as I’ve been able to find, DACP isn’t very well documented. My research turned up only one page that gave any real detail. Fortunately, there are also a number of people who have already written code to speak this protocol, so that made things a lot easier.
What little free time I had over the past couple of weeks was spent implementing a client in C.
Once you’re past the binary response format (which itself isn’t that complex) it’s fairly straightforward. Using the components mentioned above (plus a simple HTTP client) I was able to pair my simulation with iTunes and issue various commands. The following have proven to work:
- Pause
- Stop
- Play
- Next Track
- Previous Track
It also waits for player status updates and displays them as they arrive, including the current track name. This is just a simple text interface since I don’t yet have hardware to play with, but works very well.
Unfortunately, I also ran into a bit of a snag: I can’t set song ratings.
It turns out that DACP, for some functions at least, appears to require certificate-based cryptographic client authentication. How this works is unclear; the more recent versions haven’t been reverse engineered yet. That makes this a non-start, so I’m probably back to a custom desktop app to support at least the extended feature set.
I’m putting that aside for now; things are far enough along that I’m comfortable that I can make NuttX do what I want (either via DACP or a custom bridge app, depending on what I decide in the end).
Next Steps
Now that I have an idea of how the firmware should work, I need to look at the hardware. Primarily, that means deciding what kind of interface I want on it, and what features it should have. This is probably going to take a couple of weeks to figure out (mostly because I’m busy at work).
On the input side, it’s mostly down to two questions: what to do about the volume control, and whether or not to put enough buttons on there to support a menuing system for configuration. The alternative to the latter would be to use a desktop app to configure it, which may be an option.
I’ll deal with that later, though; I’m much more interested in the display right now.
I picked up a development kit for a small electrophoretic display (otherwise known as electronic paper), so that’ll be my next target for tinkering. I want to connect it up to an Arduino and see how it performs. The refresh rate might be an issue for what I want to do; a more standard OLED display may be necessary if it isn’t acceptable.
That should be a lot of fun.