Pico-PIO-USB: [BUG] capture_hid_report fails on cheap ONN Mouse from Walmart using QT Py RP2040
UPDATE: Thanks to Dan Halbert at Adafruit, I tried testing with not just two mice, but two DIFFERENT mice and the original code worked with the other brand. So, this issue is very different now… it’s that Pico-PIO-USB doesn’t enumerate or support a very cheap mouse from Walmart. My gaming mouse enumerated and reported fine.
So, we could close this one and open a new issue or update this one - I’ll add the new info in a separate comment and we can split if if needed. Thanks
I have wired a USB Female jack via 2x 22Ohm resistors to pins A0 and A1 of a QT Py RP2040 (GPIO29 and GPIO28) respectively. (See Image).

When trying the capture_hid_report example, I get errors when an optical mouse is plugged in. (See attached USB Descriptor from USBView) opticalmouseDescr.txt …
I expected to see HID reports printed to the USB console. …
When run as-is, with only the config.pin_dp set to 28 instead of the default of 0, I got no terminal at all. When placed back in bootloader mode, Putty would fail with “unable to configure USB”.
When I commented out the config.alarm_pool line (Line 18 of capture_hid_report) which I thought would put the interrupts in the main core, I got
Root 0 connected
Device 0 Connected
control in[timeout]
Enumeration failed(-2)
When I moved all of the processing to core0 by calling core1_main() directly from main() and moving pio_usb_host_task() to the main loop, I got:
Root 0 connected
Device 0 Connected
control in[complete]
Enumerating 093a:2510, class:0, address:1
control out[complete]
control in[timeout]
Failed to get string descriptor (Manufacture)
control in[timeout]
Failed to get string descriptor (Product)
control in[timeout]
Enumeration failed(-2)
Root 0 disconnected
Disconnect
Disconnect device 1
Root 0 connected
Device 0 Connected
control in[complete]
Enumerating 093a:2510, class:0, address:1
control out[complete]
control in[timeout]
Failed to get string descriptor (Manufacture)
control in[timeout]
Failed to get string descriptor (Product)
control in[timeout]
Enumeration failed(-2)
Root 0 disconnected
Disconnect
Disconnect device 1
Root 0 connected
Device 0 Connected
I realize that this may be my fault, but I am at a loss as to what to try next… 😕
Any ideas?
About this issue
- Original URL
- State: open
- Created 2 years ago
- Comments: 30 (7 by maintainers)
Thank you for the detailed report. Removing bit stuffing handler from the PIO is the hard way… Let me think of a good way
@sekigon-gonnoc I had a go re-working (butchering) the receive pio code. Attempting to resync on the edges, while still handing bit stuffing.
It’s nowhere near PR ready but might serve as inspiration for a fix: https://github.com/pgreenland/Pico-PIO-USB/tree/attempt_to_resync_on_edges
Moved the eop sm from pio1 to pio0, freeing up some code space in pio1 for the larger receive routine.
The biggest snag I found was with 8 x oversampling, there’s not that much time for additional instructions between bits.
For my low speed device, I’ve upped the oversampling to 16 x for now, which has it enumerating and working reliably. That same tweak won’t be possible with the full speed version though 😦.
Also discovered (yet haven’t tested) something that may allow you to share a single rx / eop program, rather than have to keep swapping programs.
The pico has an input invert option between the input pads and peripherals, page 248 of the datasheet (INOVER).
I may be wrong, but the RX code was identical, while only the initial two wait’s in the eop code needed to change. This is due to the idle state of the lines with the pullup resistor moving between dm and dp to indicate the speed. You may be able to use a single rx / eop program and just toggle the invert option when updating the clock dividers if full speed is required.
@ATMakersBill if you have a moment, try running the capture_hid_report example on my branch, be interested to see if your walmart mouse behaves any better?
While exploring other options to add additional USB ports to the pico, SPI connected host controllers for example. I came up empty, without adding lots of additional cost to the BOM. Getting this working universally would be awesome. Ideally keeping a state machine and 6 instructions spare, allowing the Pico-W to continue using its wifi (it makes use of the PIO for its SPI comms with the wifi module).
I’m also having problems trying to use some old low speed devices like keyboard, mouse, joystick. New devices works, like a “gaming” keyboard and mouse. Old ones simply does not enumerate. It’s possible to fix this with any external hardware? Like using a usb hub?
@pgreenland Thank you for your report. The simulator and your test bench were really helpful
@sekigon-gonnoc working perfectly now, great work!
The USB keyboard that was failing previously now enumerates every time (at least 10 times in a row). No unexpected disconnections and hid data streaming over the serial port when running the capture_hid_report example.
I love that there is a known problem. Our assistive tech mouse and joystick projects would still greatly benefit from this. If there’s anything I can do (including funding to help this) please let me know.
Believe I’ve found the problem…and it’s going to be tricky to solve.
Enabled the debug version of the rx pio code (nice touch). Updated the side sets to drive the debug output high, for one cycle when dm pin was being sampled via the jump instruction.
During a failure I see the following at the start of the packet:
The debug pulses indicating the execution of the “jmp pin” instructions.
Nicely centered in the bits.
The pico read 804B12011001000000 before going wrong at 88FC7B, which should have been 081177.
Looking at those bytes near the end of the packet:
The sample point appears to be getting closer and closer to the transitions.
Given that there’s a two flip flop synchroniser on the PIO inputs, assuming that synchroniser is clocked off the pio clock (the datasheet isn’t clear), then the input being processed is almost certainly from the previous bit.
Which would make sense in at least the first error case with 0x08 becoming 0x88 by the last bit getting mis-sampled, with the transition missed and a rouge 1 added.
The pio code could do with resynchronising on each edge. It seems to only synchronise on the first edge at present then assumes a perfect clock from the remote device for the entire 96 bit run. Seems that doesn’t go well with old / cheap devices which don’t have accurate clocks.
At the same time squeezing that functionality in is going to be tricky.
Possibly moving the bit decoding and unstuffing into the host to free up some instructions. Allowing the input to be re-synchronised on each edge. While adding a counter to take timed samples where there are no edges.
Then its just a case of working out when the packet ends and how long it is / ensuring the final bits in the ISR which may not be a multiple of 8 due to the stuff bits are offloaded to the host.
I’m tempted to have a go…for the challenge…but I’m also looking at a $3 usb hub chip that’ll save me hours of pio tinkering 😅.
Hi All,
Amazing concept and great work getting it this far!
Unfortunately I’m seeing this issue too.
Using a similar random USB keyboard, I see lots of disconnections and reconnections, with occasional brief periods of stability. Not a new keyboard yet similar wallmart special, first time I’ve had an issue with it.
@moc32 's suggestion gave me a good place to start. I’m seeing missing acks too. Adding a dash of debug to the pio_usb_bus_receive_packet_and_handshake function is pointing at the rx pio code experiencing some sort of bit errors.
I’m printing the following inside pio_usb_bus_receive_packet_and_handshake based on good / bad crc (ignore the various CRC prints, I was trying to match them up to that calculated by my logic analyser):
Here’s the debug output produced:
The good packet as seen by the logic analyser:
The data of which matches the good print statement (if you byte swap the crc).
The bad packet as seen by the logic analyser:
Here’s the data captured by the pico vs data captured by the logic analyser:
Seems the rx pio somehow got way otta wack with the bitstream at the bytes indicated by the carot?
I’ll start poking around in the pio code, @sekigon-gonnoc any pointers?
Thanks,
Phil