{"id":25540967,"url":"https://github.com/kevinisabelle/raspigamecontroller","last_synced_at":"2026-04-19T17:03:00.864Z","repository":{"id":278276130,"uuid":"918755422","full_name":"kevinisabelle/raspigamecontroller","owner":"kevinisabelle","description":"Raspberry Pi Bluetooth LE HID Game controller","archived":false,"fork":false,"pushed_at":"2025-02-18T22:18:22.000Z","size":1501,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-02-18T23:24:18.224Z","etag":null,"topics":["ble","bluetooth","bluez","gamepad","hid","raspberrypi"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/kevinisabelle.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2025-01-18T19:13:09.000Z","updated_at":"2025-02-18T22:18:25.000Z","dependencies_parsed_at":"2025-02-18T23:34:23.939Z","dependency_job_id":null,"html_url":"https://github.com/kevinisabelle/raspigamecontroller","commit_stats":null,"previous_names":["kevinisabelle/raspigamecontroller"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kevinisabelle%2Fraspigamecontroller","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kevinisabelle%2Fraspigamecontroller/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kevinisabelle%2Fraspigamecontroller/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kevinisabelle%2Fraspigamecontroller/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/kevinisabelle","download_url":"https://codeload.github.com/kevinisabelle/raspigamecontroller/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":239786903,"owners_count":19696930,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ble","bluetooth","bluez","gamepad","hid","raspberrypi"],"created_at":"2025-02-20T06:22:30.315Z","updated_at":"2025-10-07T10:33:47.168Z","avatar_url":"https://github.com/kevinisabelle.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# RaspberryPi Bluetooth LE HID Game controller\n\n## Reference documentation\n\n- _HID Usage Table_: https://www.usb.org/document-library/hid-usage-tables-16\n- _Device Class Definition for Human Interface Devices (HID)_: https://www.usb.org/document-library/device-class-definition-hid-111\n- _HID over GATT Profile 1.0_: https://www.bluetooth.com/specifications/specs/hid-over-gatt-profile-1-0/\n- _Bluez dbus interfaces_: https://github.com/bluez/bluez/tree/master/doc\n\n## Current Issues:\n\n- Reconnection not working automatically\n- The number of axies also seems to be limited.\n\n## Setup\n\n### Install python and bluez\n\n```bash\n#sudo apt get py3-setuptools\nsudo apt update \u0026\u0026 sudo apt upgrade -y\nsudo apt install libbluetooth-dev\n\n# Blutooth\nsudo systemctl enable bluetooth\nsudo systemctl start bluetooth\n```\n\n### Enable Spi0 and Spi1\n\n- Run the raspi-config app and enable Interface Spi.\n- Then modify /boot/firmware/config.txt\n- Add these if not present:\n\n```\ndtparam=spi=on\ndtoverlay=spi1-3cs\n```\n\n- Reboot\n\n### Setup bluez service parameters\n\n```bash\nsudo nano /lib/systemd/system/bluetooth.service\n\n# Modify the exec start\nExecStart=/usr/libexec/bluetooth/bluetoothd -E --noplugin=*\n\n# Save the file and restart bluetooth\n\nsudo systemctl daemon-reload\nsudo systemctl restart bluetooth\n```\n\n### Generating the Hid Report Map and Payload Interface\n\nIn the HidTools folder, use the HidReportMapCreator program.\n\n- Create a copy of the KiGPSimple.cs class and name it as you like\n- Modify the Program.cs and use the new class to create the device reference\n- Run the program\n- Copy the python code from the output in the python/GamepadValues.py file\n- Modify the python/UpdaterService.py and adapt the \\_update_gamepad_controls according to the generated GamepadValues1 class\n\n## Running\n\n### Start the program\n\n- Copy the content of the python folder on the Pi\n- Run with\n\n```bash\nsudo python3 App.py\n```\n\n- Once the program is properly started, you can connect it to Windows using the Bluetooth configuration like any device.\n- Once the device is bonded in Windows, you can open the \"Set up USB controller\" to display connected Gamepads.\n\n## Debugging\n\n### Using dbus-tool\n\nUse dbus-tool to check Bluez is connecting with the program properly:\n\n```\n\nsudo dbus-monitor --system\n\n```\n\n### Using Wireshark\n\nUse Wireshark on Windows to inspect the GATT services packets.\n\n- Start a session using Pcap2\n- Filter out protocol USB\n\n```\n\n!(\\_ws.col.protocol == \"USB\")\n\n```\n\n- Or you can filter in the proper services:\n\n```\n\n(\\_ws.col.protocol == \"ATT\" || \\_ws.col.protocol == \"SDP\" || \\_ws.col.protocol == \"SMP\" || \\_ws.col.protocol == \"GAP\" || \\_ws.col.protocol == \"GATT\" || \\_ws.col.protocol == \"HCI_EVT\")\n\n```\n\n### Using Hid Explorer\n\nYou can also use this great online tool (which runs in the browser): https://nondebug.github.io/webhid-explorer/ . Using this tool you can connect to the recognized HID devices and see their definition and notifications.\n\n## Hid Creator output example:\n\n```\nHid Report Map and Payload interface creator\n---------------------------------------------------\n\nNotes:\n-------\n1. This tool is used to generate a report map and payload for a HID device.\n2. The report map is a list of instructions that the device uses to send data to the host.\n3. It will generate the Python class code for the report payload to use in the python part of this project.\n\n---------------------\nReport Map Descriptor\n---------------------\n\n0x05, 0x01      # Usage Page (Generic Desktop Controls)\n0x09, 0x05      # Usage (Gamepad)\n0xA1, 0x01      # Collection (Application)\n0x85, 0x01      # Report ID 1\n0x05, 0x09      # Usage Page (Button)\n0x19, 0x01      # Usage Minimum (1)\n0x29, 0x08      # Usage Maximum (8)\n0x15, 0x00      # Logical Minimum (0)\n0x25, 0x01      # Logical Maximum (1)\n0x75, 0x01      # Report Size (1)\n0x95, 0x08      # Report Count (8)\n0x81, 0x02      # Input (00000010)\n0x05, 0x01      # Usage Page (Generic Desktop Controls)\n0x09, 0x36      # Usage (Slider)\n0x19, 0x01      # Usage Minimum (1)\n0x29, 0x01      # Usage Maximum (1)\n0x15, 0x00      # Logical Minimum (0)\n0x25, 0xFF      # Logical Maximum (255)\n0x75, 0x08      # Report Size (8)\n0x95, 0x01      # Report Count (1)\n0x81, 0x02      # Input (00000010)\n0x05, 0x01      # Usage Page (Generic Desktop Controls)\n0x09, 0x36      # Usage (Slider)\n0x19, 0x01      # Usage Minimum (1)\n0x29, 0x01      # Usage Maximum (1)\n0x15, 0x00      # Logical Minimum (0)\n0x25, 0xFF      # Logical Maximum (255)\n0x75, 0x08      # Report Size (8)\n0x95, 0x01      # Report Count (1)\n0x81, 0x02      # Input (00000010)\n0x05, 0x01      # Usage Page (Generic Desktop Controls)\n0x09, 0x36      # Usage (Slider)\n0x19, 0x01      # Usage Minimum (1)\n0x29, 0x01      # Usage Maximum (1)\n0x15, 0x00      # Logical Minimum (0)\n0x25, 0xFF      # Logical Maximum (255)\n0x75, 0x08      # Report Size (8)\n0x95, 0x01      # Report Count (1)\n0x81, 0x02      # Input (00000010)\n0xC0    # End Collection\n\n----------------------\nReport Payload Example\n----------------------\n\nBtn1 #1 (1 bits) Unspecified\nBtn1 #2 (1 bits) Unspecified\nBtn1 #3 (1 bits) Unspecified\nBtn1 #4 (1 bits) Unspecified\nBtn1 #5 (1 bits) Unspecified\nBtn1 #6 (1 bits) Unspecified\nBtn1 #7 (1 bits) Unspecified\nBtn1 #8 (1 bits) Unspecified\nSlider #1 (8 bits) Slider\nSlider2 #1 (8 bits) Slider\nSlider3 #1 (8 bits) Slider\n\n----------------------------------\nReport Payload Example (Formatted)\n----------------------------------\n\nBBBBBBBB SSSSSSSS SSSSSSSS SSSSSSSS\n12345678 11111111 11111111 11111111\n\n------------\nPython class\n------------\n\nclass GamepadValues1:\n    def __init__(self, Btn10=0, Btn11=0, Btn12=0, Btn13=0, Btn14=0, Btn15=0, Btn16=0, Btn17=0, Slider0=0, Slider20=0, Slider30=0):\n        self.Btn10 = Btn10 \u0026 0x1\n        self.Btn11 = Btn11 \u0026 0x1\n        self.Btn12 = Btn12 \u0026 0x1\n        self.Btn13 = Btn13 \u0026 0x1\n        self.Btn14 = Btn14 \u0026 0x1\n        self.Btn15 = Btn15 \u0026 0x1\n        self.Btn16 = Btn16 \u0026 0x1\n        self.Btn17 = Btn17 \u0026 0x1\n        self.Slider0 = Slider0 \u0026 0xFF\n        self.Slider20 = Slider20 \u0026 0xFF\n        self.Slider30 = Slider30 \u0026 0xFF\n\n    def set_Btn10(self, value):\n        self.Btn10 = value \u0026 0x1\n\n    def set_Btn11(self, value):\n        self.Btn11 = value \u0026 0x1\n\n    def set_Btn12(self, value):\n        self.Btn12 = value \u0026 0x1\n\n    def set_Btn13(self, value):\n        self.Btn13 = value \u0026 0x1\n\n    def set_Btn14(self, value):\n        self.Btn14 = value \u0026 0x1\n\n    def set_Btn15(self, value):\n        self.Btn15 = value \u0026 0x1\n\n    def set_Btn16(self, value):\n        self.Btn16 = value \u0026 0x1\n\n    def set_Btn17(self, value):\n        self.Btn17 = value \u0026 0x1\n\n    def set_Slider0(self, value):\n        self.Slider0 = value \u0026 0xFF\n\n    def set_Slider20(self, value):\n        self.Slider20 = value \u0026 0xFF\n\n    def set_Slider30(self, value):\n        self.Slider30 = value \u0026 0xFF\n\n    def get_report(self):\n        total = 0\n        total = (total \u003c\u003c 1) | self.Btn10\n        total = (total \u003c\u003c 1) | self.Btn11\n        total = (total \u003c\u003c 1) | self.Btn12\n        total = (total \u003c\u003c 1) | self.Btn13\n        total = (total \u003c\u003c 1) | self.Btn14\n        total = (total \u003c\u003c 1) | self.Btn15\n        total = (total \u003c\u003c 1) | self.Btn16\n        total = (total \u003c\u003c 1) | self.Btn17\n        total = (total \u003c\u003c 8) | self.Slider0\n        total = (total \u003c\u003c 8) | self.Slider20\n        total = (total \u003c\u003c 8) | self.Slider30\n        result = total.to_bytes(4, byteorder='big')\n        return bytes([0x01] + list(result))\n\n\n    def get_report_map(self):\n        return bytes([\n            0x05, 0x01,   # Usage Page (Generic Desktop Controls)\n            0x09, 0x05,   # Usage (Gamepad)\n            0xA1, 0x01,   # Collection (Application)\n            0x85, 0x01,   # Report ID 1\n            0x05, 0x09,   # Usage Page (Button)\n            0x19, 0x01,   # Usage Minimum (1)\n            0x29, 0x08,   # Usage Maximum (8)\n            0x15, 0x00,   # Logical Minimum (0)\n            0x25, 0x01,   # Logical Maximum (1)\n            0x75, 0x01,   # Report Size (1)\n            0x95, 0x08,   # Report Count (8)\n            0x81, 0x02,   # Input (00000010)\n            0x05, 0x01,   # Usage Page (Generic Desktop Controls)\n            0x09, 0x36,   # Usage (Slider)\n            0x19, 0x01,   # Usage Minimum (1)\n            0x29, 0x01,   # Usage Maximum (1)\n            0x15, 0x00,   # Logical Minimum (0)\n            0x25, 0xFF,   # Logical Maximum (255)\n            0x75, 0x08,   # Report Size (8)\n            0x95, 0x01,   # Report Count (1)\n            0x81, 0x02,   # Input (00000010)\n            0x05, 0x01,   # Usage Page (Generic Desktop Controls)\n            0x09, 0x36,   # Usage (Slider)\n            0x19, 0x01,   # Usage Minimum (1)\n            0x29, 0x01,   # Usage Maximum (1)\n            0x15, 0x00,   # Logical Minimum (0)\n            0x25, 0xFF,   # Logical Maximum (255)\n            0x75, 0x08,   # Report Size (8)\n            0x95, 0x01,   # Report Count (1)\n            0x81, 0x02,   # Input (00000010)\n            0x05, 0x01,   # Usage Page (Generic Desktop Controls)\n            0x09, 0x36,   # Usage (Slider)\n            0x19, 0x01,   # Usage Minimum (1)\n            0x29, 0x01,   # Usage Maximum (1)\n            0x15, 0x00,   # Logical Minimum (0)\n            0x25, 0xFF,   # Logical Maximum (255)\n            0x75, 0x08,   # Report Size (8)\n            0x95, 0x01,   # Report Count (1)\n            0x81, 0x02,   # Input (00000010)\n            0xC0,   # End Collection\n        ])\n\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkevinisabelle%2Fraspigamecontroller","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkevinisabelle%2Fraspigamecontroller","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkevinisabelle%2Fraspigamecontroller/lists"}