Skip to content

Instantly share code, notes, and snippets.

@4yn
Created January 30, 2022 07:40
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save 4yn/7dec4fed11a1f07a31951a742f61ded1 to your computer and use it in GitHub Desktop.
Save 4yn/7dec4fed11a1f07a31951a742f61ded1 to your computer and use it in GitHub Desktop.
#By GiverOfCake, 2021.
import usb.core
import usb.util
import sys
import time
#Connect to Tasoller (vendor and product ID can be read from device manager or Zadig)
dev = usb.core.find(idVendor=0x1CCF, idProduct=0x2333)
#Debug: output valid configurations, interfaces, endpoints
for cfg in dev:
sys.stdout.write(str(cfg.bConfigurationValue) + '\n')
for intf in cfg:
sys.stdout.write('\t' + \
str(intf.bInterfaceNumber) + \
',' + \
str(intf.bAlternateSetting) + \
'\n')
for ep in intf:
sys.stdout.write('\t\t' + \
str(ep.bEndpointAddress) + \
'\n')
#Only one valid configuration, set it automatically
dev.set_configuration()
#endpoint 0x03 (output): Color data
#endpoint 0x81 (input): input data
#COLOR TESTING
msg = [0x42, 0x4c, 0x00] # starting header (no idea what this does, but message will be ignored if first two bytes are wrong)
#Color format appears to be Green, Red, Blue.
msg += [0x00, 0x00, 0xff] * 31 #touch bar, right to left
msg += [0x00, 0xff, 0x00] * 24 # left sensor, top to bottom
msg += [0xff, 0x00, 0x00] * 24 # right sensor, bottom to top
# pad message to 240 bytes
msg += ([0x00] * (200 - len(msg)))
dev.write(0x03, msg, 100) #send to endpoint 3, timeout 100
#INPUT TESTING
def get_bit(value, bit_index):
return value & (1 << bit_index)
while True:
state = dev.read(0x84, 11, 100)
for byte_index in range(3, len(state)):
for bit_index in range(8):
if get_bit(state[byte_index], bit_index):
print(f"1 at byte {byte_index}, bit {bit_index}")
print(bytes(state).hex())
time.sleep(0.1) #Don't spam the output
Unofficial "I SAY NYA-O" firmware documentation/reverse engineering, by GiverOfCake.
The Tasoller "I SAY NYA-O" requires a low level USB connection.
To connect, search for a USB device with vendor ID 0x1CCF, product ID 0x2333 (you can also find this in Device Manager).
You'll need some sort of generic USB driver loaded such as libusb-win32 -- this can be installed with a tool like Zadig.
I'll skip over how low-level USB stuff works because it's library dependent anyway,
but the Tasoller has one valid configuration with one interface, which has two endpoints.
The endpoints are 0x03 for color data,
and 0x81 for input data.
Input is sent on request. I haven't tested the polling rate, but in theory this should be capable of up to 1000Hz.
Note: LED data does not necessarily need to be updated when polling input, and vice versa!
E.g. it is possible to poll for input at 1000Hz and only update the LEDs at 60Hz.
Input (color data) format:
Input packet is 240 bytes. Smaller packets are accepted, but any LEDs out of range in the packet will retain their previous (possibly random) state. So probably best to always send 240 bytes.
The first 3 bytes are the header: 0x42, 0x4c, 0x00.
If the first two bytes are any other value, the packet will be ignored.
If a packet is valid, the lights will be set accordingly.
If a few seconds pass without sending a valid packet, the controller will return to the default 'rainbow' mode.
All bytes that follow are color data. Each color is sent as a triplet of 3 bytes: green, blue and red in that order.
Below is a list of color addresses. Note that the byte address of each is the color address * 3, + 3 since they come after the 3 header bytes
(so color 0 = byte 3,4,5, color 1 = byte 6,7,8...)
Color ID LED
0 Pad 15 (rightmost)
1 Pad 15&14 seperator
2 Pad 14
3 Pad 13&14 seperator
...
30 Pad 0 (leftmost)
31 Topmost left sensor LED (1 of 24)
32 Second topmost left sensor LED (2 of 24)
...
54 Lowest left sensor LED (24 of 24)
55 Lowest right sensor LED (1 of 24)
...
78 Highest right sensor LED (24 of 24)
3 byte header + 79 colors * 3 bytes per color = 240 bytes total.
Output (user input) format:
For all bits, 0 = no input/idle state, 1 = touched/sensed.
When the air sensors are disconnected, even at startup, they will always report 1.
Output packet 11 bytes in length. Accepting any less will cause USB errors until the Tasoller is reset.
Byte(0-10) Bit(0-7) Value
0,1,2 Any header (static, nothing of value)
3 0-3 Unused
3 4 Air sensor 1 (lowest)
3 5 Air sensor 2
3 6 Air sensor 3
3 7 Air sensor 4
4 0 Air sensor 5
4 1 Air sensor 6 (highest)
4 2 Pad 0 (leftmost) top
4 3 Pad 0 (leftmost) bottom
4 4 Pad 1 top
4 5 Pad 1 bottom
...
7 6 Pad 14 top
7 7 Pad 14 bottom
8 0 Pad 15 (rightmost) top
8 1 Pad 15 (rightmost) bottom
8 2-7 Unused
9,10 Any Unused
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment