I am using an arcade style joystick to control my K40 laser engraver running Lightburn.
The joystick moves around the selected on-screen item.
Holding down one of the buttons as a modifier key makes the joystick move the laser around.
I set additional buttons for various functions by linking them to the Lightburn keyboard shortcuts (frame, home, preview, etc)
The repeat function works better than any of the other methods I’ve tried. Hope this helps others!
let repeatState = {
dir: null,
timer: null
};
function stopRepeat() {
if (repeatState.timer) {
clearInterval(repeatState.timer);
repeatState.timer = null;
}
repeatState.dir = null;
}
function startRepeat(directionName, triggerFn) {
// If already repeating this direction, do nothing
if (repeatState.dir === directionName && repeatState.timer) return;
// Switch to new direction
stopRepeat();
repeatState.dir = directionName;
// Fire once immediately
triggerFn(directionName);
// keep firing while held
repeatState.timer = setInterval(() => {
triggerFn(directionName);
}, 80); // adjust repeat rate (ms) if needed
}
function analyzeDeviceInput(targetDevice, reportID, reportDataHex)
{
const reportBuffer = buffer.Buffer.from(reportDataHex, 'hex');
if (reportBuffer.length < 7) {
log('Unexpected report length: ' + reportBuffer.length);
return;
}
const b0 = reportBuffer.readUInt8(0);
const b1 = reportBuffer.readUInt8(1);
const b6 = reportBuffer.readUInt8(6);
const trigger = (name) => {
log(name);
bttTriggerDeviceTrigger(targetDevice, name);
};
const triggerAndPoll = (name) => {
trigger(name);
};
if (b0 === 0x7f && b1 === 0x7f && b6 === 0x00) {
stopRepeat();
// trigger('[Optional]'); //Optional Trigger at neutral position
return;
}
if (b6 === 0x00) {
// Joystick center position = stop repeating
if (b0 === 0x7f && b1 === 0x7f) {
stopRepeat();
return;
}
// Continuous diagonal joystick controls
if (b0 === 0xff && b1 === 0xff) { // Up-Right
startRepeat('UpRight', trigger);
return;
}
if (b0 === 0xff && b1 === 0x00) { // Up-Left
startRepeat('UpLeft', trigger);
return;
}
if (b0 === 0x00 && b1 === 0x00) { // Down-Left
startRepeat('DownLeft', trigger);
return;
}
if (b0 === 0x00 && b1 === 0xff) { // Down-Right
startRepeat('DownRight', trigger);
return;
}
// Continuous Up/Down/Left/Right in joystick mode
if (b0 === 0xff && b1 === 0x7f) { // Up
startRepeat('UP', trigger);
return;
}
if (b0 === 0x00 && b1 === 0x7f) { // Down
startRepeat('DN', trigger);
return;
}
if (b0 === 0x7f && b1 === 0x00) { // Left
startRepeat('LT', trigger);
return;
}
if (b0 === 0x7f && b1 === 0xff) { // Right
startRepeat('RT', trigger);
return;
}
// Any other combination: stop repeating
stopRepeat();
return;
}
// Continuous Up/Down/Left/Right while holding down specific button
if (b6 === 0x02) {
// No direction = stop repeating
if (b0 === 0x7f && b1 === 0x7f) {
stopRepeat();
return;
}
if (b0 === 0xff) { // Up with modifier
startRepeat('Up_m1', trigger);
return;
}
if (b0 === 0x00) { // Down with modifier
startRepeat('Down_m1', trigger);
return;
}
if (b1 === 0x00) { // Left with modifier
startRepeat('Left_m1', trigger);
return;
}
if (b1 === 0xff) { // Right with modifier
startRepeat('Right_m1', trigger);
return;
}
// Any other combination: stop repeating
stopRepeat();
return;
}
// Non-directional inputs (buttons etc.)
// Stop any active repeat when button action
stopRepeat();
switch (b6) {
case 0x04:
trigger('LaserCenter (7)');
return;
case 0x08:
trigger('Start (8)');
return;
case 0x10:
trigger('Home (9)');
return;
case 0x20:
trigger('Stop (10)');
return;
case 0x40:
trigger('Frame (11)');
return;
case 0x80:
trigger('Preview (12)');
return;
case 0x82:
// 0x80 (Preview) + 0x02 (arrow mode) – currently mapped as ESC
trigger('ESC');
return;
default:
// fall through to unmapped log
break;
}
// Unmapped inputs (for debugging / future mapping)
log(
'Unmapped input: ' +
'b0=0x' + b0.toString(16) +
', b1=0x' + b1.toString(16) +
', b6=0x' + b6.toString(16)
);
}