USBHost_t36 with Circuitpython serial device Keybow2040

@Liudr, many thanks again for all the help!
Its does'nt need much delay.
130ms - Fails all the time
150ms - Borderline
175ms - ok all the time
Its also importing a specific support file that causes the issue (keybow2040.py). I can import other libs and run code without any delays and its all ok.

My suspicion is that the code running on keybow causes some delay/blocking during boot so if you add some delay in teensy before it enumerates keybow, then keybow has completed initialization (could be the enable/disable that messes up enumeration if executed while it is being enumerated) and will respond to these requests properly.

In my opinion, the RP2040 probably like other systems I've read the source code, initializes USB device controller and attach the D+ pullup resistor right away. I always let my device boot first and determine whether it's time to initialize or attach resistor when situation calls for such actions. The call to an attach() can be done in the user code instead of implicit with init.
 
@Paul

I have not been able to reproduce so far on a vanilla Pico! Its loading the library specific to the keybow2040 while in enumeration that seems to cause the issue (only thru a high speed hub).
I tried uploading the beagle files but get an invalid file message, even when archived ?

Regards
Robert.

I may be missing something? What is the actual difference between the keybow 2040 versus plain Jane PICO or Adafruit Feather RP2040?
Yes I understand from their schematic that a bunch of the GPIOs are used for the Switches. and the like....

So more specifically have you tried simply loading up something like the PICO with their library and boot.py and similar sketch?

Yes I understand their IO pins are defined differently.

For example PICO (pins.c)
Code:
    { MP_ROM_QSTR(MP_QSTR_GP0), MP_ROM_PTR(&pin_GPIO0) },
    { MP_ROM_QSTR(MP_QSTR_GP1), MP_ROM_PTR(&pin_GPIO1) },
    { MP_ROM_QSTR(MP_QSTR_GP2), MP_ROM_PTR(&pin_GPIO2) },
    { MP_ROM_QSTR(MP_QSTR_GP3), MP_ROM_PTR(&pin_GPIO3) },
    { MP_ROM_QSTR(MP_QSTR_GP4), MP_ROM_PTR(&pin_GPIO4) },
    { MP_ROM_QSTR(MP_QSTR_GP5), MP_ROM_PTR(&pin_GPIO5) },
    { MP_ROM_QSTR(MP_QSTR_GP6), MP_ROM_PTR(&pin_GPIO6) },
    { MP_ROM_QSTR(MP_QSTR_GP7), MP_ROM_PTR(&pin_GPIO7) },
    { MP_ROM_QSTR(MP_QSTR_GP8), MP_ROM_PTR(&pin_GPIO8) },
    { MP_ROM_QSTR(MP_QSTR_GP9), MP_ROM_PTR(&pin_GPIO9) },
    { MP_ROM_QSTR(MP_QSTR_GP10), MP_ROM_PTR(&pin_GPIO10) },
    { MP_ROM_QSTR(MP_QSTR_GP11), MP_ROM_PTR(&pin_GPIO11) },
    { MP_ROM_QSTR(MP_QSTR_GP12), MP_ROM_PTR(&pin_GPIO12) },
    { MP_ROM_QSTR(MP_QSTR_GP13), MP_ROM_PTR(&pin_GPIO13) },
    { MP_ROM_QSTR(MP_QSTR_GP14), MP_ROM_PTR(&pin_GPIO14) },
    { MP_ROM_QSTR(MP_QSTR_GP15), MP_ROM_PTR(&pin_GPIO15) },
    { MP_ROM_QSTR(MP_QSTR_GP16), MP_ROM_PTR(&pin_GPIO16) },
    { MP_ROM_QSTR(MP_QSTR_GP17), MP_ROM_PTR(&pin_GPIO17) },
    { MP_ROM_QSTR(MP_QSTR_GP18), MP_ROM_PTR(&pin_GPIO18) },
    { MP_ROM_QSTR(MP_QSTR_GP19), MP_ROM_PTR(&pin_GPIO19) },
    { MP_ROM_QSTR(MP_QSTR_GP20), MP_ROM_PTR(&pin_GPIO20) },
    { MP_ROM_QSTR(MP_QSTR_GP21), MP_ROM_PTR(&pin_GPIO21) },
    { MP_ROM_QSTR(MP_QSTR_GP22), MP_ROM_PTR(&pin_GPIO22) },
...
But for Keybow you have:
Code:
  { MP_ROM_QSTR(MP_QSTR_SW0), MP_ROM_PTR(&pin_GPIO21) },
    { MP_ROM_QSTR(MP_QSTR_SW1), MP_ROM_PTR(&pin_GPIO20) },
    { MP_ROM_QSTR(MP_QSTR_SW2), MP_ROM_PTR(&pin_GPIO19) },
    { MP_ROM_QSTR(MP_QSTR_SW3), MP_ROM_PTR(&pin_GPIO18) },
    { MP_ROM_QSTR(MP_QSTR_SW4), MP_ROM_PTR(&pin_GPIO17) },
    { MP_ROM_QSTR(MP_QSTR_SW5), MP_ROM_PTR(&pin_GPIO16) },
    { MP_ROM_QSTR(MP_QSTR_SW6), MP_ROM_PTR(&pin_GPIO15) },
    { MP_ROM_QSTR(MP_QSTR_SW7), MP_ROM_PTR(&pin_GPIO14) },
    { MP_ROM_QSTR(MP_QSTR_SW8), MP_ROM_PTR(&pin_GPIO13) },
    { MP_ROM_QSTR(MP_QSTR_SW9), MP_ROM_PTR(&pin_GPIO12) },
    { MP_ROM_QSTR(MP_QSTR_SW10), MP_ROM_PTR(&pin_GPIO11) },
    { MP_ROM_QSTR(MP_QSTR_SW11), MP_ROM_PTR(&pin_GPIO10) },
    { MP_ROM_QSTR(MP_QSTR_SW12), MP_ROM_PTR(&pin_GPIO9) },
    { MP_ROM_QSTR(MP_QSTR_SW13), MP_ROM_PTR(&pin_GPIO8) },
    { MP_ROM_QSTR(MP_QSTR_SW14), MP_ROM_PTR(&pin_GPIO7) },
    { MP_ROM_QSTR(MP_QSTR_SW15), MP_ROM_PTR(&pin_GPIO6) },
So for example: your board.SW1 is the same pin as board.GP20 on the PICO...

So again I wonder if it is simply software configuration issue or is there some hardware differences that are making a difference?
 
Ok, I've got it set up on my workbench. I copied adafruit-circuitpython-raspberry_pi_pico-en_US-7.2.5.uf2 onto 2 Pico boards. Then copied adafruit_is31fl3731 and pmk to the CIRCUITPY lib folder, and Boot.py to its root directory. I'm running 1.57-beta1 USBHost_t36 Storage/ListFiles on Teensy 4.1. It's able to list the files on the clean Circuit Python:

Code:
Initializing USB MSC drive...initialization done.
.fseventsd/
  no_log                                          0
.metadata_never_index                             0
.Trashes                                          0
code.py                                           22
lib/
boot_out.txt                                      103
done!

But the other Pico with Boot.py, adafruit_is31fl3731 & pmk in its lib folder, and still the original "Hello World" code.py gives this:

Code:
Initializing USB MSC drive...initialization failed!

It's connected directly (well, through my Beagle 480) to Teensy 4.1's host port, no USB hub. Not sure if this is the same issue or something different. Will start work on this soon. Just wanted to first document the exact steps I've taken so far on both Pico boards and Teensy 4.1.

usb.jpg
 
Ah, I see now that "initialization failed!" is supposed to happen due to Boot.py "storage.disable_usb_drive()".

Looks like both are completing enumeration when directly connected. Looking for cables now to plug into the hub...
 
Well, I can't seem to reproduce the problem. Even plugged into a hub, enumeration completes successfully.

screenshot.jpg

I ran the USBHost_t36 "Serial" example. Beagle 480 was connected between Teensy 4.1 and hub, as you can guess from the 480 speed and split transactions in this screenshot.

Can anyone give me an idea of what other files to copy onto the Pico's CIRCUITPY drive to be able to reproduce the problem? I really can't work on this without a way to make it happen here.
 
Paul

I am using thier original Libray "keybow2040.py", https://github.com/pimoroni/keybow2040-circuitpython/tree/master/lib

Code below fails to enumerate without the delay.

Code:
# SPDX-FileCopyrightText: 2021 Sandy Macdonald
#
# SPDX-License-Identifier: MIT

# This example demonstrates how to light keys when pressed.

# Drop the keybow2040.py file into your `lib` folder on your `CIRCUITPY` drive.
import time
#import math
#import onewireio
time.sleep(0.5);#  5 seconds ok, 1 second ok, 0.5 second ok, 0.25second ok, Fails at 0.1s, Fails0.15. OK at 0.175sec

import board
from keybow2040 import Keybow2040





# Set up Keybow
i2c = board.I2C()
keybow = Keybow2040(i2c)
keys = keybow.keys

# Use cyan as the colour.
rgb = (0, 255, 255)



while True:
    # Always remember to call keybow.update() on every iteration of your loop!
    keybow.update()

    # Loop through the keys and set the LED to cyan if pressed, otherwise turn
    # it off (set it to black).
    for key in keys:
       if key.pressed:
            key.set_led(*rgb)
            serial.write(b'KeyPress\r\n')
       else:
           key.set_led(0, 0, 0)
           
    #serial.write(b"Serial Hello");

    #time.sleep(10);
    #serial.write(b"Serial Hello");
    #print("Hello world")
    #time.sleep(10);
 
Paul

Not sure this is worth much of your time as its very specific and easily worked around, but to reproduce on a vanilla pico I loaded the keybow2040 circuitpython image to give it the same pin mapping and then its fails the same without the "sleep" in the above code.
 
to reproduce on a vanilla pico I loaded the keybow2040 circuitpython image to give it the same pin mapping and then its fails the same without the "sleep" in the above code.

Can you give me direct links to *all* of the *exact* files I should load onto a Pico?

Remember, I don't use Python much and I've never really used RP2040. Please do not assume I know what you mean by "the keybow2040 circuitpython image" (because I truly do not know how that is any different from just getting the adafruit-circuitpython-raspberry_pi_pico-en_US-7.2.5.uf2 from Adafruit's website. For each file, or each folder containing many files, please be precise and specific about where I copy it onto the CIRCUITPY drive. Leave nothing to change or guesswork. Assume I know absolutely nothing about Pico or Python! Tell me ALL the files and folders to copy (with links to the exact file so I don't have to search and guess whether I've found the correct ones) and exactly where to copy each so the final result is a Pico in the state that reproduces this problem.

If a PC running Windows or Linux is able to enumerate the device by USBHost_t36 can't, that's a bug I want to investigate and fix. But I can't even get started if I'm still trying to guess all the stuff I'm supposed to copy onto the Pico to reproduce the problem!
 
Ok, trying this again.

1: Held the button on Pico while plugging into my PC. Drive appears as "RPI-PR2".

2: Copy flash_nuke.uf2 to RPI-PR2 root directory. RPI-PR2 disconnects immediately, reconnects after several seconds.

3: Copy adafruit-circuitpython-raspberry_pi_pico-en_US-7.2.5.uf2 onto the RPI-PR2 root directory. After several seconds, RPI-PR2 automatically disconnects. Several seconds more and CIRCUITPY drive appears.

4: Copy keybow2040.py and keybow_hardware into CIRCUITPY lib folder. To be specific, this is 10 files total:

Code:
paul@preston:/tmp/keybow2040-circuitpython/lib > ls -l `find . -type f`
-rwxrwxr-x 1 paul paul 13646 May 14 10:53 ./keybow2040.py
-rw-rw-r-- 1 paul paul   313 May 14 10:53 ./keybow_hardware/display/dotstar.py
-rw-rw-r-- 1 paul paul   171 May 14 10:53 ./keybow_hardware/display/__init__.py
-rw-rw-r-- 1 paul paul   321 May 14 10:53 ./keybow_hardware/display/keybow2040.py
-rw-rw-r-- 1 paul paul   514 May 14 10:53 ./keybow_hardware/__init__.py
-rw-rw-r-- 1 paul paul  1161 May 14 10:53 ./keybow_hardware/pim551.py
-rw-rw-r-- 1 paul paul   680 May 14 10:53 ./keybow_hardware/pim56x.py
-rw-rw-r-- 1 paul paul   508 May 14 10:53 ./keybow_hardware/switches/gpio.py
-rw-rw-r-- 1 paul paul   231 May 14 10:53 ./keybow_hardware/switches/__init__.py
-rw-rw-r-- 1 paul paul   629 May 14 10:53 ./keybow_hardware/switches/tca9555.py

After copying, this is the full contents of my CIRCUITPY drive.

Code:
paul@preston:/tmp/keybow2040-circuitpython/lib > ls -l `find /media/paul/CIRCUITPY/ -type f`
-rw-r--r-- 1 paul paul   103 Dec 31  2019 /media/paul/CIRCUITPY/boot_out.txt
-rw-r--r-- 1 paul paul    22 Dec 31  2019 /media/paul/CIRCUITPY/code.py
-rw-r--r-- 1 paul paul     0 Dec 31  2019 /media/paul/CIRCUITPY/.fseventsd/no_log
-rw-r--r-- 1 paul paul 13646 May 14 10:57 /media/paul/CIRCUITPY/lib/keybow2040.py
-rw-r--r-- 1 paul paul   313 May 14 10:57 /media/paul/CIRCUITPY/lib/keybow_hardware/display/dotstar.py
-rw-r--r-- 1 paul paul   171 May 14 10:57 /media/paul/CIRCUITPY/lib/keybow_hardware/display/__init__.py
-rw-r--r-- 1 paul paul   321 May 14 10:57 /media/paul/CIRCUITPY/lib/keybow_hardware/display/keybow2040.py
-rw-r--r-- 1 paul paul   514 May 14 10:57 /media/paul/CIRCUITPY/lib/keybow_hardware/__init__.py
-rw-r--r-- 1 paul paul  1161 May 14 10:57 /media/paul/CIRCUITPY/lib/keybow_hardware/pim551.py
-rw-r--r-- 1 paul paul   680 May 14 10:57 /media/paul/CIRCUITPY/lib/keybow_hardware/pim56x.py
-rw-r--r-- 1 paul paul   508 May 14 10:57 /media/paul/CIRCUITPY/lib/keybow_hardware/switches/gpio.py
-rw-r--r-- 1 paul paul   231 May 14 10:57 /media/paul/CIRCUITPY/lib/keybow_hardware/switches/__init__.py
-rw-r--r-- 1 paul paul   629 May 14 10:57 /media/paul/CIRCUITPY/lib/keybow_hardware/switches/tca9555.py
-rw-r--r-- 1 paul paul     0 Dec 31  2019 /media/paul/CIRCUITPY/.metadata_never_index
-rw-r--r-- 1 paul paul     0 Dec 31  2019 /media/paul/CIRCUITPY/.Trashes

5: Copied the code from msg #31 into a file named "code.py". Guessing that's the filename I'm supposed to use? Commented out line 11 (time.sleep(0.5)). Copied this code.py file onto the CIRCUITPY root directory (more guesswork... is this really what I'm supposed to do here?) CIRCUITPY does not automatically disconnect. I can read the file back and verify is the code.py file I just copied.

6: Unmount the CIRCUITPY drive and unplug the Pico from my PC.

7: Plug the Pico into the USB hub, which is plugged into my Beagle 480, which is plugged into the Teensy 4.1 host port.

8: In Arduino with Teensyduino 1.57-beta1, open File > Examples > USBHost_t36 > Serial. Upload to Teensy 4.1 and open the Arduino Serial Monitor.

This is the result I see.

screenshot1.png

And here is the capture from my Beagle 480. Enumeration seems to work fine. No NAK or other issues with the split transactions to set the address or read the device descriptor.

screenshot2.jpg

I must be missing or misunderstanding how to do some step. But what? Where did I go wrong? I hope by this step-by-step list you can see exactly what I tried.

Again, I want to work on a fix for this bug, but I'm stuck trying just to reproduce it. Please tell me the exact steps to follow in a similar level of detail so I can get my Pico board to reproduce the problem?
 
Ok, I realized I probably missed the Boot.py step. So....

9: Remove Pico from Teensy 4.1 - Beagle 480 - Hub and plug back into my PC. CIRCUITPY drive appears again.

10: Copy the code from msg #24 into a file named Boot.py. Copy onto the CIRCUITPY root directory. Now these are all the files on CIRCUITPY.

Code:
paul@preston:/tmp > ls -l `find /media/paul/CIRCUITPY/ -type f`
-rw-r--r-- 1 paul paul   103 Dec 31  2019 /media/paul/CIRCUITPY/boot_out.txt
-rw-r--r-- 1 paul paul   208 May 14 11:22 /media/paul/CIRCUITPY/Boot.py
-rw-r--r-- 1 paul paul  1101 May 14 11:06 /media/paul/CIRCUITPY/code.py
-rw-r--r-- 1 paul paul     0 Dec 31  2019 /media/paul/CIRCUITPY/.fseventsd/no_log
-rw-r--r-- 1 paul paul 13646 May 14 10:57 /media/paul/CIRCUITPY/lib/keybow2040.py
-rw-r--r-- 1 paul paul   313 May 14 10:57 /media/paul/CIRCUITPY/lib/keybow_hardware/display/dotstar.py
-rw-r--r-- 1 paul paul   171 May 14 10:57 /media/paul/CIRCUITPY/lib/keybow_hardware/display/__init__.py
-rw-r--r-- 1 paul paul   321 May 14 10:57 /media/paul/CIRCUITPY/lib/keybow_hardware/display/keybow2040.py
-rw-r--r-- 1 paul paul   514 May 14 10:57 /media/paul/CIRCUITPY/lib/keybow_hardware/__init__.py
-rw-r--r-- 1 paul paul  1161 May 14 10:57 /media/paul/CIRCUITPY/lib/keybow_hardware/pim551.py
-rw-r--r-- 1 paul paul   680 May 14 10:57 /media/paul/CIRCUITPY/lib/keybow_hardware/pim56x.py
-rw-r--r-- 1 paul paul   508 May 14 10:57 /media/paul/CIRCUITPY/lib/keybow_hardware/switches/gpio.py
-rw-r--r-- 1 paul paul   231 May 14 10:57 /media/paul/CIRCUITPY/lib/keybow_hardware/switches/__init__.py
-rw-r--r-- 1 paul paul   629 May 14 10:57 /media/paul/CIRCUITPY/lib/keybow_hardware/switches/tca9555.py
-rw-r--r-- 1 paul paul     0 Dec 31  2019 /media/paul/CIRCUITPY/.metadata_never_index
-rw-r--r-- 1 paul paul     0 Dec 31  2019 /media/paul/CIRCUITPY/.Trashes

11: Unmount the drive, plug from PC, plug back into HUB - Beagle 480 - Teensy 4.1.

12: Upload USBHost_t36 Serial example again.

Now I get this in the Arduino Serial Monitor

screenshot3.png

And this is the Beagle 480 capture.

screenshot4.jpg

As you can see, I *still* can't reproduce the problem. You can see the set address and get descriptor and rest of enumeration is working fine with split transactions between Teensy 4.1 and the USB hub.

What am I missing? Please be EXTREMELY SPECIFIC about the EXACT STEPS to reproduce this problem. I hope you can see I am trying and exactly the steps I tried which haven't worked based on my limited understanding of what's been said so far on this thread.

I can't even begin to try fixing this problem until I have hardware here that reproduces it.
 
Paul

This is the minimun install to recreate the issue.

Load Circuitpython for keybow2040 onto the Pico (just so it has the same pin mapping) - https://circuitpython.org/board/pimoroni_keybow2040/
Add keybow2040.py to the lib directory https://github.com/pimoroni/keybow20...ree/master/lib
Add adafruit IS31FL3731 library to the lib directory (adafruit_is31fl3731 directory within adafruit-circuitpython-is31fl3731-py-3.3.0.zip) https://github.com/adafruit/Adafruit_CircuitPython_IS31FL3731/releases/tag/3.3.0
Add adafruit bus_device library (adafruit_bus_device directory within adafruit-circuitpython-busdevice-py-5.1.8.zip) - https://github.com/adafruit/Adafruit_CircuitPython_BusDevice/releases/tag/5.1.8
Add boot.py and code.py to the root directory as below.

boot.py
Code:
import usb_cdc

usb_cdc.enable(console=True, data=True)    # Enable console and data

#import storage
#storage.disable_usb_drive()

import usb_midi
usb_midi.disable()

import usb_hid
usb_hid.disable()

# This example is for a Circuit Playground,
# or any board with buttons that are connected to +V when pressed.
##import storage, usb_cdc
##import board, digitalio
# On the Circuit Playground, pressing an on-board button
# connects the button to +V.
##button = digitalio.DigitalInOut(board.BUTTON_A)
##button.pull = digitalio.Pull.DOWN
# Disable devices only if button is not pressed.
##if not button.value:
##	storage.disable_usb_drive()
##	usb_cdc.disable()

code.py
Code:
# SPDX-FileCopyrightText: 2021 Sandy Macdonald
#
# SPDX-License-Identifier: MIT

# This example demonstrates how to light keys when pressed.

# Drop the keybow2040.py file into your `lib` folder on your `CIRCUITPY` drive.
import time
#import math
#import onewireio
#time.sleep(0.5);#  5 seconds ok, 1 second ok, 0.5 second ok, 0.25second ok, Fails at 0.1s, Fails0.15. OK at 0.175sec

import board
from keybow2040 import Keybow2040
import usb_cdc
serial = usb_cdc.data




# Set up Keybow
i2c = board.I2C()
keybow = Keybow2040(i2c)
keys = keybow.keys

# Use cyan as the colour.
rgb = (0, 255, 255)



while True:
    # Always remember to call keybow.update() on every iteration of your loop!
    keybow.update()

    # Loop through the keys and set the LED to cyan if pressed, otherwise turn
    # it off (set it to black).
    for key in keys:
       if key.pressed:
            key.set_led(*rgb)
            serial.write(b'KeyPress\r\n')
       else:
           key.set_led(0, 0, 0)
           
    #serial.write(b"Serial Hello");

    #time.sleep(10);
    #serial.write(b"Serial Hello");
    #print("Hello world")
    #time.sleep(10);

root.png
lib.png

Directory Listing
Code:
Volume in drive J is CIRCUITPY
 Volume Serial Number is 5021-0000

 Directory of J:\

01/01/2020  00:00    <DIR>          .fseventsd
01/01/2020  00:00                 0 .metadata_never_index
01/01/2020  00:00                 0 .Trashes
14/05/2022  12:44             1,136 code.py
01/01/2020  00:00    <DIR>          lib
01/01/2020  00:00               125 boot_out.txt
27/04/2022  12:20               686 boot.py
               5 File(s)          1,947 bytes

 Directory of J:\.fseventsd

01/01/2020  00:00    <DIR>          .
01/01/2020  00:00    <DIR>          ..
01/01/2020  00:00                 0 no_log
               1 File(s)              0 bytes

 Directory of J:\lib

01/01/2020  00:00    <DIR>          .
01/01/2020  00:00    <DIR>          ..
14/05/2022  19:40            14,095 keybow2040.py
14/05/2022  19:15    <DIR>          adafruit_is31fl3731
14/05/2022  19:15    <DIR>          adafruit_bus_device
               1 File(s)         14,095 bytes

 Directory of J:\lib\adafruit_is31fl3731

14/05/2022  19:40    <DIR>          .
14/05/2022  19:40    <DIR>          ..
04/04/2022  13:41             1,013 charlie_bonnet.py
04/04/2022  13:41             1,052 charlie_wing.py
04/04/2022  13:41             2,321 keybow2040.py
04/04/2022  13:41             2,909 led_shim.py
04/04/2022  13:41             2,795 matrix.py
04/04/2022  13:41             1,062 matrix_11x7.py
04/04/2022  13:41             2,488 rgbmatrix5x5.py
04/04/2022  13:41             1,017 scroll_phat_hd.py
04/04/2022  13:41            12,907 __init__.py
               9 File(s)         27,564 bytes

 Directory of J:\lib\adafruit_bus_device

14/05/2022  19:51    <DIR>          .
14/05/2022  19:51    <DIR>          ..
22/02/2022  21:07             6,565 i2c_device.py
22/02/2022  21:07             3,758 spi_device.py
22/02/2022  21:07                 0 __init__.py
               3 File(s)         10,323 bytes

 Directory of J:\System Volume Information

14/05/2022  19:39    <DIR>          .
14/05/2022  19:39    <DIR>          ..
14/05/2022  19:39                12 WPSettings.dat
14/05/2022  19:39                76 IndexerVolumeGuid
               2 File(s)             88 bytes

     Total Files Listed:
              21 File(s)         54,017 bytes
              14 Dir(s)         966,144 bytes free
 
Tried again with these instructions, but still could not reproduce the problem!

Here's what was on the CIRCUITPY drive right before unmount and plug from my PC.

Code:
paul@preston:/tmp > find /media/paul/CIRCUITPY/ -type f
/media/paul/CIRCUITPY/.fseventsd/no_log
/media/paul/CIRCUITPY/.metadata_never_index
/media/paul/CIRCUITPY/.Trashes
/media/paul/CIRCUITPY/code.py
/media/paul/CIRCUITPY/lib/keybow2040.py
/media/paul/CIRCUITPY/lib/adafruit_is31fl3731/scroll_phat_hd.py
/media/paul/CIRCUITPY/lib/adafruit_is31fl3731/led_shim.py
/media/paul/CIRCUITPY/lib/adafruit_is31fl3731/__init__.py
/media/paul/CIRCUITPY/lib/adafruit_is31fl3731/matrix_11x7.py
/media/paul/CIRCUITPY/lib/adafruit_is31fl3731/charlie_bonnet.py
/media/paul/CIRCUITPY/lib/adafruit_is31fl3731/rgbmatrix5x5.py
/media/paul/CIRCUITPY/lib/adafruit_is31fl3731/keybow2040.py
/media/paul/CIRCUITPY/lib/adafruit_is31fl3731/charlie_wing.py
/media/paul/CIRCUITPY/lib/adafruit_is31fl3731/matrix.py
/media/paul/CIRCUITPY/lib/adafruit_bus_device/spi_device.py
/media/paul/CIRCUITPY/lib/adafruit_bus_device/i2c_device.py
/media/paul/CIRCUITPY/lib/adafruit_bus_device/__init__.py
/media/paul/CIRCUITPY/boot_out.txt
/media/paul/CIRCUITPY/boot.py

When I run USBHost_t36 Serial on Teensy 4.1, I get this in the serial monitor

screenshot5.png

And the Beagle 480 sees an error free USB enumeration using split transactions.

screenshot6.jpg
 
Oh, looks like I spoke too soon. I can reproduce the problem! Does not happen immediately, but does if I unplug and reconnect the Pico.

screenshot7.jpg
 
Just to document this process, here's the test I have set up on my workbench.

usbtest.jpg

A little transistor circuit is connected to Teensy 4.1 pin 32 to control power to the Raspberry Pi Pico. This way I can turn it on after USBHost_t36 is initialized, to reproduce the problem without unplugging and reconnecting the USB cable.

This is the test code:

Code:
#include <USBHost_t36.h>

USBHost myusb;
USBHub hub1(myusb);
USBHub hub2(myusb);
USBHIDParser hid1(myusb);
USBHIDParser hid2(myusb);
USBHIDParser hid3(myusb);
USBSerial userial(myusb);  // works only for those Serial devices who transfer <=64 bytes (like T3.x, FTDI...)

USBDriver *drivers[] = {&hub1, &hub2, &hid1, &hid2, &hid3, &userial};
#define CNT_DEVICES (sizeof(drivers)/sizeof(drivers[0]))
const char * driver_names[CNT_DEVICES] = {"Hub1", "Hub2",  "HID1", "HID2", "HID3", "USERIAL1" };
bool driver_active[CNT_DEVICES] = {false, false, false, false};

void setup()
{
  pinMode(32, OUTPUT); // pin 32 high turns on power to Raspberry Pi Pico
  digitalWrite(32, LOW);
  while (!Serial && (millis() < 5000)) ; // wait for Arduino Serial Monitor
  Serial.println("\n\nUSB Host Testing - Serial");
  myusb.begin();
}

int powerState = 0;
elapsedMillis powerTime = 0;

void loop()
{
  if (powerState == 0) {
    if (powerTime > 3000) { // turn power on after 3 sec
      Serial.print("\n\n****** Power ON ******\n\n");
      digitalWrite(32, HIGH);
      powerState = 2;
    }
  }
  
  myusb.Task();
  // Print out information about different devices.
  for (uint8_t i = 0; i < CNT_DEVICES; i++) {
    if (*drivers[i] != driver_active[i]) {
      if (driver_active[i]) {
        Serial.printf("*** Device %s - disconnected ***\n", driver_names[i]);
        driver_active[i] = false;
      } else {
        Serial.printf("*** Device %s %x:%x - connected ***\n", driver_names[i], drivers[i]->idVendor(), drivers[i]->idProduct());
        driver_active[i] = true;

        const uint8_t *psz = drivers[i]->manufacturer();
        if (psz && *psz) Serial.printf("  manufacturer: %s\n", psz);
        psz = drivers[i]->product();
        if (psz && *psz) Serial.printf("  product: %s\n", psz);
        psz = drivers[i]->serialNumber();
        if (psz && *psz) Serial.printf("  Serial: %s\n", psz);

        // If this is a new Serial device.
        if (drivers[i] == &userial) {
          // Lets try first outputting something to our USerial to see if it will go out...
          userial.begin(115200);
        }
      }
    }
  }


  while (userial.available()) {
    //    Serial.println("USerial Available");

    Serial.write(userial.read());
  }
}

As a quick first test, I added a delay before sending the get descriptor request. More than 2ms is needed. About 9ms seems to be enough.

screenshot8.png
 
Is it related to this issue?

Maybe. I'm still investigating, so it's too early to really have a well informed opinion.

But initial testing shows 2ms delay alone is not nearly long enough, so my gut feeling is that delay probably isn't be the entire explanation. I'm pretty sure a lack of properly handling certain error conditions also plays a factor.


Can you please share what delay command you used? Thanks.

Sure. As a first very quick test, I just added this line in enumeration.cpp

Code:
                case 1: // request all 18 bytes of device descriptor[B][COLOR="#FF0000"]
delayMicroseconds(7000);  // barely ok=7000, marginal=6900, bad=6600[/COLOR][/B]
                        dev->address = enumsetup.wValue;
                        pipe_set_addr(dev->control_pipe, enumsetup.wValue);
                        mk_setup(enumsetup, 0x80, 6, 0x0100, 0, 18); // 6=GET_DESCRIPTOR
                        queue_Control_Transfer(dev, &enumsetup, enumbuf, NULL);
                        dev->enum_state = 2;
                        return;

Obviously a busy loop delay isn't an acceptable long-term solution. This was just done as a first quick test to explore how much delay is needed if no other changes are made.

I'm pretty sure other changes are the right way, but still too early to say.
 
Thanks. Yeah, a better way to add another case with some USB timer will be great but I didn't know how. Hope you find out what else may cause the issue and make a patch. I've heavily modified the library so I will have to wait and read git to see what you change and incorporate into my version.
 
I ran some quick experiments to see how long each operating system waits. Even though the USB spec might give a requirement, the practical reality is most USB devices probably only get tested with Windows, maybe Linux & MacOS, or perhaps Android and iOS.

Windows 10 waits 10ms.

usbdelay1.png

Linux waits 48ms.

usbdelay2.png

MacOS is the shortest at just under 5ms, but it reads the hub status in the middle of the wait. It also reads only the first 8 bytes, then waits 3ms and reads all 18 bytes.

usbdelay3.png
 
I guess there's the spec, then there's popular hosts such as windows/nix/macos, then there's device manufacturers.

Here's what I have on a square ipad stand hosting a receipt printer:
10ms between setAddress and requesting 8-byte device desc. (data are 2yr old)
32ms between setAddress and requesting 18-byte device desc. (data are 2yr old)

A square register hosting a barcode scanner:
20ms between setAddress and requesting 18-byte device desc. (data are 2yr old)

Everyone is making their worst-case-scenario delays?
 
Back
Top