Skip to content

Add External control for LEDs via HID Reports#257

Open
drstranges wants to merge 7 commits intoFreeJoy-Team:devfrom
drstranges:feature/external_led_source
Open

Add External control for LEDs via HID Reports#257
drstranges wants to merge 7 commits intoFreeJoy-Team:devfrom
drstranges:feature/external_led_source

Conversation

@drstranges
Copy link

@drstranges drstranges commented Feb 16, 2026

Summary

This PR introduces a new "External" LED source to FreeJoy, enabling host software to control up to 24 LEDs directly via HID Output Reports. This feature is useful for applications where LED states need to be synchronized with game events or external logic rather than being tied to physical buttons or axes.

This feature is supported by FreeJoy Configurator (PR #45) and is fully documented in the Wiki (FreeJoyWiki PR #89).

Changes

  • Configuration: Added SOURCE_EXTERNAL (-4) as a valid input source for LEDs.
  • Logic: Updated LEDs_LogicalProcess in leds.c to support the External source. When an LED is set to External, its state is determined by a bitmask received from the host.
  • USB Descriptor: Added a new HID Output Report (ID 6) to the Custom HID interface. The report carries a 4-byte bitmask representing the states of 24 LEDs.
  • Documentation: Updated README.md with a new "Host-controlled LEDs" section, providing technical details on the HID report format and a Python usage example.

Technical Details

The LED states are updated by sending a HID Output Report to the second HID device (Usage Page: 0xFF00, Interface: 1).

Report ID: 6
Payload: 4 bytes (little-endian bitmask)

  • Bit 0: LED 1
  • Bit 1: LED 2
  • ...
  • Bit 23: LED 24

Usage Example (Python)

import hid

# FreeJoy USB VID/PID
VID = 0x0483
PID = 0x5750

def set_freejoy_leds(led_mask):
    # Find the correct interface (Interface 1 for LEDs)
    target_path = None
    for device in hid.enumerate(VID, PID):
        if device['interface_number'] == 1:
            target_path = device['path']
            break

    if not target_path:
        print("FreeJoy interface 1 not found")
        return

    try:
        device = hid.device()
        device.open_path(target_path)
        
        # Report ID 6 + 4 bytes of bitmask (little-endian)
        report = [0x06] + list(led_mask.to_bytes(4, 'little'))
        device.write(report)
        device.close()
    except Exception as e:
        print(f"Error: {e}")

# Example: Turn on LEDs 1 and 3 (bitmask 0x05)
set_freejoy_leds(0x00000005)

Verification Results

  • Verified that the External source is correctly handled in leds.c.
  • Verified the USB report descriptor additions in usb_desc.c.
  • Documentation updated and verified.

Related PRs:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

Comments