Some of the information are from this writeup and some come from original researches.
slider_cap.zip (sigrok format)
slider_cap.zip (original ZeroPlus ALC format)
init.sr [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 of0xff
and0xfd 0xfc
in case of0xfd
). To unescape, simply discard the ESC byte and add 1 to the escaped byte. - All int16/int32 types are in big endian (MSB-first).
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) |
All the offsets below will be offsets in decoded report body unless otherwise noted.
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).
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.
Output: EnableSliderReport()
Enables the slider report.
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: 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.
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.
Output: EnableRawCountReport()
Enables the raw count report.
Output: RawCountPingPongReport(brightness, led_brg=((b, r, g), ...))
See PingPongReport
.
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.
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.
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.
Output: EnableSliderReport()
Enables the short raw count report. Probably used internally for debugging.
Output: ShortRawCountPingPongReport(brightness, led_brg=((b, r, g), ...))
See PingPongReport
.
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.
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 |
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' |
-
Output:
Reset()
(ff 10 00 f1
) -
Input:
Reset()
(ff 10 00 f1
) -
Output:
GetHWInfo()
(ff f0 00 11
) -
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
) -
Output:
EnableSliderReport()
(ff 03 00 fe
) -
Input:
SliderReport(values=(...))
(ff 01 20 <report> <sum>
) -
Output:
SetShortRawCountOffset(0x0)
(ff 09 02 00 00 f6
) -
Input:
SetShortRawCountOffset()
(ff 09 00 f8
) -
Output:
SetShortRawCountShifts(0x0)
(ff 0a 01 00 f6
) -
Input:
SetShortRawCountShifts()
(ff 0a 00 f7
) -
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.
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.
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.