Teensy 4.1 USB Host with Two Ultrastik 360's

Cotman

Member
I am updating my Ultragun Arcade controller to use a Teensy 4.1 instead of an Arduino Due with USB Host board.
After much head scratching I now have both joysticks working fine except for startup which seems to reset the Teensy after the first joystick has been ID'd.
The joysticks individually ID with D209:0511 but when 2 are connected the second will ID with D209:0512, not sure how this is handled internally.
My main question is why the Teensy seems to hang for a couple of seconds after power up, then reset and fire up fine with my joysticks talking.
Is this down to the Product ID updating for the second stick or is it some USB issue with corruption causing the reset?
Has anyone else hooked up dual Ultrastik 360's via a USB 2.0 hub to a Teensy 4.1?
 
I didn't make this clear in the last post, but it does boot on occasions fine without the reset. I have turned on USB Debug Msgs and here is the later stages of the log which shows the error msgs when it goes wrong and hangs for a reset. The second example is when it boots fine first time.

Code:
USBHub memory usage = 960
USBHub claim_device this=20006160
HIDParser claim this=20003220
HIDParser claim this=20003960
HIDParser claim this=200040A0
HIDParser claim this=200047E0
HIDParser claim this=20004F20
HIDParser claim this=20005660
Descriptor 4 = INTERFACE
HIDParser claim this=20003220
 bInterfaceNumber =   0
 bInterfaceClass =    3
 bInterfaceSubClass = 0
 bInterfaceProtocol = 0
HID Parser Claim: 09 04 00 00 01 03 00 00 00 09 21 11 01 00 01 22 2E 00 07 05 82 03 08 00 0A 09 04 01 00 01 03 01 02 00 09 21 11 01 00 01 22 32 00 07 05 81 03 08 00 0A 09 04 02 00 01 03 00 00 00 09 21 11 01 00 01 22 21 00 07 05 83 03 08 00 0A
report descriptor size = 46
Single endpoint HID:
  endpoint = 82
   size = 8
   interval = 10
new_Pipe
allocate_interrupt_pipe_bandwidth
 best_bandwidth = 3, at offset = 8, shift= 0
Descriptor 33 = HID
Descriptor 5 = ENDPOINT
Descriptor 4 = INTERFACE
HIDParser claim this=20003960
 bInterfaceNumber =   1
 bInterfaceClass =    3
 bInterfaceSubClass = 1
 bInterfaceProtocol = 2
HID Parser Claim: 09 04 01 00 01 03 01 02 00 09 21 11 01 00 01 22 32 00 07 05 81 03 08 00 0A 09 04 02 00 01 03 00 00 00 09 21 11 01 00 01 22 21 00 07 05 83 03 08 00 0A
report descriptor size = 50
Single endpoint HID:
  endpoint = 81
   size = 8
   interval = 10
new_Pipe
allocate_interrupt_pipe_bandwidth
 best_bandwidth = 3, at offset = 1, shift= 0
Descriptor 33 = HID
Descriptor 5 = ENDPOINT
Descriptor 4 = INTERFACE
HIDParser claim this=200040A0
 bInterfaceNumber =   2
 bInterfaceClass =    3
 bInterfaceSubClass = 0
 bInterfaceProtocol = 0
HID Parser Claim: 09 04 02 00 01 03 00 00 00 09 21 11 01 00 01 22 21 00 07 05 83 03 08 00 0A
report descriptor size = 33
Single endpoint HID:
  endpoint = 83
   size = 8
   interval = 10
new_Pipe
allocate_interrupt_pipe_bandwidth
 best_bandwidth = 3, at offset = 2, shift= 0
Descriptor 33 = HID
Descriptor 5 = ENDPOINT
control callback (hid)
05 01 09 04 A1 01 09 01 A1 00 09 30 09 31 15 00 25 FF 35 00 45 FF 75 08 95 02 81 02 C0 05 09 19 01 29 0F 15 00 25 01 95 10 75 01 81 02 C0
  mesg = 22000681
  got report descriptor
Found top level collection 10004
find_driver
  driver 20006EC0
ERROR Followup
    remove from followup list
    stray halted 20004A40

Code:
USBHub memory usage = 960
USBHub claim_device this=20006160
HIDParser claim this=20003220
HIDParser claim this=20003960
HIDParser claim this=200040A0
HIDParser claim this=200047E0
HIDParser claim this=20004F20
HIDParser claim this=20005660
Descriptor 4 = INTERFACE
HIDParser claim this=20003220
 bInterfaceNumber =   0
 bInterfaceClass =    3
 bInterfaceSubClass = 0
 bInterfaceProtocol = 0
HID Parser Claim: 09 04 00 00 01 03 00 00 00 09 21 11 01 00 01 22 2E 00 07 05 82 03 08 00 0A 09 04 01 00 01 03 01 02 00 09 21 11 01 00 01 22 32 00 07 05 81 03 08 00 0A 09 04 02 00 01 03 00 00 00 09 21 11 01 00 01 22 21 00 07 05 83 03 08 00 0A
report descriptor size = 46
Single endpoint HID:
  endpoint = 82
   size = 8
   interval = 10
new_Pipe
allocate_interrupt_pipe_bandwidth
 best_bandwidth = 3, at offset = 8, shift= 0
Descriptor 33 = HID
Descriptor 5 = ENDPOINT
Descriptor 4 = INTERFACE
HIDParser claim this=20003960
 bInterfaceNumber =   1
 bInterfaceClass =    3
 bInterfaceSubClass = 1
 bInterfaceProtocol = 2
HID Parser Claim: 09 04 01 00 01 03 01 02 00 09 21 11 01 00 01 22 32 00 07 05 81 03 08 00 0A 09 04 02 00 01 03 00 00 00 09 21 11 01 00 01 22 21 00 07 05 83 03 08 00 0A
report descriptor size = 50
Single endpoint HID:
  endpoint = 81
   size = 8
   interval = 10
new_Pipe
allocate_interrupt_pipe_bandwidth
 best_bandwidth = 3, at offset = 1, shift= 0
Descriptor 33 = HID
Descriptor 5 = ENDPOINT
Descriptor 4 = INTERFACE
HIDParser claim this=200040A0
 bInterfaceNumber =   2
 bInterfaceClass =    3
 bInterfaceSubClass = 0
 bInterfaceProtocol = 0
HID Parser Claim: 09 04 02 00 01 03 00 00 00 09 21 11 01 00 01 22 21 00 07 05 83 03 08 00 0A
report descriptor size = 33
Single endpoint HID:
  endpoint = 83
   size = 8
   interval = 10
new_Pipe
allocate_interrupt_pipe_bandwidth
 best_bandwidth = 3, at offset = 2, shift= 0
Descriptor 33 = HID
Descriptor 5 = ENDPOINT
control callback (hid)
05 01 09 04 A1 01 09 01 A1 00 09 30 09 31 15 00 25 FF 35 00 45 FF 75 08 95 02 81 02 C0 05 09 19 01 29 0F 15 00 25 01 95 10 75 01 81 02 C0
  mesg = 22000681
  got report descriptor
Found top level collection 10004
find_driver
  driver 20006EC0
control callback (hid)
05 01 09 02 A1 01 09 01 A1 00 05 09 19 01 29 03 15 00 25 01 95 03 75 01 81 02 95 01 75 05 81 01 05 01 09 30 09 31 15 81 25 7F 75 08 95 02 81 06 C0 C0
  mesg = 22000681
  got report descriptor
Found top level collection 10002
find_driver
  driver 20006EC0
  driver 20006EE4
No Driver claimed topusage: 10002
 
ok. I have been adding extra debug lines to "void USBHost::followup_Error(void)" in ehci.cpp
It seems the "While (p)" at line 927 never exits even though p gets sets to NULL
Code:
ERROR Followup
    remove from followup list
    not halted 20005180
    next2 20003160
    not halted 20003160
    next2 20004A40
    stray halted 20004A40
    next2 20004A80
    not halted 20004A80
    next2 0
I put another "println" in just after the "While{}" but it never gets printed!
Is this a compiler optimisation error?
I am using Arduino V1.8.19
 
Well a bit more reading of the code and I realised I needed to check the "haltedpipe" pointer.
It was NULL even though the next portion of code relied on it being a valid pointer. I then added a check for "haltedpipe" being NULL and bypassed the next while loop and if condition. This then stops the reset and seems to work.
Does this need to be applied to the Github?
 
I've added this to my list of issues to investigate. Can you give me a little more info about the devices needed to reproduce this problem?
 
The hub is just a generic chineese USB 2 one and the joysticks are 2 x Ultimarc Ultrastik 360's.
Joystick link

It was failing to boot cleanly about 50% of the time, the delay would take around 3-4 seconds. The reset must have been caused by de-referencing the halted pipe's null pointer in a while loop and then ultimately corrupting some random memory.
I am not sure what the fault is with the data stream but when it happens it is always after the first stick is ID'd correctly and just at the start of ID'ding the second.
Here is my mod to the "USBHost::followup_Error(void)" code

Code:
void USBHost::followup_Error(void)
{
    println("ERROR Followup");
    Transfer_t *p = async_followup_first;
    while (p) {
        if (followup_Transfer(p)) {
            // transfer completed
            Transfer_t *next = p->next_followup;
            remove_from_async_followup_list(p);
            println("    remove from followup list");
            if (p->qtd.token & 0x40) {
                Pipe_t *haltedpipe = p->pipe;
                free_Transfer(p);
                // traverse the rest of the list for unfinished work
                // from this halted pipe.  Remove from the followup
                // list and put onto our own temporary list
                Transfer_t *first = NULL;
                Transfer_t *last = NULL;
                p = next;
                while (p) {
                    Transfer_t *next2 = p->next_followup;
                    if (p->pipe == haltedpipe) {
                        println("    stray halted ", (uint32_t)p, HEX);
                        remove_from_async_followup_list(p);
                        if (first == NULL) {
                            first = p;
                            last = p;
                        } else {
                            last->next_followup = p;
                        }
                        p->next_followup = NULL;
                        if (next == p) next = next2;
                    }
                    p = next2;
                }
                // Check for valid pipe pointer
                if (haltedpipe) {
                    // halted pipe (probably) still has unfinished transfers
                    // find the halted pipe's dummy halt transfer
                    p = (Transfer_t *)(haltedpipe->qh.next & ~0x1F);
                    while (p && ((p->qtd.token & 0x40) == 0)) {
                        print("  qtd: ", (uint32_t)p, HEX);
                        print(", token=", (uint32_t)p->qtd.token, HEX);
                        println(", next=", (uint32_t)p->qtd.next, HEX);
                        p = (Transfer_t *)(p->qtd.next & ~0x1F);
                    }
                    if (p) {
                        // unhalt the pipe, "forget" unfinished transfers
                        // hopefully they're all on the list we made!
                        println("  dummy halt: ", (uint32_t)p, HEX);
                        haltedpipe->qh.next = (uint32_t)p;
                        haltedpipe->qh.current = 0;
                        haltedpipe->qh.token = 0;
                    } else {
                        println("  no dummy halt found, yikes!");
                        // TODO: this should never happen, but what if it does?
                    }
                }
                // Do any driver callbacks belonging to the unfinished
                // transfers.  This is done last, after retoring the
                // pipe to a working state (if possible) so the driver
                // callback can use the pipe.
                p = first;
                while (p) {
                    uint32_t token = p->qtd.token;
                    if (token & 0x8000 && haltedpipe->callback_function) {
                        // driver expects a callback
                        p->qtd.token = token | 0x40;
                        (*(p->pipe->callback_function))(p);
                    }
                    Transfer_t *next2 = p->next_followup;
                    free_Transfer(p);
                    p = next2;
                }
            } else {
                free_Transfer(p);
            }
            p = next;
        } else {
            // transfer still pending
            println("    remain on followup list");
            p = p->next_followup;
        }
    }
    // TODO: handle errors from periodic schedule!
}

Here is a full debug listing for a failed boot
Code:
USB2 PLL running
 reset waited 6
USBHS_ASYNCLISTADDR = 0
USBHS_PERIODICLISTBASE = 20003000
periodictable = 20003000
port change: 10001803
    connect
  begin reset
port change: 10001805
  port enabled
  end recovery
new_Device: 12 Mbit/sec
new_Pipe
enumeration:
enumeration:
enumeration:
Device Descriptor:
  12 01 00 02 09 00 00 08 E3 05 06 06 02 07 01 02 00 01
    VendorID = 05E3, ProductID = 0606, Version = 0702
    Class/Subclass/Protocol = 9(Hub) / 0 / 0
    Number of Configurations = 1
enumeration:
enumeration:
Manufacturer: ALCOR
enumeration:
Product: USB Hub 2.0   
enumeration:
Config data length = 25
enumeration:
Configuration Descriptor:
  09 02 19 00 01 01 00 E0 32
    NumInterfaces = 1
    ConfigurationValue = 1
  09 04 00 00 01 09 00 00 00
    Interface = 0
    Number of endpoints = 1
    Class/Subclass/Protocol = 9(Hub) / 0 / 0
  07 05 81 03 01 00 FF
    Endpoint = 1 IN
    Type = Interrupt
    Max Size = 1
    Polling Interval = 255
enumeration:
USBHub memory usage = 960
USBHub claim_device this=20005DA0
found possible interface, altsetting=0
number of interfaces found = 1
USBHub control callback
09 29 04 09 00 16 64 00 FF 00 00 00 00 00 00 00
Hub ports = 4
USBHub control callback
USBHub control callback
USBHub control callback
USBHub control callback
power turned on to all ports
device addr = 1
new_Pipe
allocate_interrupt_pipe_bandwidth
 best_bandwidth = 3, at offset = 0, shift= 0
pipe cap1 = F0010101
HUB Callback (member)
status = 6
getstatus, port = 1
deferred getstatus, port = 2
USBHub control callback
01 01 01 00
New Port Status
  status=10101  port=1
  state=0
  Device is present:
  Has Power
USBHub control callback
Port Status Cleared, port=1
getstatus, port = 2
USBHub control callback
01 01 01 00
New Port Status
  status=10101  port=2
  state=0
  Device is present:
  Has Power
USBHub control callback
Port Status Cleared, port=2
timer event (19999 us): Debounce Timer, this = 20005DA0, timer = 200060B8
ports in use bitmask = 6
getstatus, port = 1
deferred getstatus, port = 2
USBHub control callback
01 01 00 00
New Port Status
  status=101  port=1
  state=2
  Device is present:
  Has Power
getstatus, port = 2
USBHub control callback
01 01 00 00
New Port Status
  status=101  port=2
  state=2
  Device is present:
  Has Power
timer event (19998 us): Debounce Timer, this = 20005DA0, timer = 200060B8
ports in use bitmask = 6
getstatus, port = 1
deferred getstatus, port = 2
USBHub control callback
01 01 00 00
New Port Status
  status=101  port=1
  state=3
  Device is present:
  Has Power
getstatus, port = 2
USBHub control callback
01 01 00 00
New Port Status
  status=101  port=2
  state=3
  Device is present:
  Has Power
timer event (19998 us): Debounce Timer, this = 20005DA0, timer = 200060B8
ports in use bitmask = 6
getstatus, port = 1
deferred getstatus, port = 2
USBHub control callback
01 01 00 00
New Port Status
  status=101  port=1
  state=4
  Device is present:
  Has Power
getstatus, port = 2
USBHub control callback
01 01 00 00
New Port Status
  status=101  port=2
  state=4
  Device is present:
  Has Power
timer event (19998 us): Debounce Timer, this = 20005DA0, timer = 200060B8
ports in use bitmask = 6
getstatus, port = 1
deferred getstatus, port = 2
USBHub control callback
01 01 00 00
New Port Status
  status=101  port=1
  state=5
  Device is present:
  Has Power
getstatus, port = 2
USBHub control callback
01 01 00 00
New Port Status
  status=101  port=2
  state=5
  Device is present:
  Has Power
timer event (19998 us): Debounce Timer, this = 20005DA0, timer = 200060B8
ports in use bitmask = 6
getstatus, port = 1
deferred getstatus, port = 2
USBHub control callback
01 01 00 00
New Port Status
  status=101  port=1
  state=6
  Device is present:
  Has Power
sending reset
send_setreset
USBHub control callback
unhandled setup, message = 40323
getstatus, port = 2
USBHub control callback
01 01 00 00
New Port Status
  status=101  port=2
  state=6
  Device is present:
  Has Power
timer event (19998 us): Debounce Timer, this = 20005DA0, timer = 200060B8
ports in use bitmask = 4
getstatus, port = 2
USBHub control callback
01 01 00 00
New Port Status
  status=101  port=2
  state=6
  Device is present:
  Has Power
HUB Callback (member)
status = 2
getstatus, port = 1
USBHub control callback
03 01 10 00
New Port Status
  status=100103  port=1
  state=7
  Device is present:
  Enabled, speed = 12 Mbit/sec
  Has Power
USBHub control callback
unhandled setup, message = 140123
timer event (19999 us): Debounce Timer, this = 20005DA0, timer = 200060B8
ports in use bitmask = 4
getstatus, port = 2
USBHub control callback
01 01 00 00
New Port Status
  status=101  port=2
  state=6
  Device is present:
  Has Power
timer event (24998 us): Hello, I'm resettimer, this = 20005DA0, timer = 200060D4
port_doing_reset = 1
PORT_RECOVERY
new_Device: 12 Mbit/sec
new_Pipe
enumeration:
enumeration:
enumeration:
Device Descriptor:
  12 01 00 02 00 00 00 08 09 D2 11 05 33 00 04 05 06 01
    VendorID = D209, ProductID = 0511, Version = 0033
    Class/Subclass/Protocol = 0 / 0 / 0
    Number of Configurations = 1
enumeration:
enumeration:
Manufacturer: Ultimarc UltraStik
enumeration:
Product: Ultimarc Ultra-Stik Player 1
timer event (19998 us): Debounce Timer, this = 20005DA0, timer = 200060B8
ports in use bitmask = 4
getstatus, port = 2
enumeration:
Serial Number: 1
USBHub control callback
01 01 00 00
New Port Status
  status=101  port=2
  state=6
  Device is present:
  Has Power
enumeration:
Config data length = 84
enumeration:
Configuration Descriptor:
  09 02 54 00 03 01 00 80 32
    NumInterfaces = 3
    ConfigurationValue = 1
  09 04 00 00 01 03 00 00 00
    Interface = 0
    Number of endpoints = 1
    Class/Subclass/Protocol = 3(HID) / 0 / 0
  09 21 11 01 00 01 22 2E 00
    HID, 1 report descriptor
  07 05 82 03 08 00 0A
    Endpoint = 2 IN
    Type = Interrupt
    Max Size = 8
    Polling Interval = 10
  09 04 01 00 01 03 01 02 00
    Interface = 1
    Number of endpoints = 1
    Class/Subclass/Protocol = 3(HID) / 1(Boot) / 2(Mouse)
  09 21 11 01 00 01 22 32 00
    HID, 1 report descriptor
  07 05 81 03 08 00 0A
    Endpoint = 1 IN
    Type = Interrupt
    Max Size = 8
    Polling Interval = 10
  09 04 02 00 01 03 00 00 00
    Interface = 2
    Number of endpoints = 1
    Class/Subclass/Protocol = 3(HID) / 0 / 0
  09 21 11 01 00 01 22 21 00
    HID, 1 report descriptor
  07 05 83 03 08 00 0A
    Endpoint = 3 IN
    Type = Interrupt
    Max Size = 8
    Polling Interval = 10
enumeration:
USBHub memory usage = 960
USBHub claim_device this=20006160
HIDParser claim this=20003220
HIDParser claim this=20003960
HIDParser claim this=200040A0
HIDParser claim this=200047E0
HIDParser claim this=20004F20
HIDParser claim this=20005660
Descriptor 4 = INTERFACE
HIDParser claim this=20003220
 bInterfaceNumber =   0
 bInterfaceClass =    3
 bInterfaceSubClass = 0
 bInterfaceProtocol = 0
HID Parser Claim: 09 04 00 00 01 03 00 00 00 09 21 11 01 00 01 22 2E 00 07 05 82 03 08 00 0A 09 04 01 00 01 03 01 02 00 09 21 11 01 00 01 22 32 00 07 05 81 03 08 00 0A 09 04 02 00 01 03 00 00 00 09 21 11 01 00 01 22 21 00 07 05 83 03 08 00 0A
report descriptor size = 46
Single endpoint HID:
  endpoint = 82
   size = 8
   interval = 10
new_Pipe
allocate_interrupt_pipe_bandwidth
 best_bandwidth = 3, at offset = 8, shift= 0
Descriptor 33 = HID
Descriptor 5 = ENDPOINT
Descriptor 4 = INTERFACE
HIDParser claim this=20003960
 bInterfaceNumber =   1
 bInterfaceClass =    3
 bInterfaceSubClass = 1
 bInterfaceProtocol = 2
HID Parser Claim: 09 04 01 00 01 03 01 02 00 09 21 11 01 00 01 22 32 00 07 05 81 03 08 00 0A 09 04 02 00 01 03 00 00 00 09 21 11 01 00 01 22 21 00 07 05 83 03 08 00 0A
report descriptor size = 50
Single endpoint HID:
  endpoint = 81
   size = 8
   interval = 10
new_Pipe
allocate_interrupt_pipe_bandwidth
 best_bandwidth = 3, at offset = 1, shift= 0
Descriptor 33 = HID
Descriptor 5 = ENDPOINT
Descriptor 4 = INTERFACE
HIDParser claim this=200040A0
 bInterfaceNumber =   2
 bInterfaceClass =    3
 bInterfaceSubClass = 0
 bInterfaceProtocol = 0
HID Parser Claim: 09 04 02 00 01 03 00 00 00 09 21 11 01 00 01 22 21 00 07 05 83 03 08 00 0A
report descriptor size = 33
Single endpoint HID:
  endpoint = 83
   size = 8
   interval = 10
new_Pipe
allocate_interrupt_pipe_bandwidth
 best_bandwidth = 3, at offset = 2, shift= 0
Descriptor 33 = HID
Descriptor 5 = ENDPOINT
control callback (hid)
05 01 09 04 A1 01 09 01 A1 00 09 30 09 31 15 00 25 FF 35 00 45 FF 75 08 95 02 81 02 C0 05 09 19 01 29 0F 15 00 25 01 95 10 75 01 81 02 C0
  mesg = 22000681
  got report descriptor
Found top level collection 10004
find_driver
  driver 20006EC0
ERROR Followup
    remove from followup list
    stray halted 20004A40
 
Regardless of fixing the crash in followup_Error() you're still going to be facing the problem that a transfer is failing/stalling when it shouldn't be, which will likely cause the controller to not be recognized. Given that it seems to operate correctly after restarting I would suspect the initial power draw of both sticks is too much - try using an externally powered hub.
 
Regardless of fixing the crash in followup_Error() you're still going to be facing the problem that a transfer is failing/stalling when it shouldn't be, which will likely cause the controller to not be recognized. Given that it seems to operate correctly after restarting I would suspect the initial power draw of both sticks is too much - try using an externally powered hub.
My first thought was this so I powered the hub & sticks independently only taking the D+/- from the Teensy. Still got the same issue.
I do have another pair of Ultrastik360's and a different hub somewhere just need to find it. May be just the hub causing the issue.
But with the pointer check in there the ID of the second JS seems to go without a hitch. I was wondering if the fact that the JS self modifies its Product ID when the second is detected was something to do with the failed message.
 
I was wondering if the fact that the JS self modifies its Product ID when the second is detected was something to do with the failed message.
Are you sure this is really done dynamically when multiple sticks are connected? It doesn't seem possible since a device shouldn't be able to know what other devices are connected to the host, especially so if they're connected to different root hubs... and the information on this page implies that the IDs are statically assigned to each stick.
 
Are you sure this is really done dynamically when multiple sticks are connected? It doesn't seem possible since a device shouldn't be able to know what other devices are connected to the host, especially so if they're connected to different root hubs... and the information on this page implies that the IDs are statically assigned to each stick.
Good point. It's been a long time since I made the panel up with both sticks in and I have certainly used the PC app with them. It certainly didn't come up with a Change ID option but could well have done it automatically. I need to reflash one of the extra sticks I have here back to the USB firmware from the Analogue firmware, once I have done that I can see what happens if both sticks ID with the 0x0511 and are plugged into the Teensy.
 
Are you sure this is really done dynamically when multiple sticks are connected? It doesn't seem possible since a device shouldn't be able to know what other devices are connected to the host, especially so if they're connected to different root hubs... and the information on this page implies that the IDs are statically assigned to each stick.
You are correct. If I only ever connect the sticks to the PC one at a time they both ID as 0511. Then when I connect both to the Teensy they both still ID as 0511. It's when you run their Ultramap software on the PC with both sticks plugged in the second stick gets changed to 0512.

Regardless of fixing the crash in followup_Error() you're still going to be facing the problem that a transfer is failing/stalling when it shouldn't be, which will likely cause the controller to not be recognized. Given that it seems to operate correctly after restarting I would suspect the initial power draw of both sticks is too much - try using an externally powered hub.
Having put the Pipe pointer check in the followup_Error(), the startup sequence of events continues quite happily with no further errors and works 100% with my custom HID device. I do wonder what msg/packet is causing the error but for my use it is working perfectly.
 
You are correct. If I only ever connect the sticks to the PC one at a time they both ID as 0511. Then when I connect both to the Teensy they both still ID as 0511. It's when you run their Ultramap software on the PC with both sticks plugged in the second stick gets changed to 0512.
That's a little bit concerning because I wonder how you are going to tell the sticks apart (e.g. which one to interpret as "left" and which one as "right") without relying on which ports they are connected to. The obvious way would be if they had different serial numbers, but:
Manufacturer: Ultimarc UltraStik
enumeration:
Product: Ultimarc Ultra-Stik Player 1
timer event (19998 us): Debounce Timer, this = 20005DA0, timer = 200060B8
ports in use bitmask = 4
getstatus, port = 2
enumeration:
Serial Number: 1
Having a serial number of "1" doesn't exactly inspire confidence that it will be different for each individual device. Maybe there's a vendor-specific usb command to retrieve some sort of unique identifier, otherwise the ultrastik software wouldn't be able to identify which device to re-assign.

A question though: if you have used the ultrastik software to assign a stick an ID of 2, 3 or 4 does the product string for it still show up as "Ultimarc Ultra-Stik Player 1"? From the screenshots, reassigning a stick makes that name change in the windows game controllers dialog box so maybe that's how it could be done.
 
That's a little bit concerning because I wonder how you are going to tell the sticks apart (e.g. which one to interpret as "left" and which one as "right") without relying on which ports they are connected to.
A question though: if you have used the ultrastik software to assign a stick an ID of 2, 3 or 4 does the product string for it still show up as "Ultimarc Ultra-Stik Player 1"? From the screenshots, reassigning a stick makes that name change in the windows game controllers dialog box so maybe that's how it could be done.
You have to connect a single stick with the Ultrmap software and you can then assign it as Stick 2 (product ID 0512). Then you can connect the other stick and it will id as Stick 1 (0511). So I presume it reprograms the NV memory it the stick to change its ID as it remembers it until it is changed.
 
Back
Top