USB Programming with Ruby

I have a current side project of looking for a simple to construct build radiator that is reasonably affordable from a hobbyist’s point of view. I came across this blog post, that used a sub £10 USB device for displaying the state of monitors. Although good, the drivers only appear to work on linux (not on my mac!) but I decided to not let that stop me.

I spent a couple of hours yesterday reading up on USB, the options around ruby (my current project’s development language of choice) and then the source code for the linux drivers written in C++. Here’s proof that I managed to learn a few things:

Outlining some of the things that I learned:

  • Two ruby libraries exist for wrapping the generic libusb library that vouches to be cross platform (win, linux, mac). The first “ruby usb” is one that I couldn’t get working on my machine partly because I couldn’t configure it properly to read my installing libusb. I went for the libusb (for ruby) library. This proved easier as they already had a gem for it.
  • The generic USB interface is pretty well documented. This website gives a good overall introduction. The concepts translated into the library quite easily.
  • At the heart there are some simple steps, but if you plan on controlling a device, you need to know the special “byte” messages to send as instructions. I don’t have an electrical engineering background, but I’m guessing this is similar when they write device drivers. Having documentation helps (this is where looking at the device driver code helps).
  • The libusb gives some pretty reasonable error codes, and because my device simply does not respond, I didn’t have to worry so much about any other processes controlling it at the same time.

Generic steps for interacting with a USB device:

  1. Create a new USB context to discover all the devices
  2. Find the device you care about. For this device, the property idVendor is set to 0x1d34 for Dream Cheeky and the idProduct set to 0x0004). They also had some string descriptions you could use. For example, the manufacturer property returned "Dream Link" and the product returned "DL100B Dream Cheeky Generic Controller"
  3. Once you have the device you first “open” the device to get a “handle”. Your system may already have a reference to it. If so, you need to “detach the kernel driver” before claiming it. As I said, I didn’t need to although the linux driver has that code in it.
  4. With the handle, you can now send/receive information to it. The USB property “bmRequest” prepares the device for communication. This page really helped me understand more, although I simply followed what the linux driver did to know what values to set.
  5. Make sure you close the handle when you’re done.

I learned way more than I needed to about USB devices such as devices hosting several configurations (though only one at a time) and they have a concept of endpoints, but wasn’t particularly relevant. Debugging the device with irb was great fun as I could dynamically query the device as long as it was connected. I’ll write about the ruby code in a different blog post.

2 comments

  1. Lars Kanis

    Thanks for the post. It’s nice to see, that people are using the library for highly important things :). I was also impressed how easy it is to speak with USB devices through libusb. So my aim in writing that ruby gem was to bring that easiness to more users. Fine if it worked!

    Since I didn’t test the library on OSX, did you do any special steps to bring ruby with libusb to run?

  2. Patrick

    I’m looking for more devices to use the library with 🙂 The gem worked really well for me. I struggled with rvm, gemsets and getting ruby usb to talk to the libusb gem that you wrote. I went with that because it didn’t seem to cause me any problems. I’d also like to thank you for writing excellent and easy to follow documentation. Seeing the example code was very helpful.

Leave a Reply