hacking a logic controls cr3003 usb cash drawer

I had this usb cash drawer for a point of sale system, and needed to trigger it to open. I assumed it would be a serial protocol as I saw the drivers mention something about COM port selection in windows, so I figured it would be easy enough to figure out and get working. Turns out that was not the case.

The device itself shows up in lsusb and gets detected with the following information:

$ lsusb
Bus 006 Device 013: ID 0fa8:b033 Logic Controls, Inc.

[299584.252035] usb 6-1: new low-speed USB device number 5 using uhci_hcd
[299584.427042] usb 6-1: New USB device found, idVendor=0fa8, idProduct=b033
[299584.427049] usb 6-1: New USB device strings: Mfr=1, Product=2, SerialNumber=0
[299584.427053] usb 6-1: Product: LCI_CR
[299584.427056] usb 6-1: Manufacturer: Logic Controls Inc
[299584.449247] hid-generic 0003:0FA8:B033.000A: hiddev0,hidraw3: USB HID v1.00 Device [Logic Controls Inc LCI_CR] on usb-0000:00:1d.0-1/input0

I probed around the hidraw interface a bit but finally gave up after not being able to transmit anything to it via pyusb without getting usb.core.USBError: [Errno 16] Resource busy errors.

So I broke down and loaded up a windows VirtualBox instance and installed the driver. It seems to emulate a COM port in some fashion, and then there is a utility to set what codes you want to use to open the cash drawer. So I snooped the usb data and found the control code for opening the drawer. Disconnected the usb device from virtualbox, and sent the data with pyusb, and sure enough it was working! I was all happy for a moment then decided to unplug the drawer plug it back in and make sure it still worked, but of course, no. After plugging it back in I got Resource busy errors again, and could transmit the open code. So I enabled usb in virtualbox again, and disabled it a few seconds later, and then I could open it again with libusb. So the driver seems to initialize the device in some way when it first talks to it.

So I then found some info on the web about how to capture data with the usbmon kernel driver and ended up doing the following:

tcpdump -i usbmon6 -w - -U > dump.out

Then connected the device in virtualbox, let the driver do it's initialization and then disconnect from virtualbox.

With that I had the raw usb data the driver used to initalize the device in dump.out. With some google searching I found this USB Reverse Engineering toolset which had a script to replay data from a capture. So I downloaded that library and ran the following:

cat dump.out | python ./usbreplay.py --vid 0x0fa8 --pid 0xb033

This produced some errors, but ultimately turned out to work. After doing that I could open the cash drawer with the following python code:

import usb.core
import usb.util

dev = usb.core.find(idVendor=0x0fa8, idProduct=0xb033)
if dev.is_kernel_driver_active(0) is True:
    dev.detach_kernel_driver(0)

cfg = dev.get_active_configuration()
intf = cfg[(0,0)]
epout = usb.util.find_descriptor(intf, custom_match=lambda e: usb.util.endpoint_direction(e.bEndpointAddress) == usb.util.ENDPOINT_OUT)
assert epout is not None
epout.write([0x31, 0x07])

I ended up wrapping all that uglyness up into a hack that does the job, but ugh. In my google searching I did find a device called "USB Trigger For Cash Drawer (Model: DT-100U)", which would have been a better solution (along with any old cash drawer with receipt printer interface). This is just a serial usb device, and when you send it data, it triggers the cash drawer via the commonly used receipt printer interface (24V).


Previous: Using qmail-autoresponder with qmailadmin Next: mpd in debian jessie using pulse audio