Programming the Guide Lights :-)

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

  1. moss

    moss NI Product Owner

    Messages:
    170
    Uh, why? Embedded systems normally try to be as efficient as possible, so using only 1 byte instead of 2... :)

    This is Java not C++. An integer in Java is 32 bit.

    The problem is that I need that for Windows, Mac and Linux and I definitively do not want to implement this for 3 platforms on a driver level. I am wondering how NI did implement that, do you think they did on this level or is there a better library than LibUsb?!
     
  2. j0tt

    j0tt New Member

    Messages:
    12
    Well that's not the point. But having non aligned data structures and data words split across three bytes is. ;)


    This doesn't change the fact that a 16-bit number (r-)shifted by 16-bit is 0 and a 16-bit number masked with 0xFFFF is the same number (even when stored in a 32-bit field). And I don't expect that NI uses some strange 3-byte integer scheme. :confused:


    To my understanding NI wrote a custom driver for the USB devices (nikksm2usb.sys under Windows, see Device Manager) with its own set of ioctls... As for the low level USB device communication it's the same in the end but that is handled by the driver.... I am not sure if there is a portable (Java) way of doing this without messing with the driver...at least I expect macOS and Windows to behave similar with regard to the ioctls. I can check this later...
     
  3. GoaSkin

    GoaSkin NI Product Owner

    Messages:
    99
    Yeah, I misinterpreted the functions by thinking the cursor shift command is two bytes longer than it really is and so I did not realize that the pixel drawing commands start with 00 00.

    After changing the function to transmit all pixels at once, the fact is that a 16 bit integer is used to declare the amount of pixels which grows by 1 each 2 pixels what makes the 16 bit integer sufficient (in this case the hex value for 480 * 270 / 2).

    I tried to remove the skip and blit commands at the ending but then the image isn't transmitted to the screen. It may be senseless but is possibly required. All my wireshark dumps also contain 020000000300000040000000 at the ending except if there are really a few pixels to skip; then the same with a non-zero value in the skip command.
     
  4. j0tt

    j0tt New Member

    Messages:
    12
    Well, as far as I understand the protocol the "blit" command is always necessary to actually blit the current buffer to the display. This allows the transfer of multiple chunks with a final blit at the end. The 0x02 0x00 "skip pixel" command is not necessary according to my tests, but as mentioned earlier when you do a partial update you always need to fill the initial rectangle - so if there are some pixels left after your actual drawing you need to skip until you are at the lower right corner of the rectangle.
    My guess is that the NI implementation might always does the skipping in some drawing code paths even if it is not necessary (so something like skipPixels(numberOfPixelsLeft) instead of if (numberOfPixelsLeft > 0) { skipPixels(numberOfPixelsLeft) }.

    It would be nice if NI wouldn't be so closed minded and open up the specs like Ableton did for the Push so we would know for sure....
     
  5. GoaSkin

    GoaSkin NI Product Owner

    Messages:
    99
    Under Mac OS X, NIHardwareAgent starts as service after the user login. It claims the device and keeps it open. It permanently listens at the HID endpoint to decide what LEDs have to be toggled and which bitmaps are to be sent to the screens when the user pushes a specified key.

    There comes no OSX hardware driver for the Komplete Kontrol MK2. As API, the apple IOUSBlib as part of the apple IOkit seems to be used.

    The real problem under OS X and windows is not the case that another implementation is required but the fact that a service is running that avoids any other communication with the device meanwhile.

    You may use libusb and the hidapi under Mac OS X but the precondition is that NIHardwareAgent does not run.
     
  6. j0tt

    j0tt New Member

    Messages:
    12
    I can now definitely confirm that my interpretation of the protocol seems to be correct, as I now have a complete mirror of the display running without any unknown or invalid commands.

    It attaches to the stdout of USBPcapCMD and interprets every incoming frame... still needs some cleaning up and better event handling but still:
    niclone.png
     
    • Like Like x 3
  7. misterme

    misterme New Member

    Messages:
    1
    Hi! Cool work you're all doing! Did an iteration of this all get to a point where we can toggle the light guide globally on or off, whenever we want, while using KK within our daw? (example: toggle it on when using a patch that really uses the guide for keyswitches or drum designations.... but then toggle the light guide off when playing a piano patch that has no keyswitches or any reason to have lights.)
     
  8. GoaSkin

    GoaSkin NI Product Owner

    Messages:
    99

    It is known how to handle that (see the early postings on the first page of this thread). There is an USB HID command that sends a long array to the device signalizing the colors of the LED. Beside some colors there is also a possible value that signalized black / off. But there is no tool yet for this specific purpose.
     
  9. GoaSkin

    GoaSkin NI Product Owner

    Messages:
    99
    Has anybody an idea how to parse the knob and button values using the HIDAPI library? Wireshark on OS X shows that the device sends an HID report for that:

    Code:
    0000  00 01 20 01 33 00 00 00 00 00 00 00 00 00 00 00
    0010  84 14 00 00 00 00 00 00 00 00 14 1d 02 02 82 03
    0020  aa T1 00 T2 00 T3 00 T4 00 T5 00 T6 00 T7 00 T8
    0030  00 K1 00 K2 00 K3 00 K4 00 K5 00 K6 00 K7 00 K8
    0040  00 00 20 00 00 00 00 00 00 00 00 00 00 27 00 00
    0050  00 00 00
    
    Value for the buttons is 00 if off and 7F if on. Values of the knobs something bethween 00 and 7F (the value of the MIDI parameter)

    I tried to catch these reports by calling hid_read in a loop but these 51 byte long message do not appear while the report which is send if you push any non-MIDI-button (first byte 01 instead of aa) does.

    Does anybody know if there are some preconditions required until a "knob-button-value-report" is beeing sent or is the hid_read function not usable to catch the "aa"-report?
     
  10. moss

    moss NI Product Owner

    Messages:
    170
    It is not fully finished but it should get you started: https://github.com/git-moss/DrivenB...ol/usb/mkii/controller/Kontrol2UsbDevice.java
    Look at "processHIDMessage".
     
  11. GoaSkin

    GoaSkin NI Product Owner

    Messages:
    99
    moss: When you turn one of the knobs or push the ON/OFF buttons to change MIDI parameters, the device usually sends two messages:

    1.) the 0x01 message describing which buttons are currently pushed or become released
    2.) a 0xaa message containing an array with the current CC values of the 8 knobs and the 8 buttons above

    While Wireshark on OS X lists me these two messages on each action, the hid_read function under linux only gets the 0x01 messages in my test example but not the 0xAA message

    When you change the MOD- or the pitch-wheel a different message is set containing high-precision values of the current wheel position. And this message I can see.

    As far as I see in your example code, it does not care yet about the 0xAA messages but only about the 0x01 (button state). I try to find a solution to read out with the goal to display the current CC values on the screens.

    What I don't know if you need to put the device to a specific mode before it sends the 0xAA messages or if the linux HID drivers drops them for any reason. Wireshark under Linux using usbmon lists nothing on device actions on the MK2. Possibly if I unload the HID driver.

    Edit: It seems like if the knob value array (0xAA) is only sent by the device if the MIDI endpoint is also opened.
     
    Last edited: Sep 14, 2018
  12. GoaSkin

    GoaSkin NI Product Owner

    Messages:
    99
    I started to write a function to display all the current MIDI CC values above the knobs. The function creates small images to be placed on the screens using the positioning parameters in the command header.

    Shifting the start positions works well if the values are less than 100 (or less than 0x64). If I raise one of the two values from 99 to 100, the position moves to the right or down not just one pixel but around more or less 40 pixels. 101 again is one pixel away from 100.

    Can anybody confirm this gap?
     
  13. j0tt

    j0tt New Member

    Messages:
    12
    There was an (obvious) mistake in my original post regarding the start position. The x-/y-values are 16-bit of course.....

    So this makes the command definition something like:

    COMMAND_START_OF_TRANSFER (0x8400)

    This command is always the first in a bulk transfer and contains the display, as well as the area that should be updated.

    After the command code this 16-bit data block follows:
    Code:
    | Size     | Description                       |
    |----------|-----------------------------------|
    | uint8_t  | Display to address (0, 1)         |
    | uint8_t  | Unknown (always 0x60)             |
    
    After that the payload that then follows is always:
    Code:
    | Size     | Description                       |
    |----------|-----------------------------------|
    | uint16_t | Unknown (always 0x0000)           |
    | uint16_t | Unknown (always 0x0000)           |
    | uint16_t | x coordinate of rectangle to draw |
    | uint16_t | y coordinate of rectangle to draw |
    | uint16_t | width of rectangle to draw        |
    | uint16_t | height of rectangle to draw       |
    
    The size of the rectangle must be equivalent to the number of pixels send in the following operations.
     
  14. GoaSkin

    GoaSkin NI Product Owner

    Messages:
    99
    I did know that the values are 16 bits. And it works well as long as both values are less than 99. 100 continues around 3cm beside/below 99, 200 around 3cm beside/below 199 etc. Anything greater than the display size wraps around. To start the drawing somewhere near the bottom or the right end of the display, you need to be tricky (play around with very high values to force a wrapping)

    [​IMG]

    All the green values here are single small images with custom start positions. The following X and Y values are set here to get them to the right place:

    1: 90, 309
    2: 108, 309
    3: 180, 309
    5: 798, 306
     
    Last edited: Sep 15, 2018
  15. j0tt

    j0tt New Member

    Messages:
    12
    Not sure what you are doing wrong. If you provide proper (unsigned big endian!!) coordinates it works just fine with nothing special to consider. i.e.
    Code:
    0x84 0x00  START OF DATA
    0x01 Display 1
    0x60 Unknown
    0x00 0x00 0x00 0x00 Unknown
    0x01 0xd6 => 470
    0x01 0x06 => 262
    0x00 0x0a => 10
    0x00 0x0a => 10
    
    Prepares a 10x10 rectangle buffer in the lower right corner of the second display....
     
    Last edited: Sep 16, 2018
  16. Drachenkatze

    Drachenkatze NI Product Owner

    Messages:
    321
    Could be. I reverse engineered the protocol as documented within 3 days after purchase. But also keep in mind that firmware updates can change the protocol. Well, at least I got my anime running on the Maschine, that's enough protocol correctness for me ;)
     
    • Like Like x 1