Skip to content

2. SMX HID USB Stage Interface

Fernando Chorney edited this page Apr 11, 2024 · 1 revision

StepManiaX HID/USB Stage Interface

This section of the wiki is going to attempt to document and explain how the SMX Stage hardware interacts through the USB/HID interface.

StepManiaX USB Descriptors/Info

The Stage MCU (Master Control Unit) is the device that communicates between the game/computer and the stage circuit boards. This device seems to be a form of Arduino Micro (if anyone has any more specifics on this please let me know), with connects to the computer/game through a USB Mini port.

When enumerating USB devices to find the StepManiaX MCU, you want to look for the following values:

vendor_id: 0x2341
product_id: 0x8037
product_name: StepManiaX

Since the SMX MCU uses the default Arduino IDs, you generally have to check the product name to make sure that you aren't connecting to some other Arduino device.

Send/Receive to/from the HID Interface

Firstly, in my experience, the SMX Interface does not support featureReports in any HID implementations I have tested, which includes Web HIDDevice and hidapi.

Packet Size

The StepManiaX HID Interface deals in packet sizes of 64 bytes. Depending on the library you are using to communicate, you might have to adjust your packet data slightly. Here I will describe two libraries that I have used and the slight differences between them.

HIDDevice (Javascript)

sendReport()

The HIDDevice library has a function called sendReport() which takes in two arguments.

  • reportId: The report ID you are sending to. (For communicating with the SMX Stages, this is always 5).
  • data: The data in bytes.

Since this function takes in the report ID as an argument, your data must then always be 63 bytes. If you have less then that, you must pad the data with 0s.

inputReport

Similarly, to read data you must hook into the inputReport event callback.

This callback will return an HIDInputReportEvent object which contains the following properties:

  • data: The data received (this will be 63 bytes of data).
  • device: The device descriptor that sent the data.
  • reportId: The report ID that this data was sent on. (For communicating with the SMX Stages this is always 3 or 6).

hidapi (python)

write()

For hidapi, the write() function simply takes in a list of data. As mentioned above, this must be a maximum of 64 bytes, but compared to the HIDDevice implementation, you must prepend the report ID to the first byte of your data.

[5, ..., data, ...]

Similarly, the packets must always be 64 bytes so you will pad the list with 0's if you do not have enough data to make a full packet.

read()

The read() function takes in two arguments.

  • max_length: The maximum number of bytes to read in at one time. (For communicating with the SMX Stages, this should always be 64)
  • timeout_ms: The time in milliseconds to time out. (I don't actually use this, but it could be useful).

Similarly this will return 64 bytes of data with the report ID as the first byte, and the next 63 bytes will be the data sent.

SMX Interface Packet Format

As mentioned above, the interface deals strictly in 64 byte packets, with the first byte being the report ID for both input and output. The data must always be 64 bytes, so it must be padded with 0's if the data being sent is smaller than 64 bytes.

Endianness

Please Note: The SMX Stage sends and receives data in LittleEndian mode for all of its communication.

Special Packet Flags

We will see below that the packets use these flags to signify what type of packet we are sending or receiving.

  • 0x1: End of Command
  • 0x2: Host Command Finished
  • 0x4: Start of Command
  • 0x7: Acknowledgement

Send (ReportID 5) Packet Format

Single Packet

Let's look at a device info packet request. which only consists of a single packet.

The packet is split into the following sections:

[5, 5, 1, 105, 0, ..., 0]
|_|__|__|____|__________|
| |  |  |    |
| |  |  |    |___________ 5. Data/Padding bytes
| |  |  |________________ 4. Device Info Command. 105 = ASCII value for "i"
| |  |___________________ 3. The size of the total data in this particular packet
| |______________________ 2. The packet flag byte for this packet. 0x4 (Start of Command) | 0x1 (End of Command)
|________________________ 1. The report ID for the packet
  1. Report ID: On sends, this will always be 5. On receives, this will either be 3 or 6.
  2. Packet Flag: This flag defines what kind of packet this is.
  3. Data Size: The amount of data in 8-bit bytes that the interface should read from this packet.
  4. Command: The actual command we are sending.
  5. Data/Padding: Either the data we are sending, or padding with 0's.

Multi Packet

Now let's look at a multi packet request so we can dive into what the Packet Flags mean.

Let's look at a WriteConfigV5 command:

Packet 1
[5, 4, 61, 87, ...]
|_|__|___|___|____|
| |  |   |   |    
| |  |   |   |_____ 5. Data Bytes
| |  |   |_________ 4. Write Config Command. 87 = ASCII value for "W"
| |  |_____________ 3. The size of the total data in this particular packet (1 byte for command + 60 bytes for data)
| |________________ 2. The packet flag byte for this packet. 0x4 (Start of Command)
|__________________ 1. The report ID for the packet

Packet 2 to N
[5, 0, 61, ...]
|_|__|___|____|
| |  |   |
| |  |   |_____ 4. Data Bytes
| |  |_________ 3. The size of the total data in this particular packet (61 bytes of data)
| |____________ 2. The packet flag byte for this packet. 0x0 (Absence of packet flag. Just Data)
|______________ 1. The report ID for the packet

Last Packet
[5, 1, 8, ...]
|_|__|__|____|
| |  |  |
| |  |  |_____ 4. Data/Padding Bytes
| |  |________ 3. The size of the total data in this particular packet (8 bytes of data, the rest would be padding)
| |___________ 2. The packet flag byte for this packet. 0x1 (End of Command)
|_____________ 1. The report ID for the packet

Receive (ReportID 6) Packet Format

The data we read back from the SMX Interface with report ID 6 is formatted in the same way as the data we send for single and multi packet data.

The only major differences would be the 2 special flags that can be found in these packets: Command Finished, and Acknowledgement.

Command Finished

The Command finished flag (0x2) can be added to the flag of any incoming packet (as far as I can tell). This lets us know that a command we wrote to the device has finished executing, and it's safe to start writing another.

[6, 6, 61, ...]
|_|__|___|____|
| |  |   |
| |  |   |_____ 4. Data/Padding Bytes
| |  |_________ 3. The size of the total data in this particular packet
| |____________ 2. The packet flag byte for this packet. 0x4 (Start of Command) | 0x2 (Host Command Finished)
|______________ 1. The report ID for the packet

Acknowledgement

The Acknowledgement packet is simply a flag set to 0x7, with no extra data. This isn't necessarily called acknowledgement in the source data, but it's effectively a combination of 0x1 (Start of Command) | 0x2 (Host Command Finished) | 0x4 (End of Command).

This command tells us that the interface has acknowledged a command we have sent, and that we are free to send another. This is typically the response we see when we send a command that we don't expect to receive data back for.

Acknowledgement Packet
[6, 7, ...]
|_|__|____|
| |  |
| |  |_____ 3. All 0's
| |________ 2. The packet flag byte for this packet. 0x7 (Acknowledgement)
|__________ 1. The report ID for the packet

Receive (ReportID 3) Packet Format

Report ID 3 will consistently have data sent to it from the SMX Interface at a regular interval. This data consists of a 16-Bit LittleEndian encoded bit mask.

This is what we would receive if up and down were currently activated.

[3, 130, 0]
|_|____|__|
| |    |
| |    |___ 3. The first byte
| |________ 2. The second byte
|__________ 1. The report ID for the packet

To read this data, we first take the 2 given bytes and combine them (taking into account the endianness).

130 = 10000010
  0 = 00000000
Combined = 00000000 10000010

From there, we simply use a bitmask to determine which panel is currently activated. The bitmasks for the 9 panels are as follows:

  • 0x001: Up Left
  • 0x002: Up
  • 0x004: Up Right
  • 0x008: Left
  • 0x010: Center
  • 0x020: Right
  • 0x040: Down Left
  • 0x080: Down
  • 0x100: Down Right

Or in another way (The X's are unimportant/unused bits):

X X X X X X X 0 1 0 0 0 0 0 1 0
              | | | | | | | | |_ Up Left
              | | | | | | | |___ Up
              | | | | | | |_____ Up Right
              | | | | | |_______ Left
              | | | | |_________ Center
              | | | |___________ Right
              | | |_____________ Down Left
              | |_______________ Down
              |_________________ Down Right