I am the web developer who likes Windows. Yes, the only one.
I’m working on software to communicate with USB-connected devices, and most of our customers are Windows users. I have to admit, the more I’ve learned about the Windows OS, the more I appreciate it. I know, how can I consider myself a programmer at all if I actually like Windows? Yes, it’s quirky, but those quirks were earned by committing to backwards compatibility and trying (keyword) to create a natural experience for regular users. It’s not like any particular flavor of UNIX has a huge following.
I like other systems, too.
That said, I would really like it if my application (or at least the lower layers of it) were available on more systems, so I investigated NPM for packages that enable USB communication via Javascript. There are quite a few, but node-hid stood out to me because it wraps the HIDAPI library I was already using in my C# application. Other wrappers exist, like pull-hid and hidstream, but this seemed to me to be the easiest to use.
Installing and using node-hid is easy.
I was pleasantly surprised that npm install node-hid
worked without problems. I had been conditioned by other packages with native Windows bindings to expect problems due to having Visual Studio 2013 installed instead of an older, more widely distributed platform. Many native bindings expect Visual Studio 2008 or older, but the node-hid contributors must keep their node-gyp skills sharpened.
Using the library is simple. require('node-hid')
returns an HID
class constructor and a devices()
function. If you know a particular device is already connected, you can open it by simply running dev = new HID(vendorId, productId, serialNumber)
. The serial number is optional, and you can also provide a system path. The devices()
function returns a list of device metadata objects that include vendor and product IDs, serial numbers, release numbers, and system paths. You can easily filter this list for a particular device family and pass the path property of a given device metadata object to the HID
constructor.
Closing devices is hard.
The problem I ran into is that, while HID devices are easy to open, they can be a pain to close. This comes down to an issue with how read threads are managed on different platforms at the native HID layer. On Mac and on Linux systems with libusb installed, the hidapi actively manages asynchronous read threads to prevent any problems when closing a device handle. On other systems, particularly Windows XP, this is not supported. The library should be available to everyone, and while Microsoft has stopped supporting XP, a lot of potential customers still use it.
The issue is that as long as a read thread is waiting for device data, the operating system can’t close the device handle. The node-hid library will go ahead and set the pointer to this handle to null when you call dev.close()
, but the handle is still there waiting for data from the physical device. Naturally, any further attempt to communicate with the device (calling dev.write()
for example) at this results in a null reference error, and Node crashes.
The solution
I got on the node-hid Google group to see who else had this problem, and I got some great advice from member Chris Teague, who pointed out that if you can get the device to send one last signal before closing, the read thread will exit and the device will close properly. Here’s what that code looks like:
1 2 3 4 5 6 7 |
var hid = require("node-hid"); var dev = new hid.HID(myVendorId, myProductId); dev.on("data", myListener); // ... Communication happening ... dev.removeAllListeners("data"); dev.write([EndPoint, 1, EndOfCommandToken]); // Makes device send an error message dev.close(); |
Basically, you have to remove the “data” event listener, then write any message to the device that will begin a response, and then close it. Trying other orders of operation didn’t work for me on all systems. This does.
Conclusions
HIDAPI is a great open-source C library for simplifying cross-platform HID communication. The HID device class is great because most systems will accept HID devices with no special drivers, meaning you don’t have to get a special Microsoft or Apple stamp to avoid “Unsafe driver detected” messages when your users try to connect to them. For Windows 8 and above, users won’t even see an option to “connect to the device anyway” without booting their system into a special mode. The node-hid package expands the usefulness of HIDAPI by making it easy to create and install multi-platform applications and services that help users connect to new devices. I think this will become more important in the future as the “maker revolution” expands and more single-purpose consumer devices come to market. Of course, the wireless Internet of Things may render some USB connections unnecessary, but I think there will always be a use for low-power wired communication.