Sharing my configuration for a generic joystick and 12 arcade button USB controller

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)
    );
}

great work! Maybe move these into a preset that could be exported, then others could simply import it :slight_smile:

1 Like

Great suggestion! Thanks!

I moved all the related triggers and actions to a separate config file and attached it here.

Joystick Presets.bttpreset (430.9 KB)

FYI -

The hardware kit I am using is no longer available, but I believe it’s the exact same board as this one on Amazon. Any similar DIY joystick kit should work with minimal modification to the BTT config.