diff --git a/README.md b/README.md index 2b0e1a6..80bb1a6 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,11 @@ # Pico-Game-Controller -WIP code for a rhythm game controller using a Raspberry Pi Pico. Intended for SDVX but is capable of handling 11 buttons, 11 LEDs, and 2 encoders. +WIP code for a rhythm game controller using a Raspberry Pi Pico. Intended for SDVX or IIDX but is capable of handling 11 buttons, 11 LEDs, and 2 encoders. Currently working/fixed: - Gamepad mode - default boot mode -- Keyboard & Mouse Mode - limited to 6KRO - hold btn-a to enter kb mode +- NKRO Keyboard & Mouse Mode - hold first button to enter kb mode - HID LEDs with Reactive LED fallback - sdvx/iidx spoof - Tested on EAC - 1000hz polling @@ -16,7 +16,6 @@ Currently working/fixed: TODO: - Store last mode in flash memory https://www.raspberrypi.org/forums/viewtopic.php?t=305570 -- nkro - debounce - ws2812b rgb on second core? - split input updating into pi pico's second core? @@ -29,3 +28,13 @@ How to Use: - Move pico-sdk back outside to the same level directory as Pico-Game-Controller. - Open Pico-Game-Controller in VSCode(assuming this is setup for the Pi Pico) and see if everything builds. - Tweakable parameters: Pinout, bindings in pico_game_controller.c, USB device reporting as SDVX/IIDX(con_mode) in usb_descriptors.c + +Thanks to: + +- Everyone in the Cons & Stuff Discord for providing near instant support to questions. +- https://github.com/hathach/tinyusb/tree/master/examples/device/hid_composite +- https://github.com/mdxtinkernick/pico_encoders for encoders which performed better than both interrupts and polling. +- My SE buddies who helped come up with a solution for the encoder rollover edge case scenario. +- https://github.com/Drewol/rp2040-gamecon for usb gamepad descriptor info. +- https://github.com/veroxzik/arduino-konami-spoof for konami spoof usb descriptor info. +- https://github.com/veroxzik/roxy-firmware for nkro descriptor and logic info. \ No newline at end of file diff --git a/build_uf2/Pico_Game_Controller.uf2 b/build_uf2/Pico_Game_Controller.uf2 index 1af9ff5..048bd73 100644 Binary files a/build_uf2/Pico_Game_Controller.uf2 and b/build_uf2/Pico_Game_Controller.uf2 differ diff --git a/src/pico_game_controller.c b/src/pico_game_controller.c index 6dfa7e8..119d81c 100644 --- a/src/pico_game_controller.c +++ b/src/pico_game_controller.c @@ -1,9 +1,6 @@ /* * Pico Game Controller * @author SpeedyPotato - * - * Based off tinyusb's hid_composite, mdxtinkernick/pico_encoders, and - * Drewol/rp2040-gamecon */ #include @@ -29,7 +26,7 @@ // MODIFY KEYBINDS HERE, MAKE SURE LENGTHS MATCH SW_GPIO_SIZE const uint8_t SW_KEYCODE[] = {HID_KEY_D, HID_KEY_F, HID_KEY_J, HID_KEY_K, HID_KEY_C, HID_KEY_M, HID_KEY_A, HID_KEY_B, - HID_KEY_1, HID_KEY_C, HID_KEY_D}; + HID_KEY_1, HID_KEY_E, HID_KEY_G}; const uint8_t SW_GPIO[] = { 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 27, }; @@ -157,31 +154,23 @@ void key_mode() { if (tud_hid_ready()) { /*------------- Keyboard -------------*/ if (sw_changed) { - bool is_pressed = false; - int keycode_idx = 0; - uint8_t keycode[6] = {0}; // looks like we are limited to 6kro? + uint8_t nkro_report[32] = {0}; for (int i = 0; i < SW_GPIO_SIZE; i++) { if (sw_val[i]) { - // use to avoid send multiple consecutive zero report for keyboard - keycode[keycode_idx] = SW_KEYCODE[i]; - keycode_idx = ++keycode_idx % SW_GPIO_SIZE; - is_pressed = true; + uint8_t bit = SW_KEYCODE[i] % 8; + uint8_t byte = (SW_KEYCODE[i] / 8) + 1; + if (SW_KEYCODE[i] >= 240 && SW_KEYCODE[i] <= 247) { + nkro_report[0] |= (1 << bit); + } else if (byte > 0 && byte <= 31) { + nkro_report[byte] |= (1 << bit); + } prev_sw_val[i] = sw_val[i]; - // Reactive Lighting On - gpio_put(LED_GPIO[i], 1); - } else { - // Reactive Lighting Off - gpio_put(LED_GPIO[i], 0); } } - if (is_pressed) { - // Send key report - tud_hid_keyboard_report(REPORT_ID_KEYBOARD, 0, keycode); - } else { - // Send empty key report if previously has key pressed - tud_hid_keyboard_report(REPORT_ID_KEYBOARD, 0, NULL); - } + // Send key report + tud_hid_n_report(0x00, REPORT_ID_KEYBOARD, &nkro_report, + sizeof(nkro_report)); sw_changed = false; } diff --git a/src/tusb_config.h b/src/tusb_config.h index 071ae69..2c1de22 100644 --- a/src/tusb_config.h +++ b/src/tusb_config.h @@ -103,7 +103,7 @@ extern "C" { #define CFG_TUD_VENDOR 0 // HID buffer size Should be sufficient to hold ID (if any) + Data -#define CFG_TUD_HID_EP_BUFSIZE 16 +#define CFG_TUD_HID_EP_BUFSIZE 64 #ifdef __cplusplus } diff --git a/src/usb_descriptors.c b/src/usb_descriptors.c index 708b533..a879cf9 100644 --- a/src/usb_descriptors.c +++ b/src/usb_descriptors.c @@ -76,7 +76,7 @@ uint8_t const* tud_descriptor_device_cb(void) { uint8_t const desc_hid_report[] = { GAMECON_REPORT_DESC_JOYSTICK(HID_REPORT_ID(REPORT_ID_JOYSTICK)), GAMECON_REPORT_DESC_LIGHTS(HID_REPORT_ID(REPORT_ID_LIGHTS)), - TUD_HID_REPORT_DESC_KEYBOARD(HID_REPORT_ID(REPORT_ID_KEYBOARD)), + GAMECON_REPORT_DESC_NKRO(HID_REPORT_ID(REPORT_ID_KEYBOARD)), TUD_HID_REPORT_DESC_MOUSE(HID_REPORT_ID(REPORT_ID_MOUSE))}; // Invoked when received GET HID REPORT DESCRIPTOR diff --git a/src/usb_descriptors.h b/src/usb_descriptors.h index c09dfba..7b65e53 100644 --- a/src/usb_descriptors.h +++ b/src/usb_descriptors.h @@ -42,3 +42,15 @@ enum { HID_REPORT_COUNT(1), HID_REPORT_SIZE(40), /*Padding*/ \ HID_INPUT(HID_CONSTANT | HID_VARIABLE | HID_ABSOLUTE), \ HID_COLLECTION_END + +// NKRO Descriptor +#define GAMECON_REPORT_DESC_NKRO(...) \ + HID_USAGE_PAGE(HID_USAGE_PAGE_DESKTOP), HID_USAGE(HID_USAGE_PAGE_KEYBOARD), \ + HID_COLLECTION(HID_COLLECTION_APPLICATION), \ + __VA_ARGS__ HID_REPORT_SIZE(1), HID_REPORT_COUNT(8), \ + HID_USAGE_PAGE(HID_USAGE_PAGE_KEYBOARD), HID_USAGE_MIN(224), \ + HID_USAGE_MAX(231), HID_LOGICAL_MIN(0), HID_LOGICAL_MAX(1), \ + HID_INPUT(HID_VARIABLE), HID_REPORT_SIZE(1), HID_REPORT_COUNT(31 * 8), \ + HID_LOGICAL_MIN(0), HID_LOGICAL_MAX(1), \ + HID_USAGE_PAGE(HID_USAGE_PAGE_KEYBOARD), HID_USAGE_MIN(0), \ + HID_USAGE_MAX(31 * 8 - 1), HID_INPUT(HID_VARIABLE), HID_COLLECTION_END