diff --git a/README.md b/README.md index e98078b..8867480 100644 --- a/README.md +++ b/README.md @@ -9,14 +9,15 @@ product ID or have a K87 layout, so the keyboard is ignored without this plugin. Copy `Womier_K87_EVision.js` to `%USERPROFILE%\Documents\WhirlwindFX\Plugins\` and restart SignalRGB. The device settings have a "Key test" option (column or row sweep) to check the LED map, -"Low frame-rate mode" in case the keyboard drops frames at full speed, and a "Frame rate limit" -(Unlimited / 30 / 15 / 10 fps). Known issue: single keys occasionally flash a random colour for a moment. +"Low frame-rate mode" (normally not needed) and a "Frame rate limit" (Unlimited / 30 / 15 / 10 fps). ## Protocol - HID interface 1, usage page `0xFF1C`, usage `0x92` (collection 4), 64-byte output reports. -- Colour frame: packets of `[0x04, sumLo, sumHi, 0x12, len(56), offLo, offHi, 0x00, ...56 RGB bytes]`, - where the checksum is the 16-bit sum of bytes 3..63. 7 packets cover LED slots 0..127. +- Colour frame: packets of `[0x04, sumLo, sumHi, 0x12, len, offLo, offHi, 0x00, ...len RGB bytes]`, + where the checksum is the 16-bit sum of bytes 3..63. The plugin uses len 54: 7 packets cover LEDs 0..125. +- The keyboard shows each packet as it arrives, so `len` must be a multiple of 3. With the stock + plugin's 56, LEDs 18, 74 and 93 (W, O, Menu) are split across packets and flash random colours. - The keyboard falls back to its own effect when frames stop, so colours must be streamed. ## LED map diff --git a/Womier_K87_EVision.js b/Womier_K87_EVision.js index 5abdf87..1133784 100644 --- a/Womier_K87_EVision.js +++ b/Womier_K87_EVision.js @@ -24,7 +24,7 @@ export function ControllableParameters() { {property:"shutdownColor", group:"lighting", label:"Shutdown Color", description: "This color is applied to the device when the System, or SignalRGB is shutting down", min:"0", max:"360", type:"color", default:"#000000"}, {property:"LightingMode", group:"lighting", label:"Lighting Mode", description: "Determines where the device's RGB comes from. Canvas will pull from the active Effect, while Forced will override it to a specific color", type:"combobox", values:["Canvas", "Forced"], default:"Canvas"}, {property:"forcedColor", group:"lighting", label:"Forced Color", description: "The color used when 'Forced' Lighting Mode is enabled", min:"0", max:"360", type:"color", default:"#009bde"}, - {property:"lowFPS", group:"lighting", label:"Low frame-rate mode", description: "Sends smaller packets with pauses in between, can improve stability", type:"boolean", default:false}, + {property:"lowFPS", group:"lighting", label:"Low frame-rate mode", description: "Sends smaller packets with pauses in between; normally not needed", type:"boolean", default:false}, {property:"frameLimit", group:"lighting", label:"Frame rate limit", description: "Maximum frames per second sent to the keyboard", type:"combobox", values:["Unlimited", "30", "15", "10"], default:"Unlimited"}, {property:"keyTest", group:"lighting", label:"Key test", description: "Sweeps a white line across the keys to check the LED map", type:"combobox", values:["Off", "Column sweep", "Row sweep"], default:"Off"}, ]; @@ -57,8 +57,8 @@ const vLedPositions = [ [0, 5], [1, 5], [2, 5], [6, 5], [10, 5], [11, 5], [12, 5], [13, 5], [14, 5], [15, 5], [16, 5], ]; -// Highest LED index is 125; 128 slots = 384 bytes = 7 full packets of 56 -const LED_SLOTS = 128; +// Highest LED index is 125; 126 slots = 378 bytes = exactly 7 packets of 54 +const LED_SLOTS = 126; export function LedNames() { return vLedNames; } export function LedPositions() { return vLedPositions; } @@ -118,7 +118,9 @@ function sendColors(overrideColor) { } function writeRGBPackage(RGBData) { - const bytesToSend = lowFPS ? 48 : 56; + // Must be a multiple of 3: the keyboard shows each packet as it arrives, so an LED split + // across two packets briefly flashes a mix of its old and new colour + const bytesToSend = lowFPS ? 48 : 54; for (let offset = 0; offset < RGBData.length; offset += bytesToSend) { const data = RGBData.slice(offset, offset + bytesToSend);