Programming the Guide Lights :-)

Discussion in 'KOMPLETE KONTROL SERIES' started by Anykey, Sep 21, 2017.

  1. Anykey

    Anykey NI Product Owner

    Messages:
    3
    Hi Everyone,

    As I wanted to do some integration between Synthesia and Komplete Kontrol, I did some reverse engineering in the USB protocol from the Komplete Kontrol and surprisingly easily figured out how they control the lights.

    If you want to have some fun controlling your lights check out my code:
    https://github.com/AnykeyNL/SynthesiaKomplete

    Basically you just send a bunch of Bytes, 3 Bytes for each Key, representing RGB. The first byte starts with 0x82 with is the command to set the colors.

    Enjoy,
    Richard
     
    • Like Like x 5
  2. jasonbrent

    jasonbrent NI Product Owner

    Messages:
    36
    Richard,

    Good work. You initialize by sending 0xa0 to report[3]. Can you provide more information on what data data is in that HidReport object? A print() of report[3] would be useful (I don't have a windows system handy to test it myself there). I'd like to get this code working multi-platform... hidapi seems to be cross platform (win/mac/linux), but the semantics don't match 1:1 with pywinusb.

    Thank you!

    -j
     
  3. jasonbrent

    jasonbrent NI Product Owner

    Messages:
    36
    I took the time to install a windows machine and python in a VM... the code I wrote using hidapi that doesn't work on my mac, but _does_ work on windows. It is as simple as sending 0xa0 to initialize, and then 0x82 with a list of RGB values as you said.

    using cython hidapi, it's as simple as the following to initialize under win32-- this same code SHOULD work under mac/linux, but doesn't on my test mac at the moment (no failure in the API, just nothing happens on the keyboard):

    import hid
    device=hid.device()
    device.open(0x17cc, 0x1360)
    device.write([0xa0])

    From there, you can device.write() a list of RGB tuples, prepended by 0x82 for the lights.

    An interesting observation, as soon as the 0xa0 hits, the scribble strips under the encoders clears out -- there must be a way to control them also via USB....

    Also, one only needs to disable the lightguide in komplete kontrol software if you want to control the lights with KK running. Otherwise, that setting is fine to leave on.

    -j
     
  4. jasonbrent

    jasonbrent NI Product Owner

    Messages:
    36
    So, good news/bad news: It seems that on MacOS the ability to write to a HID device "raw" is limited. I've been able to control the keyboard on windows using python and the pywinusb library, python and the hidapi cython library, and the hidapi C library with some C code. This same code can read from the keyboard under MacOS just fine, but cannot write.

    Disappointing. I'm going to try another Mac just to ensure my hackintosh isn't doing something dumb, and then go straight to the
    IOHidManager in objective-C for troubleshooting next.

    If I can get this to work, I intend to generate a cross-platform library for accessing the light guide.


    EDIT: Have now tried with IOHidManager directly, no luck. Something in MacOS is inhibiting my ability to write to these devices is my best guess. I'm going to keep digging, but without a hardware usb analyzer, i think i'm at the end of the road for macos. :/
     
    Last edited: Sep 29, 2017
    • Like Like x 2
  5. jasonbrent

    jasonbrent NI Product Owner

    Messages:
    36
    Gave up on chasing down why I can't write to the device from MacOS for now, but I was able to spend some time with the device and an analyzer.

    Commands on the keyboard via USB so far... work in progress.

    0xa0 - Seems to be overall device state/Mode Sending just 0xa0 initializes the device and keyboard light control works.
    0xa0 0xd9 0x00 - sets the device to "midi" mode? Light control of Transport controls works, keyboard doesn't.
    0xa0 0xd8 0x00 - seems to be the same as previous
    0xa4 - Unknown
    0xa6 - Unknown

    0xe0 - addresses the scribble strip. 3 Rows, addressable by the 3rd byte in the sequence (0x00 = top row, 0x01, middle row, 0x02, bottom row)

    0x80 - Byte array that controls the brightness for each of the keys in the following order:
    Shift, Scale, Arp, Loop, RWD, FWD, PLAY, REC, STOP, Left Arrow, Right Arrow, Browse, White Up Button, Instance, White Down Button, Back, Black Up Arrow, Enter, Black Left Arrow, Black Down Arrow, Black Right Arrow. There is a 4 byte pad at the end.
    Example: 0x80 0xFF would turn on only the "SHIFT" key to full brightness. 0x80 0x00 0xFF would turn the SCALE key to full brightness...

    0x82 - Controls the RGBs on the Keys. 3 bytes per key, with a value of 0-255 for each of R,G,B.

    Example: 0x82 0xFF 0xFF 0xFF would turn the left most key full white.

    It is possible to gain full control of the keyboard, lights, scribble strip, and transport controls. Just requires some reverse engineering and some code writing.
     
  6. jasonbrent

    jasonbrent NI Product Owner

    Messages:
    36
    This is even easier with the Mk2. Lights are off of 0x81 and are a single byte per key for the color (hmmm; I expect there's another interface for full RGB control, but...). No need to send a 0xa0 first. Works on mac and windows as HID. Yay.

    Although, this can probably be improved -- sending any light overrides seems to disable track/daw control. I'll spend more time with their protocol later, but I expect it's possible to have KK, Ableton and external light guide functionality working with a little coding.
     
    Last edited: Oct 14, 2017
    • Informative Informative x 1
  7. jasonbrent

    jasonbrent NI Product Owner

    Messages:
    36
    What a mess (my brain, that is....)...

    1) I'm now able to completely initialize the MK2 keyboard and LCD screens exactly like Komplete Kontrol would, graphics on the LCDs and all under Windows. Light Guide is controllable via HID on USB endpoint 0x81, LCD is controllable via BULK transfer to the "Vendor Specific" endpoint 0x3. This code isn't cross platform due to MacOS limitations again, sigh.

    2) I need to correct a statement I made yesterday with the LightGuide: the keyboard does need to be sent 0xa0 to "initialize" it before sending single-byte-per-key color codes to 0x81 on MacOS.. this doesn't seem to be the case on windows for some reason. This *IS* cross-platform; which means I should be able to update a fork of the OPs github code that'll work on windows and mac. This means that lightguide+Synthesia should work cross-platform with a python runtime. Unfortunately, I broke my windows installation so this is stalled for tonight at least.

    I still haven't found an endpoint to send 3-byte color codes to the lightguide like one could do on the MK1.

    *EDIT*: Fixed my windows installation and confirmed that I can now control the lightguide on mac and windows with a common python library. I'll fork/update the github project from the OP soon. I don't have my MK1 anymore, and I couldn't ever talk to it properly via MacOS, so for now, only the MK2 will be platform agnostic. :/

    Unfortunately, #1 only works with "libusb1" installed at the moment- which breaks KK's ability to see the keyboard and is entirely too painful to fix for the average user. So consider it a Proof of Concept and I'll keep digging.

    If anyone has any tips/tricks on how to get platform-agnostic access to both HID and Vendor Specific USB interfaces, I'm all ears.

    For posterity, here's a dump of what the KK keyboard enumerates via USB; the MK1 is very similar.

    Code:
    DEVICE ID 17cc:1620 on Bus 029 Address 014 =================
    bLength                :   0x12 (18 bytes)
    bDescriptorType        :    0x1 Device
    bcdUSB                 :  0x200 USB 2.0
    bDeviceClass           :   0xef Miscellaneous
    bDeviceSubClass        :    0x2
    bDeviceProtocol        :    0x1
    bMaxPacketSize0        :   0x40 (64 bytes)
    idVendor               : 0x17cc
    idProduct              : 0x1620
    bcdDevice              :   0x51 Device 0.51
    iManufacturer          :    0x2 Native Instruments
    iProduct               :    0x3 KOMPLETE KONTROL S61 MK2
    iSerialNumber          :    0x1 SEKRET
    bNumConfigurations     :    0x1
      CONFIGURATION 1: 480 mA ==================================
       bLength              :    0x9 (9 bytes)
       bDescriptorType      :    0x2 Configuration
       wTotalLength         :   0xcb (203 bytes)
       bNumInterfaces       :    0x5
       bConfigurationValue  :    0x1
       iConfiguration       :    0x0
       bmAttributes         :   0x80 Bus Powered
       bMaxPower            :   0xf0 (480 mA)
        INTERFACE 0: Audio =====================================
         bLength            :    0x9 (9 bytes)
         bDescriptorType    :    0x4 Interface
         bInterfaceNumber   :    0x0
         bAlternateSetting  :    0x0
         bNumEndpoints      :    0x0
         bInterfaceClass    :    0x1 Audio
         bInterfaceSubClass :    0x1
         bInterfaceProtocol :   0x20
         iInterface         :    0x3 KOMPLETE KONTROL S61 MK2
        INTERFACE 1: Audio =====================================
         bLength            :    0x9 (9 bytes)
         bDescriptorType    :    0x4 Interface
         bInterfaceNumber   :    0x1
         bAlternateSetting  :    0x0
         bNumEndpoints      :    0x2
         bInterfaceClass    :    0x1 Audio
         bInterfaceSubClass :    0x3
         bInterfaceProtocol :    0x0
         iInterface         :    0x7 KOMPLETE KONTROL S61 MK2 MIDI
          ENDPOINT 0x81: Bulk IN ===============================
           bLength          :    0x7 (7 bytes)
           bDescriptorType  :    0x5 Endpoint
           bEndpointAddress :   0x81 IN
           bmAttributes     :    0x2 Bulk
           wMaxPacketSize   :  0x200 (512 bytes)
           bInterval        :    0x0
          ENDPOINT 0x1: Bulk OUT ===============================
           bLength          :    0x7 (7 bytes)
           bDescriptorType  :    0x5 Endpoint
           bEndpointAddress :    0x1 OUT
           bmAttributes     :    0x2 Bulk
           wMaxPacketSize   :  0x200 (512 bytes)
           bInterval        :    0x0
        INTERFACE 2: Human Interface Device ====================
         bLength            :    0x9 (9 bytes)
         bDescriptorType    :    0x4 Interface
         bInterfaceNumber   :    0x2
         bAlternateSetting  :    0x0
         bNumEndpoints      :    0x2
         bInterfaceClass    :    0x3 Human Interface Device
         bInterfaceSubClass :    0x0
         bInterfaceProtocol :    0x0
         iInterface         :    0x8 KOMPLETE KONTROL S61 MK2 HID
          ENDPOINT 0x82: Interrupt IN ==========================
           bLength          :    0x7 (7 bytes)
           bDescriptorType  :    0x5 Endpoint
           bEndpointAddress :   0x82 IN
           bmAttributes     :    0x3 Interrupt
           wMaxPacketSize   :   0x40 (64 bytes)
           bInterval        :    0x2
          ENDPOINT 0x2: Interrupt OUT ==========================
           bLength          :    0x7 (7 bytes)
           bDescriptorType  :    0x5 Endpoint
           bEndpointAddress :    0x2 OUT
           bmAttributes     :    0x3 Interrupt
           wMaxPacketSize   :   0x40 (64 bytes)
           bInterval        :    0x2
        INTERFACE 3: Vendor Specific ===========================
         bLength            :    0x9 (9 bytes)
         bDescriptorType    :    0x4 Interface
         bInterfaceNumber   :    0x3
         bAlternateSetting  :    0x0
         bNumEndpoints      :    0x1
         bInterfaceClass    :   0xff Vendor Specific
         bInterfaceSubClass :   0xbd
         bInterfaceProtocol :    0x0
         iInterface         :    0x9 KOMPLETE KONTROL S61 MK2 BD
          ENDPOINT 0x3: Bulk OUT ===============================
           bLength          :    0x7 (7 bytes)
           bDescriptorType  :    0x5 Endpoint
           bEndpointAddress :    0x3 OUT
           bmAttributes     :    0x2 Bulk
           wMaxPacketSize   :  0x200 (512 bytes)
           bInterval        :    0x1
        INTERFACE 4: Application Specific ======================
         bLength            :    0x9 (9 bytes)
         bDescriptorType    :    0x4 Interface
         bInterfaceNumber   :    0x4
         bAlternateSetting  :    0x0
         bNumEndpoints      :    0x0
         bInterfaceClass    :   0xfe Application Specific
         bInterfaceSubClass :    0x1
         bInterfaceProtocol :    0x1
         iInterface         :    0xa KOMPLETE KONTROL S61 MK2 DFU
    
    -j
     
    Last edited: Oct 15, 2017
    • Like Like x 1
  8. SlowLoop

    SlowLoop NI Product Owner

    Messages:
    31
    Hi guys,

    Sorry to thread jack, but it seems like there is intelligent conversation going on over here and it's somewhat relevant to an issue i am having with my Light Guide on a Mk1.

    Question, do you guys know how to reset the color association with komplete kontrol kit, ie light blue for piano, ect
     
  9. Vic Germani

    Vic Germani NI Product Owner

    Messages:
    2
    Sorry to be daft on this issue, but these hex bytes you're talking about sending to the Kontrol, can they be sent as midi sysex, or is it just via a programming interface? Thanks!
     
  10. Anykey

    Anykey NI Product Owner

    Messages:
    3
    I wish you could send midi, they you would not need my program :)
    Unfortunately the Kontrol Keyboard does not support Midi for controlling the lights :-(
     
  11. D-One

    D-One Moderator Moderator

    Messages:
    6,099
    Synthesia + Komplete Kontrol light guide sounds like a super cool idea to learn songs
     
  12. Benoit Peeters

    Benoit Peeters NI Product Owner

    Messages:
    82
    Nice job! Even If i undestand only about 20% of it! :)
     
  13. OlivierJ

    OlivierJ New Member

    Messages:
    3
    • Like Like x 1
  14. jasonbrent

    jasonbrent NI Product Owner

    Messages:
    36
    OlivierJ Awesome - Glad that it was useful. I've not touched this particular reverse engineering activity since my last post on it in October, but I applaud continuing this effort. Eventually I may get a "round tuit" and do more with this again. I noticed on the Synthesia forums that nicholas asked how USB was captured -- I'm not going to sign up on the forums just to respond there, but if you wouldn't mind sharing, tell him it was done using wireshark+USBPcap on windows. I ran windows in a VM on MacOS to do this, but the VM part isn't important.

    I used USBPcap to observe the USB traffic, wrote a few scripts/converters in ipython to convert the captured bytes into something I could replay, and wrote some test code on mac/windows in (i)python for the initial replay. Once I had that working, I just started walking through the bits by hand mapping out the communication protocol.

    Somewhere I have more notes on the protocol scheme (and I might have posted it in another thread here, I don't remember) that I can try to find if needed.

    Good luck and I'm glad to see this happening.

    -j
     
  15. D-One

    D-One Moderator Moderator

    Messages:
    6,099
    Oh lord please don't tell me this will be Windows only :(:(:(:(
     
  16. GoaSkin

    GoaSkin NI Product Owner

    Messages:
    91
    Hello,

    I modified the sample code a little bit to realize key mappings - and it works as well under linux.

    Ubuntu-Users: You need to install python3-hidapi and python3-hid from the repositories and then the getch-module by calling 'pip3 install getch' on the shell as root.

    Code:
    #!/usr/bin/python3.6
    
    # Little tool to figure out Native Instruments Komplete Kontrol mk2
    # key mapping scheme on 0xa4
    
    import hid
    import getch
    
    device=hid.device()
    # 0x17cc: Native Instruments. 0x1620: KK S61 MK2
    device.open(0x17cc, 0x1620)
    
    # initialization 
    # device.write([0xa0])
    
    
    # 0xa4 = Key MAP command
    #
    # because 16 zones are possible, there are always 16 zone sets transmitted
    # each of them has the following format: [identifier of the last note covered by the zone, transpose, MIDI-Channel (0=1, 1=2 etc.), type (0x32=note, 0x82=off), key color, pushed key color, always 0x00, always 0x00]
    device.write([0xa4] + [0x3f,0x00,0x00,0x32,0x2c,0x2e,0x00,0x00] + [0x7f,0x00,0x01,0x32,0x12,0x13,0x00,0x00] + [0x7f,0x00,0x00,0x32,0x2c,0x2e,0x00,0x00] + [0x7f,0x00,0x00,0x32,0x2c,0x2e,0x00,0x00] + [0x7f,0x00,0x00,0x32,0x2c,0x2e,0x00,0x00] + [0x7f,0x00,0x00,0x32,0x2c,0x2e,0x00,0x00] + [0x7f,0x00,0x00,0x32,0x2c,0x2e,0x00,0x00] + [0x7f,0x00,0x00,0x32,0x2c,0x2e,0x00,0x00] + [0x7f,0x00,0x00,0x32,0x2c,0x2e,0x00,0x00] + [0x7f,0x00,0x00,0x32,0x2c,0x2e,0x00,0x00] + [0x7f,0x00,0x00,0x32,0x2c,0x2e,0x00,0x00] + [0x7f,0x00,0x00,0x32,0x2c,0x2e,0x00,0x00] + [0x7f,0x00,0x00,0x32,0x2c,0x2e,0x00,0x00] + [0x7f,0x00,0x00,0x32,0x2c,0x2e,0x00,0x00] + [0x7f,0x00,0x00,0x32,0x2c,0x2e,0x00,0x00] + [0x7f,0x00,0x00,0x32,0x2c,0x2e,0x00,0x00] + [0x7f,0x00,0x00,0x32,0x04,0x05,0x00,0x00])
    
    #           Low, Medium, High, Saturated
    # RED:      0x04, 0x05, 0x06, 0x07
    # ORANGE:   0x08, 0x09, 0x0a, 0x0b
    # L ORANGE: 0x0c, 0x0d, 0x0e, 0x0f
    # YELLOW:   0x10, 0x11, 0x12, 0x13
    # YELLOW2:  0x14, 0x15, 0x16, x017
    # L GREEN:  0x18, 0x19, 0x1a, 0x1b
    # GREEN:    0x1c, 0x1d, 0x1e, 0x1f
    
    Next step is to code a QT-based GUI program providing all the MIDI setup under Linux but written in C++, not Python.

    Edit: Initialization commented out because it doesn't seem to be required in this case. After a little bit of testing, the script seems to be much more stable with out it.
     
    Last edited: Aug 9, 2018
    • Like Like x 1
  17. moss

    moss NI Product Owner

    Messages:
    170
    Would you mind to share some example code or explain the format?
     
  18. GoaSkin

    GoaSkin NI Product Owner

    Messages:
    91
    Hello,

    the code is quoted in the posting. Just copy it to a text editor and save it as .py file! Then run python3.6 yourfile.py
     
  19. moss

    moss NI Product Owner

    Messages:
    170
    Maybe I overlook something but I only see example code for the lights but not for the LCD display.
     
  20. GoaSkin

    GoaSkin NI Product Owner

    Messages:
    91
    Sorry. I thought you meant my posting.

    It would also interest me how it works to transfer bitmaps to the screens. I examined the data with Wireshark going to the screen subdevice (0x3) a little bit. It looks like if it is a HID communication (like the Controls setup) too but I have no idea yet how the transmitted bitmap data is encoded. The only thing I know yet about it is not always necessary to transmit the whole picture. If there are small changes to realize, only small packets are transmitted what also means that the transmitted data contains size and positioning information.