Tying In The Desktop
By this time, the core of the desktop app was already finished. Apart from some brief research on USB HID access in OS X, it was relatively simple to replace the serial code with something that spoke our new device’s language. Add a simple Objective-C wrapper around the IOHIDManager stuff from IOKit, and we’re set.
The only thing left was to make it a bit more usable. This meant turning it into a menu extra and adding some controls for adjusting the display brightness and contrast. Tack on some minor features such as displaying the track and rating in the dropdown menu, and it was pretty much done.
Having written a fair few Windows and Linux apps, I can honestly say that Cocoa is much more pleasant to work with than Win32 or X11. .NET and C# come close, but it’s not quite there. Objective-C and Cocoa make life easy.
The app itself is nothing fancy; it just listens for iTunes messages the same way the Arduino hack did, and passes them along to the device in a form it can understand. The only real change is that it sends the track and artist as well now so they can get displayed. Piece of cake.
As an afterthought, I also added a “sleep mode” to both the device and the desktop app. If iTunes is stopped, or paused for more than thirty seconds, the app will clear the title and artist on the device. When those fields are empty, the device turns the display and LEDs off to save power; there’s no need to have them on if we’re not displaying anything anyway.
Add in a “Start at Login” feature, and we’re done. Once I have final hardware I’ll make it pretty, but for now it’s functional, and that’s what counts.
Meeting Power Requirements
The final step in the whole thing was to verify that the device was within the parameters of the USB specification when it comes to power consumption. Runtime is easy enough; USB allows up to 500mA of current on most ports, provided that it’s requested in the device descriptor. We’re well within that; our device won’t consume more than about 150mA, so we request 160 to account for spikes, and we’re done.
Suspend/Resume, on the other hand, presents a more complicated issue.
When the USB bus suspends your device, you’re required to cut your current consumption down to around 2.5mA (there are exceptions, but this is the “safe” number). This isn’t a whole lot of current, really. I need to make sure my handy new toy isn’t going to draw too much current, so I pull out my trusty Fluke, plug it in, and suspend the Mac…
…and the device is happily drinking down a nice, constant 30mA of juice.
And so begins the optimization of the circuit. First, we deal with the firmware issues: the processor was never sent to sleep even if the USB bus suspended the device, so that was the first thing to fix, and relatively simple. Make a few adjustments, and we’re down to around 5mA, which is still too much.
Make sure the display is shut down when we’re suspended. Still 5mA. Hmmmm. It’s the only thing left that could be drawing that kind of power, so I start pulling jumpers – but pulling the display’s ground pin only shows a small drop in consumption.
It turns out that the display is just plain evil. When I pulled the ground pin, it managed to send the current out other pins that happened to be in a logic low state. Lovely! The end result was a transistor in series with the display’s power pin, and some firmware adjustments to tristate the data and control pins that drive the display. Problem solved.
Of course, that leaves us with one final thing: USB remote wakeup. In order to wake the computer up from a keypress, we have to be scanning the key grid. That means the processor has to be running (which is very costly power-wise at 24MHz). So we add a bit of code that re-clocks the processor down to 32KHz via the internal oscillator while in USB suspend mode, which allows us to scan the buttons but not do much else.
Final result: 330µA if remote wake-up is allowed; 230µA if it isn’t. Not too shabby, and well within the USB specification.
Of course, I still don’t have the remote wakeup working quite right. It detects the key presses, and it does wake the computer up – but, at least on the Mac, the computer only comes out of sleep for a brief moment before putting itself right back to bed. The displays never even wake up. I haven’t quite figured out why yet; it’s on my list. If anyone has any ideas, please let me know.