Skip to content

Instantly share code, notes, and snippets.

What would you like to do?
SEGA rhythm game slider protocol

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

Most of the information are from this writeup.

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.

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 Input SliderReport(values)
0x02 Output LEDReport(brightness, led_brg)
0x03 Output EnableSliderReport()
0x04 Output DisableSliderReport()
0x04 Input DisableSliderReport()
0x09 Output Packet(cmd=0x09, args=(unk_0x0, unk_0x1))
0x09 Input Packet(cmd=0x09, args=None)
0x0a Output Packet(cmd=0x0a, args=(unk_0x0, ))
0x0a Input Packet(cmd=0x0a, args=None)
0x10 Input, Output Reset()
0xee Input, Output Exception(code0, code1)
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 (probably).

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.


Output: Packet(cmd=0x09, args=(0x0, 0x0, )) (ff 09 02 00 00 f6)

A request with unknown purpose. Seems to be only available on 15275.

Input: Packet(cmd=0x09, args=None) (ff 09 00 f8)

Response to the request, purpose unknown.


Output: Packet(cmd=0x0a, args=(0x0, )) (ff 0a 01 00 f6)

A request with unknown purpose. Seems to be only available on 15275.

Input: Packet(cmd=0x0a, args=None) (ff 0a 00 f7)

Response to the request, purpose unknown.

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(code0, code1)

Raise an exception.

Offset Size Field Name Notes
0x0 1 code0 Seems to be always 0xff (?)
0x1 1 code1 See below

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

Value Description
0x1 Wrong checksum
0x2 Bus error
0xed Internal 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: Packet(cmd=0x09, args=(0x0, 0x0, )) (ff 09 02 00 00 f6)

  8. Input: Packet(cmd=0x09, args=None) (ff 09 00 f8)

  9. Output: Packet(cmd=0x0a, args=(0x0, )) (ff 0a 01 00 f6)

  10. Input: Packet(cmd=0x0a, args=None) (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.

Other commands and mysteries

(Glitched?) Commands

Output: Packet(cmd=0x0d, args=(brightness, led_brg=((b, r, g), ...)))

From alternate source. Also seem to work fine.

(0xd is 0x2 with all bits inverted. Coincidence?)

Input: Packet(cmd=0x0b, args=(values=(...)))

Looks a lot like SliderReport but with all values being 0xfc or 0xfe. Some sort of threshold or just plainly glitched output?

DIP switch

Seems like there is a 4-bit DIP switch on the slider according to the manual. What is it used for?

(Unlike TN32MSEC0003S, 837-15275 is not individually addressable so it is likely not an address selector.)

(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)

Ping or reset?

The Ping() command (0x10) took way too long than necessary (~8ms). Is it actually a reset command?

Both Chunithm and Ongeki labels 0x10 as reset.


This comment has been minimized.

Copy link

@somewhatlurker somewhatlurker commented Dec 25, 2019

Command 0x0b probably isn't glitched, but I'm not sure what it is. I think there should be something similar at 0x06. It's like they're some kind of alternate scan (possibly triggered by sending 0x0c or 0x07), but I'd be cautious about sending stuff that might be for calibration to a device with no documentation.


This comment has been minimized.

Copy link

@somewhatlurker somewhatlurker commented Aug 3, 2020

Something else: you have 0x04 listed as unconfirmed for 15275. The diva game software seems like it can send 0x04 -- not really concrete confirmation that 15275 supports it, but it's pretty likely all things considered.


This comment has been minimized.

Copy link
Owner Author

@dogtopus 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.


This comment has been minimized.

Copy link

@somewhatlurker somewhatlurker commented Aug 4, 2020

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