From: Jeff R. <je...@ro...> - 2014-12-06 21:06:34
|
Darn, I forgot a helpful subject line. Sorry about that. Fixed now...though that probably means I broke the thread structure. I was able to re-test on a laptop running an older version of Linux Mint natively (no VM), and after a few compatibility tweaks, I got it to communicate correctly AND there is no weird delay anymore! I believe the issue may be specific to the VM, although I am really unsure why, and would love any insight anyone might have. I also need to do some more testing on a different machine with the latest Ubuntu, so that I can more confidently point at the VM environment as the cause rather than something else. In case anyone is interested, I've pushed the relevant code up to a Github repo: https://github.com/jrowberg/keyglove/blob/master/host/python/kglib.py#L335 If I learn anything more, I'll be sure to follow up. --Jeff On Sat, Dec 6, 2014 at 12:55 AM, Jeff Rowberg <je...@ro...> wrote: Hello PyUSB community! > > I'm trying to solve a raw HID write delay problem when using PyUSB 1.0.0b2 > with Python 2.7 in an Ubuntu 14.10 Linux environment, actually running > inside a VMware Player virtual machine on a Windows 8.1 64-bit desktop. I'm > communicating from the Python script to a Teensy++ configured in Raw HID > mode. I have been able to make this work perfectly in Windows using > PyWinUSB, and using the Teensy++ manufacturer's own RawHID C demo code, and > *almost* in Linux with PyUSB. In fact, I even get the data moving back and > forth over the IN and OUT interrupt endpoints, intact and in order. > > The problem is that there is a very measurable delay (usually between > 200ms and 500ms, varying) whenever I try to use the .write() method to send > data to the device. I receive it instantly whenever anything new comes in; > I've narrowed down the delay very specifically to the single line that > attempts to send data: > > self.pyusb_endpoint_out.write(raw_packet) > > The "raw_packet" variable is an array of bytes. The data is delivered > properly, and everything else works great on the device and in the response > that comes back, so I know it's being interpreted correctly. It just takes > way longer than it should. > > First, here's the string representation of the interface that I'm using: > > INTERFACE 0: Human Interface Device ==================== > bLength : 0x9 (9 bytes) > bDescriptorType : 0x4 Interface > bInterfaceNumber : 0x0 > bAlternateSetting : 0x0 > bNumEndpoints : 0x2 > bInterfaceClass : 0x3 Human Interface Device > bInterfaceSubClass : 0x0 > bInterfaceProtocol : 0x0 > iInterface : 0x2 Teensyduino RawHID > ENDPOINT 0x83: Interrupt IN ========================== > bLength : 0x7 (7 bytes) > bDescriptorType : 0x5 Endpoint > bEndpointAddress : 0x83 IN > bmAttributes : 0x3 Interrupt > wMaxPacketSize : 0x40 (64 bytes) > bInterval : 0x1 > ENDPOINT 0x4: Interrupt OUT ========================== > bLength : 0x7 (7 bytes) > bDescriptorType : 0x5 Endpoint > bEndpointAddress : 0x4 OUT > bmAttributes : 0x3 Interrupt > wMaxPacketSize : 0x40 (64 bytes) > bInterval : 0x1 > > I read data from 0x83 and send it out 0x4. Both are interrupt endpoints, > so I would assume this should be pretty straightforward. > > The Python script is fairly complicated, so I won't post the whole thing > here (though I will be happy to post it on Github if needed; that's where > it will end up, but I was kinda hoping to actually make it work first). > However, salient points about its functionality are these: > > 1. It's running in VM of Ubuntu 14.10, on a Win8 machine. I can try a > non-VM Ubuntu environment, but it will take some time to arrange that. > > 2. The script starts a separate daemon thread which constantly tries to > read from endpoint 0x83, timing out every second and retrying whenever > there is no data available. This approach seems to work perfectly, but I'm > wondering if it has some adverse effect on the ability to write data in a > timely fashion. The thread runs in a single handler function that looks > like this: > > # handler for reading incoming raw HID packets via PyUSB (thread > started in local connect() method) > def pyusb_read_handler(self): > while self.pyusb_endpoint_in != None and self.connected: > try: > ret = > self.devobj.read(self.pyusb_endpoint_in.bEndpointAddress, > self.pyusb_endpoint_in.wMaxPacketSize) > if len(ret) > 0 and ret[0] > 0: > for b in ret[1:ret[0] + 1]: > if self.kgapi.parse(b) == 0xC0: > self.responses_pending = > self.responses_pending - 1 > if self.responses_pending == 0: > self.on_api_idle() > except usb.core.USBError as e: > if e.errno == 110: > # PyUSB timeout, probably just no data > sys.exc_clear() > elif e.errno == 5 or e.errno == 19: > # "Input/Output Error" or "No such device", this is > serious > self.on_unplugged() > self.disconnect() > else: > raise KeygloveHIDError("PyUSB read thread error: %s" % > e) > > 3. The packets being sent are 64 bytes in size. The trailing bytes beyond > the important part of the data payload are padded to zeros to fill the > whole size. I had originally tried without doing this (by accident, in > fact), so the byte array I wrote was only perhaps 5-10 bytes long, and then > realized that I'd forgotten to pad it. However, the same delay exists > whether or not the bytes are zero-padded out to the full report size, so > that isn't it. > > Given the above, is there ANY reason why there would be a 100+ millisecond > seemingly arbitrary delay sending a 64-byte packet? > > --Jeff > > > > ------------------------------------------------------------------------------ > Download BIRT iHub F-Type - The Free Enterprise-Grade BIRT Server > from Actuate! Instantly Supercharge Your Business Reports and Dashboards > with Interactivity, Sharing, Native Excel Exports, App Integration & more > Get technology previously reserved for billion-dollar corporations, FREE > > http://pubads.g.doubleclick.net/gampad/clk?id=164703151&iu=/4140/ostg.clktrk > _______________________________________________ > pyusb-users mailing list > pyu...@li... > https://lists.sourceforge.net/lists/listinfo/pyusb-users > > |