├── .gitattributes ├── .gitignore ├── Descriptors.c ├── Descriptors.h ├── LUFA.c ├── LUFAConfig.h ├── README.md ├── data ├── N_Switch.py ├── README.md ├── __init__.py ├── example.py └── examplePSJoyCon.py ├── nintendo.h ├── nintendo_switch_pro_controller_emulator.ino └── program.h /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | 7 | # Standard to msysgit 8 | *.doc diff=astextplain 9 | *.DOC diff=astextplain 10 | *.docx diff=astextplain 11 | *.DOCX diff=astextplain 12 | *.dot diff=astextplain 13 | *.DOT diff=astextplain 14 | *.pdf diff=astextplain 15 | *.PDF diff=astextplain 16 | *.rtf diff=astextplain 17 | *.RTF diff=astextplain 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Windows image file caches 2 | Thumbs.db 3 | ehthumbs.db 4 | 5 | # Folder config file 6 | Desktop.ini 7 | 8 | # Recycle Bin used on file shares 9 | $RECYCLE.BIN/ 10 | 11 | # Windows Installer files 12 | *.cab 13 | *.msi 14 | *.msm 15 | *.msp 16 | 17 | # Windows shortcuts 18 | *.lnk 19 | 20 | # ========================= 21 | # Operating System Files 22 | # ========================= 23 | 24 | # OSX 25 | # ========================= 26 | 27 | .DS_Store 28 | .AppleDouble 29 | .LSOverride 30 | 31 | # Thumbnails 32 | ._* 33 | 34 | # Files that might appear in the root of a volume 35 | .DocumentRevisions-V100 36 | .fseventsd 37 | .Spotlight-V100 38 | .TemporaryItems 39 | .Trashes 40 | .VolumeIcon.icns 41 | 42 | # Directories potentially created on remote AFP share 43 | .AppleDB 44 | .AppleDesktop 45 | Network Trash Folder 46 | Temporary Items 47 | .apdisk 48 | *.pyc 49 | -------------------------------------------------------------------------------- /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 | 208 | -------------------------------------------------------------------------------- /Descriptors.h: -------------------------------------------------------------------------------- 1 | #ifndef _DESCRIPTORS_H_ 2 | #define _DESCRIPTORS_H_ 3 | 4 | /* Includes: */ 5 | #include "LUFAConfig.h" 6 | 7 | #include 8 | 9 | #include 10 | // Type Defines 11 | // Device Configuration Descriptor Structure 12 | typedef struct 13 | { 14 | USB_Descriptor_Configuration_Header_t Config; 15 | 16 | // Joystick HID Interface 17 | USB_Descriptor_Interface_t HID_Interface; 18 | USB_HID_Descriptor_HID_t HID_JoystickHID; 19 | USB_Descriptor_Endpoint_t HID_ReportOUTEndpoint; 20 | USB_Descriptor_Endpoint_t HID_ReportINEndpoint; 21 | } USB_Descriptor_Configuration_t; 22 | 23 | // Device Interface Descriptor IDs 24 | enum InterfaceDescriptors_t 25 | { 26 | INTERFACE_ID_Joystick = 0, /**< Joystick interface descriptor ID */ 27 | }; 28 | 29 | // Device String Descriptor IDs 30 | enum StringDescriptors_t 31 | { 32 | STRING_ID_Language = 0, // Supported Languages string descriptor ID (must be zero) 33 | STRING_ID_Manufacturer = 1, // Manufacturer string ID 34 | STRING_ID_Product = 2, // Product string ID 35 | }; 36 | 37 | // Macros 38 | // Endpoint Addresses 39 | #define JOYSTICK_IN_EPADDR (ENDPOINT_DIR_IN | 1) 40 | #define JOYSTICK_OUT_EPADDR (ENDPOINT_DIR_OUT | 2) 41 | // HID Endpoint Size 42 | // The Switch -needs- this to be 64. 43 | // The Wii U is flexible, allowing us to use the default of 8 (which did not match the original Hori descriptors). 44 | #define JOYSTICK_EPSIZE 64 45 | // Descriptor Header Type - HID Class HID Descriptor 46 | #define DTYPE_HID 0x21 47 | // Descriptor Header Type - HID Class HID Report Descriptor 48 | #define DTYPE_Report 0x22 49 | 50 | // Function Prototypes 51 | uint16_t CALLBACK_USB_GetDescriptor( 52 | const uint16_t wValue, 53 | const uint16_t wIndex, 54 | const void** const DescriptorAddress 55 | ) ATTR_WARN_UNUSED_RESULT ATTR_NON_NULL_PTR_ARG(3); 56 | 57 | #endif 58 | 59 | -------------------------------------------------------------------------------- /LUFA.c: -------------------------------------------------------------------------------- 1 | #include "LUFAConfig.h" 2 | 3 | #include 4 | 5 | -------------------------------------------------------------------------------- /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 | #include 5 | #define F_USB F_CPU 6 | #if (ARCH == ARCH_AVR8) 7 | // Non-USB Related Configuration Tokens 8 | // #define DISABLE_TERMINAL_CODES 9 | 10 | // USB Class Driver Related Tokens 11 | // #define HID_HOST_BOOT_PROTOCOL_ONLY 12 | // #define HID_STATETABLE_STACK_DEPTH {Insert Value Here} 13 | // #define HID_USAGE_STACK_DEPTH {Insert Value Here} 14 | // #define HID_MAX_COLLECTIONS {Insert Value Here} 15 | // #define HID_MAX_REPORTITEMS {Insert Value Here} 16 | // #define HID_MAX_REPORT_IDS {Insert Value Here} 17 | // #define NO_CLASS_DRIVER_AUTOFLUSH 18 | 19 | // General USB Driver Related Tokens 20 | // #define ORDERED_EP_CONFIG 21 | #define USE_STATIC_OPTIONS (USB_DEVICE_OPT_FULLSPEED | USB_OPT_REG_ENABLED | USB_OPT_AUTO_PLL) 22 | #define USB_DEVICE_ONLY 23 | // #define USB_HOST_ONLY 24 | // #define USB_STREAM_TIMEOUT_MS {Insert Value Here} 25 | // #define NO_LIMITED_CONTROLLER_CONNECT 26 | // #define NO_SOF_EVENTS 27 | 28 | // USB Device Mode Driver Related Tokens 29 | // #define USE_RAM_DESCRIPTORS 30 | #define USE_FLASH_DESCRIPTORS 31 | // #define USE_EEPROM_DESCRIPTORS 32 | // #define NO_INTERNAL_SERIAL 33 | #define FIXED_CONTROL_ENDPOINT_SIZE 64 34 | // #define DEVICE_STATE_AS_GPIOR {Insert Value Here} 35 | #define FIXED_NUM_CONFIGURATIONS 1 36 | // #define CONTROL_ONLY_DEVICE 37 | // #define INTERRUPT_CONTROL_ENDPOINT 38 | // #define NO_DEVICE_REMOTE_WAKEUP 39 | // #define NO_DEVICE_SELF_POWER 40 | 41 | // USB Host Mode Driver Related Tokens 42 | // #define HOST_STATE_AS_GPIOR {Insert Value Here} 43 | // #define USB_HOST_TIMEOUT_MS {Insert Value Here} 44 | // #define HOST_DEVICE_SETTLE_DELAY_MS {Insert Value Here} 45 | // #define NO_AUTO_VBUS_MANAGEMENT 46 | // #define INVERTED_VBUS_ENABLE_LINE 47 | 48 | #elif (ARCH == ARCH_XMEGA) 49 | // Non-USB Related Configuration Tokens 50 | // #define DISABLE_TERMINAL_CODES 51 | 52 | // USB Class Driver Related Tokens 53 | // #define HID_HOST_BOOT_PROTOCOL_ONLY 54 | // #define HID_STATETABLE_STACK_DEPTH {Insert Value Here} 55 | // #define HID_USAGE_STACK_DEPTH {Insert Value Here} 56 | // #define HID_MAX_COLLECTIONS {Insert Value Here} 57 | // #define HID_MAX_REPORTITEMS {Insert Value Here} 58 | // #define HID_MAX_REPORT_IDS {Insert Value Here} 59 | // #define NO_CLASS_DRIVER_AUTOFLUSH 60 | 61 | // General USB Driver Related Tokens 62 | #define USE_STATIC_OPTIONS (USB_DEVICE_OPT_FULLSPEED | USB_OPT_RC32MCLKSRC | USB_OPT_BUSEVENT_PRIHIGH) 63 | // #define USB_STREAM_TIMEOUT_MS {Insert Value Here} 64 | // #define NO_LIMITED_CONTROLLER_CONNECT 65 | // #define NO_SOF_EVENTS 66 | 67 | // USB Device Mode Driver Related Tokens 68 | // #define USE_RAM_DESCRIPTORS 69 | #define USE_FLASH_DESCRIPTORS 70 | // #define USE_EEPROM_DESCRIPTORS 71 | // #define NO_INTERNAL_SERIAL 72 | #define FIXED_CONTROL_ENDPOINT_SIZE 64 73 | // #define DEVICE_STATE_AS_GPIOR {Insert Value Here} 74 | #define FIXED_NUM_CONFIGURATIONS 1 75 | // #define CONTROL_ONLY_DEVICE 76 | #define MAX_ENDPOINT_INDEX 1 77 | // #define NO_DEVICE_REMOTE_WAKEUP 78 | // #define NO_DEVICE_SELF_POWER 79 | 80 | #else 81 | #error Unsupported architecture for this LUFA configuration file. 82 | #endif 83 | #endif 84 | 85 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Nintendo switch pro controller emulator 2 | 3 | Allows action to be replayed on nintendo switch using ATmega32u4 4 | 5 | included Python Lib to allow for updating and live control see data folder. 6 | 7 | Moved to Ardino to support easer install and more features. 8 | 9 | [Arduino-Lufa](https://github.com/Palatis/Arduino-Lufa) 10 | 11 | [ATmega32U4](https://www.amazon.com/OSOYOO-ATmega32U4-arduino-Leonardo-ATmega328/dp/B012FOV17O) 12 | 13 | 14 | # Hardware 15 | 16 | Pin 2 is used as a Clear pin for the USB reports 17 | 18 | This will stop any running scripts sent over the serial port. 19 | 20 | Connecting this pin to ground will clear the reports. 21 | 22 | Pin 3 is used as swtich for serial Mode / vs embeded USB reports. 23 | 24 | Connecting this pin Low (ground) will run the embeded USB reports. 25 | 26 | Default is High Not conected and will run in serial Mode. 27 | 28 | If there is any USB reports stored in EEPROM thay will run. 29 | 30 | 31 | # Examples 32 | 33 | [Live PlayBack Example](https://github.com/odwdinc/nintendo_switch_pro_controller_emulator/blob/master/data/examplePSJoyCon.py) 34 | 35 | [![Making progress](https://img.youtube.com/vi/9iv6o_2WkOk/0.jpg)](https://www.youtube.com/watch?v=9iv6o_2WkOk "Making progress") 36 | 37 | [Coine Farming Example](https://github.com/odwdinc/nintendo_switch_pro_controller_emulator/blob/master/data/example.py) 38 | 39 | [![Coine Farming Example](https://img.youtube.com/vi/wtSV8fbQbBM/0.jpg)](https://www.youtube.com/watch?v=wtSV8fbQbBM "Coine Farming Example") 40 | 41 | 42 | # Thanks 43 | 44 | Thanks to Shiny Quagsire for his Splatoon post printer and progmem for his [original discovery.](https://github.com/shinyquagsire23/Switch-Fightstick) 45 | 46 | Thanks to Bertrand Fan for his [Automated snowball thrower](https://github.com/bertrandom/snowball-thrower) 47 | -------------------------------------------------------------------------------- /data/N_Switch.py: -------------------------------------------------------------------------------- 1 | import io 2 | from time import sleep 3 | import struct 4 | from numpy import interp 5 | from inputs import get_key 6 | from inputs import get_gamepad 7 | import copy 8 | import datetime 9 | 10 | def TimestampMillisec64(): 11 | return int((datetime.datetime.utcnow() - datetime.datetime(1970, 1, 1)).total_seconds() * 1000) 12 | 13 | def milli_time_to_report_time(x): return int(round(x/ 1000/.024)) 14 | 15 | class NS: 16 | 17 | SWITCH_Y = 0x01 18 | SWITCH_B = 0x02 19 | SWITCH_A = 0x04 20 | SWITCH_X = 0x08 21 | SWITCH_L = 0x10 22 | SWITCH_R = 0x20 23 | SWITCH_ZL = 0x40 24 | SWITCH_ZR = 0x80 25 | SWITCH_MINUS = 0x100 26 | SWITCH_PLUS = 0x200 27 | SWITCH_LCLICK = 0x400 28 | SWITCH_RCLICK = 0x800 29 | SWITCH_HOME = 0x1000 30 | SWITCH_CAPTURE = 0x2000 31 | 32 | HAT_TOP = 0x00 33 | HAT_TOP_RIGHT = 0x01 34 | HAT_RIGHT = 0x02 35 | HAT_BOTTOM_RIGHT = 0x03 36 | HAT_BOTTOM = 0x04 37 | HAT_BOTTOM_LEFT = 0x05 38 | HAT_LEFT = 0x06 39 | HAT_TOP_LEFT = 0x07 40 | HAT_CENTER = 0x08 41 | 42 | STICK_MIN = 0 43 | STICK_CENTER = 128 44 | STICK_MAX = 255 45 | 46 | X = b'a' 47 | Y = b'b' 48 | A = b'c' 49 | B = b'd' 50 | L = b'e' 51 | R = b'f' 52 | MINUS = b'g' 53 | PLUS = b'h' 54 | UP = b'i' 55 | DOWN = b'j' 56 | LEFT = b'k' 57 | RIGHT = b'l' 58 | UP_RIGHT = b'm' 59 | UP_LEFT = b'n' 60 | DOWN_RIGHT = b'o' 61 | DOWN_LEFT = b'p' 62 | THROW = b'q' 63 | TRIGGERS = b'r' 64 | NOTHING = b's' 65 | 66 | ReportPlaybackList=["HatJump","Jump"] 67 | ser = None 68 | 69 | options = {"KEY_W" : UP, #up 70 | "KEY_A": LEFT, #left 71 | "KEY_S" : DOWN, #down 72 | "KEY_D" : RIGHT, #right 73 | "KEY_SPACE": B, #B 74 | "KEY_LEFTCTRL": A, #A 75 | "KEY_LEFTSHIFT": Y, #Y 76 | } 77 | JoyoOptionsRemove = { 78 | "SYN_REPORT" : 1, 79 | } 80 | JoyoOptionsAnalog = { 81 | "ABS_Y": "LY", 82 | "ABS_X" : "LX", 83 | "ABS_RY" : "RY", 84 | "ABS_RX": "RX", 85 | } 86 | 87 | JoyoOptionsHat = { 88 | "ABS_Y": 2, 89 | "ABS_X" : 3, 90 | "ABS_RY" : 4, 91 | "ABS_RX": 5, 92 | "ABS_Z": 6, 93 | "ABS_RZ": 7, 94 | } 95 | 96 | JoyStringOut = { 97 | "SWITCH": 0, 98 | "HAT": STICK_CENTER, 99 | "LX": STICK_CENTER, 100 | "LY": STICK_CENTER, 101 | "RX": STICK_CENTER, 102 | "RY": STICK_CENTER, 103 | "TIME": 0 104 | } 105 | 106 | JoyString = { 107 | "SWITCH": 0, 108 | "HAT": STICK_CENTER, 109 | "LX": STICK_CENTER, 110 | "LY": STICK_CENTER, 111 | "RX": STICK_CENTER, 112 | "RY": STICK_CENTER, 113 | "TIME": 0 114 | } 115 | 116 | 117 | CommandlistCheck = { 118 | 36: "$", 119 | 35: "#", 120 | 33: "!", 121 | 37: "%" 122 | } 123 | 124 | JoyoOptions={ 125 | "BTN_WEST": [SWITCH_Y,False], 126 | "BTN_SOUTH": [SWITCH_B,False], 127 | "BTN_EAST": [SWITCH_A,False], 128 | "BTN_NORTH": [SWITCH_X,False], 129 | "BTN_TL": [SWITCH_L,False], 130 | "BTN_TR": [SWITCH_R,False], 131 | "BTN_START": [SWITCH_MINUS,False], 132 | "BTN_SELECT": [SWITCH_PLUS,False], 133 | "BTN_THUMBL": [SWITCH_LCLICK,False], 134 | "BTN_THUMBR": [SWITCH_RCLICK,False], 135 | "ABS_HAT0X": [SWITCH_HOME,False], 136 | "ABS_HAT0Y": [SWITCH_CAPTURE,False], 137 | "ABS_Z": [SWITCH_ZL,False], 138 | "ABS_RZ": [SWITCH_ZR,False], 139 | } 140 | USBLoopTime = 0 141 | 142 | 143 | sendcommand = {} 144 | 145 | recoredJoystickCommandSet = [] 146 | recoredJoystickCommandCount = 0 147 | def __init__(self,ser): 148 | self.ser = ser 149 | 150 | def reportCheck(self, jb,jbo): 151 | NeedsUpdate = False 152 | for byew in jb: 153 | if jbo[byew] != jb[byew]: 154 | temp = jb[byew] 155 | jbo[byew] = temp 156 | NeedsUpdate = True 157 | return NeedsUpdate 158 | 159 | def printRunCommands(self): 160 | print("Run Finished\n") 161 | self.sendUSBReport() 162 | for reportCMD in self.recoredJoystickCommandSet: 163 | print (reportCMD,",") 164 | 165 | 166 | def joystickControl(self,testing=False,timeout = 0, stopButton=0): 167 | min_Stick_Map = 5000 168 | max_Stick_Map = 33000 169 | while 1: 170 | event = get_gamepad()[0] 171 | if self.recoredJoystickCommandCount == 0: 172 | self.RunStart = TimestampMillisec64() 173 | 174 | if timeout > 0: 175 | if (TimestampMillisec64() - self.RunStart)/1000 > timeout : 176 | self.printRunCommands() 177 | break 178 | if event.code not in self.JoyoOptionsRemove: 179 | if event.code in self.JoyoOptions: 180 | if self.JoyoOptions[event.code][0] is stopButton: 181 | self.printRunCommands() 182 | break 183 | if event.state is 0: 184 | self.JoyoOptions[event.code][1] = False 185 | else: 186 | self.JoyoOptions[event.code][1] = True 187 | elif event.code in self.JoyoOptionsAnalog: 188 | if event.code is "ABS_X" or event.code is "ABS_RX": 189 | event.state = event.state*-1 190 | if (event.state > min_Stick_Map or event.state < -1*min_Stick_Map): 191 | if event.state > 0: 192 | event.state = int(interp(event.state,[min_Stick_Map,max_Stick_Map],[self.STICK_CENTER,self.STICK_MIN])) 193 | if event.state < self.STICK_MIN: 194 | event.state = self.STICK_MIN; 195 | else: 196 | event.state = int(interp(event.state,[-1*max_Stick_Map,-1*min_Stick_Map],[self.STICK_MAX,self.STICK_CENTER])) 197 | if event.state > self.STICK_MAX: 198 | event.state = self.STICK_MAX; 199 | else: 200 | event.state = self.STICK_CENTER 201 | 202 | if self.JoyoOptionsAnalog[event.code] in self.JoyString: 203 | step = 43 204 | center = 22 205 | if event.state <= step: #self.STICK_MIN - 43 206 | event.state = self.STICK_MIN #0 207 | elif event.state <= step*2 and event.state > step*1: #43 - 86 208 | event.state = self.STICK_MIN+(step*1)+22 #65 209 | elif event.state < self.STICK_CENTER and event.state > step*2: #86 - self.STICK_CENTER 210 | event.state = self.STICK_MIN+(step*2)+22 #108 211 | elif event.state <= step*4 and event.state > self.STICK_CENTER: #self.STICK_CENTER -172 212 | event.state = self.STICK_MIN+(step*3)+22 #151 213 | elif event.state <= step*5 and event.state > step*4: #172 - 215 214 | event.state = self.STICK_MIN+(step*4)+22 #194 215 | elif event.state <= self.STICK_MAX and event.state > step*5: #215 self.STICK_MAX 216 | event.state = self.STICK_MAX #255 217 | self.JoyString[self.JoyoOptionsAnalog[event.code]] = event.state 218 | else: 219 | print(event.ev_type, event.code, event.state) 220 | 221 | 222 | JoyoOptionsbuttons = 0 223 | for button in self.JoyoOptions: 224 | if self.JoyoOptions[button][1]: 225 | JoyoOptionsbuttons = JoyoOptionsbuttons | self.JoyoOptions[button][0] 226 | self.JoyString["SWITCH"] = JoyoOptionsbuttons 227 | 228 | if self.reportCheck(self.JoyString,self.JoyStringOut): 229 | if testing: 230 | self.sendUSBReportCheck(self.JoyStringOut["LX"]); 231 | self.sendUSBReportCheck(self.JoyStringOut["LY"]); 232 | self.sendUSBReportCheck(self.JoyStringOut["RX"]); 233 | self.sendUSBReportCheck(self.JoyStringOut["RY"]); 234 | print(self.JoyStringOut) 235 | if self.recoredJoystickCommandCount< 2: 236 | self.recoredJoystickCommandCount += 1 237 | else: 238 | 239 | LastCommandDelay = self.sendUSBReport(self.JoyStringOut["SWITCH"],self.JoyStringOut["HAT"],self.JoyStringOut["LX"],self.JoyStringOut["LY"],self.JoyStringOut["RX"],self.JoyStringOut["RY"]) 240 | 241 | if self.recoredJoystickCommandCount > 0: 242 | self.recoredJoystickCommandSet[self.recoredJoystickCommandCount-1]["TIME"] = LastCommandDelay 243 | self.recoredJoystickCommandSet.append(copy.deepcopy(self.JoyStringOut)) 244 | self.recoredJoystickCommandCount += 1 245 | else: 246 | self.recoredJoystickCommandSet.append(copy.deepcopy(self.JoyStringOut)) 247 | self.recoredJoystickCommandSet[self.recoredJoystickCommandCount]["TIME"] = 0 248 | self.recoredJoystickCommandCount +=1 249 | 250 | if(self.recoredJoystickCommandCount > 1 and timeout == 0 and stopButton==0): 251 | print(self.recoredJoystickCommandSet[self.recoredJoystickCommandCount-2],", #",self.recoredJoystickCommandCount) 252 | sleep(.025) 253 | 254 | 255 | def RunJoystickReport(self, reportList): 256 | self.USBLoopTime = 0 257 | for report in reportList: 258 | self.sendUSBReport(report["SWITCH"],report["HAT"],report["LX"],report["LY"],report["RX"],report["RY"]) 259 | sleep(report["TIME"]) 260 | 261 | self.sendUSBReport() 262 | 263 | def runCommandPlayback(self,commandPlayback,savetoEEPROM=False): 264 | cmdStr = b'' 265 | for command in commandPlayback: 266 | cmd = command[0] 267 | time = bytes([command[1]]) 268 | cmdStr += b'$' 269 | cmdStr += cmd 270 | cmdStr += time 271 | if(savetoEEPROM): 272 | cmdStr += b'!' 273 | cmdStr += b'#' 274 | 275 | 276 | self.ser.write(b'#') 277 | self.ser.write(cmdStr) 278 | #print(cmdStr,len(cmdStr)) 279 | 280 | def sendUSBReportCheck(self, report): 281 | while report in self.CommandlistCheck: 282 | #print("Yah found "+self.CommandlistCheck[report]) 283 | report+=1 284 | if report > 255: 285 | report = 255 286 | if report < 0: 287 | report = 0 288 | return report 289 | 290 | 291 | def sendUSBReport(self,SWITCH=0,HAT = HAT_CENTER,LX = STICK_CENTER,LY = STICK_CENTER,RX = STICK_CENTER,RY = STICK_CENTER): 292 | # report info 293 | # %[SWITCH0][SWITCH1][HAT][LX][LY][RX][RY] 294 | LX = self.sendUSBReportCheck(LX); 295 | LY = self.sendUSBReportCheck(LY); 296 | RX = self.sendUSBReportCheck(RX); 297 | RY = self.sendUSBReportCheck(RY); 298 | 299 | SWITCH0, SWITCH1 = struct.pack(' 41 | #include 42 | #include 43 | #include 44 | #include 45 | #include 46 | #include "program.h" 47 | #include 48 | #include 49 | /* Includes: */ 50 | /* Includes: */ 51 | #include 52 | #include 53 | #include 54 | #include 55 | #include 56 | 57 | #include "Descriptors.h" 58 | 59 | #include 60 | #include 61 | #include 62 | #include 63 | 64 | 65 | #define HAT_TOP 0x00 66 | #define HAT_TOP_RIGHT 0x01 67 | #define HAT_RIGHT 0x02 68 | #define HAT_BOTTOM_RIGHT 0x03 69 | #define HAT_BOTTOM 0x04 70 | #define HAT_BOTTOM_LEFT 0x05 71 | #define HAT_LEFT 0x06 72 | #define HAT_TOP_LEFT 0x07 73 | #define HAT_CENTER 0x08 74 | 75 | #define STICK_MIN 0 76 | #define STICK_CENTER 128 77 | #define STICK_MAX 255 78 | 79 | #define EEPROMCommandStartPos 20 80 | #define EEPROMCommandSize 35 81 | #define EEPROMCommandlog 19 82 | #define ECHOES 2 83 | 84 | // Type Defines 85 | // Enumeration for joystick buttons. 86 | typedef enum { 87 | SWITCH_Y = 0x01, 88 | SWITCH_B = 0x02, 89 | SWITCH_A = 0x04, 90 | SWITCH_X = 0x08, 91 | SWITCH_L = 0x10, 92 | SWITCH_R = 0x20, 93 | SWITCH_ZL = 0x40, 94 | SWITCH_ZR = 0x80, 95 | SWITCH_MINUS = 0x100, 96 | SWITCH_PLUS = 0x200, 97 | SWITCH_LCLICK = 0x400, 98 | SWITCH_RCLICK = 0x800, 99 | SWITCH_HOME = 0x1000, 100 | SWITCH_CAPTURE = 0x2000, 101 | } JoystickButtons_t; 102 | 103 | // Joystick HID report structure. We have an input and an output. 104 | typedef struct __attribute__ ((packed)) { 105 | uint16_t Button; // 16 buttons; see JoystickButtons_t for bit mapping 106 | uint8_t HAT; // HAT switch; one nibble w/ unused nibble 107 | uint8_t LX; // Left Stick X 108 | uint8_t LY; // Left Stick Y 109 | uint8_t RX; // Right Stick X 110 | uint8_t RY; // Right Stick Y 111 | uint8_t VendorSpec; 112 | } USB_JoystickReport_Input_t; 113 | 114 | 115 | bool operator==(const USB_JoystickReport_Input_t& lhs, const USB_JoystickReport_Input_t& rhs) 116 | { 117 | return (lhs.Button == rhs.Button) && (lhs.HAT == rhs.HAT) && (lhs.LX == rhs.LX) && (lhs.LY == rhs.LY) && (lhs.RX == rhs.RX) && (lhs.RY == rhs.RY) && (lhs.VendorSpec == rhs.VendorSpec); 118 | } 119 | 120 | typedef struct __attribute__ ((packed)) { 121 | USB_JoystickReport_Input_t report; 122 | uint16_t reportDelay; 123 | } USB_JoystickReport_SDRec; 124 | 125 | 126 | 127 | 128 | 129 | // The output is structured as a mirror of the input. 130 | // This is based on initial observations of the Pokken Controller. 131 | typedef struct __attribute__ ((packed)) { 132 | uint16_t Button; // 16 buttons; see JoystickButtons_t for bit mapping 133 | uint8_t HAT; // HAT switch; one nibble w/ unused nibble 134 | uint8_t LX; // Left Stick X 135 | uint8_t LY; // Left Stick Y 136 | uint8_t RX; // Right Stick X 137 | uint8_t RY; // Right Stick Y 138 | } USB_JoystickReport_Output_t; 139 | 140 | typedef enum { 141 | SYNC_CONTROLLER, 142 | BREATHE, 143 | PROCESS 144 | } State_t; 145 | 146 | 147 | 148 | // Function Prototypes 149 | // Setup all necessary hardware, including USB initialization. 150 | void SetupHardware(void); 151 | // Process and deliver data from IN and OUT endpoints. 152 | void HID_Task(void); 153 | // USB device event handlers. 154 | void EVENT_USB_Device_Connect(void); 155 | void EVENT_USB_Device_Disconnect(void); 156 | void EVENT_USB_Device_ConfigurationChanged(void); 157 | void EVENT_USB_Device_ControlRequest(void); 158 | // Prepare the next report for the host. 159 | void GetNextReport(USB_JoystickReport_Input_t* const ReportData); 160 | void LoadEEPROM(); 161 | int HandalFileOpening(String name, bool Writeing); 162 | bool slideSwitch(); 163 | bool clearButton(); 164 | void readPref(); 165 | void prossesCommandSet(byte Buffer[]); 166 | void runScript(command CommandStep[], int CommandStepSize); 167 | void startNewRecording(); 168 | void printReport(USB_JoystickReport_Input_t* TReport, String Pre); 169 | void printUSBCommands(); 170 | void flashErrot(); 171 | 172 | int echoes = 0; 173 | int bufindex = 0; 174 | int portsval = 0; 175 | int IsWriteing = 0; 176 | uint16_t loopCount = 0; 177 | int Serialstepcount = -1; 178 | bool SaveToEprom = false; 179 | bool boot = false; 180 | bool playingBack = false; 181 | int duration_count = 0; 182 | int reportCount = 0; 183 | 184 | State_t state = SYNC_CONTROLLER; 185 | 186 | USB_JoystickReport_Input_t last_report; 187 | USB_JoystickReport_SDRec curentReport; 188 | 189 | command prossing; 190 | command Serialstep[EEPROMCommandSize]; 191 | command LastProssing; 192 | 193 | static void voidDebugPrint(String str) { 194 | 195 | #ifdef Serialoutput 196 | Serial1.print(str); 197 | #endif 198 | } 199 | static void voidDebugPrint(unsigned char str, unsigned char format) { 200 | #ifdef Serialoutput 201 | Serial1.print(str, format); 202 | #endif 203 | } 204 | 205 | static void voidDebugPrintln(String str) { 206 | #ifdef Serialoutput 207 | Serial1.println(str); 208 | #endif 209 | } 210 | 211 | 212 | 213 | static void getDefaultReport(USB_JoystickReport_Input_t* reportt) { 214 | reportt->HAT = HAT_CENTER; 215 | reportt->RY = STICK_CENTER; 216 | reportt->RX = STICK_CENTER; 217 | reportt->LX = STICK_CENTER; 218 | reportt->LY = STICK_CENTER; 219 | reportt->Button = 0; 220 | } 221 | 222 | void printReport(USB_JoystickReport_Input_t* TReport, String Pre) { 223 | /* 224 | voidDebugPrint(Pre); 225 | voidDebugPrint(" reportButtons: "); 226 | voidDebugPrint(TReport->Button, DEC); 227 | voidDebugPrint(" LX,LY: ["); 228 | voidDebugPrint(TReport->LX, DEC); 229 | voidDebugPrint(","); 230 | voidDebugPrint(TReport->LY, DEC); 231 | voidDebugPrint("] RX,RY: ["); 232 | voidDebugPrint(TReport->RX, DEC); 233 | voidDebugPrint(","); 234 | voidDebugPrint(TReport->RY, DEC); 235 | voidDebugPrintln( "]"); 236 | */ 237 | 238 | } 239 | 240 | static void CommandPROCESS(USB_JoystickReport_Input_t* ReportData , command ThisCommand) { 241 | bool printCommand = false;//(LastProssing.button != ThisCommand.button); 242 | if (printCommand) printReport(ReportData, "Command Prossing: "); 243 | LastProssing = ThisCommand; 244 | switch (ThisCommand.button) 245 | { 246 | 247 | case UP: 248 | ReportData->LY = STICK_MIN; 249 | if (printCommand) voidDebugPrintln("UP"); 250 | break; 251 | 252 | case LEFT: 253 | ReportData->LX = STICK_MIN; 254 | break; 255 | 256 | case DOWN: 257 | ReportData->LY = STICK_MAX; 258 | if (printCommand) voidDebugPrintln("DOWN"); 259 | break; 260 | 261 | case RIGHT: 262 | ReportData->LX = STICK_MAX; 263 | if (printCommand) voidDebugPrintln("RIGHT"); 264 | break; 265 | 266 | case UP_RIGHT: 267 | ReportData->LY = STICK_MIN; 268 | ReportData->LX = STICK_MAX; 269 | if (printCommand) voidDebugPrintln("UP_RIGHT"); 270 | break; 271 | case UP_LEFT: 272 | ReportData->LY = STICK_MIN; 273 | ReportData->LX = STICK_MIN; 274 | if (printCommand) voidDebugPrintln("UP_LEFT"); 275 | break; 276 | case DOWN_RIGHT: 277 | ReportData->LY = STICK_MAX; 278 | ReportData->LX = STICK_MAX; 279 | if (printCommand) voidDebugPrintln("DOWN_RIGHT"); 280 | break; 281 | case DOWN_LEFT: 282 | ReportData->LY = STICK_MAX; 283 | ReportData->LX = STICK_MIN; 284 | if (printCommand) voidDebugPrintln("DOWN_LEFT"); 285 | break; 286 | 287 | case A: 288 | ReportData->Button |= SWITCH_A; 289 | if (printCommand) voidDebugPrintln("A"); 290 | break; 291 | 292 | case B: 293 | ReportData->Button |= SWITCH_B; 294 | voidDebugPrintln("B"); 295 | break; 296 | 297 | case R: 298 | ReportData->Button |= SWITCH_R; 299 | if (printCommand) voidDebugPrintln("R"); 300 | break; 301 | 302 | case L: 303 | ReportData->Button |= SWITCH_L; 304 | voidDebugPrintln("L"); 305 | break; 306 | 307 | case X: 308 | ReportData->Button |= SWITCH_X; 309 | if (printCommand) voidDebugPrintln("X"); 310 | break; 311 | 312 | case Y: 313 | ReportData->Button |= SWITCH_Y; 314 | if (printCommand) voidDebugPrintln("Y"); 315 | break; 316 | 317 | case MINUS: 318 | ReportData->Button |= SWITCH_MINUS; 319 | if (printCommand) voidDebugPrintln("MINUS"); 320 | break; 321 | 322 | case PLUS: 323 | ReportData->Button |= SWITCH_PLUS; 324 | if (printCommand) voidDebugPrintln("PLUS"); 325 | break; 326 | 327 | case THROW: 328 | ReportData->LY = STICK_MIN; 329 | ReportData->Button |= SWITCH_R; 330 | if (printCommand) voidDebugPrintln("THROWWN"); 331 | break; 332 | 333 | case TRIGGERS: 334 | ReportData->Button |= SWITCH_L | SWITCH_R; 335 | if (printCommand) voidDebugPrintln("TRIGGERS"); 336 | break; 337 | 338 | default: 339 | getDefaultReport(ReportData); 340 | if (printCommand) voidDebugPrintln("CENTERED"); 341 | break; 342 | } 343 | }; 344 | #endif 345 | 346 | -------------------------------------------------------------------------------- /nintendo_switch_pro_controller_emulator.ino: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "nintendo.h" 5 | #include "program.h" 6 | #include 7 | 8 | //Comment this line out if you do not want SD card support. 9 | #define SDCardSupport 10 | 11 | #ifdef SDCardSupport 12 | #include 13 | File myFile; 14 | void printDirectory(File dir, int numTabs); 15 | File root; 16 | #endif 17 | 18 | const int ClearButtonPin = 4; 19 | const int SwtichButtonPin = 5; 20 | 21 | const int PlayLed = 6; 22 | const int ErrorLed = 7; 23 | 24 | 25 | // Main entry point. 26 | void setup() { 27 | 28 | // We'll start by performing hardware and peripheral setup. 29 | SetupHardware(); 30 | EEPROM.write(EEPROMCommandStartPos, 0); 31 | pinMode(ClearButtonPin, INPUT); 32 | digitalWrite(ClearButtonPin, HIGH); // turn on pullup resistors 33 | 34 | pinMode(SwtichButtonPin, INPUT); 35 | digitalWrite(SwtichButtonPin, HIGH); // turn on pullup resistors 36 | 37 | pinMode(PlayLed, OUTPUT); 38 | digitalWrite(PlayLed, LOW); // turn off the LED 39 | pinMode(ErrorLed, OUTPUT); 40 | digitalWrite(ErrorLed, LOW); // turn off the LED 41 | 42 | // We'll then enable global interrupts for our use. 43 | GlobalInterruptEnable(); 44 | 45 | // Once that's done, we'll enter an infinite loop. 46 | getDefaultReport(&last_report); 47 | 48 | Serial1.begin(19200); 49 | 50 | voidDebugPrintln("Hello, world?"); 51 | prossing.button = NOTHING; 52 | 53 | #ifdef SDCardSupport 54 | voidDebugPrint("Initializing SD card..."); 55 | if (!SD.begin(10)) { 56 | voidDebugPrintln("initialization failed!"); 57 | flashErrot(); 58 | flashErrot(); 59 | flashErrot(); 60 | flashErrot(); 61 | return; 62 | } 63 | voidDebugPrintln("initialization done."); 64 | //readPref(); 65 | root = SD.open("/"); 66 | if (slideSwitch()) { 67 | startNewRecording(); 68 | } 69 | 70 | 71 | #endif 72 | voidDebugPrintln("prossing.."); 73 | } 74 | 75 | void loop() { 76 | // We need to run our task to process and deliver data for our IN and OUT endpoints. 77 | 78 | HID_Task(); 79 | USB_USBTask(); 80 | } 81 | 82 | bool LasteSwitch = true; 83 | 84 | // Prepare the next report for the host. 85 | void GetNextReport(USB_JoystickReport_Input_t* const ReportData) { 86 | 87 | // Prepare an empty report 88 | memset(ReportData, 0, sizeof(USB_JoystickReport_Input_t)); 89 | ReportData->LX = STICK_CENTER; 90 | ReportData->LY = STICK_CENTER; 91 | ReportData->RX = STICK_CENTER; 92 | ReportData->RY = STICK_CENTER; 93 | ReportData->HAT = HAT_CENTER; 94 | 95 | 96 | memcpy(&curentReport.report, &last_report, sizeof(USB_JoystickReport_Input_t)); 97 | 98 | // Repeat ECHOES times the last report 99 | if (echoes > 0) 100 | { 101 | memcpy(ReportData, &last_report, sizeof(USB_JoystickReport_Input_t)); 102 | echoes--; 103 | return; 104 | } 105 | 106 | // States and moves management 107 | switch (state) 108 | { 109 | 110 | case SYNC_CONTROLLER: 111 | state = BREATHE; 112 | break; 113 | 114 | case BREATHE: 115 | state = PROCESS; 116 | break; 117 | 118 | case PROCESS: 119 | if (!boot) { 120 | runScript(SetupStep, SetupStepStepSize); 121 | playingBack = true; 122 | } 123 | else if (slideSwitch()) { 124 | if (LasteSwitch == false) { 125 | #ifdef SDCardSupport 126 | startNewRecording(); 127 | #endif 128 | LasteSwitch = true; 129 | } 130 | 131 | digitalWrite(PlayLed, LOW); // turn off the LED 132 | if ( clearButton()) { 133 | while ( clearButton()) { 134 | } 135 | getDefaultReport(&curentReport.report); 136 | if (Serialstepcount > 0) { 137 | Serialstepcount = -1; 138 | } 139 | else if (Serialstepcount != 0) { 140 | Serialstepcount = 0; 141 | bufindex = 0; 142 | duration_count = 0; 143 | } else { 144 | Serialstepcount = -1; 145 | } 146 | #ifdef SDCardSupport 147 | startNewRecording(); 148 | #endif 149 | } 150 | 151 | if (Serial1.available() > 0) { 152 | Serialstepcount = -1; 153 | 154 | byte in = Serial1.read(); 155 | if (in == '$') { 156 | byte Buffer[sizeof(USB_JoystickReport_Input_t)]; 157 | int reportlen = Serial1.readBytes(Buffer, sizeof(USB_JoystickReport_Input_t)); 158 | if (reportlen == sizeof(USB_JoystickReport_Input_t)) { 159 | memcpy(&curentReport.report, Buffer, sizeof(USB_JoystickReport_Input_t)); 160 | } else { 161 | voidDebugPrintln("Error Reading Command"); 162 | flashErrot(); 163 | flashErrot(); 164 | flashErrot(); 165 | } 166 | 167 | } else if (in == '#') { 168 | byte Buffer[EEPROMCommandSize * 2]; 169 | int prossed = Serial1.readBytesUntil('#', Buffer, 100); 170 | prossesCommandSet(Buffer); 171 | if (SaveToEprom) { 172 | if ( prossed > 1023) { 173 | } else { 174 | EEPROM.write(EEPROMCommandStartPos, prossed); 175 | for (int i = 1; i < prossed + 1; i++) { 176 | EEPROM.write(EEPROMCommandStartPos + i, Buffer[i]); 177 | } 178 | } 179 | } 180 | } 181 | 182 | } else { 183 | if (Serialstepcount == 0) { 184 | //LoadEEPROM(); // will atemnt to load the Plogram stored on EEPROM 185 | //if (Serialstepcount == 0) { 186 | //voidDebugPrintln("Running Default Falsh Program"); 187 | //############################################################################################# 188 | //############################################################################################# 189 | //############################################################################################# 190 | // this is the bit to update the script that will run at boot 191 | // see program.h for sample FrogCoinestep 192 | 193 | runScript(FrogCoinestep, FrogCoinestepSize); 194 | 195 | 196 | //############################################################################################# 197 | //############################################################################################# 198 | //############################################################################################# 199 | //} else { 200 | //voidDebugPrintln("Loaded EEPROM Program"); 201 | //} 202 | playingBack = true; 203 | } else if (Serialstepcount > 0) { 204 | runScript(Serialstep, Serialstepcount); 205 | playingBack = true; 206 | } else { 207 | playingBack = false; 208 | } 209 | } 210 | } else { 211 | #ifdef SDCardSupport 212 | if (LasteSwitch == true) { 213 | if (reportCount > 0) { 214 | saveReportToSD(last_report); 215 | } 216 | reportCount = 0; 217 | int lognum = FindLastLog(-1); 218 | while (HandalFileOpening(LogNumberToLogName(lognum), false) < 8) { 219 | lognum--; 220 | if (lognum < 1) { 221 | break; 222 | } 223 | } 224 | LasteSwitch = false; 225 | } 226 | PlaybackButton(); 227 | if (playingBack) { 228 | if (loopCount > (curentReport.reportDelay)) { 229 | getNextUSBReport(); 230 | loopCount = 0; 231 | } 232 | loopCount++; 233 | } else { 234 | getDefaultReport(&last_report); 235 | getDefaultReport(&curentReport.report); 236 | } 237 | 238 | #endif 239 | 240 | } 241 | #ifdef SDCardSupport 242 | // Prepare to echo this report 243 | if ((curentReport.report == last_report) == false && playingBack == false && boot) { 244 | saveReportToSD(last_report); 245 | loopCount = 0; 246 | printReport(&curentReport.report, "saveReportToSD"); 247 | reportCount ++; 248 | } else { 249 | loopCount++; 250 | } 251 | #endif 252 | memcpy(ReportData, &curentReport.report, sizeof(USB_JoystickReport_Input_t)); 253 | memcpy(&last_report, ReportData, sizeof(USB_JoystickReport_Input_t)); 254 | 255 | echoes = ECHOES; 256 | break; 257 | } 258 | } 259 | 260 | 261 | void flashErrot() { 262 | digitalWrite(ErrorLed, HIGH); 263 | delay(1000); 264 | digitalWrite(ErrorLed, LOW); 265 | delay(200); 266 | } 267 | 268 | 269 | #ifdef SDCardSupport 270 | 271 | 272 | 273 | int FindLastLog(int befor) { 274 | int logNumber = -1; 275 | root.seek(0); 276 | while (true) { 277 | File entry = root.openNextFile(); 278 | if (!entry) { 279 | // no more files 280 | break; 281 | } 282 | //voidDebugPrint(entry.name()); 283 | String name = String(entry.name()); 284 | if (name.startsWith("LOG") && name.endsWith(".USB")) { 285 | String curentLog = name.substring(4, name.indexOf(".USB")); 286 | 287 | int templogNumber = curentLog.toInt(); 288 | if (befor > 0) { 289 | if ((templogNumber < befor) && (entry.size() > 8) && (templogNumber > logNumber)) { 290 | logNumber = templogNumber; 291 | } 292 | } else if ((entry.size() > 8) && (templogNumber > logNumber)) { 293 | logNumber = templogNumber; 294 | } 295 | 296 | } else { 297 | //voidDebugPrintln(" No Matchg"); 298 | } 299 | entry.close(); 300 | } 301 | return logNumber; 302 | } 303 | 304 | 305 | 306 | String LogNumberToLogName(int LogNumber) { 307 | return "log_" + String(LogNumber) + ".usb"; 308 | } 309 | 310 | void startNewRecording() { 311 | if (myFile) { 312 | if (reportCount > 0) { 313 | saveReportToSD(last_report); 314 | } 315 | myFile.close(); 316 | voidDebugPrintln(String(myFile.name()) + " closed"); 317 | } 318 | reportCount = 0; 319 | int logNumber = FindLastLog(-1); 320 | logNumber++; 321 | loopCount = 0; 322 | HandalFileOpening(LogNumberToLogName(logNumber), true); 323 | if (myFile) { 324 | voidDebugPrintln("Starting Recording.."); 325 | } else { 326 | voidDebugPrintln("Error Starting Recording.."); 327 | flashErrot(); 328 | flashErrot(); 329 | } 330 | getDefaultReport(&last_report); 331 | getDefaultReport(&curentReport.report); 332 | 333 | } 334 | 335 | 336 | 337 | void resetPlayBack() { 338 | loopCount = 0; 339 | if (myFile) { 340 | myFile.seek(0); 341 | } 342 | } 343 | 344 | void PlaybackButton() { 345 | if ( clearButton()) { 346 | while ( clearButton()) { 347 | } 348 | playingBack = !playingBack; 349 | if (playingBack) { 350 | resetPlayBack(); 351 | printUSBCommands(); 352 | resetPlayBack(); 353 | digitalWrite(PlayLed, HIGH); 354 | voidDebugPrintln("Playback Started:"); 355 | getNextUSBReport(); 356 | } else { 357 | digitalWrite(PlayLed, LOW); 358 | voidDebugPrintln("Playback Stoped:"); 359 | } 360 | } 361 | } 362 | 363 | 364 | void printUSBCommands() { 365 | byte Buffer[sizeof(USB_JoystickReport_SDRec)]; 366 | while (myFile.available()) { 367 | int reportlen = myFile.read(Buffer, sizeof(USB_JoystickReport_SDRec)); 368 | if (reportlen == sizeof(USB_JoystickReport_SDRec)) { 369 | memcpy(&curentReport, Buffer, sizeof(USB_JoystickReport_SDRec)); 370 | voidDebugPrint("reportDelay: "); 371 | voidDebugPrint(curentReport.reportDelay, DEC); 372 | voidDebugPrint(" reportButtons: "); 373 | voidDebugPrint(curentReport.report.Button, DEC); 374 | voidDebugPrint(" LX,LY: ["); 375 | voidDebugPrint(curentReport.report.LX, DEC); 376 | voidDebugPrint(","); 377 | voidDebugPrint(curentReport.report.LY, DEC); 378 | voidDebugPrint("] RX,RY: ["); 379 | voidDebugPrint(curentReport.report.RX, DEC); 380 | voidDebugPrint(","); 381 | voidDebugPrint(curentReport.report.RY, DEC); 382 | voidDebugPrintln( "]"); 383 | 384 | } else { 385 | voidDebugPrintln( "reportlen Under"); 386 | flashErrot(); 387 | } 388 | } 389 | myFile.seek(0); 390 | } 391 | 392 | void readPref() { 393 | HandalFileOpening("pref.ini", false); 394 | if (myFile) { 395 | while (myFile.available()) { 396 | Serial1.write(myFile.read()); 397 | } 398 | voidDebugPrintln("--------------------:"); 399 | voidDebugPrintln(""); 400 | } 401 | /* 402 | HandalFileOpening("log.usb", false); 403 | if (myFile) { 404 | byte Buffer[sizeof(USB_JoystickReport_SDRec)]; 405 | while (myFile.available()) { 406 | int reportlen = myFile.read(Buffer, sizeof(USB_JoystickReport_SDRec)); 407 | if (reportlen == sizeof(USB_JoystickReport_SDRec)) { 408 | memcpy(&curentReport, Buffer, sizeof(USB_JoystickReport_SDRec)); 409 | voidDebugPrint("reportDelay: "); 410 | voidDebugPrint(curentReport.reportDelay, DEC); 411 | voidDebugPrint(" reportButtons: "); 412 | voidDebugPrint(curentReport.report.Button, DEC); 413 | voidDebugPrint(" LX,LY: ["); 414 | voidDebugPrint(curentReport.report.LX, DEC); 415 | voidDebugPrint(","); 416 | voidDebugPrint(curentReport.report.LY, DEC); 417 | voidDebugPrint("] RX,RY: ["); 418 | voidDebugPrint(curentReport.report.RX, DEC); 419 | voidDebugPrint(","); 420 | voidDebugPrint(curentReport.report.RY, DEC); 421 | voidDebugPrintln( "]"); 422 | 423 | } 424 | } 425 | } 426 | */ 427 | } 428 | 429 | void saveReportToSD(USB_JoystickReport_Input_t ReportData) { 430 | if (myFile) { 431 | USB_JoystickReport_SDRec thisReport; 432 | memcpy(&thisReport.report, &ReportData, sizeof(USB_JoystickReport_SDRec)); 433 | thisReport.report = ReportData; 434 | thisReport.reportDelay = loopCount; 435 | 436 | char b[sizeof(USB_JoystickReport_SDRec)]; 437 | memcpy(b, &thisReport, sizeof(USB_JoystickReport_SDRec)); 438 | if (myFile.write(b, sizeof(b)) > 0) { 439 | voidDebugPrintln("saved: USB report"); 440 | } else { 441 | voidDebugPrintln("Error writing"); 442 | flashErrot(); 443 | } 444 | } 445 | 446 | } 447 | 448 | 449 | void getNextUSBReport() { 450 | //curentReport.report = getDefaultReport(); 451 | //curentReport.reportDelay = 1; 452 | 453 | if (myFile) { 454 | byte b[sizeof(USB_JoystickReport_SDRec)]; 455 | 456 | while (myFile.available()) { 457 | if (myFile.read(b, sizeof(b)) == sizeof(USB_JoystickReport_SDRec)) { 458 | memcpy(&curentReport, b, sizeof(USB_JoystickReport_SDRec)); 459 | printReport(&curentReport.report, "Read Report: "); 460 | break; 461 | } 462 | } 463 | 464 | } else { 465 | //voidDebugPrintln("Error No File Open"); 466 | 467 | } 468 | } 469 | 470 | int HandalFileOpening(String name, bool Writeing) { 471 | IsWriteing = -1; 472 | if (myFile) { 473 | myFile.close(); 474 | voidDebugPrintln(String(myFile.name()) + " closed"); 475 | } 476 | if (Writeing) { 477 | myFile = SD.open(name, FILE_WRITE); 478 | if (myFile) { 479 | IsWriteing = 1; 480 | voidDebugPrintln(name + " Is Writeing"); 481 | } else { 482 | voidDebugPrintln(name + " Error opening for writeing"); 483 | flashErrot(); 484 | flashErrot(); 485 | flashErrot(); 486 | flashErrot(); 487 | flashErrot(); 488 | } 489 | return 0; 490 | } else { 491 | myFile = SD.open(name); 492 | if (myFile) { 493 | IsWriteing = 0; 494 | voidDebugPrintln(name + " Is Reading"); 495 | return myFile.size(); 496 | } else { 497 | voidDebugPrintln(name + " Error opening for reading"); 498 | flashErrot(); 499 | flashErrot(); 500 | flashErrot(); 501 | flashErrot(); 502 | flashErrot(); 503 | return 0; 504 | } 505 | } 506 | } 507 | #endif 508 | 509 | 510 | // Configures hardware and peripherals, such as the USB peripherals. 511 | void SetupHardware(void) { 512 | // We need to disable watchdog if enabled by bootloader/fuses. 513 | MCUSR &= ~(1 << WDRF); 514 | wdt_disable(); 515 | 516 | // We need to disable clock division before initializing the USB hardware. 517 | clock_prescale_set(clock_div_1); 518 | // We can then initialize our hardware and peripherals, including the USB stack. 519 | 520 | // The USB stack should be initialized last. 521 | USB_Init(1); 522 | } 523 | 524 | // Fired to indicate that the device is enumerating. 525 | void EVENT_USB_Device_Connect(void) { 526 | // We can indicate that we're enumerating here (via status LEDs, sound, etc.). 527 | } 528 | 529 | // Fired to indicate that the device is no longer connected to a host. 530 | void EVENT_USB_Device_Disconnect(void) { 531 | // We can indicate that our device is not ready (via status LEDs, sound, etc.). 532 | } 533 | 534 | // Fired when the host set the current configuration of the USB device after enumeration. 535 | void EVENT_USB_Device_ConfigurationChanged(void) { 536 | bool ConfigSuccess = true; 537 | 538 | // We setup the HID report endpoints. 539 | ConfigSuccess &= Endpoint_ConfigureEndpoint(JOYSTICK_OUT_EPADDR, EP_TYPE_INTERRUPT, JOYSTICK_EPSIZE, 1); 540 | ConfigSuccess &= Endpoint_ConfigureEndpoint(JOYSTICK_IN_EPADDR, EP_TYPE_INTERRUPT, JOYSTICK_EPSIZE, 1); 541 | 542 | // We can read ConfigSuccess to indicate a success or failure at this point. 543 | } 544 | 545 | // Process control requests sent to the device from the USB host. 546 | void EVENT_USB_Device_ControlRequest(void) { 547 | // We can handle two control requests: a GetReport and a SetReport. 548 | 549 | // Not used here, it looks like we don't receive control request from the Switch. 550 | } 551 | 552 | // Process and deliver data from IN and OUT endpoints. 553 | void HID_Task(void) { 554 | // If the device isn't connected and properly configured, we can't do anything here. 555 | if (USB_DeviceState != DEVICE_STATE_Configured) 556 | return; 557 | 558 | // We'll start with the OUT endpoint. 559 | Endpoint_SelectEndpoint(JOYSTICK_OUT_EPADDR); 560 | // We'll check to see if we received something on the OUT endpoint. 561 | if (Endpoint_IsOUTReceived()) 562 | { 563 | // If we did, and the packet has data, we'll react to it. 564 | if (Endpoint_IsReadWriteAllowed()) 565 | { 566 | // We'll create a place to store our data received from the host. 567 | USB_JoystickReport_Output_t JoystickOutputData; 568 | // We'll then take in that data, setting it up in our storage. 569 | while (Endpoint_Read_Stream_LE(&JoystickOutputData, sizeof(JoystickOutputData), NULL) != ENDPOINT_RWSTREAM_NoError); 570 | // At this point, we can react to this data. 571 | 572 | // However, since we're not doing anything with this data, we abandon it. 573 | } 574 | // Regardless of whether we reacted to the data, we acknowledge an OUT packet on this endpoint. 575 | Endpoint_ClearOUT(); 576 | } 577 | 578 | // We'll then move on to the IN endpoint. 579 | Endpoint_SelectEndpoint(JOYSTICK_IN_EPADDR); 580 | // We first check to see if the host is ready to accept data. 581 | if (Endpoint_IsINReady()) 582 | { 583 | // We'll create an empty report. 584 | USB_JoystickReport_Input_t JoystickInputData; 585 | // We'll then populate this report with what we want to send to the host. 586 | GetNextReport(&JoystickInputData); 587 | // Once populated, we can output this data to the host. We do this by first writing the data to the control stream. 588 | while (Endpoint_Write_Stream_LE(&JoystickInputData, sizeof(JoystickInputData), NULL) != ENDPOINT_RWSTREAM_NoError); 589 | // We then send an IN packet on this endpoint. 590 | Endpoint_ClearIN(); 591 | } 592 | } 593 | 594 | 595 | void runScript(command CommandStep[], int CommandStepSize) { 596 | 597 | if (bufindex < CommandStepSize) { 598 | digitalWrite(PlayLed, HIGH); 599 | prossing = CommandStep[bufindex]; 600 | CommandPROCESS(&curentReport.report, prossing); 601 | duration_count++; 602 | if (duration_count > prossing.duration) 603 | { 604 | bufindex++; 605 | duration_count = 0; 606 | } 607 | return; 608 | } else if ( !boot) { 609 | boot = true; 610 | getDefaultReport(&last_report); 611 | getDefaultReport(&curentReport.report); 612 | } 613 | bufindex = 0; 614 | duration_count = 0; 615 | 616 | digitalWrite(PlayLed, LOW); 617 | getDefaultReport(&curentReport.report); 618 | state = BREATHE; 619 | } 620 | 621 | void prossesCommandSet(byte Buffer[]) { 622 | byte _sentencePos = 0; 623 | Serialstepcount = 0; 624 | SaveToEprom = false; 625 | while (1) 626 | { 627 | if (Buffer[_sentencePos] == NULL) { 628 | break; 629 | } 630 | if (Buffer[_sentencePos] == '!') { 631 | SaveToEprom = true; 632 | _sentencePos++; 633 | } 634 | if (Buffer[_sentencePos] == '$') 635 | { 636 | _sentencePos++; 637 | byte button = Buffer[_sentencePos]; 638 | prossing.button = button; 639 | _sentencePos++; 640 | byte duration = Buffer[_sentencePos]; 641 | prossing.duration = duration; 642 | Serialstep[Serialstepcount] = prossing; 643 | Serialstepcount++; 644 | _sentencePos++; 645 | } else { 646 | _sentencePos++; 647 | } 648 | } 649 | } 650 | 651 | void LoadEEPROM() { 652 | int CommandLen = EEPROM.read(EEPROMCommandStartPos); 653 | byte Buffer[CommandLen]; 654 | if (CommandLen > 0) { 655 | memset(Buffer, 0, sizeof(Buffer)); 656 | for (int i = 0; i < CommandLen; i++) { 657 | Buffer[i] = EEPROM.read(EEPROMCommandStartPos + i + 1); 658 | } 659 | prossesCommandSet(Buffer); 660 | if (Serialstepcount > 0) { 661 | //good 662 | } else { 663 | flashErrot(); 664 | } 665 | } else { 666 | flashErrot(); 667 | } 668 | delay(200); 669 | 670 | } 671 | 672 | bool slideSwitch() { 673 | return digitalRead(SwtichButtonPin); 674 | } 675 | 676 | bool clearButton() { 677 | if (digitalRead(ClearButtonPin) == 0) { 678 | return true; 679 | } 680 | return false; 681 | } 682 | 683 | 684 | -------------------------------------------------------------------------------- /program.h: -------------------------------------------------------------------------------- 1 | #ifndef _PROGRAM_H_ 2 | #define _PROGRAM_H_ 3 | 4 | 5 | #include "nintendo.h" 6 | 7 | 8 | 9 | 10 | typedef enum { 11 | X ='a', 12 | Y,//b 13 | A,//c 14 | B,//d 15 | L,//e 16 | R,//f 17 | MINUS,//g 18 | PLUS,//h 19 | UP,//i 20 | DOWN,//j 21 | LEFT,//k 22 | RIGHT,//l 23 | UP_RIGHT,//m 24 | UP_LEFT,//n 25 | DOWN_RIGHT,//o 26 | DOWN_LEFT,//p 27 | THROW,//q 28 | TRIGGERS,//r 29 | NOTHING,//s 30 | 31 | } Buttons_t; 32 | 33 | typedef struct { 34 | Buttons_t button; 35 | uint16_t duration; 36 | } command; 37 | 38 | 39 | 40 | static const command SetupStep[] = { 41 | // Setup controller 42 | { NOTHING, 100 }, 43 | { TRIGGERS, 5 }, 44 | { NOTHING, 50 }, 45 | { TRIGGERS, 5 }, 46 | { NOTHING, 50 }, 47 | { A, 10 }, 48 | { NOTHING, 50 }, 49 | }; 50 | 51 | static int SetupStepStepSize = (int)( sizeof(SetupStep) / sizeof(SetupStep[0])); 52 | 53 | 54 | //Frog Coine Farming 55 | static const command FrogCoinestep[] = { 56 | // Find Coin Froge 57 | { NOTHING, 10 }, 58 | { DOWN, 20 }, // Start Down the hall 59 | { NOTHING, 10 }, 60 | { R, 5 }, 61 | { NOTHING, 20 }, 62 | { UP, 110 }, 63 | { LEFT, 15 }, 64 | { NOTHING, 10 }, 65 | { UP, 180}, 66 | { LEFT, 10 }, 67 | // Capture Coin Froge 68 | { Y, 20 }, 69 | { NOTHING, 180 }, 70 | //Return to WayPoint 71 | { MINUS, 5 },//Eding 72 | { NOTHING, 40 }, 73 | { UP, 5 }, 74 | { NOTHING, 20 }, 75 | { A, 5 }, 76 | { NOTHING, 250 }, 77 | { NOTHING, 250 }, 78 | { NOTHING, 50 }, 79 | 80 | }; 81 | 82 | static int FrogCoinestepSize = (int)( sizeof(FrogCoinestep) / sizeof(FrogCoinestep[0])); 83 | #endif 84 | 85 | --------------------------------------------------------------------------------