Created
December 4, 2020 22:35
-
-
Save stash/638fa4075424c146a0fdbfa4a0b501c0 to your computer and use it in GitHub Desktop.
Prototype "blinky" host scripts for QMK keyboards over Raw HID
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Raw HID "blinky" interface protocol test script | |
// Can also be modified to identify the LED index of each key! | |
const EventEmitter = require('events'); | |
const HID = require('node-hid'); | |
const VENDOR_ID = 12951; | |
const PRODUCT_ID = 18806; | |
const RAW_USAGE_PAGE = 0xFF60; | |
const RAW_USAGE_ID = 0x61; | |
const BLINKY_CMD_INIT = 1; | |
const BLINKY_CMD_SET_ALL = 2; | |
const BLINKY_CMD_SET_ONE = 3; | |
const BLINKY_CMD_SET_MANY = 4; | |
const BLINKY_EVT_INIT = 0x80; | |
const BLINKY_EVT_LAYER = 0x81; | |
const matches = HID.devices().filter((d) => { | |
return d.vendorId == VENDOR_ID && | |
d.productId == PRODUCT_ID && | |
d.usagePage == RAW_USAGE_PAGE && | |
d.usage == RAW_USAGE_ID; | |
}); | |
if (matches.length == 0) { | |
console.error("no raw-HID devices"); | |
process.exit(1); | |
} | |
else if (matches.length > 1) { | |
console.error("too many matches"); | |
process.exit(2); | |
} | |
const ee = new EventEmitter(); | |
const device = new HID.HID(matches[0].path); | |
const reportNumber = 0x42; | |
let epSize = 8; // start small | |
let totalLEDs = 1; | |
device.on('data', onData); | |
device.on('error', onError); | |
begin(); | |
function onData(msg) { | |
console.log("recieved", msg.length, msg); | |
const eventNum = msg[0]; | |
console.log("event 0x"+eventNum.toString(16)); | |
if (eventNum == BLINKY_EVT_INIT) { | |
ee.emit('init', msg); | |
} | |
else if (eventNum == BLINKY_EVT_LAYER) { | |
const layer = msg[1].toString(16); | |
ee.emit('layer', layer); | |
} | |
else { | |
console.error("unknown event"); | |
} | |
} | |
function onError(e) { | |
console.error(e); | |
process.exit(1); | |
} | |
function write(msg) { | |
msg[0] = reportNumber; | |
console.log('writing', msg); | |
var wrote = device.write(msg); | |
console.log('sent bytes', wrote); | |
} | |
/////// | |
function begin() { | |
const packet = Buffer.alloc(epSize); | |
packet[1] = BLINKY_CMD_INIT; | |
ee.on('init', (msg) => { | |
const version = msg[1]; | |
epSize = msg[2]; | |
totalLEDs = msg.readInt16BE(3); | |
if (version != 2) { | |
console.error("wrong protocol version"); | |
process.exit(1); | |
} | |
console.log("epsize (RAW HID packet size) set to", epSize); | |
console.log("total LEDs", totalLEDs); | |
process.nextTick(allOn); | |
}); | |
write(packet); | |
} | |
function allOn() { | |
const packet = Buffer.alloc(epSize + 1); | |
packet[1] = BLINKY_CMD_SET_ALL; | |
packet[2] = packet[3] = packet[4] = 0xFF; | |
write(packet); | |
setTimeout(allOff, 1000); | |
} | |
function allOff() { | |
const packet = Buffer.alloc(epSize + 1); | |
packet[1] = BLINKY_CMD_SET_ALL; | |
packet[2] = packet[3] = packet[4] = 0; | |
write(packet); | |
setTimeout(setAllYellow, 1000); | |
} | |
function setAllYellow() { | |
const packet = Buffer.alloc(epSize + 1); | |
packet[1] = BLINKY_CMD_SET_ALL; | |
packet[2] = 64; | |
packet[3] = 64; | |
packet[4] = 0; | |
write(packet); | |
setTimeout(scanStart, 1000); | |
} | |
let scanIndex = 0; | |
function scanStart() { | |
scanIndex = 0; | |
scan(); | |
} | |
function scan() { | |
const packet = Buffer.alloc(epSize + 1); | |
if (scanIndex >= totalLEDs) { | |
setTimeout(setMany, 1000); | |
return; | |
} | |
let i = 1; | |
packet[i++] = BLINKY_CMD_SET_ONE; // set index command | |
packet.writeInt16BE(scanIndex++, i); // which LED | |
i += 2; | |
packet[i++] = 0; // r | |
packet[i++] = 255; // g | |
packet[i++] = 255; // b | |
write(packet); | |
setTimeout(scan, 250); | |
} | |
function setMany() { | |
let layout = []; | |
for (let n = 0; n < totalLEDs; n++) { | |
let r = (n * 11) % 256; | |
let g = (n * 13) % 256; | |
let b = (n * 7) % 256; | |
layout.push({r,g,b}); | |
} | |
setLayout(layout); | |
} | |
function setLayout(layout) { | |
const headerSize = 3; | |
const perPacket = Math.floor((epSize - headerSize) / 3); | |
let led = 0; | |
while (led < totalLEDs) { | |
console.log("start index", led); | |
let packet = Buffer.alloc(epSize + 1); | |
let i = 1; | |
packet[i++] = BLINKY_CMD_SET_MANY; | |
packet.writeInt16BE(led, i); // initial LED offset | |
i += 2; | |
for (let p = 0; p < perPacket; p++) { | |
let rgb = layout[led++]; | |
packet[i++] = rgb.r; | |
packet[i++] = rgb.g; | |
packet[i++] = rgb.b; | |
if (led >= totalLEDs) break; | |
} | |
write(packet); | |
} | |
// no next function; just sit and read packets | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const EventEmitter = require('events'); | |
const HID = require('node-hid'); | |
const VENDOR_ID = 12951; | |
const PRODUCT_ID = 18806; | |
const RAW_USAGE_PAGE = 0xFF60; | |
const RAW_USAGE_ID = 0x61; | |
const BLINKY_CMD_INIT = 1; | |
const BLINKY_CMD_SET_ALL = 2; | |
const BLINKY_CMD_SET_ONE = 3; | |
const BLINKY_CMD_SET_MANY = 4; | |
const BLINKY_EVT_INIT = 0x80; | |
const BLINKY_EVT_LAYER = 0x81; | |
// maps key position to LED index, as determined experimentally using the led-test.js script | |
const ergodoxEzLayoutMap = [ | |
28, 27, 26, 25, 24, 0, 1, 2, 3, 4, | |
33, 32, 31, 30, 29, 5, 6, 7, 8, 9, | |
38, 37, 36, 35, 34, 10, 11, 12, 13, 14, | |
43, 42, 41, 40, 39, 15, 16, 17, 18, 19, | |
47, 46, 45, 44, 20, 21, 22, 23, | |
]; | |
function convertPrettyLayout(layout, layoutMap) { | |
let output = Array(48); | |
for (let i = 0; i<layout.length; i++) { | |
output[layoutMap[i]] = layout[i]; | |
} | |
return output; | |
} | |
const RED = {r:0xff, g:0, b:0}; | |
const ORANG = {r:0xff, g:0x7f, b:0}; | |
const YELLO = {r:0xff, g:0xff, b:0}; | |
const GREEN = {r:0, g:0xff, b:0}; | |
const CYAN = {r:0, g:0xff, b:0xff}; | |
const BLUE = {r:0, g:0, b:0xff}; | |
const MGNTA = {r:0xff, g:0, b:0xff}; | |
const PINK = {r:0xff, g:0x7f, b:0xff}; | |
const PURPL = {r:0x7f, g:0, b:0xff}; | |
const WHITE = {r:0xff, g:0xff, b:0xff}; | |
const GREY = {r:0x7f, g:0x7f, b:0x7f}; | |
const BLACK = {r:0, g:0, b:0}; | |
const _____ = BLACK; | |
const myLayout = [ | |
RED , RED , RED , ORANG, CYAN , CYAN , PINK , PINK , ORANG, BLUE , | |
MGNTA, _____, _____, MGNTA, YELLO, YELLO, YELLO, _____, _____, _____, | |
WHITE, BLUE , RED , GREEN, YELLO, _____, _____, _____, _____, _____, | |
WHITE, _____, YELLO, PURPL, YELLO, _____, _____, _____, _____, _____, | |
_____, _____, _____, _____, _____, _____, _____, _____, | |
]; | |
const matches = HID.devices().filter((d) => { | |
return d.vendorId == VENDOR_ID && | |
d.productId == PRODUCT_ID && | |
d.usagePage == RAW_USAGE_PAGE && | |
d.usage == RAW_USAGE_ID; | |
}); | |
if (matches.length == 0) { | |
console.error("no raw-HID devices"); | |
process.exit(1); | |
} | |
else if (matches.length > 1) { | |
console.error("too many matches"); | |
process.exit(2); | |
} | |
else { | |
console.log("connected", matches[0]); | |
} | |
const ee = new EventEmitter(); | |
const device = new HID.HID(matches[0].path); | |
const reportNumber = 0x42; | |
let epSize = 8; // start small | |
let totalLEDs = 1; | |
device.on('data', onData); | |
device.on('error', onError); | |
ee.on('layer',onLayer); | |
begin(); | |
function onData(msg) { | |
//console.log("recieved", msg.length, msg); | |
const eventNum = msg[0]; | |
//console.log("event 0x"+eventNum.toString(16)); | |
if (eventNum == BLINKY_EVT_INIT) { | |
ee.emit('init', msg); | |
} | |
else if (eventNum == BLINKY_EVT_LAYER) { | |
const layer = msg[1].toString(16); | |
ee.emit('layer', layer); | |
} | |
else { | |
console.error("unknown event"); | |
} | |
} | |
function onError(e) { | |
console.error(e); | |
process.exit(1); | |
} | |
function write(msg) { | |
msg[0] = reportNumber; | |
//console.log('writing', msg); | |
var wrote = device.write(msg); | |
//console.log('sent bytes', wrote); | |
} | |
/////// | |
function begin() { | |
const packet = Buffer.alloc(epSize); | |
packet[1] = BLINKY_CMD_INIT; | |
ee.on('init', (msg) => { | |
const version = msg[1]; | |
epSize = msg[2]; | |
totalLEDs = msg.readInt16BE(3); | |
if (version != 2) { | |
console.error("wrong protocol version"); | |
process.exit(1); | |
} | |
let currentLayer = msg[5]; | |
console.log("epsize (RAW HID packet size) set to", epSize); | |
console.log("total LEDs", totalLEDs); | |
console.log("current layer", currentLayer); | |
ee.emit('layer', currentLayer); | |
}); | |
write(packet); | |
} | |
function onLayer(layer) { | |
console.log("new layer", layer); | |
layer = layer || 0; | |
if (layer != 1) return; | |
const layout = convertPrettyLayout(myLayout, ergodoxEzLayoutMap); | |
setLayout(layout); | |
} | |
function setLayout(layout) { | |
const headerSize = 3; | |
const perPacket = Math.floor((epSize - headerSize) / 3); | |
let led = 0; | |
while (led < totalLEDs) { | |
//console.log("start index", led); | |
let packet = Buffer.alloc(epSize + 1); | |
let i = 1; | |
packet[i++] = BLINKY_CMD_SET_MANY; | |
packet.writeInt16BE(led, i); // initial LED offset | |
i += 2; | |
for (let p = 0; p < perPacket; p++) { | |
let rgb = layout[led++]; | |
packet[i++] = rgb.r; | |
packet[i++] = rgb.g; | |
packet[i++] = rgb.b; | |
if (led >= totalLEDs) break; | |
} | |
write(packet); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
And also https://github.com/stash/qmk_firmware/blob/blinky/quantum/blinky.h