Skip to content

Instantly share code, notes, and snippets.

Last active February 18, 2024 06:50
  • Star 21 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
Star You must be signed in to star a gist
Save dogtopus/b61992cfc383434deac5fab11a458597 to your computer and use it in GitHub Desktop.
SEGA rhythm game slider protocol

SEGA touch slider (837-15275 and 837-15330) protocol

Some of the information are from this writeup and some come from original researches.

Logic analyzer captures (sigrok format) (original ZeroPlus ALC format) [bootup(power-on).alc]: Captured during initialization

{blue,green,red}.sr [led({blue,green,red}).alc]: Captured after setting SERVICE MENU/OUTPUT TEST/TOUCH SLIDER LED {BLUE,GREEN,RED} options to on.


TX/RX follows standard Nu convention (RS232, 115200 8n1).

The packet is encoded in a JVS-esque coding scheme (with 0xff being the sync byte (SYNC) and the 0xfd being the escape byte (ESC)).

Each packet starts with a SYNC (0xff) byte and the decoded packet structure looks like:

Offset Size Field Name Notes
0 1 cmd Command/report type
1 1 argc Length of arguments/report body (should be n)
2 n args/argv Arguments/report body
n+2 1 checksum Checksum
  • Arguments/report body can be empty (i.e. zero length).
  • Length field represents the data length AFTER decoding or BEFORE encoding.
  • The checksum is calculated by adding all NEGATED bytes between SYNC and the end of arguments/report body (both inclusive) and taking the lower 8 bits, or
checksum = (sum(packet_without_checksum) - 0xff) & 0xff
  • To verify the checksum, simply add all bytes between SYNC and checksum (both inclusive) and check if the result is 0.
  • In case of having SYNC/ESC in the packet, the byte is escaped by writing ESC byte-1 (0xfd 0xfe in case of 0xff and 0xfd 0xfc in case of 0xfd). To unescape, simply discard the ESC byte and add 1 to the escaped byte.
  • All int16/int32 types are in big endian (MSB-first).

List of known commands/report types

Definition of directions:

  • Output: Host to device
  • Input: Device to host

See below for detailed formats of each command/report.

ID Direction Human Readable Form
0x01 Output SliderReport()
0x01 Input SliderReport(values)
0x02 Output LEDReport(brightness, led_brg)
0x03 Output EnableSliderReport()
0x04 Output DisableSliderReport()
0x04 Input DisableSliderReport()
0x05 Output PingPongReport(brightness, led_brg)
0x06 Output RawCountReport()
0x06 Input RawCountReport(raw_counts)
0x07 Output EnableRawCountReport()
0x08 Output RawCountPingPongReport(brightness, led_brg)
0x09 Output SetShortRawCountOffset(offset)
0x09 Input SetShortRawCountOffset()
0x0a Output SetShortRawCountShifts(shifts)
0x0a Input SetShortRawCountShifts()
0x0b Output ShortRawCountReport()
0x0b Input ShortRawCountReport(values)
0x0c Output EnableShortRawCountReport()
0x0d Output ShortRawCountPingPongReport(brightness, led_brg)
0x10 Input, Output Reset()
0xe0 Output GetCPUStatus()
0xe0 Input GetCPUStatus(cpu_scr0, cpu_scr1)
0xee Input Exception(context, error)
0xf0 Output GetHWInfo()
0xf0 Input GetHWInfo(model, device_class, chip_pn, unk_0xe, fw_ver, unk_0x10, unk_0x11)

Protocol description

All the offsets below will be offsets in decoded report body unless otherwise noted.

0x01 - SliderReport

Output: SliderReport()

Requests a single shot slider report.

Input: SliderReport(values=(...))

Slider report.

For 15275, bytes are mapped according to the physical construction of the slider (left bytes on the left side, etc.). Also the values showed on the SERVICE MENU/INPUT TEST starts from left side as well (0-31 being left to right).

0 1 ... 31

For 15330, the slider has an odd-even layout, with the electrode 0 located at the top right corner.

30 ... 2 0
31 ... 3 1

Report interval is about 12ms (83.3 FPS).

0x02 - LEDReport

Output: LEDReport(brightness, led_brg=((b, r, g), ...))

Set the LED overall brightness and color.

Offset Size Field Name Notes
0x0 1 brightness Slider brightness. (Probably between 0-63 as THL3504's global brightness control value is within this range.)
0x1 + 3*led_index 1 led_brg[led_index][0] LED brightness (blue)
0x2 + 3*led_index 1 led_brg[led_index][1] LED brightness (red)
0x3 + 3*led_index 1 led_brg[led_index][2] LED brightness (green)

(For reducing protocol overhead it may be desirable to avoid using SYNC and ESC as LED values.)

NOTE: For 15330, there are only 31 LEDs (16 for keys and 15 for key separators) and the index starts from the right hand side.

0x03 - EnableSliderReport

Output: EnableSliderReport()

Enables the slider report.

0x04 - DisableSliderReport

Output: DisableSliderReport()

Disable periodical input report from slider. Seen on 15330 and unconfirmed on 15275.

Input: DisableSliderReport()

Response. Seen on 15330 and unconfirmed on 15275.

0x05 - PingPongReport

Output: PingPongReport(brightness, led_brg=((b, r, g), ...))

Same format as LEDReport, but also requests a single-shot SliderReport at the same time. Report will be delivered in the Input SliderReport format.

0x06 - RawCountReport

Output: RawCountReport()

Requests a single shot raw count report.

Input: RawCountReport(values=(...))

Raw count report. Returns the full 16-bit raw counts of all the sensors. See SliderReport for sensor layout.

0x07 - EnableRawCountReport

Output: EnableRawCountReport()

Enables the raw count report.

0x08 - RawCountPingPongReport

Output: RawCountPingPongReport(brightness, led_brg=((b, r, g), ...))

See PingPongReport.

0x09 - SetShortRawCountOffset

Output: SetShortRawCountOffset(offset) (ff 09 02 <offset_hi> <offset_lo> f6)

Set how much to subtract from each raw count for preparing short raw count report. Probably used internally for debugging.

Offset Size Field Name Notes
0x0 2 offset The offset.

Input: SetShortRawCountOffset() (ff 09 00 f8)

Response to the request.

0x0a - SetShortRawCountShifts

Output: SetShortRawCountShifts(shifts) (ff 0a 01 <shifts> f6)

Set how many bits to shift right for preparing short raw count report. Probably used internally for debugging.

Offset Size Field Name Notes
0x0 1 shifts How many bits to shift right.

Input: SetShortRawCountShifts() (ff 0a 00 f7)

Response to the request.

0x0b - ShortRawCountReport

Output: ShortRawCountReport()

Requests a single shot short raw count report.

Input: ShortRawCountReport(values=(...))

Short (8-bit) raw count report.

This uses the same format as regular report, but the values are populated by values[i] = (raw_count[i] >> shifts) - offset, where offset and shifts are set via SetShortRawCountOffset and SetShortRawCountShifts Probably used internally for debugging.

0x0c - EnableShortRawCountReport

Output: EnableSliderReport()

Enables the short raw count report. Probably used internally for debugging.

0x0d - ShortRawCountPingPongReport

Output: ShortRawCountPingPongReport(brightness, led_brg=((b, r, g), ...))

See PingPongReport.

0x10 - Reset

Output: Reset()

A reset request to the slider.

Input: Reset()

Sent by the slider as a response to the reset request.

NOTE: Sometimes (probably during initialization) the slider might not be responsive or return Exception(0xff, WRONG_CHECKSUM) (ff ee 02 fd fe 01 11). In this case simply retry after about every 100ms.

0xee - Exception

Input/Output: Exception(context, error)

Raise an exception.

Offset Size Field Name Notes
0x0 1 context Current command, or 0xed if not in a command handler, or 0xff when lost sync (?) (on 15275).
0x1 1 error See below

Possible error are listed below (as labelled according to symbols from ongeki).

Value Description
0x1 Wrong checksum
0x2 Bus error

0xf0 - GetHWInfo

Output: GetHWInfo()

Request for hardware information.

Input: GetHWInfo(model, device_class, chip_pn, unk_0xe, fw_ver, unk_0x10, unk_0x11)

Hardware information as seen in SERVICE MENU/GAME SYSTEM INFORMATION.

Offset Size Field Name Notes
0x0 8 model 8-bytes model number (after 837-) (showed in the BD NUMBER field)
0x8 1 device_class Probably a magic number that identifies the device class.
0x9 5 chip_pn 5-bytes chip part number
0xe 1 unk_0xe Unknown (seems to be 0xff on 15275)
0xf 1 fw_ver Firmware version (144 on both 15275 and 15330)
0x10 1 unk_0x10 Unknown (seems to be 0x0 on 15275)
0x11 1 unk_0x11 Unknown (seems to be 0x64 on 15275)

Below shows the difference in the GetHWInfo response among different models of the slider.

Board type Device Class Model Chip Part Number
Diva (837-15275) 0xa0 b'15275 ' b'06687'
Chunithm (CHU-2000) (837-15330) 0xa0 (enforced) b'15330 ' b'06712'

Initialization sequence (diva)

  1. Output: Reset() (ff 10 00 f1)

  2. Input: Reset() (ff 10 00 f1)

  3. Output: GetHWInfo() (ff f0 00 11)

  4. Input: GetHWInfo(model=b'15275 ', device_class=0xa0, chip_pn=b'06687', unk_0xe=0xff, fw_ver=0x90, unk_0x10=0x00, unk_0x11=0x64) (ff f0 12 31 35 32 37 35 20 20 20 a0 30 36 36 38 37 fd fe 90 00 64 fd fc)

  5. Output: EnableSliderReport() (ff 03 00 fe)

  6. Input: SliderReport(values=(...)) (ff 01 20 <report> <sum>)

  7. Output: SetShortRawCountOffset(0x0) (ff 09 02 00 00 f6)

  8. Input: SetShortRawCountOffset() (ff 09 00 f8)

  9. Output: SetShortRawCountShifts(0x0) (ff 0a 01 00 f6)

  10. Input: SetShortRawCountShifts() (ff 0a 00 f7)

  11. Output: SetLEDReport(brightness=0x3f, led_brg=((b, r, g), ...)) (ff 02 61 <brightness=0x3f> <led_brg> <sum>)

After 11 the LED reports and the slider reports are periodically transmitted by host and the slider respectively.

Misc notes

DIP switch

According to a recent update of original writeup, position 3 (SCL) and 4 (SDA) connects the two PSoC together via I2C. Setting both switches to OFF will isolate the I2C communication so both devices can be programmed separately via the ISSP header i.e. CN3 and 4. Position 1 and 2 are for controlling the I2C line pullups. So In order to use the programming mode all the switches must be set to OFF position, and for normal operations all the switch must be set to ON position.

Copy link

dogtopus commented Aug 3, 2020

@somewhatlurker yea iirc that O game also has 0x04 listed in its unused 15275 driver's enum. The reason I listed it as unconfirmed is mainly because diva.exe never actually send it and I don't have a real slider to test.

Copy link

Ah right. Diva has real logic based around it -- I think (untested) that if it receives a response for the wrong scan type (three types if I'm not mistaken: 0x01, 0x06, 0x0b -- triggered using scan start commands 0x03, 0x07, 0x0c), it should send a 0x04 stop. But the only missing part is that I don't see any code to switch the scan type.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment