├── .gitignore ├── C ├── Config │ └── LUFAConfig.h ├── Descriptors.c ├── Descriptors.h ├── HORI_Descriptors ├── Joystick.c ├── Joystick.h ├── ironic.data ├── makefile ├── uart.c └── uart.h ├── LUFA_LICENSE ├── Python ├── JoystickEnums.py ├── constants.py ├── controllerMapping.csv ├── example.py ├── keys.txt ├── maths.py └── seriallib.py └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | *.d 3 | *.o 4 | *.bin 5 | *.elf 6 | *.hex 7 | *.lss 8 | *.map 9 | *.sym 10 | *.eep 11 | *.pyc 12 | LUFA/ 13 | .idea/misc.xml 14 | .idea/modules.xml 15 | .idea/Switch-Fightstick.iml 16 | .idea/vcs.xml 17 | .idea/workspace.xml 18 | .vscode/settings.json 19 | C/cleanup.sh 20 | C/Joystick_new.c 21 | Python/__pycache__/serialManager.cpython-37.pyc 22 | Test/example.c 23 | Test/HORI_Descriptors 24 | Test/makeAndClean.sh 25 | Test/Makefile 26 | Test/uart.c 27 | Test/uart.h 28 | .vscode/c_cpp_properties.json 29 | Python/__pycache__/maths.cpython-36.pyc 30 | Python/__pycache__/payload.cpython-36.pyc 31 | Python/test.py 32 | C/._.DS_Store 33 | C/._Joystick.c 34 | C/._Joystick.h 35 | Python/serialMain.py 36 | venv/ 37 | Python/.idea -------------------------------------------------------------------------------- /C/Config/LUFAConfig.h: -------------------------------------------------------------------------------- 1 | // LUFA Library Configuration Header File. Used to configure LUFA's compile time options, as an alternative to the compile-time defines. 2 | #ifndef _LUFA_CONFIG_H_ 3 | #define _LUFA_CONFIG_H_ 4 | #if (ARCH == ARCH_AVR8) 5 | // Non-USB Related Configuration Tokens 6 | // #define DISABLE_TERMINAL_CODES 7 | 8 | // USB Class Driver Related Tokens 9 | // #define HID_HOST_BOOT_PROTOCOL_ONLY 10 | // #define HID_STATETABLE_STACK_DEPTH {Insert Value Here} 11 | // #define HID_USAGE_STACK_DEPTH {Insert Value Here} 12 | // #define HID_MAX_COLLECTIONS {Insert Value Here} 13 | // #define HID_MAX_REPORTITEMS {Insert Value Here} 14 | // #define HID_MAX_REPORT_IDS {Insert Value Here} 15 | // #define NO_CLASS_DRIVER_AUTOFLUSH 16 | 17 | // General USB Driver Related Tokens 18 | // #define ORDERED_EP_CONFIG 19 | #define USE_STATIC_OPTIONS (USB_DEVICE_OPT_FULLSPEED | USB_OPT_REG_ENABLED | USB_OPT_AUTO_PLL) 20 | #define USB_DEVICE_ONLY 21 | // #define USB_HOST_ONLY 22 | // #define USB_STREAM_TIMEOUT_MS {Insert Value Here} 23 | // #define NO_LIMITED_CONTROLLER_CONNECT 24 | // #define NO_SOF_EVENTS 25 | 26 | // USB Device Mode Driver Related Tokens 27 | // #define USE_RAM_DESCRIPTORS 28 | #define USE_FLASH_DESCRIPTORS 29 | // #define USE_EEPROM_DESCRIPTORS 30 | // #define NO_INTERNAL_SERIAL 31 | #define FIXED_CONTROL_ENDPOINT_SIZE 64 32 | // #define DEVICE_STATE_AS_GPIOR {Insert Value Here} 33 | #define FIXED_NUM_CONFIGURATIONS 1 34 | // #define CONTROL_ONLY_DEVICE 35 | // #define INTERRUPT_CONTROL_ENDPOINT 36 | // #define NO_DEVICE_REMOTE_WAKEUP 37 | // #define NO_DEVICE_SELF_POWER 38 | 39 | // USB Host Mode Driver Related Tokens 40 | // #define HOST_STATE_AS_GPIOR {Insert Value Here} 41 | // #define USB_HOST_TIMEOUT_MS {Insert Value Here} 42 | // #define HOST_DEVICE_SETTLE_DELAY_MS {Insert Value Here} 43 | // #define NO_AUTO_VBUS_MANAGEMENT 44 | // #define INVERTED_VBUS_ENABLE_LINE 45 | 46 | #elif (ARCH == ARCH_XMEGA) 47 | // Non-USB Related Configuration Tokens 48 | // #define DISABLE_TERMINAL_CODES 49 | 50 | // USB Class Driver Related Tokens 51 | // #define HID_HOST_BOOT_PROTOCOL_ONLY 52 | // #define HID_STATETABLE_STACK_DEPTH {Insert Value Here} 53 | // #define HID_USAGE_STACK_DEPTH {Insert Value Here} 54 | // #define HID_MAX_COLLECTIONS {Insert Value Here} 55 | // #define HID_MAX_REPORTITEMS {Insert Value Here} 56 | // #define HID_MAX_REPORT_IDS {Insert Value Here} 57 | // #define NO_CLASS_DRIVER_AUTOFLUSH 58 | 59 | // General USB Driver Related Tokens 60 | #define USE_STATIC_OPTIONS (USB_DEVICE_OPT_FULLSPEED | USB_OPT_RC32MCLKSRC | USB_OPT_BUSEVENT_PRIHIGH) 61 | // #define USB_STREAM_TIMEOUT_MS {Insert Value Here} 62 | // #define NO_LIMITED_CONTROLLER_CONNECT 63 | // #define NO_SOF_EVENTS 64 | 65 | // USB Device Mode Driver Related Tokens 66 | // #define USE_RAM_DESCRIPTORS 67 | #define USE_FLASH_DESCRIPTORS 68 | // #define USE_EEPROM_DESCRIPTORS 69 | // #define NO_INTERNAL_SERIAL 70 | #define FIXED_CONTROL_ENDPOINT_SIZE 64 71 | // #define DEVICE_STATE_AS_GPIOR {Insert Value Here} 72 | #define FIXED_NUM_CONFIGURATIONS 1 73 | // #define CONTROL_ONLY_DEVICE 74 | #define MAX_ENDPOINT_INDEX 1 75 | // #define NO_DEVICE_REMOTE_WAKEUP 76 | // #define NO_DEVICE_SELF_POWER 77 | 78 | #else 79 | #error Unsupported architecture for this LUFA configuration file. 80 | #endif 81 | #endif 82 | -------------------------------------------------------------------------------- /C/Descriptors.c: -------------------------------------------------------------------------------- 1 | #include "Descriptors.h" 2 | 3 | // HID Descriptors. 4 | const USB_Descriptor_HIDReport_Datatype_t PROGMEM JoystickReport[] = { 5 | HID_RI_USAGE_PAGE(8,1), /* Generic Desktop */ 6 | HID_RI_USAGE(8,5), /* Joystick */ 7 | HID_RI_COLLECTION(8,1), /* Application */ 8 | // Buttons (2 bytes) 9 | HID_RI_LOGICAL_MINIMUM(8,0), 10 | HID_RI_LOGICAL_MAXIMUM(8,1), 11 | HID_RI_PHYSICAL_MINIMUM(8,0), 12 | HID_RI_PHYSICAL_MAXIMUM(8,1), 13 | // The Switch will allow us to expand the original HORI descriptors to a full 16 buttons. 14 | // The Switch will make use of 14 of those buttons. 15 | HID_RI_REPORT_SIZE(8,1), 16 | HID_RI_REPORT_COUNT(8,16), 17 | HID_RI_USAGE_PAGE(8,9), 18 | HID_RI_USAGE_MINIMUM(8,1), 19 | HID_RI_USAGE_MAXIMUM(8,16), 20 | HID_RI_INPUT(8,2), 21 | // HAT Switch (1 nibble) 22 | HID_RI_USAGE_PAGE(8,1), 23 | HID_RI_LOGICAL_MAXIMUM(8,7), 24 | HID_RI_PHYSICAL_MAXIMUM(16,315), 25 | HID_RI_REPORT_SIZE(8,4), 26 | HID_RI_REPORT_COUNT(8,1), 27 | HID_RI_UNIT(8,20), 28 | HID_RI_USAGE(8,57), 29 | HID_RI_INPUT(8,66), 30 | // There's an additional nibble here that's utilized as part of the Switch Pro Controller. 31 | // I believe this -might- be separate U/D/L/R bits on the Switch Pro Controller, as they're utilized as four button descriptors on the Switch Pro Controller. 32 | HID_RI_UNIT(8,0), 33 | HID_RI_REPORT_COUNT(8,1), 34 | HID_RI_INPUT(8,1), 35 | // Joystick (4 bytes) 36 | HID_RI_LOGICAL_MAXIMUM(16,255), 37 | HID_RI_PHYSICAL_MAXIMUM(16,255), 38 | HID_RI_USAGE(8,48), 39 | HID_RI_USAGE(8,49), 40 | HID_RI_USAGE(8,50), 41 | HID_RI_USAGE(8,53), 42 | HID_RI_REPORT_SIZE(8,8), 43 | HID_RI_REPORT_COUNT(8,4), 44 | HID_RI_INPUT(8,2), 45 | // ??? Vendor Specific (1 byte) 46 | // This byte requires additional investigation. 47 | HID_RI_USAGE_PAGE(16,65280), 48 | HID_RI_USAGE(8,32), 49 | HID_RI_REPORT_COUNT(8,1), 50 | HID_RI_INPUT(8,2), 51 | // Output (8 bytes) 52 | // Original observation of this suggests it to be a mirror of the inputs that we sent. 53 | // The Switch requires us to have these descriptors available. 54 | HID_RI_USAGE(16,9761), 55 | HID_RI_REPORT_COUNT(8,8), 56 | HID_RI_OUTPUT(8,2), 57 | HID_RI_END_COLLECTION(0), 58 | }; 59 | 60 | // Device Descriptor Structure 61 | const USB_Descriptor_Device_t PROGMEM DeviceDescriptor = { 62 | .Header = {.Size = sizeof(USB_Descriptor_Device_t), .Type = DTYPE_Device}, 63 | 64 | .USBSpecification = VERSION_BCD(2,0,0), 65 | .Class = USB_CSCP_NoDeviceClass, 66 | .SubClass = USB_CSCP_NoDeviceSubclass, 67 | .Protocol = USB_CSCP_NoDeviceProtocol, 68 | 69 | .Endpoint0Size = FIXED_CONTROL_ENDPOINT_SIZE, 70 | 71 | .VendorID = 0x0F0D, 72 | .ProductID = 0x0092, 73 | .ReleaseNumber = VERSION_BCD(1,0,0), 74 | 75 | .ManufacturerStrIndex = STRING_ID_Manufacturer, 76 | .ProductStrIndex = STRING_ID_Product, 77 | .SerialNumStrIndex = NO_DESCRIPTOR, 78 | 79 | .NumberOfConfigurations = FIXED_NUM_CONFIGURATIONS 80 | }; 81 | 82 | // Configuration Descriptor Structure 83 | const USB_Descriptor_Configuration_t PROGMEM ConfigurationDescriptor = { 84 | .Config = 85 | { 86 | .Header = {.Size = sizeof(USB_Descriptor_Configuration_Header_t), .Type = DTYPE_Configuration}, 87 | 88 | .TotalConfigurationSize = sizeof(USB_Descriptor_Configuration_t), 89 | .TotalInterfaces = 1, 90 | 91 | .ConfigurationNumber = 1, 92 | .ConfigurationStrIndex = NO_DESCRIPTOR, 93 | 94 | .ConfigAttributes = 0x80, 95 | 96 | .MaxPowerConsumption = USB_CONFIG_POWER_MA(500) 97 | }, 98 | 99 | .HID_Interface = 100 | { 101 | .Header = {.Size = sizeof(USB_Descriptor_Interface_t), .Type = DTYPE_Interface}, 102 | 103 | .InterfaceNumber = INTERFACE_ID_Joystick, 104 | .AlternateSetting = 0x00, 105 | 106 | .TotalEndpoints = 2, 107 | 108 | .Class = HID_CSCP_HIDClass, 109 | .SubClass = HID_CSCP_NonBootSubclass, 110 | .Protocol = HID_CSCP_NonBootProtocol, 111 | 112 | .InterfaceStrIndex = NO_DESCRIPTOR 113 | }, 114 | 115 | .HID_JoystickHID = 116 | { 117 | .Header = {.Size = sizeof(USB_HID_Descriptor_HID_t), .Type = HID_DTYPE_HID}, 118 | 119 | .HIDSpec = VERSION_BCD(1,1,1), 120 | .CountryCode = 0x00, 121 | .TotalReportDescriptors = 1, 122 | .HIDReportType = HID_DTYPE_Report, 123 | .HIDReportLength = sizeof(JoystickReport) 124 | }, 125 | 126 | .HID_ReportINEndpoint = 127 | { 128 | .Header = {.Size = sizeof(USB_Descriptor_Endpoint_t), .Type = DTYPE_Endpoint}, 129 | 130 | .EndpointAddress = JOYSTICK_IN_EPADDR, 131 | .Attributes = (EP_TYPE_INTERRUPT | ENDPOINT_ATTR_NO_SYNC | ENDPOINT_USAGE_DATA), 132 | .EndpointSize = JOYSTICK_EPSIZE, 133 | .PollingIntervalMS = 0x05 134 | }, 135 | 136 | .HID_ReportOUTEndpoint = 137 | { 138 | .Header = {.Size = sizeof(USB_Descriptor_Endpoint_t), .Type = DTYPE_Endpoint}, 139 | 140 | .EndpointAddress = JOYSTICK_OUT_EPADDR, 141 | .Attributes = (EP_TYPE_INTERRUPT | ENDPOINT_ATTR_NO_SYNC | ENDPOINT_USAGE_DATA), 142 | .EndpointSize = JOYSTICK_EPSIZE, 143 | .PollingIntervalMS = 0x05 144 | }, 145 | }; 146 | 147 | // Language Descriptor Structure 148 | const USB_Descriptor_String_t PROGMEM LanguageString = USB_STRING_DESCRIPTOR_ARRAY(LANGUAGE_ID_ENG); 149 | 150 | // Manufacturer and Product Descriptor Strings 151 | const USB_Descriptor_String_t PROGMEM ManufacturerString = USB_STRING_DESCRIPTOR(L"HORI CO.,LTD."); 152 | const USB_Descriptor_String_t PROGMEM ProductString = USB_STRING_DESCRIPTOR(L"POKKEN CONTROLLER"); 153 | 154 | // USB Device Callback - Get Descriptor 155 | uint16_t CALLBACK_USB_GetDescriptor( 156 | const uint16_t wValue, 157 | const uint16_t wIndex, 158 | const void** const DescriptorAddress 159 | ) { 160 | const uint8_t DescriptorType = (wValue >> 8); 161 | const uint8_t DescriptorNumber = (wValue & 0xFF); 162 | 163 | const void* Address = NULL; 164 | uint16_t Size = NO_DESCRIPTOR; 165 | 166 | switch (DescriptorType) 167 | { 168 | case DTYPE_Device: 169 | Address = &DeviceDescriptor; 170 | Size = sizeof(USB_Descriptor_Device_t); 171 | break; 172 | case DTYPE_Configuration: 173 | Address = &ConfigurationDescriptor; 174 | Size = sizeof(USB_Descriptor_Configuration_t); 175 | break; 176 | case DTYPE_String: 177 | switch (DescriptorNumber) 178 | { 179 | case STRING_ID_Language: 180 | Address = &LanguageString; 181 | Size = pgm_read_byte(&LanguageString.Header.Size); 182 | break; 183 | case STRING_ID_Manufacturer: 184 | Address = &ManufacturerString; 185 | Size = pgm_read_byte(&ManufacturerString.Header.Size); 186 | break; 187 | case STRING_ID_Product: 188 | Address = &ProductString; 189 | Size = pgm_read_byte(&ProductString.Header.Size); 190 | break; 191 | } 192 | 193 | break; 194 | case DTYPE_HID: 195 | Address = &ConfigurationDescriptor.HID_JoystickHID; 196 | Size = sizeof(USB_HID_Descriptor_HID_t); 197 | break; 198 | case DTYPE_Report: 199 | Address = &JoystickReport; 200 | Size = sizeof(JoystickReport); 201 | break; 202 | } 203 | 204 | *DescriptorAddress = Address; 205 | return Size; 206 | } 207 | -------------------------------------------------------------------------------- /C/Descriptors.h: -------------------------------------------------------------------------------- 1 | #ifndef _DESCRIPTORS_H_ 2 | #define _DESCRIPTORS_H_ 3 | 4 | // Includes 5 | #include 6 | 7 | #include 8 | 9 | // Type Defines 10 | // Device Configuration Descriptor Structure 11 | typedef struct 12 | { 13 | USB_Descriptor_Configuration_Header_t Config; 14 | 15 | // Joystick HID Interface 16 | USB_Descriptor_Interface_t HID_Interface; 17 | USB_HID_Descriptor_HID_t HID_JoystickHID; 18 | USB_Descriptor_Endpoint_t HID_ReportOUTEndpoint; 19 | USB_Descriptor_Endpoint_t HID_ReportINEndpoint; 20 | } USB_Descriptor_Configuration_t; 21 | 22 | // Device Interface Descriptor IDs 23 | enum InterfaceDescriptors_t 24 | { 25 | INTERFACE_ID_Joystick = 0, /**< Joystick interface descriptor ID */ 26 | }; 27 | 28 | // Device String Descriptor IDs 29 | enum StringDescriptors_t 30 | { 31 | STRING_ID_Language = 0, // Supported Languages string descriptor ID (must be zero) 32 | STRING_ID_Manufacturer = 1, // Manufacturer string ID 33 | STRING_ID_Product = 2, // Product string ID 34 | }; 35 | 36 | // Macros 37 | // Endpoint Addresses 38 | #define JOYSTICK_IN_EPADDR (ENDPOINT_DIR_IN | 1) 39 | #define JOYSTICK_OUT_EPADDR (ENDPOINT_DIR_OUT | 2) 40 | // HID Endpoint Size 41 | // The Switch -needs- this to be 64. 42 | // The Wii U is flexible, allowing us to use the default of 8 (which did not match the original Hori descriptors). 43 | #define JOYSTICK_EPSIZE 64 44 | // Descriptor Header Type - HID Class HID Descriptor 45 | #define DTYPE_HID 0x21 46 | // Descriptor Header Type - HID Class HID Report Descriptor 47 | #define DTYPE_Report 0x22 48 | 49 | // Function Prototypes 50 | uint16_t CALLBACK_USB_GetDescriptor( 51 | const uint16_t wValue, 52 | const uint16_t wIndex, 53 | const void** const DescriptorAddress 54 | ) ATTR_WARN_UNUSED_RESULT ATTR_NON_NULL_PTR_ARG(3); 55 | 56 | #endif 57 | -------------------------------------------------------------------------------- /C/HORI_Descriptors: -------------------------------------------------------------------------------- 1 | ID 0f0d:0092 Hori Co., Ltd 2 | Device Descriptor: 3 | bLength 18 4 | bDescriptorType 1 5 | bcdUSB 2.00 6 | bDeviceClass 0 7 | bDeviceSubClass 0 8 | bDeviceProtocol 0 9 | bMaxPacketSize0 64 10 | idVendor 0x0f0d Hori Co., Ltd 11 | idProduct 0x0092 12 | bcdDevice 1.00 13 | iManufacturer 1 HORI CO.,LTD. 14 | iProduct 2 POKKEN CONTROLLER 15 | iSerial 0 16 | bNumConfigurations 1 17 | Configuration Descriptor: 18 | bLength 9 19 | bDescriptorType 2 20 | wTotalLength 41 21 | bNumInterfaces 1 22 | bConfigurationValue 1 23 | iConfiguration 0 24 | bmAttributes 0x80 25 | (Bus Powered) 26 | MaxPower 500mA 27 | Interface Descriptor: 28 | bLength 9 29 | bDescriptorType 4 30 | bInterfaceNumber 0 31 | bAlternateSetting 0 32 | bNumEndpoints 2 33 | bInterfaceClass 3 Human Interface Device 34 | bInterfaceSubClass 0 35 | bInterfaceProtocol 0 36 | iInterface 0 37 | HID Device Descriptor: 38 | bLength 9 39 | bDescriptorType 33 40 | bcdHID 1.11 41 | bCountryCode 0 Not supported 42 | bNumDescriptors 1 43 | bDescriptorType 34 Report 44 | wDescriptorLength 90 45 | Report Descriptor: (length is 90) 46 | Item(Global): Usage Page, data= [ 0x01 ] 1 47 | Generic Desktop Controls 48 | Item(Local ): Usage, data= [ 0x05 ] 5 49 | Gamepad 50 | Item(Main ): Collection, data= [ 0x01 ] 1 51 | Application 52 | Item(Global): Logical Minimum, data= [ 0x00 ] 0 53 | Item(Global): Logical Maximum, data= [ 0x01 ] 1 54 | Item(Global): Physical Minimum, data= [ 0x00 ] 0 55 | Item(Global): Physical Maximum, data= [ 0x01 ] 1 56 | Item(Global): Report Size, data= [ 0x01 ] 1 57 | Item(Global): Report Count, data= [ 0x0d ] 13 58 | Item(Global): Usage Page, data= [ 0x09 ] 9 59 | Buttons 60 | Item(Local ): Usage Minimum, data= [ 0x01 ] 1 61 | Button 1 (Primary) 62 | Item(Local ): Usage Maximum, data= [ 0x0d ] 13 63 | (null) 64 | Item(Main ): Input, data= [ 0x02 ] 2 65 | Data Variable Absolute No_Wrap Linear 66 | Preferred_State No_Null_Position Non_Volatile Bitfield 67 | Item(Global): Report Count, data= [ 0x03 ] 3 68 | Item(Main ): Input, data= [ 0x01 ] 1 69 | Constant Array Absolute No_Wrap Linear 70 | Preferred_State No_Null_Position Non_Volatile Bitfield 71 | Item(Global): Usage Page, data= [ 0x01 ] 1 72 | Generic Desktop Controls 73 | Item(Global): Logical Maximum, data= [ 0x07 ] 7 74 | Item(Global): Physical Maximum, data= [ 0x3b 0x01 ] 315 75 | Item(Global): Report Size, data= [ 0x04 ] 4 76 | Item(Global): Report Count, data= [ 0x01 ] 1 77 | Item(Global): Unit, data= [ 0x14 ] 20 78 | System: English Rotation, Unit: Degrees 79 | Item(Local ): Usage, data= [ 0x39 ] 57 80 | Hat Switch 81 | Item(Main ): Input, data= [ 0x42 ] 66 82 | Data Variable Absolute No_Wrap Linear 83 | Preferred_State Null_State Non_Volatile Bitfield 84 | Item(Global): Unit, data= [ 0x00 ] 0 85 | System: None, Unit: (None) 86 | Item(Global): Report Count, data= [ 0x01 ] 1 87 | Item(Main ): Input, data= [ 0x01 ] 1 88 | Constant Array Absolute No_Wrap Linear 89 | Preferred_State No_Null_Position Non_Volatile Bitfield 90 | Item(Global): Logical Maximum, data= [ 0xff 0x00 ] 255 91 | Item(Global): Physical Maximum, data= [ 0xff 0x00 ] 255 92 | Item(Local ): Usage, data= [ 0x30 ] 48 93 | Direction-X 94 | Item(Local ): Usage, data= [ 0x31 ] 49 95 | Direction-Y 96 | Item(Local ): Usage, data= [ 0x32 ] 50 97 | Direction-Z 98 | Item(Local ): Usage, data= [ 0x35 ] 53 99 | Rotate-Z 100 | Item(Global): Report Size, data= [ 0x08 ] 8 101 | Item(Global): Report Count, data= [ 0x04 ] 4 102 | Item(Main ): Input, data= [ 0x02 ] 2 103 | Data Variable Absolute No_Wrap Linear 104 | Preferred_State No_Null_Position Non_Volatile Bitfield 105 | Item(Global): Usage Page, data= [ 0x00 0xff ] 65280 106 | (null) 107 | Item(Local ): Usage, data= [ 0x20 ] 32 108 | (null) 109 | Item(Global): Report Count, data= [ 0x01 ] 1 110 | Item(Main ): Input, data= [ 0x02 ] 2 111 | Data Variable Absolute No_Wrap Linear 112 | Preferred_State No_Null_Position Non_Volatile Bitfield 113 | Item(Local ): Usage, data= [ 0x21 0x26 ] 9761 114 | (null) 115 | Item(Global): Report Count, data= [ 0x08 ] 8 116 | Item(Main ): Output, data= [ 0x02 ] 2 117 | Data Variable Absolute No_Wrap Linear 118 | Preferred_State No_Null_Position Non_Volatile Bitfield 119 | Item(Main ): End Collection, data=none 120 | Endpoint Descriptor: 121 | bLength 7 122 | bDescriptorType 5 123 | bEndpointAddress 0x02 EP 2 OUT 124 | bmAttributes 3 125 | Transfer Type Interrupt 126 | Synch Type None 127 | Usage Type Data 128 | wMaxPacketSize 0x0040 1x 64 bytes 129 | bInterval 5 130 | Endpoint Descriptor: 131 | bLength 7 132 | bDescriptorType 5 133 | bEndpointAddress 0x81 EP 1 IN 134 | bmAttributes 3 135 | Transfer Type Interrupt 136 | Synch Type None 137 | Usage Type Data 138 | wMaxPacketSize 0x0040 1x 64 bytes 139 | bInterval 5 140 | Device Status: 0x0000 141 | (Bus Powered) -------------------------------------------------------------------------------- /C/Joystick.c: -------------------------------------------------------------------------------- 1 | /* 2 | Nintendo Switch Fightstick - Proof-of-Concept 3 | 4 | Based on the LUFA library's Low-Level Joystick Demo 5 | (C) Dean Camera 6 | Based on the HORI's Pokken Tournament Pro Pad design 7 | (C) HORI 8 | 9 | This project implements a modified version of HORI's Pokken Tournament Pro Pad 10 | USB descriptors to allow for the creation of custom controllers for the 11 | Nintendo Switch. This also works to a limited degree on the PS3. 12 | 13 | Since System Update v3.0.0, the Nintendo Switch recognizes the Pokken 14 | Tournament Pro Pad as a Pro Controller. Physical design limitations prevent 15 | the Pokken Controller from functioning at the same level as the Pro 16 | Controller. However, by default most of the descriptors are there, with the 17 | exception of Home and Capture. Descriptor modification allows us to unlock 18 | these buttons for our use. 19 | */ 20 | 21 | /** \file 22 | * 23 | * Main source file for the posts printer demo. This file contains the main tasks of 24 | * the demo and is responsible for the initial application hardware configuration. 25 | */ 26 | 27 | #include "Joystick.h" 28 | #include "uart.h" 29 | 30 | //Define Serial Baudrate 31 | #define BAUD 38400 32 | #define CPU_PRESCALE(n) (CLKPR = 0x80, CLKPR = (n)) 33 | // Main entry point. 34 | int main(void) { 35 | // We'll start by performing hardware and peripheral setup. 36 | SetupHardware(); 37 | // We'll then enable global interrupts for our use. 38 | GlobalInterruptEnable(); 39 | // Once that's done, we'll enter an infinite loop. 40 | 41 | 42 | uart_init(BAUD); 43 | 44 | for (;;) 45 | { 46 | // We need to run our task to process and deliver data for our IN and OUT endpoints. 47 | HID_Task(); 48 | // We also need to run the main USB management task. 49 | USB_USBTask(); 50 | } 51 | } 52 | 53 | // Configures hardware and peripherals, such as the USB peripherals. 54 | void SetupHardware(void) { 55 | // We need to disable watchdog if enabled by bootloader/fuses. 56 | MCUSR &= ~(1 << WDRF); 57 | wdt_disable(); 58 | 59 | // We need to disable clock division before initializing the USB hardware. 60 | clock_prescale_set(clock_div_1); 61 | // We can then initialize our hardware and peripherals, including the USB stack. 62 | CPU_PRESCALE(0); 63 | // The USB stack should be initialized last. 64 | USB_Init(); 65 | } 66 | 67 | // Fired to indicate that the device is enumerating. 68 | void EVENT_USB_Device_Connect(void) { 69 | // We can indicate that we're enumerating here (via status LEDs, sound, etc.). 70 | } 71 | 72 | // Fired to indicate that the device is no longer connected to a host. 73 | void EVENT_USB_Device_Disconnect(void) { 74 | // We can indicate that our device is not ready (via status LEDs, sound, etc.). 75 | } 76 | 77 | // Fired when the host set the current configuration of the USB device after enumeration. 78 | void EVENT_USB_Device_ConfigurationChanged(void) { 79 | bool ConfigSuccess = true; 80 | 81 | // We setup the HID report endpoints. 82 | ConfigSuccess &= Endpoint_ConfigureEndpoint(JOYSTICK_OUT_EPADDR, EP_TYPE_INTERRUPT, JOYSTICK_EPSIZE, 1); 83 | ConfigSuccess &= Endpoint_ConfigureEndpoint(JOYSTICK_IN_EPADDR, EP_TYPE_INTERRUPT, JOYSTICK_EPSIZE, 1); 84 | 85 | // We can read ConfigSuccess to indicate a success or failure at this point. 86 | } 87 | 88 | // Process control requests sent to the device from the USB host. 89 | void EVENT_USB_Device_ControlRequest(void) { 90 | // We can handle two control requests: a GetReport and a SetReport. 91 | 92 | // Not used here, it looks like we don't receive control request from the Switch. 93 | } 94 | 95 | // Process and deliver data from IN and OUT endpoints. 96 | void HID_Task(void) { 97 | // If the device isn't connected and properly configured, we can't do anything here. 98 | if (USB_DeviceState != DEVICE_STATE_Configured) 99 | return; 100 | 101 | // We'll start with the OUT endpoint. 102 | Endpoint_SelectEndpoint(JOYSTICK_OUT_EPADDR); 103 | // We'll check to see if we received something on the OUT endpoint. 104 | if (Endpoint_IsOUTReceived()) 105 | { 106 | // If we did, and the packet has data, we'll react to it. 107 | if (Endpoint_IsReadWriteAllowed()) 108 | { 109 | // We'll create a place to store our data received from the host. 110 | USB_JoystickReport_Output_t JoystickOutputData; 111 | // We'll then take in that data, setting it up in our storage. 112 | while(Endpoint_Read_Stream_LE(&JoystickOutputData, sizeof(JoystickOutputData), NULL) != ENDPOINT_RWSTREAM_NoError); 113 | // At this point, we can react to this data. 114 | 115 | // However, since we're not doing anything with this data, we abandon it. 116 | } 117 | // Regardless of whether we reacted to the data, we acknowledge an OUT packet on this endpoint. 118 | Endpoint_ClearOUT(); 119 | } 120 | 121 | // We'll then move on to the IN endpoint. 122 | Endpoint_SelectEndpoint(JOYSTICK_IN_EPADDR); 123 | // We first check to see if the host is ready to accept data. 124 | if (Endpoint_IsINReady()) 125 | { 126 | // We'll create an empty report. 127 | USB_JoystickReport_Input_t JoystickInputData; 128 | // We'll then populate this report with what we want to send to the host. 129 | GetNextReport(&JoystickInputData); 130 | // Once populated, we can output this data to the host. We do this by first writing the data to the control stream. 131 | while(Endpoint_Write_Stream_LE(&JoystickInputData, sizeof(JoystickInputData), NULL) != ENDPOINT_RWSTREAM_NoError); 132 | // We then send an IN packet on this endpoint. 133 | Endpoint_ClearIN(); 134 | } 135 | } 136 | 137 | 138 | #define DATA_LENGTH 7 139 | #define EXTERNAL_INPUT 0 140 | #define SYNC_CONTROLLER 1 141 | #define LED_ON (PORTD |= (1<<6)) 142 | #define LED_OFF (PORTD &= ~(1<<6)) 143 | char state = EXTERNAL_INPUT; 144 | 145 | USB_JoystickReport_Input_t last_report; 146 | 147 | int report_count = 0; 148 | 149 | 150 | // Prepare the next report for the host. 151 | void GetNextReport(USB_JoystickReport_Input_t* const ReportData) { 152 | // Prepare an empty report 153 | memset(ReportData, 0, sizeof(USB_JoystickReport_Input_t)); 154 | // Set stick and HAT values to default values 155 | ReportData->LX = STICK_CENTER; 156 | ReportData->LY = STICK_CENTER; 157 | ReportData->RX = STICK_CENTER; 158 | ReportData->RY = STICK_CENTER; 159 | ReportData->HAT = HAT_CENTER; 160 | 161 | // When device first starts, run controller sync procedure 162 | if(state == SYNC_CONTROLLER) { 163 | if(report_count > 100) { 164 | report_count = 0; 165 | state = EXTERNAL_INPUT; 166 | } 167 | else if(report_count == 25 || report_count == 50) { 168 | ReportData->Button |= SWITCH_L | SWITCH_R; 169 | } 170 | else if(report_count == 75 || report_count == 100){ 171 | ReportData->Button |= SWITCH_A; 172 | } 173 | report_count++; 174 | 175 | _delay_ms(1000/30); 176 | } else { 177 | // After syncing procedure, use last report and modify it based on serial data 178 | memcpy(ReportData, &last_report, sizeof(USB_JoystickReport_Input_t)); 179 | // If number of bytes received is greater than or equal to payload size, update Report with data 180 | if(uart_available() >= DATA_LENGTH) { 181 | LED_ON; 182 | // loop through received bytes 183 | uint8_t c; 184 | for(int i = 0; i < DATA_LENGTH - 1; i++){ 185 | // Get byte from uart buffer 186 | c = uart_getchar(); 187 | //uart_putchar(c); 188 | //Depending on index of the byte, update that part of the report 189 | switch(i) { 190 | case 0: 191 | ReportData->LX = c; 192 | break; 193 | case 1: 194 | ReportData->LY = c; 195 | break; 196 | case 2: 197 | ReportData->RX = c; 198 | break; 199 | case 3: 200 | ReportData->RY = c; 201 | break; 202 | case 4: 203 | ReportData->HAT = c; 204 | break; 205 | case 5: ; // We read both bytes for the 16bit button value in the same cycle 206 | uint16_t c2 = uart_getchar(); 207 | uart_putchar(c2); 208 | ReportData->Button = (c2 << 8) | c; 209 | break; 210 | } 211 | } 212 | } else{ 213 | LED_OFF; 214 | } 215 | //_delay_ms(1000/30); 216 | } 217 | memcpy(&last_report, ReportData, sizeof(USB_JoystickReport_Input_t)); 218 | } 219 | -------------------------------------------------------------------------------- /C/Joystick.h: -------------------------------------------------------------------------------- 1 | /* 2 | LUFA Library 3 | Copyright (C) Dean Camera, 2014. 4 | 5 | dean [at] fourwalledcubicle [dot] com 6 | www.lufa-lib.org 7 | */ 8 | 9 | /* 10 | Copyright 2014 Dean Camera (dean [at] fourwalledcubicle [dot] com) 11 | 12 | Permission to use, copy, modify, distribute, and sell this 13 | software and its documentation for any purpose is hereby granted 14 | without fee, provided that the above copyright notice appear in 15 | all copies and that both that the copyright notice and this 16 | permission notice and warranty disclaimer appear in supporting 17 | documentation, and that the name of the author not be used in 18 | advertising or publicity pertaining to distribution of the 19 | software without specific, written prior permission. 20 | 21 | The author disclaims all warranties with regard to this 22 | software, including all implied warranties of merchantability 23 | and fitness. In no event shall the author be liable for any 24 | special, indirect or consequential damages or any damages 25 | whatsoever resulting from loss of use, data or profits, whether 26 | in an action of contract, negligence or other tortious action, 27 | arising out of or in connection with the use or performance of 28 | this software. 29 | */ 30 | 31 | /** \file 32 | * 33 | * Header file for Joystick.c. 34 | */ 35 | 36 | #ifndef _JOYSTICK_H_ 37 | #define _JOYSTICK_H_ 38 | 39 | /* Includes: */ 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | #include 46 | 47 | #include 48 | #include 49 | #include 50 | #include 51 | #include 52 | 53 | #include "Descriptors.h" 54 | 55 | // Type Defines 56 | // Enumeration for joystick buttons. 57 | typedef enum { 58 | SWITCH_Y = 0x01, 59 | SWITCH_B = 0x02, 60 | SWITCH_A = 0x04, 61 | SWITCH_X = 0x08, 62 | SWITCH_L = 0x10, 63 | SWITCH_R = 0x20, 64 | SWITCH_ZL = 0x40, 65 | SWITCH_ZR = 0x80, 66 | SWITCH_MINUS = 0x100, 67 | SWITCH_PLUS = 0x200, 68 | SWITCH_LCLICK = 0x400, 69 | SWITCH_RCLICK = 0x800, 70 | SWITCH_HOME = 0x1000, 71 | SWITCH_CAPTURE = 0x2000, 72 | } JoystickButtons_t; 73 | 74 | #define HAT_TOP 0x00 75 | #define HAT_TOP_RIGHT 0x01 76 | #define HAT_RIGHT 0x02 77 | #define HAT_BOTTOM_RIGHT 0x03 78 | #define HAT_BOTTOM 0x04 79 | #define HAT_BOTTOM_LEFT 0x05 80 | #define HAT_LEFT 0x06 81 | #define HAT_TOP_LEFT 0x07 82 | #define HAT_CENTER 0x08 83 | 84 | #define STICK_MIN 0 85 | #define STICK_CENTER 128 86 | #define STICK_MAX 255 87 | 88 | // Joystick HID report structure. We have an input and an output. 89 | typedef struct { 90 | uint16_t Button; // 16 buttons; see JoystickButtons_t for bit mapping 91 | uint8_t HAT; // HAT switch; one nibble w/ unused nibble 92 | uint8_t LX; // Left Stick X 93 | uint8_t LY; // Left Stick Y 94 | uint8_t RX; // Right Stick X 95 | uint8_t RY; // Right Stick Y 96 | uint8_t VendorSpec; 97 | } USB_JoystickReport_Input_t; 98 | 99 | // The output is structured as a mirror of the input. 100 | // This is based on initial observations of the Pokken Controller. 101 | typedef struct { 102 | uint16_t Button; // 16 buttons; see JoystickButtons_t for bit mapping 103 | uint8_t HAT; // HAT switch; one nibble w/ unused nibble 104 | uint8_t LX; // Left Stick X 105 | uint8_t LY; // Left Stick Y 106 | uint8_t RX; // Right Stick X 107 | uint8_t RY; // Right Stick Y 108 | } USB_JoystickReport_Output_t; 109 | 110 | // Function Prototypes 111 | // Setup all necessary hardware, including USB initialization. 112 | void SetupHardware(void); 113 | // Process and deliver data from IN and OUT endpoints. 114 | void HID_Task(void); 115 | // USB device event handlers. 116 | void EVENT_USB_Device_Connect(void); 117 | void EVENT_USB_Device_Disconnect(void); 118 | void EVENT_USB_Device_ConfigurationChanged(void); 119 | void EVENT_USB_Device_ControlRequest(void); 120 | // Prepare the next report for the host. 121 | void GetNextReport(USB_JoystickReport_Input_t* const ReportData); 122 | 123 | #endif 124 | -------------------------------------------------------------------------------- /C/ironic.data: -------------------------------------------------------------------------------- 1 |  -------------------------------------------------------------------------------- /C/makefile: -------------------------------------------------------------------------------- 1 | # 2 | # LUFA Library 3 | # Copyright (C) Dean Camera, 2014. 4 | # 5 | # dean [at] fourwalledcubicle [dot] com 6 | # www.lufa-lib.org 7 | # 8 | # -------------------------------------- 9 | # LUFA Project Makefile. 10 | # -------------------------------------- 11 | 12 | # Run "make help" for target help. 13 | 14 | # Set the MCU accordingly to your device (e.g. at90usb1286 for a Teensy 2.0++, or atmega16u2 for an Arduino UNO R3) 15 | MCU = at90usb1286 16 | ARCH = AVR8 17 | F_CPU = 16000000 18 | F_USB = $(F_CPU) 19 | OPTIMIZATION = s 20 | TARGET = Joystick 21 | SRC = $(TARGET).c Descriptors.c uart.c $(LUFA_SRC_USB) 22 | LUFA_PATH = ../LUFA/LUFA 23 | CC_FLAGS = -DUSE_LUFA_CONFIG_HEADER -IConfig/ 24 | LD_FLAGS = 25 | 26 | # Default target 27 | all: 28 | 29 | # Include LUFA build script makefiles 30 | include $(LUFA_PATH)/Build/lufa_core.mk 31 | include $(LUFA_PATH)/Build/lufa_sources.mk 32 | include $(LUFA_PATH)/Build/lufa_build.mk 33 | include $(LUFA_PATH)/Build/lufa_cppcheck.mk 34 | include $(LUFA_PATH)/Build/lufa_doxygen.mk 35 | include $(LUFA_PATH)/Build/lufa_dfu.mk 36 | include $(LUFA_PATH)/Build/lufa_hid.mk 37 | include $(LUFA_PATH)/Build/lufa_avrdude.mk 38 | include $(LUFA_PATH)/Build/lufa_atprogram.mk 39 | 40 | # Target for LED/buzzer to alert when print is done 41 | with-alert: all 42 | with-alert: CC_FLAGS += -DALERT_WHEN_DONE 43 | -------------------------------------------------------------------------------- /C/uart.c: -------------------------------------------------------------------------------- 1 | /* UART Example for Teensy USB Development Board 2 | * http://www.pjrc.com/teensy/ 3 | * Copyright (c) 2009 PJRC.COM, LLC 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in 13 | * all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | * THE SOFTWARE. 22 | */ 23 | 24 | // Version 1.0: Initial Release 25 | // Version 1.1: Add support for Teensy 2.0, minor optimizations 26 | 27 | 28 | #include 29 | #include 30 | 31 | #include "uart.h" 32 | 33 | // These buffers may be any size from 2 to 256 bytes. 34 | #define RX_BUFFER_SIZE 64 35 | #define TX_BUFFER_SIZE 40 36 | 37 | static volatile uint8_t tx_buffer[TX_BUFFER_SIZE]; 38 | static volatile uint8_t tx_buffer_head; 39 | static volatile uint8_t tx_buffer_tail; 40 | static volatile uint8_t rx_buffer[RX_BUFFER_SIZE]; 41 | static volatile uint8_t rx_buffer_head; 42 | static volatile uint8_t rx_buffer_tail; 43 | 44 | // Initialize the UART 45 | void uart_init(uint32_t baud) 46 | { 47 | cli(); 48 | UBRR1 = (F_CPU / 4 / baud - 1) / 2; 49 | UCSR1A = (1<= TX_BUFFER_SIZE) i = 0; 64 | while (tx_buffer_tail == i) ; // wait until space in buffer 65 | //cli(); 66 | tx_buffer[i] = c; 67 | tx_buffer_head = i; 68 | UCSR1B = (1<= RX_BUFFER_SIZE) i = 0; 80 | c = rx_buffer[i]; 81 | rx_buffer_tail = i; 82 | return c; 83 | } 84 | 85 | // Return the number of bytes waiting in the receive buffer. 86 | // Call this before uart_getchar() to check if it will need 87 | // to wait for a byte to arrive. 88 | uint8_t uart_available(void) 89 | { 90 | uint8_t head, tail; 91 | 92 | head = rx_buffer_head; 93 | tail = rx_buffer_tail; 94 | if (head >= tail) return head - tail; 95 | return RX_BUFFER_SIZE + head - tail; 96 | } 97 | 98 | // Transmit Interrupt 99 | ISR(USART1_UDRE_vect) 100 | { 101 | uint8_t i; 102 | 103 | if (tx_buffer_head == tx_buffer_tail) { 104 | // buffer is empty, disable transmit interrupt 105 | UCSR1B = (1<= TX_BUFFER_SIZE) i = 0; 109 | UDR1 = tx_buffer[i]; 110 | tx_buffer_tail = i; 111 | } 112 | } 113 | 114 | // Receive Interrupt 115 | ISR(USART1_RX_vect) 116 | { 117 | uint8_t c, i; 118 | 119 | c = UDR1; 120 | i = rx_buffer_head + 1; 121 | if (i >= RX_BUFFER_SIZE) i = 0; 122 | if (i != rx_buffer_tail) { 123 | rx_buffer[i] = c; 124 | rx_buffer_head = i; 125 | } 126 | } 127 | 128 | -------------------------------------------------------------------------------- /C/uart.h: -------------------------------------------------------------------------------- 1 | #ifndef _uart_included_h_ 2 | #define _uart_included_h_ 3 | 4 | #include 5 | 6 | void uart_init(uint32_t baud); 7 | void uart_putchar(uint8_t c); 8 | uint8_t uart_getchar(void); 9 | uint8_t uart_available(void); 10 | 11 | #endif 12 | -------------------------------------------------------------------------------- /LUFA_LICENSE: -------------------------------------------------------------------------------- 1 | LUFA Library 2 | Copyright (C) Dean Camera, 2014. 3 | 4 | dean [at] fourwalledcubicle [dot] com 5 | www.lufa-lib.org 6 | 7 | 8 | Permission to use, copy, modify, and distribute this software 9 | and its documentation for any purpose is hereby granted without 10 | fee, provided that the above copyright notice appear in all 11 | copies and that both that the copyright notice and this 12 | permission notice and warranty disclaimer appear in supporting 13 | documentation, and that the name of the author not be used in 14 | advertising or publicity pertaining to distribution of the 15 | software without specific, written prior permission. 16 | 17 | The author disclaims all warranties with regard to this 18 | software, including all implied warranties of merchantability 19 | and fitness. In no event shall the author be liable for any 20 | special, indirect or consequential damages or any damages 21 | whatsoever resulting from loss of use, data or profits, whether 22 | in an action of contract, negligence or other tortious action, 23 | arising out of or in connection with the use or performance of 24 | this software. 25 | -------------------------------------------------------------------------------- /Python/JoystickEnums.py: -------------------------------------------------------------------------------- 1 | from enum import Enum 2 | 3 | 4 | class Button(Enum): 5 | Y = 0x01 6 | B = 0x02 7 | A = 0x04 8 | X = 0x08 9 | L = 0x10 10 | R = 0x20 11 | ZL = 0x40 12 | ZR = 0x80 13 | MINUS = 0x100 14 | PLUS = 0x200 15 | LCLICK = 0x400 16 | RCLICK = 0x800 17 | HOME = 0x1000 18 | CAPTURE = 0x2000 19 | 20 | @classmethod 21 | def hasValue(cls, value): 22 | return any(value == item.value for item in cls) 23 | 24 | 25 | class HAT(Enum): 26 | TOP = 0x00 27 | TOP_RIGHT = 0x01 28 | RIGHT = 0x02 29 | BOTTOM_RIGHT = 0x03 30 | BOTTOM = 0x04 31 | BOTTOM_LEFT = 0x05 32 | LEFT = 0x06 33 | TOP_LEFT = 0x07 34 | CENTER = 0x08 35 | 36 | @classmethod 37 | def hasValue(cls, value): 38 | return any(value == item.value for item in cls) 39 | 40 | 41 | class Stick(Enum): 42 | MIN = 0 43 | CENTER = 128 44 | MAX = 255 45 | -------------------------------------------------------------------------------- /Python/constants.py: -------------------------------------------------------------------------------- 1 | 2 | validButtonValues = ["-LX", "+LX", "-LY", "+LY", 3 | "-RX", "+RX", "-RY", "+RY", 4 | "DUP", "DDOWN", "DLEFT", "DRIGHT", 5 | "Y", "B", "A", "X", 6 | "L", "R", "ZL", "ZR", 7 | "MINUS", "PLUS", 8 | "L_CLICK", "R_CLICK", 9 | "HOME", "CAPTURE", ] 10 | 11 | nameKeyValDict = {'backspace': 8, 'tab': 9, 'clear': 12, 'return': 13, 12 | 'pause': 19, 'escape': 27, 'space': 32, '!': 33, '"': 34, 13 | '#': 35, '$': 36, '&': 38, "'": 39, '(': 40, ')': 41, '*': 42, 14 | '+': 43, ',': 44, '-': 45, '.': 46, '/': 47, '0': 48, '1': 49, 15 | '2': 50, '3': 51, '4': 52, '5': 53, '6': 54, '7': 55, '8': 56, 16 | '9': 57, ':': 58, ';': 59, '<': 60, '=': 61, '>': 62, '?': 63, 17 | '@': 64, '[': 91, '\\': 92, ']': 93, '^': 94, '_': 95, '`': 96, 18 | 'a': 97, 'b': 98, 'c': 99, 'd': 100, 'e': 101, 'f': 102, 'g': 103, 19 | 'h': 104, 'i': 105, 'j': 106, 'k': 107, 'l': 108, 'm': 109, 'n': 110, 20 | 'o': 111, 'p': 112, 'q': 113, 'r': 114, 's': 115, 't': 116, 'u': 117, 21 | 'v': 118, 'w': 119, 'x': 120, 'y': 121, 'z': 122, 'delete': 127, 22 | '[0]': 256, '[1]': 257, '[2]': 258, '[3]': 259, '[4]': 260, '[5]': 261, 23 | '[6]': 262, '[7]': 263, '[8]': 264, '[9]': 265, '[.]': 266, '[/]': 267, 24 | '[*]': 268, '[-]': 269, '[+]': 270, 'enter': 271, 'equals': 272, 'up': 273, 25 | 'down': 274, 'right': 275, 'left': 276, 'insert': 277, 'home': 278, 26 | 'end': 279, 'pageup': 280, 'pagedown': 281, 'f1': 282, 'f2': 283, 27 | 'f3': 284, 'f4': 285, 'f5': 286, 'f6': 287, 'f7': 288, 'f8': 289, 'f9': 290, 28 | 'f10': 291, 'f11': 292, 'f12': 293, 'f13': 294, 'f14': 295, 'f15': 296, 29 | 'numlock': 300, 'capslock': 301, 'scrolllock': 302, 'rightshift': 303, 30 | 'leftshift': 304, 'rightctrl': 305, 'leftctrl': 306, 'rightalt': 307, 31 | 'leftalt': 308, 'rightmeta': 309, 'leftmeta': 310, 'leftsuper': 311, 32 | 'rightsuper': 312, 'altgr': 313, 'compose': 314, 'help': 315, 33 | 'printscreen': 316, 'm1': 'm1', 'm2': 'm2', 'm3': 34 | 'm3', 'm4': 'm4', 'm5': 'm5', 'm6': 'm6', 'm7': 'm7', 35 | 'mx': 'mx', 'my': 'my'} -------------------------------------------------------------------------------- /Python/controllerMapping.csv: -------------------------------------------------------------------------------- 1 | button,key1,key2,key3 2 | -LX, a 3 | +LX, d 4 | -LY, w 5 | +LY, s 6 | -RX, mx 7 | +RX, mx 8 | -RY, my 9 | +RY, my 10 | A, space 11 | B, left shift 12 | Y 13 | X 14 | L 15 | R 16 | ZL, m3 17 | ZR, m1 18 | L_CLICK, m2 19 | R_CLICK 20 | DUP, up 21 | DDOWN, down 22 | DLEFT, left 23 | DRIGHT, right 24 | HOME, f1 25 | CAPTURE, f2 26 | PLUS, + 27 | MINUS, - -------------------------------------------------------------------------------- /Python/example.py: -------------------------------------------------------------------------------- 1 | 2 | import time 3 | 4 | from serial.tools import list_ports 5 | 6 | import seriallib 7 | from JoystickEnums import Button, HAT 8 | 9 | 10 | 11 | def get_port_from_user(): 12 | port_list = list(list_ports.grep("")) 13 | if len(port_list) == 0: 14 | raise LookupError("Unable to detect Serial Device.") 15 | index_port_list_str = [f"Index: {index}, Port: {port.device}, Description: {port.description}" 16 | for index, port in enumerate(port_list)] 17 | print(index_port_list_str) 18 | while True: 19 | ind = input("What port index should be used? ") 20 | if not str.isdigit(ind): 21 | print(f"Value given is not a digit") 22 | elif not (0 <= int(ind) < len(port_list)): 23 | print("Value given is not an index in the list") 24 | else: 25 | return port_list[int(ind)].device 26 | 27 | def packet_cycle(ser_man, payload): 28 | ser_man.write(payload.as_byte_arr()) 29 | while ser_man.in_waiting < 1: 30 | time.sleep(1/1000) 31 | ser_man.read() 32 | 33 | if __name__ == "__main__": 34 | BAUD = 38400 35 | UPDATES_PER_SECOND = 60 36 | 37 | pack = seriallib.Payload() 38 | with seriallib.SerialManager(get_port_from_user(), BAUD) as ser: 39 | print("Flushing Serial Port") 40 | ser.flush() 41 | print("Starting Main Loop") 42 | pack.apply_buttons(Button.A) 43 | packet_cycle(ser, pack) 44 | while True: 45 | time.sleep(1) 46 | pack.set_left_stick(128, 255) 47 | packet_cycle(ser, pack) 48 | pack.reset_inputs() 49 | time.sleep(1) 50 | pack.set_hat(0,-1) 51 | packet_cycle(ser, pack) 52 | 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /Python/keys.txt: -------------------------------------------------------------------------------- 1 | mx 2 | my 3 | m1 4 | m2 5 | m3 6 | m4 7 | m5 8 | m6 9 | m7 10 | backspace 11 | tab 12 | clear 13 | return 14 | pause 15 | escape 16 | space 17 | ! 18 | " 19 | # 20 | $ 21 | & 22 | ' 23 | ( 24 | ) 25 | * 26 | + 27 | , 28 | - 29 | . 30 | / 31 | 0 32 | 1 33 | 2 34 | 3 35 | 4 36 | 5 37 | 6 38 | 7 39 | 8 40 | 9 41 | : 42 | ; 43 | < 44 | = 45 | > 46 | ? 47 | @ 48 | [ 49 | \ 50 | ] 51 | ^ 52 | _ 53 | ` 54 | a 55 | b 56 | c 57 | d 58 | e 59 | f 60 | g 61 | h 62 | i 63 | j 64 | k 65 | l 66 | m 67 | n 68 | o 69 | p 70 | q 71 | r 72 | s 73 | t 74 | u 75 | v 76 | w 77 | x 78 | y 79 | z 80 | delete 81 | [0] 82 | [1] 83 | [2] 84 | [3] 85 | [4] 86 | [5] 87 | [6] 88 | [7] 89 | [8] 90 | [9] 91 | [.] 92 | [/] 93 | [*] 94 | [-] 95 | [+] 96 | enter 97 | equals 98 | up 99 | down 100 | right 101 | left 102 | insert 103 | home 104 | end 105 | pageup 106 | pagedown 107 | f1 108 | f2 109 | f3 110 | f4 111 | f5 112 | f6 113 | f7 114 | f8 115 | f9 116 | f10 117 | f11 118 | f12 119 | f13 120 | f14 121 | f15 122 | numlock 123 | capslock 124 | scrolllock 125 | rightshift 126 | leftshift 127 | rightctrl 128 | leftctrl 129 | rightalt 130 | leftalt 131 | rightmeta 132 | leftmeta 133 | leftsuper 134 | rightsuper 135 | altgr 136 | compose 137 | help 138 | printscreen -------------------------------------------------------------------------------- /Python/maths.py: -------------------------------------------------------------------------------- 1 | def clamp(num, min_value, max_value): 2 | return max(min(num, max_value), min_value) 3 | -------------------------------------------------------------------------------- /Python/seriallib.py: -------------------------------------------------------------------------------- 1 | import serial 2 | 3 | from maths import clamp 4 | from JoystickEnums import Button, Stick, HAT 5 | 6 | 7 | class SerialManager(serial.Serial): 8 | debug = False 9 | 10 | def write_as_bytes(self, *args) -> None: 11 | byteArr = bytearray() 12 | for item in args: 13 | if type(item) == str: 14 | for char in item: 15 | byteArr.append(ord(char)) 16 | elif 0 <= item <= 255: 17 | byteArr.append(item) 18 | if self.debug: 19 | print(f"Writing byte array ({byteArr}) to port {self.port}") 20 | self.write(byteArr) 21 | 22 | def read_as_int_arr(self) -> tuple: 23 | serialBytes = self.read_all() 24 | intArray = [] 25 | for singleByte in serialBytes: 26 | if type(singleByte) == int: 27 | intArray.append(singleByte) 28 | else: 29 | intArray.append(int.from_bytes(singleByte, byteorder="big")) 30 | return tuple(intArray) 31 | 32 | 33 | class Payload: 34 | """ 35 | Serial data payload class to handle controller serial data in order 36 | to prevent errors from input. 37 | """ 38 | MAX_BUTTON_VALUE = 16383 39 | 40 | def __init__(self): 41 | self.left_stick = (Stick.CENTER.value, Stick.CENTER.value) 42 | self.right_stick = (Stick.CENTER.value, Stick.CENTER.value) 43 | self.hat = HAT.CENTER.value 44 | self.buttons = 0 45 | 46 | def __repr__(self): 47 | return f"{self.__dict__}" 48 | 49 | def __str__(self): 50 | button_list = [button.name 51 | for ind, button in enumerate(Button) 52 | if self.buttons & (1 << ind)] 53 | st = f"LeftStick: {self.left_stick}, RightStick: {self.right_stick}," + \ 54 | f"HAT: {HAT(self.hat).name}, Buttons: {button_list}" 55 | return st 56 | 57 | def set_left_x(self, x: int) -> None: 58 | self.left_stick = (clamp(x, Stick.MIN.value, Stick.MAX.value), 59 | self.left_stick[1]) 60 | 61 | def set_left_y(self, y: int) -> None: 62 | self.left_stick = (self.left_stick[0], 63 | clamp(y, Stick.MIN.value, Stick.MAX.value)) 64 | 65 | def set_left_stick(self, x: int, y: int) -> None: 66 | self.left_stick = (clamp(x, Stick.MIN.value, Stick.MAX.value), 67 | clamp(y, Stick.MIN.value, Stick.MAX.value)) 68 | 69 | def set_right_x(self, x: int) -> None: 70 | self.right_stick = (clamp(x, Stick.MIN.value, Stick.MAX.value), 71 | self.right_stick[1]) 72 | 73 | def set_right_y(self, y: int) -> None: 74 | self.right_stick = (self.right_stick[0], clamp(y, Stick.MIN.value, Stick.MAX.value)) 75 | 76 | def set_right_stick(self, x: int, y: int) -> None: 77 | self.right_stick = (clamp(x, Stick.MIN.value, Stick.MAX.value), 78 | clamp(y, Stick.MIN.value, Stick.MAX.value)) 79 | 80 | def set_hat(self, x, y) -> None: 81 | dpad_list = [ 82 | [7, 0, 1], 83 | [6, 8, 2], 84 | [5, 4, 3] 85 | ] 86 | self.hat = dpad_list[y + 1][x + 1] 87 | 88 | def apply_buttons(self, *args: Button) -> None: 89 | if not len(args) > 0: 90 | return 91 | for item in args: 92 | if type(item) == Button: 93 | value = item.value 94 | else: 95 | value = item 96 | self.buttons |= value 97 | self.buttons = clamp(self.buttons, 0, self.MAX_BUTTON_VALUE) 98 | 99 | def release_all_buttons(self) -> None: 100 | self.buttons = 0 101 | 102 | def reset_inputs(self) -> None: 103 | self.left_stick = (Stick.CENTER.value, Stick.CENTER.value) 104 | self.right_stick = (Stick.CENTER.value, Stick.CENTER.value) 105 | self.buttons = 0 106 | self.hat = HAT.CENTER.value 107 | 108 | def as_byte_arr(self) -> bytearray: 109 | buttons1, buttons2 = (b for b in self.buttons.to_bytes(2, byteorder="little")) 110 | return bytearray([self.left_stick[0], self.left_stick[1], 111 | self.right_stick[0], self.right_stick[1], 112 | self.hat, buttons1, buttons2]) 113 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # NOTE: Currently doesn't work on switch, will be updating soon 2 | I've been having some issues sending data to the microcontroller while connected to the switch. It works fine while connected to PC/Mac. Will be looking into this and fixing it as soon as I can. 3 | 4 | ## Switch UART Controller Tools 5 | UART Reference/Library for Switch-FightStick Controller 6 | 7 | Uses the LUFA library and reverse-engineering of the Pokken Tournament Pro Pad for the Wii U to enable custom fightsticks on the Switch System v3.0.0 8 | 9 | Required libaries for helper classes: `pyserial` 10 | 11 | Required libraries for example.py: `pygame` 12 | 13 | Components used in this project: `Teensy 2.0++`, `USB to UART bridge` 14 | 15 | Inspired by (and forked from) ShinyQuagsire's Splat printer (https://github.com/shinyquagsire23/Switch-Fightstick) and PiManRules' Super Mario Odyssey bots (https://www.youtube.com/watch?v=hu3HEwc6Pwk&list=PLRqz09NxzVqYLX_1F3xB01hgpDFpSxUEi) 16 | 17 | ## Writing Your Own Automated Programs 18 | This project is meant for a starting point on writing bots for the the switch. The main branch example makes use of pygame in order to get the user's keyboard and mouse input and modify the serial data according to how the buttons are mapped. You can modify the csv file "controllerMapping.csv" and use the accepted keys (taken from pygames list of key names) found in the "keys.txt" file. 19 | 20 | Feel free to fork this repo as a basis your own projects and if you want to, help contribute to make this one better for new users looking for a place to get started. 21 | 22 | This is my first project trying to make classes that can be used for such a wide range of projects, so I apologize for some of the classes being a bit messy. 23 | 24 | #### Compiling C and Flashing onto the Teensy 2.0++ 25 | Go to the Teensy website and download/install the [Teensy Loader application](https://www.pjrc.com/teensy/loader.html). For Linux, follow their instructions for installing the [GCC Compiler and Tools](https://www.pjrc.com/teensy/gcc.html). For Windows, you will need the [latest AVR toolchain](http://www.atmel.com/tools/atmelavrtoolchainforwindows.aspx) from the Atmel site. See [this issue](https://github.com/LightningStalker/Splatmeme-Printer/issues/10) and [this thread](http://gbatemp.net/threads/how-to-use-shinyquagsires-splatoon-2-post-printer.479497/) on GBAtemp for more information. (Note for Mac users - the AVR MacPack is now called AVR CrossPack. If that does not work, you can try installing `avr-gcc` with `brew`.) 26 | 27 | Next, you need to grab the LUFA library. You can download it in a zipped folder at the bottom of [this page](http://www.fourwalledcubicle.com/LUFA.php). Unzip the folder, rename it `LUFA`, and place it where you like. Then, download or clone the contents of this repository onto your computer. Next, you'll need to make sure the `LUFA_PATH` inside of the `makefile` points to the `LUFA` subdirectory inside your `LUFA` directory. My `Switch-Fightstick` directory is in the same directory as my `LUFA` directory, so I set `LUFA_PATH = ../LUFA/LUFA`. 28 | 29 | Now you should be ready to rock. Open a terminal window in the `Switch-Fightstick` directory, type `make`, and hit enter to compile. If all goes well, the printout in the terminal will let you know it finished the build! Follow the directions on flashing `Joystick.hex` onto your Teensy, which can be found page where you downloaded the Teensy Loader application. 30 | --------------------------------------------------------------------------------