USB Host Shield - Nutzung für älteres Spielzeug

Hallo Community,

ich versuche ein altes USB Spielzeug zum laufen mit einem Arduino zu bringen. Das Gerät ist alt. Die Software läuft nicht vernünftig unter Windows 10 (nur teilweise). Außerdem habe ich mit einem USB Sniffer "versucht" Daten auszulesen. Diese Ergebnisse stimmen weitestgehend mit Coding aus dem Netz überein.

Sniffing Variante 1

000003: Class-Specific Request (DOWN), 2025-06-18 18:29:00,315954 +0,000139. (1. Device: Tenx Nonstandard Devic (Rocket Launcher))
Destination: Interface, Index 1
Reserved Bits: 34
Request: 0x9
Value: 0x200
Send 0x8 bytes to the device
000004: Control Transfer (UP), 2025-06-18 18:29:00,316561 +0,000607. (1. Device: Tenx Nonstandard Devic (Rocket Launcher)) Status: 000000x0
Pipe Handle: Control Pipe

 55 53 42 43 00 40 02 00                           USBC.@..
Setup Packet

000005: Class-Specific Request (DOWN), 2025-06-18 18:29:00,401962 +0,085401. (1. Device: Tenx Nonstandard Devic (Rocket Launcher))
Destination: Interface, Index 1
Reserved Bits: 34
Request: 0x9
Value: 0x200
Send 0x8 bytes to the device
000006: Control Transfer (UP), 2025-06-18 18:29:00,402416 +0,000454. (1. Device: Tenx Nonstandard Devic (Rocket Launcher)) Status: 000000x0
Pipe Handle: Control Pipe

 55 53 42 43 00 00 04 00                           USBC....
Setup Packet

Sniffing Variante 2

Setup Packet
000013: Class-Specific Request (DOWN), 2025-06-18 18:23:45,088762 +2,512216. (1. Device: Tenx Nonstandard Devic (Rocket Launcher))
Destination: Interface, Index 0
Reserved Bits: 34
Request: 0x9
Value: 0x200
Send 0x40 bytes to the device

000014: Control Transfer (UP), 2025-06-18 18:23:45,093767 +0,005005. (1. Device: Tenx Nonstandard Devic (Rocket Launcher)) Status: 000000x0
Pipe Handle: Control Pipe

 00 00 00 01 00 00 08 08 00 00 00 00 00 00 00 00   ................
 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................
 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................
 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................

Das ganze habe ich nun mit dem Host Shield versucht umzusetzen. Ist ein Version 1.0 Modell. Ich bekomme - endlich - eine Verbindung und konnte auch das Gerät auslesen.

Start
Device addressed... Requesting device descriptor.
Device descriptor: 

Descriptor Length:	12
USB version:		1.10
Class:			00 Use class information in the Interface Descriptor
Subclass:		00
Protocol:		00
Max.packet size:	08
Vendor  ID:		1130
Product ID:		0202
Revision ID:		0100
Mfg.string index:	00
Prod.string index:	02 Length: 46 Contents: Tenx Nonstandard Devic
Serial number index:	00
Number of conf.:	01

Configuration number 0
Total configuration length: 59 bytes

Configuration descriptor:
Total length:		003B
Number of interfaces:	02
Configuration value:	01
Configuration string:	00
Attributes:		80
Max.power:		32 100ma

Interface descriptor:
Interface number:	00
Alternate setting:	00
Endpoints:		01
Class:			03 HID (Human Interface Device)
Subclass:		00
Protocol:		00
Interface string:	00

HID descriptor:
Descriptor length:	09 9 bytes
HID version:		1.10
Country Code:		0 Not Supported
Class Descriptors:	1
Class Descriptor Type:	22 Report
Class Descriptor Length:43 bytes

HID report descriptor:
Descriptor length:	09 9 bytes
HID version:		1.10
Country Code:		0 Not Supported
Class Descriptors:	1
Class Descriptor Type:	22 Report
Class Descriptor Length:43 bytes

HID report descriptor:

Length: 1  Type: Global		Tag: Usage Page		Generic Desktop Controls  Data: 01
Length: 1  Type: Local		Tag: Usage		  Data: 00
Length: 1  Type: Main		Tag: Collection		Application (mouse, keyboard)  Data: 01
Length: 1  Type: Global		Tag: Report Count	  Data: 08
Length: 1  Type: Global		Tag: Report Size	  Data: 08
Length: 1  Type: Global		Tag: Logical Minimum	  Data: 00
Length: 2  Type: Global		Tag: Logical Maximum	  Data: FF  Data: 00
Length: 1  Type: Global		Tag: Usage Page		Keyboard/Keypad  Data: 07
Length: 1  Type: Local		Tag: Usage Minimum	  Data: 00
Length: 2  Type: Local		Tag: Usage Maximum	  Data: FF  Data: 00
Length: 1  Type: Main		Tag: Feature		Data,Array,Absolute,No Wrap,Linear,Preferred State,No Null Position,Non-volatile(Ignore for Input),  Data: 00
Length: 1  Type: Global		Tag: Logical Minimum	  Data: 00
Length: 2  Type: Global		Tag: Logical Maximum	  Data: FF  Data: 00
Length: 1  Type: Global		Tag: Report Count	  Data: 40
Length: 1  Type: Global		Tag: Report Size	  Data: 08
Length: 1  Type: Global		Tag: Usage Page		Keyboard/Keypad  Data: 07
Length: 1  Type: Local		Tag: Usage Minimum	  Data: 00
Length: 2  Type: Local		Tag: Usage Maximum	  Data: FF  Data: 00
Length: 1  Type: Main		Tag: Output		Data,Array,Absolute,No Wrap,Linear,Preferred State,No Null Position,Non-volatile(Ignore for Input),  Data: 00
Length: 0  Type: Main		Tag: End Collection	

Endpoint descriptor:
Endpoint address:	01 Direction: IN
Attributes:		03 Transfer type: Interrupt
Max.packet size:	0008
Polling interval:	01 1 ms

Interface descriptor:
Interface number:	01
Alternate setting:	00
Endpoints:		01
Class:			03 HID (Human Interface Device)
Subclass:		00
Protocol:		00
Interface string:	00

HID descriptor:
Descriptor length:	09 9 bytes
HID version:		1.10
Country Code:		33 US
Class Descriptors:	1
Class Descriptor Type:	22 Report
Class Descriptor Length:23 bytes

HID report descriptor:

Length: 1  Type: Global		Tag: Usage Page		Generic Desktop Controls  Data: 01
Length: 1  Type: Local		Tag: Usage		  Data: 00
Length: 1  Type: Main		Tag: Collection		Application (mouse, keyboard)  Data: 01
Length: 1  Type: Global		Tag: Report Count	  Data: 08
Length: 1  Type: Global		Tag: Report Size	  Data: 08
Length: 1  Type: Global		Tag: Logical Minimum	  Data: 00
Length: 2  Type: Global		Tag: Logical Maximum	  Data: FF  Data: 00
Length: 1  Type: Global		Tag: Usage Page		Keyboard/Keypad  Data: 07
Length: 1  Type: Local		Tag: Usage Minimum	  Data: 00
Length: 2  Type: Local		Tag: Usage Maximum	  Data: FF  Data: 00
Length: 1  Type: Main		Tag: Feature		

Endpoint descriptor:
Endpoint address:	02 Direction: IN
Attributes:		03 Transfer type: Interrupt
Max.packet size:	0008
Polling interval:	01 1 ms

Das ganze habe ich nun - analog einem nahezu deckungsgleichen Beispiels aus dem Web - versucht umzusetzen.

  uint8_t data[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };

  data[0] = 'U';
  data[1] = 'S';
  data[2] = 'B';
  data[3] = 'C';
  data[4] = 0;
  data[5] = 0;
  data[6] = 4;
  data[7] = 0;

  // ctrlReq(addr, endPoint, bmReqType, bRequest, wValLo, wValHi, wInd, total, sizeof(data) / sizeof(byte), data, NULL);
  ctrlReq(addr, endPoint, bmReqType, bRequest, wValLo, wValHi, wInd, sizeof(data) / sizeof(uint8_t), data);

  data[0] = 'U';
  data[1] = 'S';
  data[2] = 'B';
  data[3] = 'C';
  data[4] = 0;
  data[5] = 64;
  data[6] = 2;
  data[7] = 0;

  ctrlReq(addr, endPoint, bmReqType, bRequest, wValLo, wValHi, wInd, sizeof(data) / sizeof(byte), data);

  data[0] |= 0x0;
  data[1] |= 0x0;
  data[2] |= 0x0;
  data[3] |= 0x0;
  data[4] |= 0x0;
  data[5] |= 0x0;
  data[6] |= 0x0;
  data[7] |= 0x0;

  switch (action) {
    case MissileLauncherActions::LEFT:
      data[1] |= 1;
      break;
    case MissileLauncherActions::RIGHT:
      data[2] |= 1;
      break;
    case MissileLauncherActions::UP:
      data[3] |= 1;
      break;
    case MissileLauncherActions::DOWN:
      data[4] |= 1;
      break;
    case MissileLauncherActions::FIRE:
      data[5] |= 1;
      break;
  }

  data[6] |= 8;
  data[7] |= 8;

  ctrlReq(addr, endPoint, bmReqType, bRequest, wValLo, wValHi, wInd, sizeof(data) / sizeof(byte), data);

Es funktioniert nicht. Unabhängig davon habe ich das Gefühl, dass ich einer Lösung sehr nahe bin. Leider verstehe ich nicht, was der Aufruf von ctrlReq genau tut bzw. was die Parameter bedeuten (außer addr und data und natürlich die Berechnung der Array-Größe). Also: endPoint, bmReqType, bRequest, wValLo, wValHi, wInd

MissileLauncher::MissileLauncher()
  : USB() {
  addr = 1;  // Todo need to change it if connected through a USB Hub
  endPoint = 0;
  bmReqType = 0x21;
  bRequest = 0x09;
  wValLo = 0x00;
  wValHi = 0x02;
  wInd = 0;
  total = 1;
  // addr = 1;  // Todo need to change it if connected through a USB Hub
  // endPoint = 0;
  // bmReqType = 0x21;
  // bRequest = 0x09;
  // wValLo = 0x00;
  // wValHi = 0x02;
  // wInd = 0x200;
}

Über Hilfe und Anregungen würde ich mich freuen...

Ja, das könnte sein.

Das ist nicht das Gleiche.
Mach mal in
data[6] = 0x04
Und so natürlich dann auch alle anderen Werte - aber hier sieht man den Unterschied sehr deutlich....

Du siehst in der Ausgabe aus dem Sniffer HEX-Werte.

Das 'U' z.B. ist 0x55 oder decimal 85.

Was ist das? Die Datanbank meint "Rocket Launcher" Hört sich spannend an :slight_smile:

// edit: aahhh...
Da hat sich schon mal einer mit python dran versucht GitHub - iconicporridge/tenx_launcher_hq: A repository for my python codes to operate a Tenx (M&S) USB foam missile launcher

// EDIT2 Hier hat das jemand mit einem Arduino gemacht: Controlling USB Missile Launchers using ArduinoHardware Fun | Hardware Fun
Das ganze gibt es als lib und zum sofortigen Einsatz dann hier: GitHub - sudar/MissileLauncher: Arduino Library to control USB Missile Launcher

@my_xy_projekt das ist mega, was du mir da geschickt hast. Ich frage mich halt, wie ihr das immer so schnell macht. Ich habe Tage gesucht und etwas ähnliches gefunden. Was ich gefunden habe Ist wohl eine Umsetzung für den Apple. Da habe ich auch mein Sniffing im Coding grob wieder gefunden (kann verschiedene Modell abbilden). Also dass sie die Steuersignale in verteilten Bytes senden obwohl da immer nur 1 oder 0 drin steht. Bei Weiterentwicklungen des Modells ist es anders. Da werden nur Bits übertragen. 1 -> Links, 2 -> Rechts, 4 -> runter, 8 -> hoch, etc.

Dein Link verweißt allerdings genau auf mein Modell. Mein Problem: Ich verstehe grob Python und werde mir das heute anschauen. Nur leider verstehe ich nicht, was die Funktion ctrlReq vom USB Shield benötigt. Also die oben fett markierten Parameter. Wenn du mir dazu vielleicht noch einen Tipp geben könntest, bin ich zuversichtlich, dass ich es zum Laufen bringe!

Mein Coding ist übrigens eine Adaption von wiederrum einem anderen Codingschnippsel den ich gefunden hatte, aber für ein anderes Rocket Launcher Modell. Ich habe mich auch schon gewundert, dass sie dort mal in Klartext (USBC) dann in Dezimalschreibweise und einmal auch in Hex den Datensatz befüllen.

Das ist meine Basis. Hatte ich gefunden. Dort siehst du auch das mit den Hochkomma... Das Modell braucht aber andere Parameter. Ist zu meinen Modell nicht kompatibel. Aber der LInk1 ist sehr, sehr und nochmal sehr vielversprechend.

Das frag ich mich auch, wenn ich sehe, wie ihr da python-Code in die Tasten kloppt und der funktioniert.... :wink:
Das mit der lib sollte klappen.
Wenns dann noch Fragen dazu gibt, helf ich gerne.

Okay... also wenn das die Lösung ist... ich habe mich auf das Coding von GitHub verlassen. Aber du hast natürlich recht. Und das ich mich genau darüber schon gewundert habe, hatte ich in meinen vorher gehenden Post geschrieben.

Das erste was geändert werden muss, ist VID/PID:

Dann sollte das auch kommunizieren.

Deine Werte sind 0x1130 und 0x0202

Wenn Deine Werte tatsächlich anders sind, dann muss das hier geändert werden:

Und dann passt das :wink:

Ja... ich schaue mir das mal in Ruhe an. Das wusste ich. Ich brauch exakt das aus dem Python script. Das habe ich ja auch schon halb richtig umgesetzt. Das mit dem HEX passe ich an. Bleibt aber das Problem, dass ich nicht weiß, wie iich es dem USB Shield übergebe. Also was muss in die Werte rein...

    def check_connection(self):
        dev = usb.core.find(idVendor=0x1130, idProduct=0x0202)
        if (dev is None):
            return False
        else:
            return True

    def setup_commands(self):
        self.INITA = (85, 83, 66, 67,  0,  0,  4,  0)
        self.INITB = (85, 83, 66, 67,  0, 64,  2,  0)
        self.CMDFILL = ( 8,  8,
            0,  0,  0,  0,  0,  0,  0,  0,
            0,  0,  0,  0,  0,  0,  0,  0,
            0,  0,  0,  0,  0,  0,  0,  0,
            0,  0,  0,  0,  0,  0,  0,  0,
            0,  0,  0,  0,  0,  0,  0,  0,
            0,  0,  0,  0,  0,  0,  0,  0,
            0,  0,  0,  0,  0,  0,  0,  0)
        self.stop = (0,  0,  0,  0,  0,  0)
        self.left = (0,  1,  0,  0,  0,  0)
        self.right = (0,  0,  1,  0,  0,  0)
        self.up = (0,  0,  0,  1,  0,  0)
        self.down = (0,  0,  0,  0,  1,  0)
        self.leftup = (0,  1,  0,  1,  0,  0)
        self.rightup = (0,  0,  1,  1,  0,  0)
        self.leftdown = (0,  1,  0,  0,  1,  0)
        self.rightdown = (0,  0,  1,  0,  1,  0)
        self.fire = (0,  0,  0,  0,  0,  1)

    def fire_launcher(self):
        if (self.dev is not None):
            self.dev.ctrl_transfer(bmRequestType=33, bRequest=9, wValue=2, wIndex=1, data_or_wLength=self.INITA)
            self.dev.ctrl_transfer(0x21, 0x09, 0x02, 0x01, self.INITB)
            self.dev.ctrl_transfer(0x21, 0x09, 0x02, 0x01, self.fire+self.CMDFILL)

    def move(self, cmd):
        if (self.dev is not None):
            self.dev.ctrl_transfer(0x21, 0x09, 0x02, 0x01, self.INITA)
            self.dev.ctrl_transfer(0x21, 0x09, 0x02, 0x01, self.INITB)
            self.dev.ctrl_transfer(0x21, 0x09, 0x02, 0x01, cmd+self.CMDFILL)

Also was mache ich hier...

 // ctrlReq(addr, endPoint, bmReqType, bRequest, wValLo, wValHi, wInd, total, sizeof(data) / sizeof(byte), data, NULL);
  ctrlReq(addr, endPoint, bmReqType, bRequest, wValLo, wValHi, wInd, sizeof(data) / sizeof(uint8_t), data);

was muss ich bei " endPoint, bmReqType, bRequest, wValLo, wValHi, wInd" mitgeben. Oder stehe ich gerade auf der Leitung. Möchte ich nicht ausschließen. Nach mehreren Tagen rumgefrickel bin ich nicht mehr ganz klar im Kopf :wink:

self.dev.ctrl_transfer(bmRequestType=33, bRequest=9, wValue=2, wIndex=1, data_or_wLength=self.INITA)

oder

self.dev.ctrl_transfer(0x21, 0x09, 0x02, 0x01, self.INITA)

das ist die halbe Miete... das bleibt offen. in der Python Version gibt es keinen endPoint und auch nur ein Value und nicht Low und High. Was auch immer das ist.

endPoint
wValLo, wValHi

Etwas im Internet zu suchen braucht Erfahrung und Wissen. Oft haperts an den richtigen Suchbegriffen. Wenn man das öfters macht weiß man einfach nach was man suchen muß.

PS auch ich finde nicht alles und suche manchmal stundenlang nach Bildern die das Teil das ich haben will darstellt, das ich mal gesehen hab.

Grüße Uwe

ich habe das jetzt analog dem Python-Script umgebaut. Gefühlt bin ich kurz vor dem Ziel. Ich bekomme eine Fehlermeldung:

Serieller Monitor

Data packet error: 5

So ist das Coding (Auszug)

  addr = 1;  // Todo need to change it if connected through a USB Hub
  endPoint = 0x00;
  bmReqType = 0x21;
  bRequest = 0x09;
  wValLo = 0x00;
  wValHi = 0x02;
  wInd = 0x01;

Coding Teil 2

void issueCommand(...){
  uint8_t INITA[8] = { 0x55, 0x53, 0x42, 0x43, 0x00, 0x00, 0x04, 0 };
  uint8_t INITB[8] = { 0x55, 0x53, 0x42, 0x43, 0x00, 0x40, 0x02, 0 };

  uint8_t DOACT[64] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x08,
                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };

  // aus dem python script
  // self.stop = (0,  0,  0,  0,  0,  0)
  // self.left = (0,  1,  0,  0,  0,  0)
  // self.right = (0,  0,  1,  0,  0,  0)
  // self.up = (0,  0,  0,  1,  0,  0)
  // self.down = (0,  0,  0,  0,  1,  0)
  // self.leftup = (0,  1,  0,  1,  0,  0)
  // self.rightup = (0,  0,  1,  1,  0,  0)
  // self.leftdown = (0,  1,  0,  0,  1,  0)
  // self.rightdown = (0,  0,  1,  0,  1,  0)
  // self.fire = (0,  0,  0,  0,  0,  1)

  // vergleich aufruf python versus c++ mit usb host shield
  // self.dev.ctrl_transfer(bmRequestType=33, bRequest=9, wValue=2, wIndex=1, data_or_wLength=self.INITA)
  // ctrlReq(addr, endPoint, bmReqType, bRequest, wValLo, wValHi, wInd, sizeof(data) / sizeof(byte), data);

  // aufrufe python
  // self.dev.ctrl_transfer(0x21, 0x09, 0x02, 0x01, self.INITA)
  // self.dev.ctrl_transfer(0x21, 0x09, 0x02, 0x01, self.INITB)
  // self.dev.ctrl_transfer(0x21, 0x09, 0x02, 0x01, cmd+self.CMDFIL)

  ctrlReq(1, endPoint, bmReqType, bRequest, wValLo, wValHi, wInd, sizeof(INITA) / sizeof(uint8_t), INITA);

  ctrlReq(1, endPoint, bmReqType, bRequest, wValLo, wValHi, wInd, sizeof(INITB) / sizeof(uint8_t), INITB);

  switch (action) {
    case LEFT:
      DOACT[1] = 0x01;
      break;
    case RIGHT:
      DOACT[2] = 0x01;
      break;
    case UP:
      DOACT[3] = 0x01;
      break;
    case DOWN:
      DOACT[4] = 0x01;
      break;
    case FIRE:
      DOACT[5] = 0x01;
      break;
  }

  ctrlReq(1, endPoint, bmReqType, bRequest, wValLo, wValHi, wInd, sizeof(DOACT) / sizeof(uint8_t), DOACT);
}

Habt ihr vielleicht noch eine Idee?