├── BlueRetro_Latency_Testing ├── temp.txt └── README.md ├── platformio.ini ├── README.md ├── LightWing └── main.cpp ├── HeavyWing └── main.cpp └── main.cpp /BlueRetro_Latency_Testing/temp.txt: -------------------------------------------------------------------------------- 1 | test garbage 2 | -------------------------------------------------------------------------------- /platformio.ini: -------------------------------------------------------------------------------- 1 | ; PlatformIO Project Configuration File 2 | ; 3 | ; Build options: build flags, source filter 4 | ; Upload options: custom upload port, speed and extra flags 5 | ; Library options: dependencies, extra library storages 6 | ; Advanced options: extra scripting 7 | ; 8 | ; Please visit documentation for the other options and examples 9 | ; https://docs.platformio.org/page/projectconf.html 10 | 11 | [env:featheresp32] 12 | platform = espressif32 13 | board = featheresp32 14 | framework = arduino 15 | lib_deps = 16 | lemmingdev/ESP32-BLE-Gamepad@^0.5.1 17 | h2zero/NimBLE-Arduino@^1.4.0 18 | adafruit/Adafruit SSD1306@^2.5.4 19 | adafruit/Adafruit GFX Library@^1.11.2 20 | adafruit/Adafruit Unified Sensor@^1.1.5 21 | arduino-libraries/Bridge@^1.7.0 22 | adafruit/Adafruit MPU6050@^2.2.1 23 | monitor_speed = 115200 24 | monitor_filters = esp32_exception_decoder 25 | -------------------------------------------------------------------------------- /BlueRetro_Latency_Testing/README.md: -------------------------------------------------------------------------------- 1 | # This is currently under construction. 2 | 3 | # Forward (Rant) 4 | If you are chasing low latency controllers and have never tested your display lag, you are approaching this *completely backwards*. If you are trying to blame controller latency before video as the culprit for how poorly you play games, reconsider. LAG is cumulative from input to output as far as your perception is concerned. 5 | 6 | Some of the better projectors for example alone add 30-70ms of lag. It's not uncommon to see with a series of connected devices in path 40-60ms before hitting your "2ms" LCD. Even your LCD is unlikely to perform at advertised speeds outside of VERY specific configurations. Wrong setting, add 18+ms. That's omitting any upscaler, AV Reciver in the middle, media converters, hdmi switches, etc - each of which may have setting based penalties. 7 | 8 | [RetroRGB](https://www.retrorgb.com/) has done extensive testing and how-to videos on the subject. 9 | 10 | 11 | # Controller Latency Testing in General 12 | Before we dive into the BLEGamepad/BlueRetro stuff, it's useful to understand how this testing is done in the wild elsewhere. Consistency is important across numerous controllers and setups. The best place to start is with the MiSTER. 13 | 14 | ## What you need: 15 | - [x] A controller - This acts as the "Transmitter" for the keypress 16 | 17 | - [x] Arduino Pro Micro - This performs time based calculation for Press->Recieve 18 | 19 | - [x] Your reciver - This is the "Receiving" device 20 | - For Mister this is via a custom USB Cable 21 | - It's BlueRetro for Bluetooth Controllers 22 | 23 | ## Detailed guide on how latency testing is performed on the Mister Platform: 24 | https://www.cathoderayblog.com/lag-test-your-controller-mister-fpga-input-latency-tester/ 25 | 26 | I strongly advise taking a look over the content above as it will make everything from here much easier to understand. There are also a fair amount of videos floating around about this. Effectively you're using an arduino to "press" a button, and then another pin which will detect how fast that button is recieved on the other end. The arduino acts as a timer to count the time from when the press is sent to the time it is received and starts the next loop. After *10,000+* itterations, you have a good average input latency. 27 | 28 | ## The BIG Mister Controller Latency List: 29 | https://rpubs.com/misteraddons/inputlatency 30 | 31 | This is a list of controller input latency tests providing a useful comparison across a wide array of controllers as to what is "low latency", whats "Normal" and what is unbearably slow. 32 | 33 | 34 | # Latency Testing with [BlueRetro](https://github.com/darthcloud/BlueRetro) 35 | Darthcloud has included in BlueRetro a method to perform testing with the same arduino code that Mister uses. In brief you have to use the correct firmware, set the correct profile, and then use the right GPIO pin to connect to your arduino. You can read some more on this from his dev journal [here](https://hackaday.io/project/170365-blueretro/log/187443-2020-12-26-update-latency-tests-release-v010). 36 | 37 | There are some caveats you should be aware of I have spent *many* hours beating my head on a desk over. 38 | 39 | - [x] You **MUST** use the "parallel_1P_external.bin" when flashing firmware (Any firmware release version) 40 | - Console specific or embedded firmwares wont work 41 | - DO NOT connect your BR reciever to a console (Use **ONLY** external power) 42 | 43 | - [x] Before connecting your controller you **MUST** enable the "Latency Test" profile 44 | - If you fail to do this before connecting the controller you will have to power off the controller and reset blueretro or wait some time 45 | - Note: This often does not survive power down so confirm each time you need to power the BR adapter up again 46 | 47 | - [x] You **MUST** have access to GPIO26 on your BlueRetro testing device. 48 | - This could be risky if you only have console BR specific versions that require modification for output pin and the external power (eg. Pre-built N64 Adapter) 49 | 50 | - [x] You **MUST** have access to the PCB of your controller to solder on a wire for arduino to "press" your testing button 51 | - This typically requires modification to your controller PCB, like scraping away the coating to hit a copper trace. 52 | - This is also why my 3D Controller Lightwing PCBs have a dedicated pin on them to avoid damage. 53 | - Some controllers (including the Saturn 3D Pad) require a specific voltage (not just ground) so you may need to identify expected resistance 54 | 55 | 56 | # Latency Testing with [ESP32-BLE-Gamepad](https://github.com/lemmingDev/ESP32-BLE-Gamepad) 57 | ESP32-BLE-Gamepad does not contain any "specific" latency testing function. For testing you simply define a button that works when pulled low and to connect it to the correct controller facing arduino pin. In the code linked this pin should connect to arduino pin 5. 58 | - A lesson learned here is to avoid sending a Button ID/Number used elsewhere in code as it will self cancel in your initial loop not being detected **OR** make sure your code contains a check for **all** places that should use the pin and send the update accordingly. 59 | 60 | Defining a latency pin code example: 61 | ``` 62 | if (digitalRead(latPin) == LOW) { //when arduino pulls this pin low send button press 63 | bleGamepad.press(BUTTON_1); 64 | } else { 65 | bleGamepad.release(BUTTON_1); 66 | } 67 | ``` 68 | 69 | # Latency test your ESP32-BLE-Gamepad controller with BlueRetro 70 | I have used "BR" in place of BlueRetro below 71 | 72 | ### The Arduino Pro Micro 73 | 1. Flash your Arduino with the [latency test code](https://github.com/misteraddons/inputlatency/blob/main/arduino/MiSTer_USB_Latency_Test_Lemonici/MiSTer_USB_Latency_Test_Lemonici.ino) 74 | 75 | 2. Unplug your Arduino 76 | 77 | ### The Controller 78 | 3. Upload your ESP32-BLE-Gamepad code to your controller 79 | 80 | 4. Turn the controller OFF 81 | 82 | 5. Solder a wire to your controller pin to use for testing 83 | 84 | 6. Connect the wire soldered to the controller to Arduino Pin 5 85 | 86 | ### BlueRetro 87 | 7. [Flash the "parallel_1P_external.bin"](https://github.com/darthcloud/BlueRetro/wiki/BlueRetro-DIY-Build-Instructions) to your BlueRetro reciever and power cycle it 88 | 89 | 8. Connect GPIO26 (some boards may show as IO26) on BR to Pin 2 of the Arduino 90 | 91 | 9. Using **Chrome Browser** [connect to BR over bluetooth](https://hackaday.io/project/170365-blueretro/log/180020-web-bluetooth-ble-configuration-interface) and set the mapping profile to "Latency Test" and click save 92 | - Note: There is a "Test" profile in the second box - do not use this. 93 | 94 | - Example of the correct setting: 95 | ![the right way](https://user-images.githubusercontent.com/106001964/181081926-28c2eff5-fb45-421e-913e-e1e7c2fb68aa.jpg) 96 | 97 | ### Start Latency Testing 98 | 10. Connect to the serial console of your arduino com port (115200 - 8 n 1) 99 | - Failure to connect to serial will not allow the code to start running 100 | 101 | 11. Power on your controller 102 | 103 | 12. Check your Serial console to the arduino and confirm it is incrimenting. If so Allow 10k+ itterations to complete to get your latency data. 104 | Example of the blinking you should see when testing is working correctly. Note, I you will only see 1 blink (on arduino) vs my install. 105 | Seizure Warning: ![the right way](https://twitter.com/GamingNJncos/status/1548031893722972160) 106 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # BLE-3D # 2 | Sega Saturn 3D Controller Bluetooth(BLE) Adapter 3 | 4 | # **What does it do?** # 5 | - This is an open source and completely solderless adapter for the Sega Saturn 3D Controller that converts it to a Bluetooth(BLE) HID Gamepad. 6 | - The end result demonstrates a battery powered and solderless adapter that allows you to use one of the best controllers ever made to date across numerous consoles 7 | 8 | ### Highlights ### 9 | - **Zero modifications to the factory controller** are neccesary to build, test, or play with this adapter. 10 | - Both Analog, and Digital mode (switch on controller) are fully supported 11 | - Want to revert back to OE? Simply plug the OE cable back in. 12 | - Latency testing pin on-pcb offers consistent and reproducible testing across code changes and development 13 | 14 | ### Lowlights ### 15 | - This isn't a "retail ready product", it's a proof of concept 16 | - No case for this has been published yet (bare pcb) 17 | - Power circuit has a couple issues. More details in Caveats section 18 | 19 | 20 | # **Compatibility** # 21 | This is an area I'm extremely proud of as of writing the BLE3D is known to work with: 22 | - Windows, Linux, and Steamdeck 23 | - ALL supported [Blueretro](https://github.com/darthcloud/BlueRetro) consoles 24 | - That means the 3D is now available on (in no particular order) 25 | - NeoGeo, Supergun, JAMMA 26 | - Atari 2600/7800, Master System 27 | - NES, PCE / TG16, Mega Drive / Genesis, SNES 28 | - CD-i, 3DO, Jaguar, Saturn, PSX, PC-FX, 29 | - JVS (Arcade), Virtual Boy, N64, Dreamcast, PS2, GameCube & Wii 30 | - I'm sure I'm missing a handful 31 | 32 | - Note: the analog pad/triggers support are per console, there is no 'digital emulation' on the analog stick. 33 | 34 | 35 | # **Background** # 36 | For years I've dreamed of a wireless Sega Saturn 3d Pad, just like the patents intended. 37 | 38 | - [US Patent Number US 7488,254 B2](https://patentimages.storage.googleapis.com/a2/97/47/e77a8c63165461/US7488254.pdf) 39 | - [US Patent Number Des. 409, 149](https://patentimages.storage.googleapis.com/3d/96/ad/edd675699738cf/USD409149.pdf) 40 | - [European Patent Number EP 1 332 778 B1](https://patentimages.storage.googleapis.com/ed/ba/17/c22422d5d3cc47/EP1332778B1.pdf) 41 | ![An inspiration image I have been using in development](https://i.imgur.com/Myag1Ka.png) 42 | 43 | Fast forward many years, I stumbled across some work [Hexfreq](https://twitter.com/hexfreq) was doing with arduino and asked if I could take a look at the code. The rest is history. 44 | From there [Humble Bazooka](https://twitter.com/humblebazooka) and I have spent countless hours to make sure this was a reality. I'd be remissed to leave out [darthcloud](https://twitter.com/darthcloud64) has also been an immense help along the way. 45 | 46 | # **PCBs** # 47 | - There are 2 publicly available DIY pcb's for "dev kits". 48 | - There are various issues or design misses with both pcbs 49 | - They aren't intended for retail use, but you can build one yourself **today**. 50 | - Beveling the controller facing pins is advised 51 | 52 | ## **LightWing** ## 53 | - This is a 'new' revision of the BLE-Saturn-3D that focuses on core functionality. This was optimized as the low cost option for home builders. At current, this is the advised version to use until development can progress farther on the extra features of the HeavyWing 54 | - PCB: https://oshpark.com/shared_projects/7RqwDWJT 55 |

56 | 57 | ## **HeavyWing** ## 58 | - The original PCB. Take the above patents put them in a blender. It adds LCD Support, Motion Controls & Rumble. You can use the LightWing code (better performance) on a Heavywing PCB but will need to change some pin mappings. 59 | - PCB: https://oshpark.com/shared_projects/ki7HbZV4 60 |


61 | 62 | # **BOM** # 63 | ## LightWing ### 64 | - 1x [LightWing PCB](https://oshpark.com/shared_projects/7RqwDWJT) 65 | - 1x [Adafruit Feather Huzzah32](https://www.adafruit.com/product/3405) 66 | - 1x Battery 67 |

68 | 69 | ## HeavyWing ## 70 | - 1x [HeavyWing PCB](https://oshpark.com/shared_projects/ki7HbZV4) 71 | - 1x [Adafruit Feather Huzzah32](https://www.adafruit.com/product/3405) 72 | - 1x [HiLetgo GY-521 AKA MPU-6050 Tilt Sensor](http://hiletgo.com/ProductDetail/2157948.html)* (optional - while it does "work" the kalman filter needs some rework. Must support 3.3v, some are 5v only) 73 | - 1x OLED 128X64 SSD1306 (optional) 74 | - 1x 3.3v Rumble Motor* (optional - this circuit is not populated on the PCB currently) 75 | - 1x Battery 76 |

77 | 78 | 79 | # **Assembly** # 80 | - Assembly is straightforward, it's just a PCB sandwhich and some solder 81 | - Orientation might be counter intuitive 82 | 83 | ### **LightWing - Assembly** ### 84 | - When you build this lay Segata Face-Down, then set the Feather through the holes with the ESP, USB, and Battery plug facing you 85 | - This provies some additional space for the battery directly under the PCB which would closet match the original patent docs visually 86 |

87 | 88 | ### **LightWing - Installation** ### 89 | - Sega Logo and Button Face top 90 | - Segata faces triggers or back of controller 91 |


92 | 93 | ### **HeavyWing - Assembly** ### 94 | - *Pictures to be uploaded soon* 95 | 96 |

97 | # **Before you compile the code** # 98 | - [PlatformIO installed in VScode](https://docs.platformio.org/en/stable/tutorials/espressif32/arduino_debugging_unit_testing.html), the [platformio.ini](https://github.com/GamingNJncos/BLE-3D-Saturn-Public/blob/main/platformio.ini), and the main.cpp for the Lightwing or the Heavywing 99 | - This has not been tested with Arduino IDE extensively but should compile fine however extremely slow 100 | - If you are mixing and matching LightWing code with the HeavyWing PCB, make sure you change the pinmapping. 101 | - This is well documented in the code if you jump to "Pinmappings for your PCB version" 102 | 103 | ## Powering the Device ## 104 | This is direct from the [Adafruit Documentation](https://learn.adafruit.com/adafruit-huzzah32-esp32-feather/power-management) 105 | - There's two ways to power a Feather: 106 | - **You can connect with a USB cable** (just plug into the jack) and the Feather will regulate the 5V USB down to 3.3V. 107 | - **You can also connect a 4.2/3.7V Lithium Polymer** (LiPo/LiPoly) or Lithium Ion (LiIon) battery to the JST jack. This will let the Feather run on a rechargeable battery. 108 | - **When the USB power is powered, it will automatically switch over to USB for power**, as well as start charging the battery (if attached). 109 | - **This happens 'hot-swap' style** so you can always keep the LiPoly connected as a 'backup' power that will only get used when USB power is lost. 110 | 111 |

112 | # **Caveats and Gotchas** # 113 | ## A Note on the Huzzah32 revisions ## 114 | - Check your WROOM version before flashing! 115 | - There is a NEW revision of the huzzah32 shipping with the Wroom-32-**E** that will have some problems initially when plugged into the controller 116 | - This is a result of changes to pin IO12 between the D and E modules (and a bootstrap pin) 117 | - PCB's were made based on result on the Wroom-32-**D** module and have no issues "out of the box" 118 | 119 | - On Wroom-E when the pcb is plugged into the controller at boot similar to boot:0x33 (SPI_FAST_FLASH_BOOT) **invalid header: 0xffffffff** 120 | - You can burn an efuse to work around this with the espefuse tool. Note this is irreversible, if you are uncomfortable with this try to find another vendor with the 32D in stock - How to burn the efuse: `espefuse.py --port [com port] --baud 115200 set_flash_voltage 3.3V` confirm the fuse status with `espefuse -p [com port] --baud 115200 summary`. At the bottom it will say **Flash voltage (VDD_SDIO) set to 3.3V by efuse.** 121 | - At the bottom it will say **Flash voltage (VDD_SDIO) set to 3.3V by efuse.** 122 | 123 | ## Battery Warning ## 124 | - It is *extremely* important that you check the polarity on your battery jack is oriented correctly. There is 0 polarity protection. The charging circuit WILL fry if you plug it in and it's backwards. 125 | 126 | ## Powering Down ## 127 | - Due to the design of the adafruit feather (esp32) there is no formal power switch. 128 | - You must either solder a physical switch on to the battery, **or** unplug it. This is not always easy with the JST connector used so be careful. 129 | 130 | ## Charging ## 131 | - Another unfortunate aspect of the power circuit design on the feather is that the device must be powered on to charge. 132 | - There is no "charging mode" so anytime the controller needs to charge it will be broadcasting. 133 | - Deep sleep is the best solution for this long term but there are reassons it's not included just yet. 134 | 135 | ## Missing Trigger ## 136 | - In windows if you go to look at the button inputs in joy.cpl the right trigger is not displayed. 137 | - The trigger is there, but you have to use another pad tester to "see it". 138 | - The picture below *is the expected behavior* and appears to be a problem with joy.cpl in particular. 139 |


140 | 141 | 142 | ## Various Demos and feature teasers ## 143 | - [Taking Nights for a spin over Bluetooth](https://twitter.com/GamingNJncos/status/1537364206881755136) 144 | - [LCD and customizable Boot Logo](https://twitter.com/GamingNJncos/status/1532819934551724033) 145 | - [Pad Test Demo in X-Men COTA](https://twitter.com/GamingNJncos/status/1537362477817765889) 146 | - [Steamdek Test](https://twitter.com/GamingNJncos/status/1540845741710745602) 147 | - [Early External Unit Testing](https://twitter.com/GamingNJncos/status/1404144038693986307) 148 | 149 | ## Latency Testing ## 150 | - A significant amount of consideration went into making it easy to test code changes and the impact on latency 151 | - The Lightwing PCB has a dedicated solder pad for consistent testing methodology and code incorporates easy to toggle enable/disable for the pin 152 | - Documentation on the process is available [here](https://github.com/GamingNJncos/BLE-3D-Saturn-Public/tree/main/BlueRetro_Latency_Testing) 153 | 154 | ## **Note on the Boot Logos (Supported on HeavyWing PCB)** ## 155 | This is documented in the code however if you want to convert images [this tool](https://lcd-image-converter.riuson.com/en/about/) is really useful 156 |

157 | 158 | 159 | ## Whats with the Names? ## 160 | - Feather add-ons or expansions (hat equivalent for raspberry pi) are called "Wings". 161 | - Combine that with some Sega nerd-lore and you the [Heavy Wing](https://panzerdragoon.fandom.com/wiki/Heavy_Wing) and [Light Wing](https://panzerdragoon.fandom.com/wiki/Light_Wing) 162 | 163 |

164 | 165 | # **Strange notes and pedantic details** # 166 | ## Protocol Details and Resources ## 167 | - [A great overview across numerous sega consoles](https://hackaday.io/project/170365-blueretro/log/180790-evolution-of-segas-io-interface-from-sg-1000-to-saturn) by [darthcloud](https://twitter.com/darthcloud64) 168 | - [Additional protocol details for the Saturn](http://forums.modretro.com/index.php?threads/saturn-controller-protocol-mk80116-mk80100.11328/) 169 | 170 | ## Saturn 3D supported Game list, patent details, and more ## 171 | https://segaretro.org/3D_Control_Pad 172 | 173 | ## Polling Oddity or Opportunity? ## 174 | The [Saturn only polls the 3D protocol at 16ms intervals](https://nfggames.com/forum2/index.php?topic=5055.0) (screen blank). There is however some suggestion in the below post that the Action Replay (this is firmware version specific) can attempt polling faster. It's worth taking a look at as it may be possible to force games into a faster mode with patching. 175 | 176 | Qoute: 177 | "For whatever reason, it's only polling the controller for 14.25ms, with around 310us pauses in between, so it's polling much quicker, but the real issue is after this 14.25ms is up, it then drives the SEL line Hi and goes about it's business for ~2.5ms after that, then back to polling." 178 | 179 | ## Other demos and informational links ## 180 | - [A DIY internal BlueRetro receiver for Saturn](https://twitter.com/nosIndulgences/status/1573719805496299520) 181 | - [Hexfreq showing off a completely differnt bluetooth saturn solution](https://twitter.com/hexfreq/status/1468282054978818062) 182 | 183 | ## **Libraries in use** ## 184 | - [ESP32-BLE-Gamepad](https://github.com/lemmingDev/ESP32-BLE-Gamepad) 185 | - This is subject to change long term as new and improved libraries emerge to support BLE HID gamepads 186 | 187 | - [NimBLE-Arduino](https://github.com/h2zero/NimBLE-Arduino) 188 | 189 | ## **Details on the Feather** ## 190 | - If you want to remix the PCB's or understand more about the feather itself check out the following 191 | - [Feather Pinouts and hardware overview](https://learn.adafruit.com/adafruit-huzzah32-esp32-feather/pinouts) 192 | -------------------------------------------------------------------------------- /LightWing/main.cpp: -------------------------------------------------------------------------------- 1 | // Add BLE Gamepad 2 | #include 3 | // Add Sleep 4 | //#include "esp_sleep.h" 5 | 6 | /* 7 | .____ .__ .__ __ __ __.__ 8 | | | |__| ____ | |___/ |_/ \ / \__| ____ ____ 9 | | | | |/ __ \| | \ __\ \/\/ / |/ \ / __ \ 10 | | |___| / /_/ | Y \ | \ /| | | \/ /_/ > 11 | |_______ \ \___ /|___| /__| \__/\ / | |___| /\___ / 12 | \_____\/_____/ \/ \/___\/_____\/______/ 13 | 14 | Brought to you by GaminNJncos, Humble Bazooka & Hexfreq 15 | All bugs, poor code choices, and existing PCB designs courtesy of [at]GamingNJncos 16 | */ 17 | 18 | /* About this fork 19 | Goal of this fork is to focus on core functionality with the lowest latency 20 | This fork has no support for the Heavywing features including rumble, tilt, or LCD 21 | 22 | As of writing there is no deep sleep support which means you have to unplug the battery on the feather (esp32) 23 | to turn it off, or install a switch on your battery. There is an additional caveat that for charging, the 24 | feather must be turned on. This is a result of the feather and it's charging circuit and not intended as a long 25 | term solution 26 | */ 27 | 28 | // BLEGamePad Settings 29 | // =================== 30 | // Values are Advertised Name, Manufacturer, Battery level 31 | // There is a char limit on these 32 | BleGamepad bleGamepad("LightWing", "GnJ & Humble Bazooka", 100); 33 | // Purposely set high to permit ease of future additional button combinationsfor to send Meta, home button, etc 34 | #define numOfButtons 16 35 | #define numOfHatSwitches 1 // DPAD (not Analog pad) 36 | #define enableX true // Analog Pad 37 | #define enableY true // Analog Pad 38 | #define enableAccelerator true // RT 39 | #define enableBrake true // LT 40 | // Additional button options currently disabled 41 | #define enableZ false 42 | #define enableRZ false 43 | #define enableSlider1 false 44 | #define enableSlider2 false 45 | #define enableRudder false 46 | #define enableThrottle false 47 | #define enableRX false 48 | #define enableRY false 49 | #define enableSteering false 50 | // End BLEGamePad Settings 51 | 52 | // Button State Tracking 53 | // This nets a significant advantage in performance vs not tracking states 54 | // BLE expects ACK per packet so flooding "not pressed" or identical data can add extreme delays 55 | // 56 | byte previousButtonStates[numOfButtons]; 57 | byte currentButtonStates[numOfButtons]; 58 | // Button State Array Map 59 | // The below numeric values are the data returned from poll controller 60 | // Some are binary 0 or 1, while others are a range as read from the 3d controller protocol 61 | // 62 | // -------------------------------------------------------------------- 63 | // MODE, DPAD, START, A, B, C, X, Y, Z, L, R, Analog PAD 64 | // 65 | // -------------------------------------------------------------------- 66 | // Note Left and Right stick in digital mode will return Binary [0-1] vs [0-15] value in analog mode 67 | // In Digital mode no values are returned from the Analog Stick 68 | 69 | // BLE Gamepad index Mapping 70 | // These are the values for each individual button as they relate to mappings in BLE Gamepad library 71 | // This table might be out of date? 72 | 73 | // PAD BLE Index 74 | // --------------------- 75 | 76 | // Mode 0 77 | // DPAD 1 78 | // START 2 79 | // A 3 80 | // B 4 81 | // C 5 82 | // X 6 83 | // Y 7 84 | // Z 8 85 | // LT 9 86 | // RT 10 87 | 88 | 89 | // Pinmappings for your PCB version 90 | // ================================ 91 | // It is manditory you select the correct pinout for your board type 92 | // Failure to do so will cause Segata to light your 3d controller on fire and beat your loved ones 93 | 94 | // Feather LightWing to Controller Pin Mapping 95 | // =========================================== 96 | // PCB: https://oshpark.com/shared_projects/7RqwDWJT 97 | // 98 | int dataPinD0 = 12; // Arthrimus Pin 3 = Data D0 99 | int dataPinD1 = 13; // Arthrimus Pin 2 = Data D1 100 | int dataPinD2 = 14; // Arthrimus Pin 8 = Data D2 101 | int dataPinD3 = 32; // Arthrimus Pin 7 = Data D3 102 | int THPin = 27; // Arthrimus Pin 4 = TH Select 103 | int TRPin = 33; // Arthrimus Pin 5 = TR Request 104 | int TLPin = 15; // Arthrimus Pin 6 = TL Response 105 | int latPin = 4; // Latency testing pin 106 | // End Feather HeavyWing to Controller Pin Mapping 107 | // End all Pin Mappings 108 | 109 | // BleGamepad Variables 110 | // ==================== 111 | int16_t bleAnalogX; // Analog pad X Axis Output 112 | int16_t bleAnalogY; // Analog pad Y Axis Output 113 | // DeadZone defined as smallest value to exceed before sending in blegamepad report function 114 | int16_t bleDeadZone = 400; // Set Deadzone via minimum number 115 | // Analog Triggers 116 | int16_t bleRTrigger; // Right Trigger Analog Output 117 | int16_t bleLTrigger; // Left Trigger Analog Output 118 | // End BleGamepad Variables 119 | 120 | // Controller protocol and timing variables 121 | // ======================================== 122 | // Data Array for each byte from protocol 123 | boolean dataArrayBit0[8]; 124 | boolean dataArrayBit1[8]; 125 | boolean dataArrayBit2[8]; 126 | boolean dataArrayBit3[8]; 127 | boolean dataArrayBit4[8]; 128 | boolean dataArrayBit5[8]; 129 | boolean dataArrayBit6[8]; 130 | boolean dataArrayBit7[8]; 131 | // Assorted Protocol Variables 132 | int nibble0Read = 0; // Confirm 1st Nibble has been read 133 | int byteCounter = 0; // Current Byte Tracker 134 | int modeCounter = 4; // Reference for which mode we are currently in Analogue or Digital 135 | int DecodeData = 1; // Controller Successfully Decoded Data Variable 136 | int curMode = 0; // This stores the Controller Mode to pass between ESP cores 137 | // Assorted timing oriented variables 138 | unsigned long timeStamp = 0; // Store time of last update 139 | unsigned long bleTimer = 0; 140 | unsigned long lastTimeStamp = 0; // Store Last time for comparison 141 | const long interval = 1000; // Data update check timing 142 | unsigned long currentMillis = millis(); // check timing 143 | // Directional Variables 144 | int upDownValue = 0; 145 | int leftRightValue = 0; 146 | // Trigger Variables 147 | int ltValue = 0; 148 | int rtValue = 0; 149 | // Variables for Data send request and Acknowledgments 150 | int THSEL = 0; 151 | int TRREQ = 0; 152 | int TLACK = 0; 153 | // Sets the number of Bytes required for Digital or Analogue Modes 154 | int AnalogMode = 6; 155 | int DigitalMode = 3; 156 | // End Controller protocol and timing variables 157 | 158 | // Start CPU variables 159 | // =================== 160 | // These variables are used for CPU Speed settings and tasks 161 | uint32_t Freq = 0; //Used for getCPU debug 162 | //CPU MHz // 163 | int CpuMhz = 40; //Change to set CPU Freq 164 | // End CPU variables 165 | 166 | //int firstBoot = 0; //First Boot check 167 | //-----------------Deep Sleep test--------------- 168 | //int firstBoot = 0; //First Boot check //Disabled to test firstboot check in RTC 169 | // This doesn't need to currently be stored in RTC but is helpful to call troubleshooting routines only on power up 170 | RTC_DATA_ATTR int firstBoot = 0; //First Boot check 171 | 172 | // Allocate the interrupt. 173 | ///esp_err_t err = esp_intr_alloc(ETS_INTR_DEFAULT, my_interrupt_handler, NULL, 0); 174 | //if (err != ESP_OK) { 175 | // Handle the error. 176 | //} 177 | 178 | //-----------------Deep Sleep test--------------- 179 | 180 | // End CPU variables 181 | // =================== 182 | 183 | 184 | // RTC deep sleep time 185 | // 186 | // This variable defines the length of time to sleep prior to checking for button press 187 | // Intended design is that when in deep sleep a user must hold down a button on the controller 188 | // For longer than the configured deep sleep to wake back up 189 | // 190 | // Current example is 10 seconds 191 | #define uS_TO_S_FACTOR 1000000ULL /* Conversion factor for micro seconds to seconds */ 192 | #define TIME_TO_SLEEP 30 /* Time ESP32 wi go to sleep (in seconds) */ 193 | 194 | 195 | // Enter Deep Sleep 196 | void enterDeepSleep() 197 | { 198 | Serial.println("GOING TO DEEP SLEEP ZZZzz..."); 199 | // Disable the Bluetooth receiver 200 | //esp_bt_controller_disable(); 201 | // Set the Bluetooth controller to power-off mode. 202 | //esp_bt_controller_set_power_mode(ESP_BT_POWER_MODE_OFF, ESP_PD_OPTION_OFF) 203 | 204 | // Set the Bluetooth host stack to power-off mode. 205 | btStop(); 206 | 207 | // Delete all of the FreeRTOS tasks 208 | //vTaskDelete(NULL); Needed when core pinning is used with RTOS 209 | 210 | 211 | delay(10); 212 | // Go to deep sleep 213 | esp_deep_sleep_start(); 214 | } 215 | // End CPU variables 216 | // =================== 217 | 218 | 219 | // Controller related items 220 | // ======================== 221 | void pollController() 222 | { 223 | //bleGamepad.setRightThumb(0,0); 224 | // Check ESP Core Controller Polling Occurs on 225 | // Serial.print("Controller Polling Core "); 226 | // Serial.println(xPortGetCoreID()); 227 | 228 | // Start The Data Read 229 | // Is the Controller ready to send the data? 230 | TLACK = digitalRead(TLPin); 231 | 232 | // Have we read all the bytes needed for each mode? 233 | if (byteCounter > modeCounter) 234 | { 235 | // Set Not ready for Data Coms 236 | digitalWrite(THPin, HIGH); 237 | digitalWrite(TRPin, HIGH); 238 | 239 | // Are we using Analogue Mode or Digital? 240 | if (modeCounter == AnalogMode) 241 | { 242 | curMode = 0; 243 | currentButtonStates[0] = 0; 244 | } 245 | else 246 | { 247 | curMode = 1; 248 | currentButtonStates[0] = 1; 249 | } 250 | 251 | // Decoded Controller Data Successfully 252 | // Proceed based on Analog or Digital Mode 253 | // 254 | // Note bleGamepad.isConnected()isconnected check should always happen before gamepadsend 255 | 256 | // This avoids a bug in NIMBLE where blegamepad could under some circumstances attempt button send prior to connecting and cause abort 257 | 258 | if (DecodeData == 1 && bleGamepad.isConnected()) 259 | 260 | { 261 | // Analog 262 | if (modeCounter == AnalogMode) 263 | { 264 | 265 | // Analog Mode - Process Input 266 | // Begin all Analog Mode data processing 267 | 268 | // DPAD 269 | if (dataArrayBit6[1] != 0 && dataArrayBit7[1] != 0 && dataArrayBit5[1] != 0 && dataArrayBit4[1] != 0) 270 | { 271 | bleGamepad.setHat1(DPAD_CENTERED); 272 | currentButtonStates[1] = 0; 273 | } 274 | if (dataArrayBit4[1] == 0 && (dataArrayBit7[1] != 0 && dataArrayBit6[1] != 0)) 275 | { 276 | // Serial.print(" U"); 277 | bleGamepad.setHat1(DPAD_UP); 278 | currentButtonStates[1] = 1; 279 | } 280 | if (dataArrayBit5[1] == 0 && (dataArrayBit7[1] != 0 && dataArrayBit6[1] != 0)) 281 | { 282 | // Serial.print(" D"); 283 | bleGamepad.setHat1(DPAD_DOWN); 284 | currentButtonStates[1] = 2; 285 | } 286 | if (dataArrayBit6[1] == 0 && (dataArrayBit4[1] != 0 && dataArrayBit5[1] != 0)) 287 | { 288 | // Serial.print(" L"); 289 | bleGamepad.setHat1(DPAD_LEFT); 290 | currentButtonStates[1] = 3; 291 | } 292 | if (dataArrayBit7[1] == 0 && (dataArrayBit4[1] != 0 && dataArrayBit5[1] != 0)) 293 | { 294 | // Serial.print(" R"); 295 | bleGamepad.setHat1(DPAD_RIGHT); 296 | currentButtonStates[1] = 4; 297 | } 298 | if (dataArrayBit4[1] == 0 && dataArrayBit6[1] == 0) 299 | { 300 | // Serial.print(" U/L"); 301 | bleGamepad.setHat1(DPAD_UP_LEFT); 302 | currentButtonStates[1] = 5; 303 | } 304 | if (dataArrayBit4[1] == 0 && dataArrayBit7[1] == 0) 305 | { 306 | // Serial.print(" U/R"); 307 | bleGamepad.setHat1(DPAD_UP_RIGHT); 308 | currentButtonStates[1] = 6; 309 | } 310 | if (dataArrayBit5[1] == 0 && dataArrayBit6[1] == 0) 311 | { 312 | // Serial.print(" D/L"); 313 | bleGamepad.setHat1(DPAD_DOWN_LEFT); 314 | currentButtonStates[1] = 7; 315 | } 316 | if (dataArrayBit5[1] == 0 && dataArrayBit7[1] == 0) 317 | { 318 | // Serial.print(" D/R"); 319 | bleGamepad.setHat1(DPAD_DOWN_RIGHT); 320 | currentButtonStates[1] = 8; 321 | } 322 | // End DPAD 323 | 324 | // Buttons 325 | if (dataArrayBit3[2] == 0) 326 | { 327 | // Serial.print(" Start "); 328 | bleGamepad.press(BUTTON_12); 329 | currentButtonStates[2] = 1; 330 | } 331 | else 332 | { 333 | bleGamepad.release(BUTTON_12); 334 | currentButtonStates[2] = 0; 335 | } 336 | if (dataArrayBit2[2] == 0) 337 | { 338 | // Serial.print(" A"); 339 | bleGamepad.press(BUTTON_4); 340 | currentButtonStates[3] = 1; 341 | } 342 | else 343 | { 344 | // Serial.print(" "); 345 | bleGamepad.release(BUTTON_4); 346 | currentButtonStates[3] = 0; 347 | } 348 | if (dataArrayBit0[2] == 0) 349 | { 350 | // Serial.print(" B"); 351 | bleGamepad.press(BUTTON_1); 352 | currentButtonStates[4] = 1; 353 | } 354 | else 355 | { 356 | // Serial.print(" "); 357 | bleGamepad.release(BUTTON_1); 358 | currentButtonStates[4] = 0; 359 | } 360 | if (dataArrayBit1[2] == 0) 361 | { 362 | // Serial.print(" C"); 363 | bleGamepad.press(BUTTON_2); 364 | currentButtonStates[5] = 1; 365 | } 366 | else 367 | { 368 | // Serial.print(" "); 369 | bleGamepad.release(BUTTON_2); 370 | currentButtonStates[5] = 0; 371 | } 372 | if (dataArrayBit6[2] == 0) 373 | { 374 | // Serial.print(" X"); 375 | bleGamepad.press(BUTTON_7); 376 | currentButtonStates[6] = 1; 377 | } 378 | else 379 | { 380 | // Serial.print(" "); 381 | bleGamepad.release(BUTTON_7); 382 | currentButtonStates[6] = 0; 383 | } 384 | if (dataArrayBit5[2] == 0) 385 | { 386 | // Serial.print(" Y"); 387 | bleGamepad.press(BUTTON_5); 388 | currentButtonStates[7] = 1; 389 | } 390 | else 391 | { 392 | // Serial.print(" "); 393 | bleGamepad.release(BUTTON_5); 394 | currentButtonStates[7] = 0; 395 | } 396 | if (dataArrayBit4[2] == 0) 397 | { 398 | // Serial.print(" Z"); 399 | bleGamepad.press(BUTTON_8); 400 | currentButtonStates[8] = 1; 401 | } 402 | else 403 | { 404 | // Serial.print(" "); 405 | bleGamepad.release(BUTTON_8); 406 | currentButtonStates[8] = 0; 407 | } 408 | // These can send the trigger presses as a button vs an axis 409 | 410 | /* 411 | if (dataArrayBit3[3] == 0) { 412 | Serial.print(" LTD "); 413 | bleGamepad.press(BUTTON_9); 414 | currentButtonStates[13] = 1; //This cant overlap with LTA state index 415 | } else { 416 | bleGamepad.release(BUTTON_9); 417 | currentButtonStates[13] = 0; //This cant overlap with LTA state index 418 | } 419 | 420 | 421 | // if (dataArrayBit7[2] == 0) { 422 | if (dataArrayBit7[2] == 0) { 423 | Serial.print(" RTD "); 424 | bleGamepad.press(BUTTON_13); 425 | currentButtonStates[14] = 1; //This cant overlap with RTA state index 426 | } else { 427 | bleGamepad.release(BUTTON_13); 428 | currentButtonStates[14] = 0; //This cant overlap with RTA state index 429 | } 430 | 431 | */ 432 | 433 | 434 | // Latency Test Pin 435 | // Intended for BlueRetro latency button 436 | // Can be used for other additional buttons 437 | // 438 | // Note this sends "Start" but can be adjusted to press any other button 439 | 440 | /* 441 | if (digitalRead(latPin) == LOW) 442 | { 443 | // Serial.print("LAT"); 444 | bleGamepad.press(BUTTON_3); 445 | currentButtonStates[3] = 1; 446 | } 447 | else 448 | { 449 | bleGamepad.release(BUTTON_3); 450 | currentButtonStates[3] = 0; 451 | } 452 | 453 | */ 454 | // Up / Down 455 | if (dataArrayBit7[4] == 1) 456 | { 457 | upDownValue = dataArrayBit0[5] + (dataArrayBit1[5] << 1) + (dataArrayBit2[5] << 2) + (dataArrayBit3[5] << 3) + (dataArrayBit4[4] << 4) + (dataArrayBit5[4] << 5) + (dataArrayBit6[4] << 6); 458 | 459 | 460 | bleAnalogX = map(upDownValue, 0, 127, 16384, 32767); 461 | 462 | 463 | } 464 | else 465 | { 466 | upDownValue = !dataArrayBit0[5] + (!dataArrayBit1[5] << 1) + (!dataArrayBit2[5] << 2) + (!dataArrayBit3[5] << 3) + (!dataArrayBit4[4] << 4) + (!dataArrayBit5[4] << 5) + (!dataArrayBit6[4] << 6); 467 | 468 | 469 | bleAnalogX = map(upDownValue, 0, 127, 16383, 1); 470 | 471 | 472 | } 473 | // Deadzone fix - X Axis 474 | // Value is checked based on map output 475 | if ((bleAnalogX < 0 && bleAnalogX > -600)||(bleAnalogX < 0 && bleAnalogX > -600)){ 476 | bleAnalogX = 0; 477 | } 478 | 479 | // Analog Debugs 480 | // Serial.print(" bleAnalogX "); 481 | // Serial.println(bleAnalogX); 482 | // Format Value to 3 Chars long 483 | // char buf[] = "000"; 484 | // sprintf(buf, "%03i", upDownValue); 485 | 486 | // Left / Right 487 | if (dataArrayBit7[3] == 1) 488 | { 489 | // Serial.print(" R = "); 490 | leftRightValue = dataArrayBit0[4] + (dataArrayBit1[4] << 1) + (dataArrayBit2[4] << 2) + (dataArrayBit3[4] << 3) + (dataArrayBit4[3] << 4) + (dataArrayBit5[3] << 5) + (dataArrayBit6[3] << 6); 491 | 492 | 493 | bleAnalogY = map(leftRightValue, 0, 127, 16384, 32767); 494 | 495 | 496 | } 497 | else 498 | { 499 | leftRightValue = !dataArrayBit0[4] + (!dataArrayBit1[4] << 1) + (!dataArrayBit2[4] << 2) + (!dataArrayBit3[4] << 3) + (!dataArrayBit4[3] << 4) + (!dataArrayBit5[3] << 5) + (!dataArrayBit6[3] << 6); 500 | 501 | 502 | bleAnalogY = map(leftRightValue, 0, 127, 16383, 1); 503 | 504 | 505 | } 506 | 507 | // Deadzone fix - Y Axis 508 | // Value is checked based on map output 509 | if ((bleAnalogY > 0 && bleAnalogY < 600)||(bleAnalogY < 0 && bleAnalogY > -600)){ 510 | bleAnalogY = 0; 511 | } 512 | // Set Analog Pad/Left Thumb Stick X Y Values in Blegamepad 513 | // This is an easy place to flip the X and Y results if backwards for your use case 514 | bleGamepad.setX(bleAnalogY); 515 | bleGamepad.setY(bleAnalogX); 516 | // Track State of Analog Sticks X and Y Values 517 | currentButtonStates[11] = bleAnalogX; 518 | currentButtonStates[12] = bleAnalogY; 519 | // unused at this time 520 | // currentButtonStates[13] = 0; 521 | // currentButtonStates[14] = 0; 522 | 523 | // Decode Triggers 524 | char trbuf[] = "00"; 525 | // Left Trigger 526 | ltValue = (dataArrayBit7[6] * 8) + (dataArrayBit6[6] * 4) + (dataArrayBit5[6] * 2) + (dataArrayBit4[6] * 1); 527 | bleLTrigger = map(ltValue, 0, 15, 0, 32767); 528 | // Example center fixes if axis issue occur 529 | //if (bleLTrigger == 0){bleLTrigger = 16384;} //center fix 530 | //bleLTrigger = map(ltValue, 0, 15, 0, 255); 531 | //if (bleLTrigger == 0){bleLTrigger = 128;} 532 | // bleGamepad.setLeftTrigger(bleLTrigger); 533 | 534 | bleGamepad.setBrake(bleLTrigger); 535 | // Track State of L trigger 536 | currentButtonStates[9] = bleLTrigger; 537 | 538 | // Debug contents of LTA 539 | //if (bleLTrigger != 128){Serial.print(" LTA = ");Serial.println(bleLTrigger);} 540 | 541 | //sprintf(trbuf, "%02i", ltValue); 542 | // Serial.print(" LT = "); 543 | // Serial.print(trbuf); 544 | // Serial.println(bleLTrigger); 545 | 546 | 547 | // Right Trigger 548 | rtValue = (dataArrayBit7[5] * 8) + (dataArrayBit6[5] * 4) + (dataArrayBit5[5] * 2) + (dataArrayBit4[5] * 1); 549 | 550 | bleRTrigger = map(rtValue, 0, 15, 0, 32767); //Map if 0-32K is range 551 | // Example center fixes if axis issue occur 552 | 553 | 554 | //if (bleRTrigger == 0){bleRTrigger = 16384;} //<--Center Fix for 0-32k Range 555 | //bleRTrigger = map(rtValue, 0, 15, 0, 255); //<-- Map if 0-255 Range 556 | //if (bleRTrigger == 0){bleRTrigger = 128;} // <--Center Fix for 0-255 Range 557 | 558 | 559 | bleGamepad.setAccelerator(bleRTrigger); 560 | // Track State of R trigger 561 | currentButtonStates[10] = bleRTrigger; 562 | // sprintf(trbuf, "%02i", rtValue); 563 | 564 | //Debug contents of LTA 565 | //if (bleRTrigger != 128){Serial.print(" RTA = ");Serial.println(bleRTrigger);} 566 | 567 | // End all Analog Mode data 568 | } 569 | else 570 | { 571 | // Digital Mode - Process Input 572 | // ============================ 573 | 574 | // DPAD 575 | if (dataArrayBit6[0] != 0 && dataArrayBit7[0] != 0 && dataArrayBit5[0] != 0 && dataArrayBit4[0] != 0) 576 | { 577 | bleGamepad.setHat1(DPAD_CENTERED); 578 | currentButtonStates[1] = 0; 579 | } 580 | if (dataArrayBit4[0] == 0 && (dataArrayBit7[0] != 0 || dataArrayBit6[0] != 0)) 581 | { 582 | // Serial.print(" U "); 583 | bleGamepad.setHat1(DPAD_UP); 584 | currentButtonStates[1] = 1; 585 | } 586 | if (dataArrayBit5[0] == 0 && (dataArrayBit7[0] != 0 || dataArrayBit6[0] != 0)) 587 | { 588 | // Serial.print(" D "); 589 | bleGamepad.setHat1(DPAD_DOWN); 590 | currentButtonStates[1] = 2; 591 | } 592 | if (dataArrayBit6[0] == 0 && (dataArrayBit4[0] != 0 || dataArrayBit5[0] != 0)) 593 | { 594 | // Serial.print(" L "); 595 | bleGamepad.setHat1(DPAD_LEFT); 596 | currentButtonStates[1] = 3; 597 | } 598 | if (dataArrayBit7[0] == 0 && (dataArrayBit4[0] != 0 || dataArrayBit5[0] != 0)) 599 | { 600 | // Serial.print(" R "); 601 | bleGamepad.setHat1(DPAD_RIGHT); 602 | currentButtonStates[1] = 4; 603 | } 604 | if (dataArrayBit4[0] == 0 && dataArrayBit6[0] == 0) 605 | { 606 | // Serial.print(" U/L "); 607 | bleGamepad.setHat1(DPAD_UP_LEFT); 608 | currentButtonStates[1] = 5; 609 | } 610 | if (dataArrayBit4[0] == 0 && dataArrayBit7[0] == 0) 611 | { 612 | // Serial.print(" U/R "); 613 | bleGamepad.setHat1(DPAD_UP_RIGHT); 614 | currentButtonStates[1] = 6; 615 | } 616 | if (dataArrayBit5[0] == 0 && dataArrayBit6[0] == 0) 617 | { 618 | // Serial.print(" D/L "); 619 | bleGamepad.setHat1(DPAD_DOWN_LEFT); 620 | currentButtonStates[1] = 7; 621 | } 622 | if (dataArrayBit5[0] == 0 && dataArrayBit7[0] == 0) 623 | { 624 | // Serial.print(" D/R "); 625 | bleGamepad.setHat1(DPAD_DOWN_RIGHT); 626 | currentButtonStates[1] = 8; 627 | } 628 | 629 | // Buttons 630 | 631 | //------- Start Button -------// 632 | 633 | if (dataArrayBit3[1] == 0) 634 | { 635 | bleGamepad.press(BUTTON_12); 636 | currentButtonStates[2] = 1; 637 | } 638 | else 639 | { 640 | bleGamepad.release(BUTTON_12); 641 | currentButtonStates[2] = 0; 642 | } 643 | 644 | //------- A Button -------// 645 | 646 | if (dataArrayBit2[1] == 0) 647 | { 648 | bleGamepad.press(BUTTON_4); 649 | currentButtonStates[3] = 1; 650 | } 651 | else 652 | { 653 | bleGamepad.release(BUTTON_4); 654 | currentButtonStates[3] = 0; 655 | } 656 | 657 | //------- B Button -------// 658 | 659 | if (dataArrayBit0[1] == 0) 660 | { 661 | bleGamepad.press(BUTTON_1); 662 | currentButtonStates[4] = 1; 663 | } 664 | else 665 | { 666 | bleGamepad.release(BUTTON_1); 667 | currentButtonStates[4] = 0; 668 | } 669 | 670 | //------- C Button -------// 671 | 672 | if (dataArrayBit1[1] == 0) 673 | { 674 | bleGamepad.press(BUTTON_2); 675 | currentButtonStates[5] = 1; 676 | } 677 | else 678 | { 679 | bleGamepad.release(BUTTON_2); 680 | currentButtonStates[5] = 0; 681 | } 682 | 683 | //------- X Button -------// 684 | 685 | if (dataArrayBit6[1] == 0) 686 | { 687 | bleGamepad.press(BUTTON_7); 688 | currentButtonStates[6] = 1; 689 | } 690 | else 691 | { 692 | bleGamepad.release(BUTTON_7); 693 | currentButtonStates[6] = 0; 694 | } 695 | 696 | //------- Y Button -------// 697 | 698 | if (dataArrayBit5[1] == 0) 699 | { 700 | bleGamepad.press(BUTTON_5); 701 | currentButtonStates[7] = 1; 702 | } 703 | else 704 | { 705 | bleGamepad.release(BUTTON_5); 706 | currentButtonStates[7] = 0; 707 | } 708 | 709 | //------- Z Button -------// 710 | 711 | if (dataArrayBit4[1] == 0) 712 | { 713 | bleGamepad.press(BUTTON_8); 714 | currentButtonStates[8] = 1; 715 | } 716 | else 717 | { 718 | bleGamepad.release(BUTTON_8); 719 | currentButtonStates[8] = 0; 720 | } 721 | 722 | //------- LT Button -------// 723 | 724 | if (dataArrayBit3[2] == 0) 725 | { 726 | //bleGamepad.press(BUTTON_9); 727 | //bleGamepad.setBrake(32767); 728 | bleGamepad.setBrake(255); 729 | currentButtonStates[9] = 1; 730 | } 731 | else 732 | { 733 | //bleGamepad.release(BUTTON_9); 734 | bleGamepad.setBrake(0); 735 | currentButtonStates[9] = 0; 736 | } 737 | 738 | //------- RT Button -------// 739 | 740 | if (dataArrayBit7[1] == 0) 741 | { 742 | //bleGamepad.setAccelerator(32767); 743 | bleGamepad.setAccelerator(255); 744 | currentButtonStates[10] = 1; 745 | } 746 | else 747 | { 748 | //bleGamepad.release(BUTTON_10); 749 | bleGamepad.setAccelerator(0); 750 | currentButtonStates[10] = 0; 751 | } 752 | 753 | /* 754 | // Latency Test Pin 755 | // Intended for BlueRetro latency test pad to measure BLEGamepad update speeds without modifying the Controller 756 | // 757 | // Use print enabled to test your setup, then disable the prints as they will impact latency negatively 758 | if (digitalRead(latPin) == LOW) 759 | { 760 | Serial.print("Latency Test Press"); 761 | bleGamepad.press(BUTTON_3); 762 | currentButtonStates[2] = 1; 763 | } 764 | else 765 | { 766 | bleGamepad.release(BUTTON_3); 767 | currentButtonStates[2] = 0; 768 | } 769 | */ 770 | } 771 | // End Digital Mode - Process Input 772 | // ================================ 773 | 774 | // RTOS Watermark check 775 | // unsigned int wMark2 = uxTaskGetStackHighWaterMark(nullptr); 776 | // printf("Watermark - SendReport B loop %u\n", wMark2); 777 | 778 | // All Input states gathered Send Update if state has changed 779 | // This is how blegampad library runs the check but you can't compare arrays like this so condition is always true 780 | // Substantially higher power consumption as a result of constant transmit 781 | if (currentButtonStates != previousButtonStates) 782 | { 783 | for (byte currentIndex = 0; currentIndex < numOfButtons; currentIndex++) 784 | // To do this the correct way - itterate over each index but it has problems with loss of single packet 785 | //if (currentButtonStates[currentIndex] != previousButtonStates[currentIndex]) { 786 | // for (byte currentIndex = 0; currentIndex < numOfButtons; currentIndex++){ 787 | { 788 | previousButtonStates[currentIndex] = currentButtonStates[currentIndex]; 789 | } 790 | bleGamepad.sendReport(); 791 | bleTimer = timeStamp; 792 | //bleTimer = millis(); 793 | } 794 | } 795 | 796 | 797 | // Invalid Data or no connection error state 798 | // ========================================= 799 | // Communication or wiring issue 800 | 801 | else 802 | { 803 | for (int printByteCounter = 0; printByteCounter < modeCounter + 1; printByteCounter++) 804 | { 805 | int derpvar; 806 | derpvar = 0; 807 | // Serial.println("Error: Gamepad may not be paired OR Pinout/Timing Incorrect"); 808 | // Uncomment lines for additional debug output 809 | // Serial.print(" -- Byte "); 810 | // Serial.print(printByteCounter); 811 | // Serial.print(" - "); 812 | // Serial.print(dataArrayBit0[printByteCounter]); 813 | // Serial.print(dataArrayBit1[printByteCounter]); 814 | // Serial.print(dataArrayBit2[printByteCounter]); 815 | // Serial.print(dataArrayBit3[printByteCounter]); 816 | // Serial.print(dataArrayBit4[printByteCounter]); 817 | // Serial.print(dataArrayBit5[printByteCounter]); 818 | // Serial.print(dataArrayBit6[printByteCounter]); 819 | // Serial.print(dataArrayBit7[printByteCounter]); 820 | } 821 | // End Invalid Data 822 | //================= 823 | } 824 | // End Polling 825 | // =========== 826 | 827 | // Reset variables ready for data read. 828 | byteCounter = 0; 829 | nibble0Read = 0; 830 | 831 | // Setting Current Time for data read 832 | timeStamp = millis(); 833 | } 834 | 835 | // Check to see if the Controller is ready to send data and if we have read any data yet 836 | if (TLACK == HIGH and nibble0Read == 0) 837 | { 838 | digitalWrite(THPin, LOW); 839 | delayMicroseconds(4); // I don't think I need this 840 | digitalWrite(TRPin, LOW); 841 | delayMicroseconds(4); 842 | // delayMicroseconds(1); 843 | } 844 | 845 | // If the 1st nibble is being sent and we havn't read the 1st nibble yet get the data from the pins 846 | if (TLACK == LOW and nibble0Read == 0) 847 | { 848 | // Sets ready to read the 1st nibble of data 849 | digitalWrite(TRPin, HIGH); 850 | delayMicroseconds(8); // For ESP32 with lower setting get random button press 851 | 852 | // Read the Data for the 1st Nibble 853 | dataArrayBit0[byteCounter] = digitalRead(dataPinD0); 854 | dataArrayBit1[byteCounter] = digitalRead(dataPinD1); 855 | dataArrayBit2[byteCounter] = digitalRead(dataPinD2); 856 | dataArrayBit3[byteCounter] = digitalRead(dataPinD3); 857 | 858 | if (dataArrayBit0[byteCounter] > 1) 859 | { 860 | dataArrayBit0[byteCounter] = 0; 861 | } 862 | if (dataArrayBit1[byteCounter] > 1) 863 | { 864 | dataArrayBit1[byteCounter] = 0; 865 | } 866 | if (dataArrayBit2[byteCounter] > 1) 867 | { 868 | dataArrayBit2[byteCounter] = 0; 869 | } 870 | if (dataArrayBit3[byteCounter] > 1) 871 | { 872 | dataArrayBit3[byteCounter] = 0; 873 | } 874 | 875 | // 1st Nibble has been read 876 | nibble0Read = 1; 877 | 878 | // Check if we are reading data for Analogue mode or Digital Mode 879 | // The mode defines the number of Bytes we need to read 880 | if (byteCounter == 0) 881 | { 882 | if ((dataArrayBit0[0] == 1 and dataArrayBit1[0] == 0 and dataArrayBit2[0] == 0 and dataArrayBit3[0] == 0) or (dataArrayBit0[0] == 0 and dataArrayBit1[0] == 1 and dataArrayBit2[0] == 1 and dataArrayBit3[0] == 0) or (dataArrayBit0[0] == 1 and dataArrayBit1[0] == 1 and dataArrayBit2[0] == 1 and dataArrayBit3[0] == 0)) 883 | { 884 | modeCounter = AnalogMode; 885 | } 886 | else 887 | { 888 | modeCounter = DigitalMode; 889 | } 890 | } 891 | } 892 | 893 | // Check if the 2nd Nibble is being sent and if we have already read the 1st Nibble 894 | if (TLACK == HIGH and nibble0Read == 1) 895 | { 896 | // Sets ready to read the 2nd Nibble 897 | digitalWrite(TRPin, LOW); 898 | delayMicroseconds(8); 899 | // Read the data for the 2nd Nibble 900 | dataArrayBit4[byteCounter] = digitalRead(dataPinD0); 901 | dataArrayBit5[byteCounter] = digitalRead(dataPinD1); 902 | dataArrayBit6[byteCounter] = digitalRead(dataPinD2); 903 | dataArrayBit7[byteCounter] = digitalRead(dataPinD3); 904 | if (dataArrayBit4[byteCounter] > 1) 905 | { 906 | dataArrayBit4[byteCounter] = 0; 907 | } 908 | if (dataArrayBit5[byteCounter] > 1) 909 | { 910 | dataArrayBit5[byteCounter] = 0; 911 | } 912 | if (dataArrayBit6[byteCounter] > 1) 913 | { 914 | dataArrayBit6[byteCounter] = 0; 915 | } 916 | if (dataArrayBit7[byteCounter] > 1) 917 | { 918 | dataArrayBit7[byteCounter] = 0; 919 | } 920 | 921 | // Reset the 1st Nibble read variable, ready for the next Byte of data 922 | nibble0Read = 0; 923 | 924 | // Increase the Byte counter by 1 so we are ready to read the next Byte of data 925 | byteCounter++; 926 | } 927 | 928 | // Time Stamp Routines to check if data has paused. 929 | currentMillis = millis(); 930 | 931 | if (currentMillis - timeStamp >= interval) 932 | { 933 | // Resetting All Variables 934 | timeStamp = millis(); 935 | digitalWrite(THPin, HIGH); 936 | digitalWrite(TRPin, HIGH); 937 | byteCounter = 0; 938 | nibble0Read = 0; 939 | Serial.println("Init Controller Protocol..."); 940 | } 941 | } 942 | //============================= 943 | // End Controller related items 944 | 945 | 946 | 947 | // CPU Speed related items 948 | //============================= 949 | //Set CPU Speed 950 | // 951 | // This will heavily impact GPIO speed and protocol level checkins with the controller 952 | // Seriously. 953 | // For more information see the following link where this code was shamelessly borrowed from 954 | // https://deepbluembedded.com/esp32-change-cpu-speed-clock-frequency/ 955 | // 956 | void setCpu() 957 | { 958 | //This sets the CPU frequency 959 | //Possible frequencies are directly tied to your own device so dont just yolo values in here and then cry 960 | // 961 | //function takes the following frequencies as valid values: 962 | // 240, 160, 80 <<< For all XTAL types 963 | // 40, 20, 10 <<< For 40MHz XTAL 964 | // 26, 13 <<< For 26MHz XTAL 965 | // 24, 12 <<< For 24MHz XTAL 966 | //Print initial Speed 967 | Freq = getCpuFrequencyMhz(); 968 | Serial.print("Current CPU Freq = "); 969 | Serial.print(Freq); 970 | Serial.println(" MHz"); 971 | delay(30); 972 | Serial.end(); //Required for ESP bug 973 | setCpuFrequencyMhz(40); 974 | delay(30); 975 | Serial.begin(115200); // Required for ESP bug 976 | delay(30); 977 | Freq = getCpuFrequencyMhz(); 978 | Serial.print("New CPU Freq = "); 979 | Serial.print(Freq); 980 | Serial.println(" MHz"); 981 | } 982 | 983 | //Read CPU Speed and Clock 984 | void getCPU() 985 | { 986 | Freq = getCpuFrequencyMhz(); 987 | Serial.print("Boot CPU Freq = "); 988 | Serial.print(Freq); 989 | Serial.println(" MHz"); 990 | Freq = getXtalFrequencyMhz(); 991 | Serial.print("My XTAL Freq = "); 992 | Serial.print(Freq); 993 | Serial.println(" MHz"); 994 | Freq = getApbFrequency(); 995 | Serial.print("My APB Freq = "); 996 | Serial.print(Freq); 997 | Serial.println(" Hz"); 998 | } 999 | //============================= 1000 | // End CPU Speed related items 1001 | 1002 | 1003 | // This is the main loop due to threading in arduino + core tasking in RTOS 1004 | //========================================================================== 1005 | void setup() 1006 | { 1007 | 1008 | 1009 | // Start BLE Gamepad 1010 | BleGamepadConfiguration bleGamepadConfig; 1011 | bleGamepadConfig.setControllerType(CONTROLLER_TYPE_GAMEPAD); // CONTROLLER_TYPE_JOYSTICK, CONTROLLER_TYPE_GAMEPAD (DEFAULT), CONTROLLER_TYPE_MULTI_AXIS 1012 | //bleGamepadConfig.setControllerType(CONTROLLER_TYPE_MULTI_AXIS); //Axis will break Windows Gamepad test function but may work in games 1013 | bleGamepadConfig.setButtonCount(numOfButtons); 1014 | bleGamepadConfig.setWhichAxes(enableX, enableY, enableZ, enableRX, enableRY, enableRZ, enableSlider1, enableSlider2); // Can also be done per-axis individually. All are true by default 1015 | bleGamepadConfig.setHatSwitchCount(numOfHatSwitches); // 1 by default 1016 | 1017 | //Trigger settings 1018 | //Emulate Xbox behavior to avoid rY and rZ use 1019 | bleGamepadConfig.setIncludeAccelerator(true); 1020 | bleGamepadConfig.setIncludeBrake(true); 1021 | 1022 | //Modify HID Range Values 1023 | // WARNING: This impacts all MAP statements and Im lazy so it's not just defined as global variable 1024 | 1025 | // 1026 | //Set Range default -32k to +32k 1027 | //bleGamepadConfig.setAxesMin(0x8001); // -32767 --> int16_t - 16 bit signed integer - Can be in decimal or hexadecimal 1028 | //bleGamepadConfig.setAxesMax(0x7FFF); // 32767 --> int16_t - 16 bit signed integer - Can be in decimal or hexadecimal 1029 | // 1030 | //== Set Range Axes Range 1031 | // Emulate Xbox 0-255 1032 | bleGamepadConfig.setAxesMin(0x00); //Set Min to 0 //try 0 as min similar to xbox 1033 | //bleGamepadConfig.setAxesMax(0xFF); //Set Max to 255 // try 255 as Max similar to xbox //removed leading 00s 1034 | ///== Set Trigger Range 1035 | // Emulate Xbox 0-255 1036 | bleGamepadConfig.setSimulationMin(0x00); 1037 | //bleGamepadConfig.setSimulationMax(0xFF); //Max 255 //This doesn't seem to work right for BLEgamepad 1038 | 1039 | bleGamepadConfig.setAxesMin(0x0001); // -32767 --> int16_t - 16 bit signed integer - Can be in decimal or hexadecimal 1040 | // 1041 | // if blegamepad for Accel and Bake don't report try this instead 1042 | //bleGamepadConfig.setWhichSimulationControls(enableRudder, enableThrottle, enableAccelerator, enableBrake, enableSteering); 1043 | 1044 | //==Adjust Vid and PID 1045 | // These can be set to advertise various vendor and product IDs from other controllers 1046 | // bleGamepadConfig.setVid(0x45e); 1047 | // bleGamepadConfig.setPid(0x2e0); 1048 | 1049 | // ==Other Available Special Button Options 1050 | // Intermittent results with these "special buttons" across all tested remote devices 1051 | // 1052 | // bleGamepadConfig.setIncludeStart(true); 1053 | // bleGamepadConfig.setIncludeSelect(true); 1054 | // bleGamepadConfig.setIncludeMenu(true); 1055 | // bleGamepadConfig.setIncludeHome(true); 1056 | // bleGamepadConfig.setIncludeBack(true); 1057 | // bleGamepadConfig.setIncludeVolumeInc(true); 1058 | // bleGamepadConfig.setIncludeVolumeDec(true); 1059 | // bleGamepadConfig.setIncludeVolumeMute(true); 1060 | // 1061 | // ==Use or disable BLEGampead AutoReporting 1062 | // AutoReport can impact latency negatively with high speed of updates 1063 | bleGamepadConfig.setAutoReport(false); // This is true by default 1064 | 1065 | //Use all the bleGamepad Settings to initiate controller 1066 | bleGamepad.begin(&bleGamepadConfig); 1067 | 1068 | // Setting input and output pins for Comms with Controller 1069 | // Use Heavy and Lightwing pinout settings instead of changing this 1070 | //==========================// Pin 1 = +5V 1071 | pinMode(dataPinD1, INPUT); // Pin 2 = Data D1 1072 | pinMode(dataPinD0, INPUT); // Pin 3 = Data D0 1073 | pinMode(THPin, OUTPUT); // Pin 4 = TH Select 1074 | pinMode(TRPin, OUTPUT); // Pin 5 = TR Request 1075 | pinMode(TLPin, INPUT); // Pin 6 = TL Response 1076 | pinMode(dataPinD3, INPUT); // Pin 7 = Data D3 1077 | pinMode(dataPinD2, INPUT); // Pin 8 = Data D2 1078 | //==========================// Pin 9 = GND 1079 | 1080 | // Set pin state to begin reading controller state 1081 | digitalWrite(THPin, HIGH); 1082 | digitalWrite(TRPin, HIGH); 1083 | 1084 | // Define Latency test pin 1085 | pinMode(latPin, INPUT_PULLUP); 1086 | 1087 | // Setup Serial comms for Serial Monitor 1088 | Serial.begin(115200); 1089 | Serial.println("Device Booting...."); 1090 | } 1091 | 1092 | // Loop will always run on Core 1 1093 | void loop() 1094 | { 1095 | //Settings related to initial boot of device 1096 | //This doesn't get called on wake from deep sleep 1097 | if (firstBoot == 0){ 1098 | getCPU(); 1099 | //setCpu(); 1100 | firstBoot=1; 1101 | } 1102 | 1103 | pollController(); 1104 | } 1105 | -------------------------------------------------------------------------------- /HeavyWing/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | // Add BLE Gamepad 4 | #include 5 | // Add OLED 6 | #include 7 | #include 8 | // Add Adafruit Sensor Libs for Tilt Controls 9 | #include 10 | #include 11 | 12 | //==================================================== 13 | // __________.____ ___________.________ _________ 14 | // \______ \ | \_ _____/ \_____ \\______ \ 15 | // | | _/ | | __)_ _(__ < | | \ 16 | // | | \ |___ | \ / \| ` \ 17 | // |______ /_______ \/_______ / /______ /________ / 18 | // \/ \/ \/ \/ \/ 19 | // Greetz and massive thanks to Hexfreq for being a complete bad ass 20 | // 21 | // All reliable controller code foundation is from [at]Hexfreq 22 | // All bugs, poor code choices, and existing PCB designs courtesy of [at]GamingNJncos 23 | // Cheers to [at]Arthrimus for lighting this fire on accident 24 | // 25 | // As I have limited time for mainting this codebase long term attempts have been made to over document code provided 26 | // No Warranty expressed or implied 27 | //==================================================== 28 | 29 | 30 | // OLED Screen Settings 31 | #define SCREEN_WIDTH 128 // OLED display width, in pixels 32 | #define SCREEN_HEIGHT 64 // OLED display height, in pixels 33 | #define OLED_RESET 4 // Reset pin # (or -1 if sharing Arduino reset pin) 34 | #define SCREEN_ADDRESS 0x3C ///< See datasheet for Address; 0x3D for 128x64, 0x3C for 128x32 35 | Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET); 36 | 37 | // Establish tasks for ESP32 CPU Core Pinning 38 | TaskHandle_t Task1; 39 | TaskHandle_t Task2; 40 | 41 | // BLEGamePad Settings 42 | // =================== 43 | // Values are Advertised Name, Manufacturer, Battery level 44 | // Note the battery value is not from a read value here but later blegamepad examples support live update vs 1 time poll 45 | BleGamepad bleGamepad("Reala", "GamingNJncos.HexFreq", 100); 46 | // Purposely set high to permit ease of future additional button combinationsfor to send Meta, home button, etc 47 | #define numOfButtons 12 48 | #define numOfHatSwitches 1 //DPAD (not Analog pad) 49 | #define enableX true // Analog Pad 50 | #define enableY true // Analog Pad 51 | #define enableRX true // Triggers 52 | #define enableRY true // Triggers 53 | // Additional button options currently disabled 54 | #define enableZ false 55 | #define enableRZ false 56 | #define enableSlider1 false 57 | #define enableSlider2 false 58 | #define enableRudder false 59 | #define enableThrottle false 60 | #define enableAccelerator false 61 | #define enableBrake false 62 | #define enableSteering false 63 | // End BLEGamePad Settings 64 | 65 | // Button State Tracking 66 | // This nets a significant advantage in performance vs not tracking states 67 | // BLE expects ACK per packet so flooding "not pressed" or identical data can add extreme delays 68 | // 69 | byte previousButtonStates[12]; 70 | byte currentButtonStates[12]; 71 | // Button State Array Map 72 | // ====================== 73 | // MODE, DPAD, START, A, B, C, X, Y, Z, L, R, Analog PAD 74 | // [0-1] [0-8] [- Binary Values 0-1 -] [0-15] [0-127,0-127] 75 | // Note Left and Right stick in digital mode will return Binary [0-1] vs [0-15] value in analog mode 76 | 77 | // Pinmappings for your PCB version 78 | // It is manditory you select the correct pinout for your board type 79 | 80 | // Feather HeavyWing to Controller Pin Mapping 81 | // =========================================== 82 | //PCB: https://oshpark.com/shared_projects/ki7HbZV4 83 | // 84 | //int dataPinD0 = 32; // Arthrimus Pin 3 = Data D0 85 | //int dataPinD1 = 14; // Arthrimus Pin 2 = Data D1 86 | //int dataPinD2 = 13; // Arthrimus Pin 8 = Data D2 87 | //int dataPinD3 = 12; // Arthrimus Pin 7 = Data D3 88 | //int THPin = 15; // Arthrimus Pin 4 = TH Select 89 | //int TRPin = 33; // Arthrimus Pin 5 = TR Request 90 | //int TLPin = 27; // Arthrimus Pin 6 = TL Response 91 | //int batPin = A13; // Used by BLEGamepad library 92 | //int latPin = 4; // Latency testing pin 93 | // End Feather HeavyWing to Controller Pin Mapping 94 | 95 | 96 | // Feather LightWing to Controller Pin Mapping 97 | // =========================================== 98 | // PCB: https://oshpark.com/shared_projects/7RqwDWJT 99 | // 100 | int dataPinD0 = 12; // Arthrimus Pin 3 = Data D0 101 | int dataPinD1 = 13; // Arthrimus Pin 2 = Data D1 102 | int dataPinD2 = 14; // Arthrimus Pin 8 = Data D2 103 | int dataPinD3 = 32; // Arthrimus Pin 7 = Data D3 104 | int THPin = 27; // Arthrimus Pin 4 = TH Select 105 | int TRPin = 33; // Arthrimus Pin 5 = TR Request 106 | int TLPin = 15; // Arthrimus Pin 6 = TL Response 107 | int batPin = A13; // Used by BLEGamepad library 108 | int latPin = 4; // Latency testing pin 109 | // End Feather HeavyWing to Controller Pin Mapping 110 | // End all Pin Mappings 111 | 112 | // BleGamepad Variables 113 | // ==================== 114 | int16_t bleAnalogX; //Analog pad X Axis Output 115 | int16_t bleAnalogY; //Analog pad Y Axis Output 116 | // DeadZone defined as smallest value to exceed before sending in blegamepad report function 117 | int16_t bleDeadZone = 400; //Set Deadzone via minimum number 118 | // Analog Triggers 119 | int16_t bleRTrigger; //Right Trigger Analog Output 120 | int16_t bleLTrigger; //Left Trigger Analog Output 121 | // Battery Monitoring variables for BLEGamepad 122 | // Note BLE Gamepad can read battery percent but can not check to transmit value after initial connection 123 | int adcRead = 0; 124 | int batVolt = 0; 125 | // End BleGamepad Variables 126 | 127 | 128 | // Controller protocol and timing variables 129 | // ======================================== 130 | // Data Array for each byte from protocol 131 | boolean dataArrayBit0[8]; 132 | boolean dataArrayBit1[8]; 133 | boolean dataArrayBit2[8]; 134 | boolean dataArrayBit3[8]; 135 | boolean dataArrayBit4[8]; 136 | boolean dataArrayBit5[8]; 137 | boolean dataArrayBit6[8]; 138 | boolean dataArrayBit7[8]; 139 | // Assorted Protocol Variables 140 | int nibble0Read = 0; // Confirm 1st Nibble has been read 141 | int byteCounter = 0; // Current Byte Tracker 142 | int modeCounter = 4; // Reference for which mode we are currently in Analogue or Digital 143 | int DecodeData = 1; // Controller Successfully Decoded Data Variable 144 | int curMode = 0; // This stores the Controller Mode to pass between ESP cores 145 | // Assorted timing oriented variables 146 | unsigned long timeStamp = 0; // Store time of last update 147 | unsigned long lastTimeStamp = 0; // Store Last time for comparison 148 | const long interval = 1000; // Data update check timing 149 | unsigned long currentMillis = millis(); // check timing 150 | // Directional Variables 151 | int upDownValue = 0; 152 | int leftRightValue = 0; 153 | // Trigger Variables 154 | int ltValue = 0; 155 | int rtValue = 0; 156 | // Variables for Data send request and Acknowledgments 157 | int THSEL = 0; 158 | int TRREQ = 0; 159 | int TLACK = 0; 160 | // Sets the number of Bytes required for Digital or Analogue Modes 161 | int AnalogMode = 6; 162 | int DigitalMode = 3; 163 | // End Controller protocol and timing variables 164 | 165 | 166 | // Gyro and Accelerometer Variables 167 | // ================================= 168 | // Gyro is currently disabled in code but my terrible examples should be useful if you wish to use this feature 169 | const int MPU_addr=0x68; // Define Gyro I2C Address 170 | int16_t AcX,AcY,AcZ,Tmp,GyX,GyY,GyZ; // Store all values from MPU 171 | int minVal=265; 172 | int maxVal=402; 173 | double x; 174 | double y; 175 | double z; 176 | // Converted XY Values to BLEGamepad ranges (negative to positive 32K 177 | int xmap; 178 | int ymap; 179 | // Max Values to limit full range of 360 degree motion. 180 | // Will require calibration with your MPU-6050 to identify most comfortable ranges 181 | // Suggest 35-45 Degrees or less relatively comfortable for most use cases 182 | int xmax = 200; 183 | int ymax = 200; 184 | int xmin = 160; 185 | int ymin = 160; 186 | // End Gyro and Accelerometer Variables 187 | 188 | 189 | // Boot Logo 190 | // ========== 191 | //Set Image Size for Boot Logo 192 | #define imageWidth 128 193 | #define imageHeight 64 194 | // Image to be displayed 195 | // These can be changed and alternatively set as animations 196 | // Create with "LCD Image Converter" tool or similar 197 | // https://lcd-image-converter.riuson.com/en/about/ 198 | const unsigned char myBitmap [] PROGMEM= 199 | { 200 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 201 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 202 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 203 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 204 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 205 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 206 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 207 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 208 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 209 | 0x0F, 0xFE, 0x0C, 0x00, 0xF6, 0x0C, 0x3C, 0x00, 0x60, 0x41, 0x80, 0x87, 0xFF, 0xF1, 0x80, 0x1E, 210 | 0x1E, 0x3F, 0xFB, 0xE7, 0x9E, 0x0E, 0x3E, 0x00, 0xE7, 0xBE, 0x7F, 0x03, 0xBF, 0x1E, 0xE0, 0x0E, 211 | 0x3C, 0x03, 0xE0, 0x7E, 0x03, 0x1E, 0x3E, 0x00, 0xE4, 0x1E, 0x0F, 0x03, 0x1E, 0x1E, 0x78, 0x0C, 212 | 0x3E, 0x01, 0xE0, 0x1E, 0x01, 0x1E, 0x3E, 0x01, 0xF0, 0x1E, 0x0F, 0x03, 0x1E, 0x0F, 0x3C, 0x0C, 213 | 0x3F, 0x01, 0xE1, 0x3E, 0x00, 0x3F, 0x1F, 0x03, 0xF0, 0x1E, 0x0F, 0x03, 0x1E, 0x0F, 0x3F, 0x0C, 214 | 0x1F, 0xC1, 0xE3, 0x3C, 0x00, 0x3D, 0x1F, 0x03, 0xD8, 0x1E, 0x0F, 0x03, 0x1E, 0x1E, 0x3F, 0x8C, 215 | 0x0F, 0xF1, 0xFF, 0x3C, 0x00, 0x79, 0x9F, 0x87, 0xD8, 0x1E, 0x0F, 0x03, 0x1E, 0x3E, 0x3F, 0xCC, 216 | 0x03, 0xF9, 0xFF, 0x3C, 0x00, 0xF9, 0x8F, 0x87, 0x8C, 0x1E, 0x0F, 0x03, 0x1E, 0xF0, 0x27, 0xEC, 217 | 0x00, 0xFD, 0xE3, 0x3C, 0x1F, 0xF0, 0xCF, 0xCF, 0x8C, 0x1E, 0x0F, 0x03, 0x1E, 0x78, 0x31, 0xFC, 218 | 0x40, 0x7D, 0xE0, 0x3E, 0x0F, 0xF0, 0xC7, 0xCF, 0x06, 0x1E, 0x0F, 0x03, 0x1E, 0x78, 0x30, 0xFC, 219 | 0x60, 0x3D, 0xE0, 0x3E, 0x07, 0xE0, 0x67, 0xDF, 0x06, 0x1E, 0x0F, 0x03, 0x1E, 0x3C, 0x30, 0x7C, 220 | 0x30, 0x3D, 0xE0, 0x7E, 0x07, 0xE0, 0x63, 0xFE, 0x03, 0x1E, 0x07, 0x87, 0x1E, 0x3E, 0x30, 0x3C, 221 | 0x3E, 0xF9, 0xF3, 0xFF, 0x0F, 0xE0, 0x73, 0xFE, 0x07, 0xBE, 0x07, 0xFE, 0x1F, 0x1F, 0x30, 0x0C, 222 | 0x1F, 0xE3, 0xFF, 0xE3, 0xFF, 0xE0, 0xFB, 0xFE, 0x07, 0xFF, 0x01, 0xF8, 0x3F, 0x87, 0xF0, 0x04, 223 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x00, 224 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 225 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 226 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 227 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 228 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 229 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 230 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 231 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 232 | }; 233 | // End Boot Logo 234 | 235 | 236 | // Boot Logo and Screen Setup 237 | // ========================== 238 | void bootLogo(){ 239 | //Check ESP Core Boot Logo is running on 240 | //Serial.print("BootLogo Core "); 241 | //Serial.println(xPortGetCoreID()); 242 | 243 | if(!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) { 244 | Serial.println(F("SSD1306 allocation failed")); 245 | for(;;); // Don't proceed, loop forever 246 | } 247 | // Rotate 248 | display.setRotation(2); 249 | // Clear the Buffer 250 | display.clearDisplay(); 251 | // Draw Animation 252 | // Draw Bitmap syntax 253 | // display.drawBitmap(x position, y position, bitmap data, bitmap width, bitmap height, color) 254 | display.drawBitmap(0, 32, myBitmap, 128, 32, WHITE); 255 | display.display(); 256 | // Set Delay for Boot Animation 257 | delay(1500); 258 | // Reset Display 259 | display.display(); 260 | display.setRotation(2); 261 | delay(10); 262 | // Clear the buffer 263 | display.clearDisplay(); 264 | delay(2000); 265 | //End Screen Setup 266 | } 267 | 268 | 269 | // Battery Related Checks 270 | // ====================== 271 | void getBatStat(){ 272 | // Check ESP Core 273 | //Serial.print("Battery Status Core "); 274 | //Serial.println(xPortGetCoreID()); 275 | 276 | adcRead = analogRead(batPin); 277 | batVolt = adcRead; 278 | // Multiply by 2 to correct reading for Featherwing Huzzah32 279 | //batVolt = adcRead * 2; 280 | 281 | int BatteryPct; 282 | int oldBatPct = 0; 283 | // Map Battery voltage range to a percentage 284 | BatteryPct = map (batVolt, 1200, 4200, 0, 100); 285 | // Make sure its populated on initial run 286 | if (oldBatPct < 1){ 287 | oldBatPct = BatteryPct; 288 | } 289 | // Super hack check to omit negative values from OLED Updates 290 | if (BatteryPct < 0 ){ 291 | BatteryPct = oldBatPct; 292 | } 293 | 294 | // Build and send screen update 295 | // ============================ 296 | String lcdBat; 297 | lcdBat = "Battery: " + String(BatteryPct) + "% "; 298 | //display.setCursor(0,50); 299 | //display.print("Battery "); 300 | //display.setCursor(0,50); 301 | //display.print(lcdBat); 302 | //display.display(); 303 | // End Battery Updates 304 | } 305 | 306 | // OLED Updates 307 | // ============ 308 | void oled_Updates(){ 309 | if (curMode== 0 ) { 310 | curMode = 0; 311 | display.setTextSize(1); // Normal 1:1 pixel scale 312 | display.setTextColor(SSD1306_WHITE, BLACK); // Draw white text 313 | display.setCursor(0,1); // Start at top-left corner 314 | display.println(F("Mode: Analog ")); 315 | display.display(); 316 | } else { 317 | curMode = 1; 318 | display.setTextSize(1); // Normal 1:1 pixel scale 319 | display.setTextColor(SSD1306_WHITE, BLACK); // Draw white text 320 | display.setCursor(0,1); // Start at top-left corner 321 | display.println(F("Mode: Digital ")); 322 | display.display(); 323 | } 324 | // End OLED Updates 325 | } 326 | 327 | 328 | // Controller related items 329 | // ======================== 330 | void pollController(){ 331 | //Check ESP Core Controller Polling Occurs on 332 | //Serial.print("Controller Polling Core "); 333 | //Serial.println(xPortGetCoreID()); 334 | 335 | //Make sure that we sync timestamp and last timestamp on first run 336 | if (lastTimeStamp = 0){ 337 | lastTimeStamp = timeStamp; 338 | //hacktest MPU initialization on first run 339 | //initializeSensor(); 340 | } 341 | 342 | // Start The Data Read 343 | // Is the Controller ready to send the data? 344 | TLACK = digitalRead(TLPin); 345 | 346 | // Have we read all the bytes needed for each mode? 347 | if (byteCounter > modeCounter) { 348 | // Set Not ready for Data Coms 349 | digitalWrite(THPin, HIGH); 350 | digitalWrite(TRPin, HIGH); 351 | 352 | // Are we using Analogue Mode or Digital? 353 | if (modeCounter == AnalogMode) { 354 | curMode = 0; 355 | currentButtonStates[0] = 0; 356 | } else { 357 | curMode = 1; 358 | currentButtonStates[0] = 1; 359 | } 360 | 361 | // Decoded Controller Data Successfully 362 | // Proceed based on Analog or Digital Mode 363 | // 364 | // Note that BLEgamepad check occurs in combination with successfully decoded data 365 | // This avoids a bug in NIMBLE where blegamepad could under some circumstances attempt button send prior to connecting and cause abort 366 | if (DecodeData == 1 && bleGamepad.isConnected()) { 367 | //Analog 368 | if (modeCounter == AnalogMode) { 369 | //Analog Mode - Process Input 370 | //Begin all Analog Mode data processing 371 | 372 | //DPAD 373 | if (dataArrayBit6[1] != 0 && dataArrayBit7[1] != 0 && dataArrayBit5[1] != 0 && dataArrayBit4[1] != 0){ 374 | bleGamepad.setHat1(DPAD_CENTERED); 375 | currentButtonStates[1] = 0; 376 | } 377 | if (dataArrayBit4[1] == 0 && (dataArrayBit7[1] != 0 && dataArrayBit6[1] != 0)) { 378 | //Serial.print(" U"); 379 | bleGamepad.setHat1(DPAD_UP); 380 | currentButtonStates[1] = 1; 381 | } 382 | if (dataArrayBit5[1] == 0 && (dataArrayBit7[1] != 0 && dataArrayBit6[1] != 0)) { 383 | //Serial.print(" D"); 384 | bleGamepad.setHat1(DPAD_DOWN); 385 | currentButtonStates[1] = 2; 386 | } 387 | if (dataArrayBit6[1] == 0 && (dataArrayBit4[1] != 0 && dataArrayBit5[1] != 0)) { 388 | //Serial.print(" L"); 389 | bleGamepad.setHat1(DPAD_LEFT); 390 | currentButtonStates[1] = 3; 391 | } 392 | if (dataArrayBit7[1] == 0 && (dataArrayBit4[1] != 0 && dataArrayBit5[1] != 0)) { 393 | //Serial.print(" R"); 394 | bleGamepad.setHat1(DPAD_RIGHT); 395 | currentButtonStates[1] = 4; 396 | } 397 | if (dataArrayBit4[1] == 0 && dataArrayBit6[1] == 0) { 398 | //Serial.print(" U/L"); 399 | bleGamepad.setHat1(DPAD_UP_LEFT); 400 | currentButtonStates[1] = 5; 401 | } 402 | if (dataArrayBit4[1] == 0 && dataArrayBit7[1] == 0) { 403 | //Serial.print(" U/R"); 404 | bleGamepad.setHat1(DPAD_UP_RIGHT); 405 | currentButtonStates[1] = 6; 406 | } 407 | if (dataArrayBit5[1] == 0 && dataArrayBit6[1] == 0) { 408 | //Serial.print(" D/L"); 409 | bleGamepad.setHat1(DPAD_DOWN_LEFT); 410 | currentButtonStates[1] = 7; 411 | } 412 | if (dataArrayBit5[1] == 0 && dataArrayBit7[1] == 0) { 413 | //Serial.print(" D/R"); 414 | bleGamepad.setHat1(DPAD_DOWN_RIGHT); 415 | currentButtonStates[1] = 8; 416 | } 417 | // End DPAD 418 | 419 | //Buttons 420 | if (dataArrayBit2[2] == 0) { 421 | Serial.print(" A"); 422 | bleGamepad.press(BUTTON_4); 423 | currentButtonStates[3] = 1; 424 | } else { 425 | //Serial.print(" "); 426 | bleGamepad.release(BUTTON_4); 427 | currentButtonStates[3] = 0; 428 | } 429 | if (dataArrayBit0[2] == 0) { 430 | //Serial.print(" B"); 431 | bleGamepad.press(BUTTON_1); 432 | currentButtonStates[4] = 1; 433 | } else { 434 | //Serial.print(" "); 435 | bleGamepad.release(BUTTON_1); 436 | currentButtonStates[4] = 0; 437 | } 438 | if (dataArrayBit1[2] == 0) { 439 | //Serial.print(" C"); 440 | bleGamepad.press(BUTTON_2); 441 | currentButtonStates[5] = 1; 442 | } else { 443 | //Serial.print(" "); 444 | bleGamepad.release(BUTTON_2); 445 | currentButtonStates[5] = 0; 446 | } 447 | if (dataArrayBit6[2] == 0) { 448 | //Serial.print(" X"); 449 | bleGamepad.press(BUTTON_7); 450 | currentButtonStates[6] = 1; 451 | } else { 452 | //Serial.print(" "); 453 | bleGamepad.release(BUTTON_7); 454 | currentButtonStates[6] = 0; 455 | } 456 | if (dataArrayBit5[2] == 0) { 457 | // Serial.print(" Y"); 458 | bleGamepad.press(BUTTON_5); 459 | currentButtonStates[7] = 1; 460 | } else { 461 | //Serial.print(" "); 462 | bleGamepad.release(BUTTON_5); 463 | currentButtonStates[7] = 0; 464 | } 465 | if (dataArrayBit4[2] == 0) { 466 | //Serial.print(" Z"); 467 | bleGamepad.press(BUTTON_8); 468 | currentButtonStates[8] = 1; 469 | } else { 470 | //Serial.print(" "); 471 | bleGamepad.release(BUTTON_8); 472 | currentButtonStates[8] = 0; 473 | } 474 | // Uncomment for L/R triggers to send digital in addition to analog values 475 | // May have wrong button_number defined here in the blegamepad.press setting 476 | //if (dataArrayBit3[3] == 0) { 477 | //Serial.print(" LT"); 478 | // bleGamepad.press(BUTTON_5); 479 | // currentButtonStates[9] = 1; 480 | //} else { 481 | //Serial.print(" "); 482 | // bleGamepad.release(BUTTON_5); 483 | // currentButtonStates[9] = 0; 484 | //} 485 | //if (dataArrayBit7[2] == 0) { 486 | //Serial.print(" RT"); 487 | // bleGamepad.press(BUTTON_6); 488 | // currentButtonStates[10] = 1; 489 | //} else { 490 | //Serial.print(" "); 491 | // bleGamepad.release(BUTTON_6); 492 | // currentButtonStates[10] = 1; 493 | //} 494 | if (dataArrayBit3[2] == 0) { 495 | //Serial.print(" Start "); 496 | bleGamepad.press(BUTTON_12); 497 | currentButtonStates[2] = 1; 498 | } else { 499 | bleGamepad.release(BUTTON_12); 500 | currentButtonStates[2] = 0; 501 | } 502 | // Latency Test Pin 503 | // Intended for BlueRetro latency button 504 | // Can be used for other additional buttons 505 | // 506 | // Note this sends "Start" but can be adjusted to press any other button 507 | if (digitalRead(latPin) == LOW) { 508 | //Serial.print("LAT"); 509 | bleGamepad.press(BUTTON_3); 510 | currentButtonStates[3] = 1; 511 | } else { 512 | bleGamepad.release(BUTTON_3); 513 | currentButtonStates[3] = 0; 514 | } 515 | 516 | // Up / Down 517 | if (dataArrayBit7[4] == 1) { 518 | upDownValue = dataArrayBit0[5] + (dataArrayBit1[5]<<1) + (dataArrayBit2[5]<<2) + (dataArrayBit3[5]<<3) + (dataArrayBit4[4]<<4) + (dataArrayBit5[4]<<5) + (dataArrayBit6[4]<<6); 519 | //bleAnalogX = map(upDownValue, 0, 127, 1, 32767); 520 | bleAnalogX = map(upDownValue, 0, 127, 16384, 32767); 521 | 522 | } else { 523 | upDownValue = !dataArrayBit0[5] + (!dataArrayBit1[5]<<1) + (!dataArrayBit2[5]<<2) + (!dataArrayBit3[5]<<3) + (!dataArrayBit4[4]<<4) + (!dataArrayBit5[4]<<5) + (!dataArrayBit6[4]<<6); 524 | //bleAnalogX = map(upDownValue, 1, 127, -1, -32767); 525 | bleAnalogX = map(upDownValue, 0, 127, 16383, 1); 526 | } 527 | // Deadzone fix - X Axis 528 | // Value is checked based on map output 529 | //=======COMMENTED THIS OUT TEMPORARILY 530 | //if ((bleAnalogX < 0 && bleAnalogX > -500)||(bleAnalogX < 0 && bleAnalogX > -500)){ 531 | // bleAnalogX = 0; 532 | // } 533 | 534 | 535 | //Analog Debugs 536 | //Serial.print(" bleAnalogX "); 537 | //Serial.println(bleAnalogX); 538 | // Format Value to 3 Chars long 539 | //char buf[] = "000"; 540 | //sprintf(buf, "%03i", upDownValue); 541 | 542 | // Left / Right 543 | if (dataArrayBit7[3] == 1) { 544 | //Serial.print(" R = "); 545 | leftRightValue = dataArrayBit0[4] + (dataArrayBit1[4]<<1) + (dataArrayBit2[4]<<2) + (dataArrayBit3[4]<<3) + (dataArrayBit4[3]<<4) + (dataArrayBit5[3]<<5) + (dataArrayBit6[3]<<6); 546 | //bleAnalogY = map(leftRightValue, 1, 127, 1, 32767); 547 | bleAnalogY = map(leftRightValue, 0, 127, 16384, 32767); 548 | } else { 549 | leftRightValue = !dataArrayBit0[4] + (!dataArrayBit1[4]<<1) + (!dataArrayBit2[4]<<2) + (!dataArrayBit3[4]<<3) + (!dataArrayBit4[3]<<4) + (!dataArrayBit5[3]<<5) + (!dataArrayBit6[3]<<6); 550 | 551 | bleAnalogY = map(leftRightValue, 0, 127, 16383, 1); 552 | } 553 | //=======COMMENTED THIS OUT TEMPORARILY 554 | // Deadzone fix - Y Axis 555 | // Value is checked based on map output 556 | //if ((bleAnalogY > 0 && bleAnalogY < 500)||(bleAnalogY < 0 && bleAnalogY > -500)){ 557 | // bleAnalogY = 0; 558 | //} 559 | // Set Analog Pad/Left Thumb Stick X Y Values in Blegamepad 560 | // This is an easy place to flip the X and Yresults if backwards for your use case 561 | bleGamepad.setX(bleAnalogY); 562 | bleGamepad.setY(bleAnalogX); 563 | // Track State of Analog Sticks X and Y Values 564 | currentButtonStates[11] = bleAnalogX; 565 | currentButtonStates[12] = bleAnalogY; 566 | //unused at this time 567 | //currentButtonStates[13] = 0; 568 | //currentButtonStates[14] = 0; 569 | 570 | // Decode Triggers 571 | char trbuf[] = "00"; 572 | //Left Trigger 573 | ltValue = (dataArrayBit7[6] * 8) + (dataArrayBit6[6] * 4) + (dataArrayBit5[6] * 2) + (dataArrayBit4[6] * 1); 574 | //bleLTrigger = map(ltValue, 0, 15, -32767, 32767); 575 | bleLTrigger = map(ltValue, 0, 15, 1, 32767); 576 | bleGamepad.setLeftTrigger(bleLTrigger); 577 | //Track State of L trigger 578 | currentButtonStates[9] = bleLTrigger; 579 | 580 | //sprintf(trbuf, "%02i", ltValue); 581 | //Serial.print(" LT = "); 582 | //Serial.print(trbuf); 583 | 584 | //Right Trigger 585 | rtValue = (dataArrayBit7[5] * 8) + (dataArrayBit6[5] * 4) + (dataArrayBit5[5] * 2) + (dataArrayBit4[5] * 1); 586 | //bleRTrigger = map(rtValue, 0, 15, 32767, -32767); 587 | bleRTrigger = map(rtValue, 0, 15, 1, 32767); 588 | bleGamepad.setRightTrigger(bleRTrigger); 589 | //Track State of R trigger 590 | currentButtonStates[10] = bleRTrigger; 591 | //sprintf(trbuf, "%02i", rtValue); 592 | //Serial.print(" RT = "); 593 | //Serial.print(trbuf); 594 | 595 | //Debug 596 | unsigned int wMark1 = uxTaskGetStackHighWaterMark(nullptr); 597 | printf("Watermark before SendReportA loop is is %u\n", wMark1); 598 | 599 | //All Input states gathered Send Update if current and previous states do not match 600 | //bleGamepad.sendReport(); 601 | if (currentButtonStates != previousButtonStates) 602 | { 603 | for (byte currentIndex = 0; currentIndex < numOfButtons; currentIndex++) 604 | { 605 | previousButtonStates[currentIndex] = currentButtonStates[currentIndex]; 606 | } 607 | 608 | bleGamepad.sendReport(); 609 | } 610 | //End all Analog Mode data 611 | 612 | 613 | } else { 614 | // Digital Mode - Process Input 615 | // ============================ 616 | 617 | // DPAD 618 | if (dataArrayBit6[1] != 0 && dataArrayBit7[1] != 0 && dataArrayBit5[1] != 0 && dataArrayBit4[1] != 0){ 619 | bleGamepad.setHat1(DPAD_CENTERED); 620 | currentButtonStates[1] = 0; 621 | } 622 | if (dataArrayBit4[0] == 0 && (dataArrayBit7[0] != 0 || dataArrayBit6[0] != 0)) { 623 | //Serial.print(" U "); 624 | bleGamepad.setHat1(DPAD_UP); 625 | currentButtonStates[1] = 1; 626 | } 627 | if (dataArrayBit5[0] == 0 && (dataArrayBit7[0] != 0 || dataArrayBit6[0] != 0)) { 628 | //Serial.print(" D "); 629 | bleGamepad.setHat1(DPAD_DOWN); 630 | currentButtonStates[1] = 2; 631 | } 632 | if (dataArrayBit6[0] == 0 && (dataArrayBit4[0] !=0 || dataArrayBit5[0] != 0)) { 633 | //Serial.print(" L "); 634 | bleGamepad.setHat1(DPAD_LEFT); 635 | currentButtonStates[1] = 3; 636 | } 637 | if (dataArrayBit7[0] == 0 && (dataArrayBit4[0] !=0 || dataArrayBit5[0] != 0)) { 638 | //Serial.print(" R "); 639 | bleGamepad.setHat1(DPAD_RIGHT); 640 | currentButtonStates[1] = 4; 641 | } 642 | if (dataArrayBit4[0] == 0 && dataArrayBit6[0] == 0) { 643 | //Serial.print(" U/L "); 644 | bleGamepad.setHat1(DPAD_UP_LEFT); 645 | currentButtonStates[1] = 5; 646 | } 647 | if (dataArrayBit4[0] == 0 && dataArrayBit7[0] == 0) { 648 | //Serial.print(" U/R "); 649 | bleGamepad.setHat1(DPAD_UP_RIGHT); 650 | currentButtonStates[1] = 6; 651 | } 652 | if (dataArrayBit5[0] == 0 && dataArrayBit6[0] == 0) { 653 | //Serial.print(" D/L "); 654 | bleGamepad.setHat1(DPAD_DOWN_LEFT); 655 | currentButtonStates[1] = 7; 656 | } 657 | if (dataArrayBit5[0] == 0 && dataArrayBit7[0] == 0) { 658 | //Serial.print(" D/R "); 659 | bleGamepad.setHat1(DPAD_DOWN_RIGHT); 660 | currentButtonStates[1] = 8; 661 | } 662 | 663 | //Buttons 664 | if (dataArrayBit2[1] == 0) { 665 | //Serial.print(" A "); 666 | bleGamepad.press(BUTTON_4); 667 | currentButtonStates[3] = 1; 668 | } else { 669 | bleGamepad.release(BUTTON_4); 670 | currentButtonStates[3] = 0; 671 | } 672 | if (dataArrayBit0[1] == 0) { 673 | //Serial.print(" B "); 674 | bleGamepad.press(BUTTON_1); 675 | currentButtonStates[4] = 1; 676 | } else { 677 | bleGamepad.release(BUTTON_1); 678 | currentButtonStates[4] = 0; 679 | } 680 | if (dataArrayBit1[1] == 0) { 681 | //Serial.print(" C "); 682 | bleGamepad.press(BUTTON_2); 683 | currentButtonStates[5] = 1; 684 | } else { 685 | bleGamepad.release(BUTTON_2); 686 | currentButtonStates[5] = 0; 687 | } 688 | if (dataArrayBit6[1] == 0) { 689 | //Serial.print(" X "); 690 | bleGamepad.press(BUTTON_7); 691 | currentButtonStates[6] = 1; 692 | } else { 693 | bleGamepad.release(BUTTON_7); 694 | currentButtonStates[6] = 0; 695 | } 696 | if (dataArrayBit5[1] == 0) { 697 | //Serial.print(" Y "); 698 | bleGamepad.press(BUTTON_5); 699 | currentButtonStates[7] = 1; 700 | } else { 701 | bleGamepad.release(BUTTON_5); 702 | currentButtonStates[7] = 0; 703 | } 704 | if (dataArrayBit4[1] == 0) { 705 | //Serial.print(" Z "); 706 | bleGamepad.press(BUTTON_8); 707 | currentButtonStates[8] = 1; 708 | } else { 709 | bleGamepad.release(BUTTON_8); 710 | currentButtonStates[8] = 0; 711 | } 712 | if (dataArrayBit3[2] == 0) { 713 | //Serial.print(" LT "); 714 | bleGamepad.press(BUTTON_11); 715 | currentButtonStates[9] = 1; 716 | } else { 717 | bleGamepad.release(BUTTON_11); 718 | currentButtonStates[9] = 0; 719 | } 720 | if (dataArrayBit7[1] == 0) { 721 | //Serial.print(" RT "); 722 | bleGamepad.press(BUTTON_13); 723 | currentButtonStates[10] = 1; 724 | } else { 725 | bleGamepad.release(BUTTON_13); 726 | currentButtonStates[10] = 0; 727 | } 728 | if (dataArrayBit3[1] == 0) { 729 | //Serial.println(" Start "); 730 | bleGamepad.press(BUTTON_12); 731 | currentButtonStates[2] = 1; 732 | } else { 733 | ////Serial.println(" "); 734 | bleGamepad.release(BUTTON_12); 735 | currentButtonStates[2] = 0; 736 | } 737 | // Latency Test Pin 738 | // Intended for BlueRetro latency test pad to measure BLEGamepad update speeds without modifying the Controller 739 | if (digitalRead(latPin) == LOW) { 740 | Serial.print("Latency Test Press"); 741 | bleGamepad.press(BUTTON_3); 742 | currentButtonStates[2] = 1; 743 | } else { 744 | bleGamepad.release(BUTTON_3); 745 | currentButtonStates[2] = 0; 746 | } 747 | 748 | // unsigned int wMark2 = uxTaskGetStackHighWaterMark(nullptr); 749 | // printf("Watermark - SendReport B loop %u\n", wMark2); 750 | 751 | // All Input states gathered Send Update if state has changed 752 | if (currentButtonStates != previousButtonStates){ 753 | for (byte currentIndex = 0; currentIndex < numOfButtons; currentIndex++) 754 | { 755 | previousButtonStates[currentIndex] = currentButtonStates[currentIndex]; 756 | } 757 | bleGamepad.sendReport(); 758 | } 759 | } 760 | // End Digital Mode - Process Input 761 | // ================================ 762 | 763 | 764 | // Invalid Data or no connection error state 765 | // ========================================= 766 | // Communication or wiring issue 767 | } else { 768 | for (int printByteCounter = 0; printByteCounter < modeCounter + 1; printByteCounter++) { 769 | int derpvar; 770 | derpvar=0; 771 | //Serial.println("Error: Gamepad may not be paired OR Pinout/Timing Incorrect"); 772 | //Uncomment lines for additional debug output 773 | //Serial.print(" -- Byte "); 774 | //Serial.print(printByteCounter); 775 | //Serial.print(" - "); 776 | //Serial.print(dataArrayBit0[printByteCounter]); 777 | //Serial.print(dataArrayBit1[printByteCounter]); 778 | //Serial.print(dataArrayBit2[printByteCounter]); 779 | //Serial.print(dataArrayBit3[printByteCounter]); 780 | //Serial.print(dataArrayBit4[printByteCounter]); 781 | //Serial.print(dataArrayBit5[printByteCounter]); 782 | //Serial.print(dataArrayBit6[printByteCounter]); 783 | //Serial.print(dataArrayBit7[printByteCounter]); 784 | } 785 | // End Invalid Data 786 | //================= 787 | } 788 | // End Polling 789 | // =========== 790 | 791 | // Reset variables ready for data read. 792 | byteCounter = 0; 793 | nibble0Read = 0; 794 | 795 | // Setting Current Time for data read 796 | timeStamp = millis(); 797 | } 798 | 799 | 800 | // Check to see if the Controller is ready to send data and if we have read any data yet 801 | if (TLACK == HIGH and nibble0Read == 0) { 802 | digitalWrite(THPin, LOW); 803 | delayMicroseconds(4); // I don't think I need this 804 | digitalWrite(TRPin, LOW); 805 | delayMicroseconds(4); 806 | //delayMicroseconds(1); 807 | } 808 | 809 | 810 | // If the 1st nibble is being sent and we havn't read the 1st nibble yet get the data from the pins 811 | if (TLACK == LOW and nibble0Read == 0) { 812 | // Sets ready to read the 1st nibble of data 813 | digitalWrite(TRPin, HIGH); 814 | delayMicroseconds(8); //For ESP32 with lower setting get random button press 815 | 816 | // Read the Data for the 1st Nibble 817 | dataArrayBit0[byteCounter] = digitalRead(dataPinD0); 818 | dataArrayBit1[byteCounter] = digitalRead(dataPinD1); 819 | dataArrayBit2[byteCounter] = digitalRead(dataPinD2); 820 | dataArrayBit3[byteCounter] = digitalRead(dataPinD3); 821 | 822 | if (dataArrayBit0[byteCounter] > 1) { 823 | dataArrayBit0[byteCounter] = 0; 824 | } 825 | if (dataArrayBit1[byteCounter] > 1) { 826 | dataArrayBit1[byteCounter] = 0; 827 | } 828 | if (dataArrayBit2[byteCounter] > 1) { 829 | dataArrayBit2[byteCounter] = 0; 830 | } 831 | if (dataArrayBit3[byteCounter] > 1) { 832 | dataArrayBit3[byteCounter] = 0; 833 | } 834 | 835 | // 1st Nibble has been read 836 | nibble0Read = 1; 837 | 838 | // Check if we are reading data for Analogue mode or Digital Mode 839 | // The mode defines the number of Bytes we need to read 840 | if (byteCounter == 0) { 841 | if ((dataArrayBit0[0] == 1 and dataArrayBit1[0] == 0 and dataArrayBit2[0] == 0 and dataArrayBit3[0] == 0) or (dataArrayBit0[0] == 0 and dataArrayBit1[0] == 1 and dataArrayBit2[0] == 1 and dataArrayBit3[0] == 0) or (dataArrayBit0[0] == 1 and dataArrayBit1[0] == 1 and dataArrayBit2[0] == 1 and dataArrayBit3[0] == 0) ) { 842 | modeCounter = AnalogMode; 843 | } else { 844 | modeCounter = DigitalMode; 845 | } 846 | } 847 | } 848 | 849 | // Check if the 2nd Nibble is being sent and if we have already read the 1st Nibble 850 | if (TLACK == HIGH and nibble0Read == 1) { 851 | // Sets ready to read the 2nd Nibble 852 | digitalWrite(TRPin, LOW); 853 | delayMicroseconds(8); 854 | // Read the data for the 2nd Nibble 855 | dataArrayBit4[byteCounter] = digitalRead(dataPinD0); 856 | dataArrayBit5[byteCounter] = digitalRead(dataPinD1); 857 | dataArrayBit6[byteCounter] = digitalRead(dataPinD2); 858 | dataArrayBit7[byteCounter] = digitalRead(dataPinD3); 859 | if (dataArrayBit4[byteCounter] > 1) { 860 | dataArrayBit4[byteCounter] = 0; 861 | } 862 | if (dataArrayBit5[byteCounter] > 1) { 863 | dataArrayBit5[byteCounter] = 0; 864 | } 865 | if (dataArrayBit6[byteCounter] > 1) { 866 | dataArrayBit6[byteCounter] = 0; 867 | } 868 | if (dataArrayBit7[byteCounter] > 1) { 869 | dataArrayBit7[byteCounter] = 0; 870 | } 871 | 872 | // Reset the 1st Nibble read variable, ready for the next Byte of data 873 | nibble0Read = 0; 874 | 875 | // Increase the Byte counter by 1 so we are ready to read the next Byte of data 876 | byteCounter++; 877 | } 878 | 879 | // Time Stamp Routines to check if data has paused. 880 | currentMillis = millis(); 881 | 882 | if (currentMillis - timeStamp >= interval) { 883 | // Resetting All Variables 884 | timeStamp = millis(); 885 | digitalWrite(THPin, HIGH); 886 | digitalWrite(TRPin, HIGH); 887 | byteCounter = 0; 888 | nibble0Read = 0; 889 | Serial.println("Resetting"); 890 | } 891 | } 892 | //============================= 893 | // End Controller related items 894 | 895 | // Pin anything that isn't the controller or bluetooth to core 0 896 | //============================================================== 897 | 898 | void Task1code( void * pvParameters ){ 899 | //Serial.print("OLED Update ESP Core "); 900 | //Serial.println(xPortGetCoreID()); 901 | 902 | for(;;){ 903 | //Serial.print("timeStamp val" ); 904 | if (timeStamp == 0){ 905 | //bootLogo(); 906 | timeStamp = 1; 907 | } 908 | //oled_Updates(); 909 | //getBatStat(); 910 | Serial.print(" "); 911 | } 912 | } 913 | // End Core Pinning 914 | //================= 915 | 916 | 917 | // Controller and Bluetooth Jobs here 918 | void Task2code( void * pvParameters ){ 919 | //Serial.print("Task2code running on core "); 920 | //Serial.println(xPortGetCoreID()); 921 | 922 | for(;;){ 923 | //debug 924 | // unsigned int wMark3 = uxTaskGetStackHighWaterMark(nullptr); 925 | // printf("Watermark before FOR Loop is %u\n", wMark3); 926 | 927 | pollController(); 928 | //debug 929 | // unsigned int wMark = uxTaskGetStackHighWaterMark(nullptr); 930 | // printf("Watermark after FOR Loop is %u\n", wMark); 931 | } 932 | } 933 | 934 | 935 | // This is the main loop due to threading in arduino + core tasking in RTOS 936 | //========================================================================== 937 | void setup() { 938 | // Check ESP Core 939 | //Serial.print("Setup Core"); 940 | //Serial.println(xPortGetCoreID()); 941 | 942 | 943 | // Start BLE Gamepad 944 | BleGamepadConfiguration bleGamepadConfig; 945 | bleGamepadConfig.setControllerType(CONTROLLER_TYPE_GAMEPAD); // CONTROLLER_TYPE_JOYSTICK, CONTROLLER_TYPE_GAMEPAD (DEFAULT), CONTROLLER_TYPE_MULTI_AXIS 946 | bleGamepadConfig.setButtonCount(numOfButtons); 947 | bleGamepadConfig.setWhichAxes(enableX, enableY, enableZ, enableRX, enableRY, enableRZ, enableSlider1, enableSlider2); // Can also be done per-axis individually. All are true by default 948 | bleGamepadConfig.setHatSwitchCount(numOfHatSwitches); // 1 by default 949 | bleGamepadConfig.setAxesMin(0x8001); // -32767 --> int16_t - 16 bit signed integer - Can be in decimal or hexadecimal 950 | // 951 | //bleGamepadConfig.setAxesMin(0x0001); // -32767 --> int16_t - 16 bit signed integer - Can be in decimal or hexadecimal 952 | bleGamepadConfig.setAxesMax(0x7FFF); // 32767 --> int16_t - 16 bit signed integer - Can be in decimal or hexadecimal 953 | // Other Available Special Button Options 954 | // bleGamepadConfig.setIncludeStart(true); 955 | // bleGamepadConfig.setIncludeSelect(true); 956 | // bleGamepadConfig.setIncludeMenu(true); 957 | // bleGamepadConfig.setIncludeHome(true); 958 | // bleGamepadConfig.setIncludeBack(true); 959 | // bleGamepadConfig.setIncludeVolumeInc(true); 960 | // bleGamepadConfig.setIncludeVolumeDec(true); 961 | // bleGamepadConfig.setIncludeVolumeMute(true); 962 | // Use or disable BLEGampead AutoReporting 963 | // AutoReport can impact latency negatively with high speed of updates 964 | bleGamepadConfig.setAutoReport(false); // This is true by default 965 | bleGamepad.begin(&bleGamepadConfig); 966 | 967 | 968 | // Setting input and output pins for Comms with Controller 969 | // Use Heavy and Lightwing pinout settings instead of changing this 970 | //==========================// Pin 1 = +5V 971 | pinMode(dataPinD1, INPUT); // Pin 2 = Data D1 972 | pinMode(dataPinD0, INPUT); // Pin 3 = Data D0 973 | pinMode(THPin, OUTPUT); // Pin 4 = TH Select 974 | pinMode(TRPin, OUTPUT); // Pin 5 = TR Request 975 | pinMode(TLPin, INPUT); // Pin 6 = TL Response 976 | pinMode(dataPinD3, INPUT); // Pin 7 = Data D3 977 | pinMode(dataPinD2, INPUT); // Pin 8 = Data D2 978 | //==========================// Pin 9 = GND 979 | 980 | // Set pin state to begin reading controller state 981 | digitalWrite(THPin, HIGH); 982 | digitalWrite(TRPin, HIGH); 983 | 984 | // Define Latency test pin 985 | pinMode(latPin,INPUT_PULLUP ); 986 | 987 | 988 | // Setup Serial comms for Serial Monitor 989 | Serial.begin(115200); 990 | Serial.print("Device Booting...."); 991 | 992 | // Creates Tasks for Core 0 993 | xTaskCreatePinnedToCore( 994 | Task1code, /* Task function. */ 995 | "Task1", /* name of task. */ 996 | 5000, /* Stack size of task */ //originally 10k 997 | NULL, /* parameter of the task */ 998 | 3, /* priority of the task */ 999 | &Task1, /* Task handle to keep track of created task */ 1000 | 0); /* pin task to core 0 */ 1001 | 1002 | //Creates Tasks for Core 0 1003 | xTaskCreatePinnedToCore( 1004 | Task2code, /* Task function. */ 1005 | "Task2", /* name of task. */ 1006 | 5000, /* Stack size of task */ 1007 | NULL, /* parameter of the task */ 1008 | 2, /* priority of the task */ 1009 | &Task2, /* Task handle to keep track of created task */ 1010 | 1); /* pin task to core 1 */ 1011 | 1012 | } 1013 | 1014 | 1015 | // Loop will always run on Core 1 1016 | void loop(){ 1017 | // Putting stuff here will break some automatic aspects of RTOS task cleanup 1018 | // All tasks/jobs should be performed from setup loop 1019 | } 1020 | 1021 | //Currently unused setup for Motion Controls 1022 | // All this code is trashed and intermixed (sorry) but the idea is 1023 | // initializeSensor -> setupMPU -> GetTiltMpu 1024 | void initializeSensor(){ 1025 | // Perfrom full reset as per MPU-6000/MPU-6050 Register Map and Descriptions, Section 4.28, pages 40 to 41. 1026 | // performing full device reset, disables temperature sensor, disables SLEEP mode 1027 | Wire.beginTransmission(0x68); // Device address. 1028 | Wire.write(0x6B); // PWR_MGMT_1 register. 1029 | Wire.write(0b10001000); // DEVICE_RESET, TEMP_DIS. 1030 | Wire.endTransmission(); 1031 | delay(100); // Wait for reset to complete. 1032 | 1033 | Wire.beginTransmission(0x68); // Device address. 1034 | Wire.write(0x68); // SIGNAL_PATH_RESET register. 1035 | Wire.write(0b00000111); // GYRO_RESET, ACCEL_RESET, TEMP_RESET. 1036 | Wire.endTransmission(); 1037 | delay(100); // Wait for reset to complete. 1038 | 1039 | // Disable SLEEP mode because the reset re-enables it. Section 3, PWR_MGMT_1 register, page 8. 1040 | Wire.beginTransmission(MPU_addr); // Device address. 1041 | Wire.write(0x6B); // PWR_MGMT_1 register. 1042 | Wire.write(0b00001000); // SLEEP = 0, TEMP_DIS = 1. 1043 | Wire.endTransmission(); 1044 | } 1045 | // ============= 1046 | // End Start MPU 1047 | 1048 | void setupMPU() { 1049 | //Setup Accelerometer 1050 | //Wire.begin(); 1051 | 1052 | //Configure MPU Connectivity 1053 | //Wire.beginTransmission(MPU_addr); 1054 | //Wire.write(0x68); 1055 | //Wire.write(0x00); // Make reset - place a 0 into the 6B register 1056 | //Wire.endTransmission(true); 1057 | //Wire.write(0); 1058 | //Wire.endTransmission(true); 1059 | 1060 | 1061 | //Adafruit_MPU6050 mpu; 1062 | //if (!Wire.begin()) { 1063 | // Serial.println("Failed to find MPU6050 chip"); 1064 | // while (1) { 1065 | // delay(10); 1066 | // } 1067 | //} 1068 | //Serial.println("MPU6050 Found!"); 1069 | //Setup Accelerometer End 1070 | } 1071 | 1072 | 1073 | 1074 | void getTiltMPU() { 1075 | //sensors_event_t a, g, temp; 1076 | //mpu.getEvent(&a, &g, &temp); 1077 | //Wire.begin(); 1078 | 1079 | //Configure MPU Connectivity 1080 | //Wire.beginTransmission(MPU_addr); 1081 | //Wire.write(0x68); 1082 | //Wire.write(0); 1083 | //Wire.endTransmission(true); 1084 | 1085 | //Serial.begin(115200); 1086 | //void loop(){ 1087 | 1088 | //Get Data from MPU 1089 | //Wire.beginTransmission(MPU_addr); 1090 | //Wire.write(0x3B); 1091 | //Wire.endTransmission(false); 1092 | //Wire.requestFrom(MPU_addr,14,true); 1093 | 1094 | //Store MPU Values 1095 | //AcX=Wire.read()<<8|Wire.read(); 1096 | //AcY=Wire.read()<<8|Wire.read(); 1097 | //AcZ=Wire.read()<<8|Wire.read(); 1098 | 1099 | Serial.print("MPU X Y Z"); 1100 | //Serial.print(a.acceleration.x, 1); 1101 | //Serial.print(a.acceleration.y, 1); 1102 | //Serial.print(a.acceleration.z, 1); 1103 | Serial.print("MPU GYRO"); 1104 | //Serial.print(g.gyro.x, 1); 1105 | //Serial.print(g.gyro.y, 1); 1106 | //Serial.print(g.gyro.z, 1); 1107 | 1108 | //Convert stored values from Rads to Degrees 1109 | //int xAng = map(AcX,minVal,maxVal,-90,90); 1110 | //int yAng = map(AcY,minVal,maxVal,-90,90); 1111 | //int zAng = map(AcZ,minVal,maxVal,-90,90); 1112 | //x= RAD_TO_DEG * (atan2(-yAng, -zAng)+PI); 1113 | //y= RAD_TO_DEG * (atan2(-xAng, -zAng)+PI); 1114 | //z= RAD_TO_DEG * (atan2(-yAng, -xAng)+PI); 1115 | 1116 | //Print current angles 1117 | //Serial.print("AngleX "); 1118 | //Serial.println(x); 1119 | //Serial.print("AngleY "); 1120 | //Serial.println(y); 1121 | //display.clearDisplay(); 1122 | //display.setTextSize(1); // Normal 1:1 pixel scale 1123 | //display.setTextColor(SSD1306_WHITE, BLACK); // Draw white text 1124 | //display.setCursor(0,10); 1125 | //display.println("Tilt: On"); 1126 | //display.setCursor(0,20); // r 1127 | //String derp1 = " X Axis: " + String(x); 1128 | //String derp2 = " Y Axis: " + String(y); 1129 | //display.clearDisplay(); 1130 | //display.println(derp1); 1131 | //display.println(F("X Axis " + String(x))); 1132 | //display.setCursor(0,30); 1133 | //display.println(derp2); 1134 | //display.println(F("y Axis " + String(y))); 1135 | //display.display(); 1136 | // 1137 | 1138 | //Limit Degrees of angle based on comfort prior to converting to BLEGamepad Vals 1139 | //Limit X 1140 | if (x < 160){ 1141 | x=160; 1142 | } 1143 | if (x > 200){ 1144 | x=200; 1145 | } 1146 | //Limit Y 1147 | if (y < 160){ 1148 | y=160; 1149 | } 1150 | if (y > 200){ 1151 | y=200; 1152 | } 1153 | 1154 | //Define DeadZones 1155 | //Will require calibration based on your install 1156 | // 1157 | //Create X deadzone 1158 | if ((x >= 170) && (x <= 190)){ 1159 | x=180; 1160 | } 1161 | // 1162 | //Create Y deadzone 1163 | if ((y >= 170) && (y <= 190)){ 1164 | y=180; 1165 | } 1166 | 1167 | //Map Values to Gamepad Ranges 1168 | xmap = map (x, 160, 200, 0, 32767); 1169 | ymap = map (y, 160, 200, 0, 32767); 1170 | //Print Mapped values 1171 | //Serial.print("Xmap BleG: "); 1172 | //Serial.println(xmap); 1173 | //Serial.print("Ymap BleG: "); 1174 | //Serial.println(ymap); 1175 | 1176 | Serial.println("-----------------------------------------"); 1177 | 1178 | } 1179 | -------------------------------------------------------------------------------- /main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | // Add BLE Gamepad 4 | #include 5 | // Add OLED 6 | #include 7 | #include 8 | // Add Adafruit Sensor Libs for Tilt Controls 9 | #include 10 | #include 11 | 12 | //==================================================== 13 | // __________.____ ___________.________ _________ 14 | // \______ \ | \_ _____/ \_____ \\______ \ 15 | // | | _/ | | __)_ _(__ < | | \ 16 | // | | \ |___ | \ / \| ` \ 17 | // |______ /_______ \/_______ / /______ /________ / 18 | // \/ \/ \/ \/ \/ 19 | // Greetz and massive thanks to Hexfreq for being a complete bad ass 20 | // 21 | // All reliable controller code foundation is from [at]Hexfreq 22 | // All bugs, poor code choices, and existing PCB designs courtesy of [at]GamingNJncos 23 | // Cheers to [at]Arthrimus for lighting this fire on accident 24 | // 25 | // As I have limited time for mainting this codebase long term attempts have been made to over document code provided 26 | // No Warranty expressed or implied 27 | //==================================================== 28 | 29 | 30 | // OLED Screen Settings 31 | #define SCREEN_WIDTH 128 // OLED display width, in pixels 32 | #define SCREEN_HEIGHT 64 // OLED display height, in pixels 33 | #define OLED_RESET 4 // Reset pin # (or -1 if sharing Arduino reset pin) 34 | #define SCREEN_ADDRESS 0x3C ///< See datasheet for Address; 0x3D for 128x64, 0x3C for 128x32 35 | Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET); 36 | 37 | // Establish tasks for ESP32 CPU Core Pinning 38 | TaskHandle_t Task1; 39 | TaskHandle_t Task2; 40 | 41 | // BLEGamePad Settings 42 | // =================== 43 | // Values are Advertised Name, Manufacturer, Battery level 44 | // Note the battery value is not from a read value here but later blegamepad examples support live update vs 1 time poll 45 | BleGamepad bleGamepad("Reala", "GamingNJncos.HexFreq", 100); 46 | // Purposely set high to permit ease of future additional button combinationsfor to send Meta, home button, etc 47 | #define numOfButtons 12 48 | #define numOfHatSwitches 1 //DPAD (not Analog pad) 49 | #define enableX true // Analog Pad 50 | #define enableY true // Analog Pad 51 | #define enableRX true // Triggers 52 | #define enableRY true // Triggers 53 | // Additional button options currently disabled 54 | #define enableZ false 55 | #define enableRZ false 56 | #define enableSlider1 false 57 | #define enableSlider2 false 58 | #define enableRudder false 59 | #define enableThrottle false 60 | #define enableAccelerator false 61 | #define enableBrake false 62 | #define enableSteering false 63 | // End BLEGamePad Settings 64 | 65 | // Button State Tracking 66 | // This nets a significant advantage in performance vs not tracking states 67 | // BLE expects ACK per packet so flooding "not pressed" or identical data can add extreme delays 68 | // 69 | byte previousButtonStates[12]; 70 | byte currentButtonStates[12]; 71 | // Button State Array Map 72 | // ====================== 73 | // MODE, DPAD, START, A, B, C, X, Y, Z, L, R, Analog PAD 74 | // [0-1] [0-8] [- Binary Values 0-1 -] [0-15] [0-127,0-127] 75 | // Note Left and Right stick in digital mode will return Binary [0-1] vs [0-15] value in analog mode 76 | 77 | // Pinmappings for your PCB version 78 | // It is manditory you select the correct pinout for your board type 79 | 80 | // Feather HeavyWing to Controller Pin Mapping 81 | // =========================================== 82 | //PCB: https://oshpark.com/shared_projects/ki7HbZV4 83 | // 84 | //int dataPinD0 = 32; // Arthrimus Pin 3 = Data D0 85 | //int dataPinD1 = 14; // Arthrimus Pin 2 = Data D1 86 | //int dataPinD2 = 13; // Arthrimus Pin 8 = Data D2 87 | //int dataPinD3 = 12; // Arthrimus Pin 7 = Data D3 88 | //int THPin = 15; // Arthrimus Pin 4 = TH Select 89 | //int TRPin = 33; // Arthrimus Pin 5 = TR Request 90 | //int TLPin = 27; // Arthrimus Pin 6 = TL Response 91 | //int batPin = A13; // Used by BLEGamepad library 92 | //int latPin = 4; // Latency testing pin 93 | // End Feather HeavyWing to Controller Pin Mapping 94 | 95 | 96 | // Feather LightWing to Controller Pin Mapping 97 | // =========================================== 98 | // PCB: https://oshpark.com/shared_projects/7RqwDWJT 99 | // 100 | int dataPinD0 = 12; // Arthrimus Pin 3 = Data D0 101 | int dataPinD1 = 13; // Arthrimus Pin 2 = Data D1 102 | int dataPinD2 = 14; // Arthrimus Pin 8 = Data D2 103 | int dataPinD3 = 32; // Arthrimus Pin 7 = Data D3 104 | int THPin = 27; // Arthrimus Pin 4 = TH Select 105 | int TRPin = 33; // Arthrimus Pin 5 = TR Request 106 | int TLPin = 15; // Arthrimus Pin 6 = TL Response 107 | int batPin = A13; // Used by BLEGamepad library 108 | int latPin = 4; // Latency testing pin 109 | // End Feather HeavyWing to Controller Pin Mapping 110 | // End all Pin Mappings 111 | 112 | // BleGamepad Variables 113 | // ==================== 114 | int16_t bleAnalogX; //Analog pad X Axis Output 115 | int16_t bleAnalogY; //Analog pad Y Axis Output 116 | // DeadZone defined as smallest value to exceed before sending in blegamepad report function 117 | int16_t bleDeadZone = 400; //Set Deadzone via minimum number 118 | // Analog Triggers 119 | int16_t bleRTrigger; //Right Trigger Analog Output 120 | int16_t bleLTrigger; //Left Trigger Analog Output 121 | // Battery Monitoring variables for BLEGamepad 122 | // Note BLE Gamepad can read battery percent but can not check to transmit value after initial connection 123 | int adcRead = 0; 124 | int batVolt = 0; 125 | // End BleGamepad Variables 126 | 127 | 128 | // Controller protocol and timing variables 129 | // ======================================== 130 | // Data Array for each byte from protocol 131 | boolean dataArrayBit0[8]; 132 | boolean dataArrayBit1[8]; 133 | boolean dataArrayBit2[8]; 134 | boolean dataArrayBit3[8]; 135 | boolean dataArrayBit4[8]; 136 | boolean dataArrayBit5[8]; 137 | boolean dataArrayBit6[8]; 138 | boolean dataArrayBit7[8]; 139 | // Assorted Protocol Variables 140 | int nibble0Read = 0; // Confirm 1st Nibble has been read 141 | int byteCounter = 0; // Current Byte Tracker 142 | int modeCounter = 4; // Reference for which mode we are currently in Analogue or Digital 143 | int DecodeData = 1; // Controller Successfully Decoded Data Variable 144 | int curMode = 0; // This stores the Controller Mode to pass between ESP cores 145 | // Assorted timing oriented variables 146 | unsigned long timeStamp = 0; // Store time of last update 147 | unsigned long lastTimeStamp = 0; // Store Last time for comparison 148 | const long interval = 1000; // Data update check timing 149 | unsigned long currentMillis = millis(); // check timing 150 | // Directional Variables 151 | int upDownValue = 0; 152 | int leftRightValue = 0; 153 | // Trigger Variables 154 | int ltValue = 0; 155 | int rtValue = 0; 156 | // Variables for Data send request and Acknowledgments 157 | int THSEL = 0; 158 | int TRREQ = 0; 159 | int TLACK = 0; 160 | // Sets the number of Bytes required for Digital or Analogue Modes 161 | int AnalogMode = 6; 162 | int DigitalMode = 3; 163 | // End Controller protocol and timing variables 164 | 165 | 166 | // Gyro and Accelerometer Variables 167 | // ================================= 168 | // Gyro is currently disabled in code but my terrible examples should be useful if you wish to use this feature 169 | const int MPU_addr=0x68; // Define Gyro I2C Address 170 | int16_t AcX,AcY,AcZ,Tmp,GyX,GyY,GyZ; // Store all values from MPU 171 | int minVal=265; 172 | int maxVal=402; 173 | double x; 174 | double y; 175 | double z; 176 | // Converted XY Values to BLEGamepad ranges (negative to positive 32K 177 | int xmap; 178 | int ymap; 179 | // Max Values to limit full range of 360 degree motion. 180 | // Will require calibration with your MPU-6050 to identify most comfortable ranges 181 | // Suggest 35-45 Degrees or less relatively comfortable for most use cases 182 | int xmax = 200; 183 | int ymax = 200; 184 | int xmin = 160; 185 | int ymin = 160; 186 | // End Gyro and Accelerometer Variables 187 | 188 | 189 | // Boot Logo 190 | // ========== 191 | //Set Image Size for Boot Logo 192 | #define imageWidth 128 193 | #define imageHeight 64 194 | // Image to be displayed 195 | // These can be changed and alternatively set as animations 196 | // Create with "LCD Image Converter" tool or similar 197 | // https://lcd-image-converter.riuson.com/en/about/ 198 | const unsigned char myBitmap [] PROGMEM= 199 | { 200 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 201 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 202 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 203 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 204 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 205 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 206 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 207 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 208 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 209 | 0x0F, 0xFE, 0x0C, 0x00, 0xF6, 0x0C, 0x3C, 0x00, 0x60, 0x41, 0x80, 0x87, 0xFF, 0xF1, 0x80, 0x1E, 210 | 0x1E, 0x3F, 0xFB, 0xE7, 0x9E, 0x0E, 0x3E, 0x00, 0xE7, 0xBE, 0x7F, 0x03, 0xBF, 0x1E, 0xE0, 0x0E, 211 | 0x3C, 0x03, 0xE0, 0x7E, 0x03, 0x1E, 0x3E, 0x00, 0xE4, 0x1E, 0x0F, 0x03, 0x1E, 0x1E, 0x78, 0x0C, 212 | 0x3E, 0x01, 0xE0, 0x1E, 0x01, 0x1E, 0x3E, 0x01, 0xF0, 0x1E, 0x0F, 0x03, 0x1E, 0x0F, 0x3C, 0x0C, 213 | 0x3F, 0x01, 0xE1, 0x3E, 0x00, 0x3F, 0x1F, 0x03, 0xF0, 0x1E, 0x0F, 0x03, 0x1E, 0x0F, 0x3F, 0x0C, 214 | 0x1F, 0xC1, 0xE3, 0x3C, 0x00, 0x3D, 0x1F, 0x03, 0xD8, 0x1E, 0x0F, 0x03, 0x1E, 0x1E, 0x3F, 0x8C, 215 | 0x0F, 0xF1, 0xFF, 0x3C, 0x00, 0x79, 0x9F, 0x87, 0xD8, 0x1E, 0x0F, 0x03, 0x1E, 0x3E, 0x3F, 0xCC, 216 | 0x03, 0xF9, 0xFF, 0x3C, 0x00, 0xF9, 0x8F, 0x87, 0x8C, 0x1E, 0x0F, 0x03, 0x1E, 0xF0, 0x27, 0xEC, 217 | 0x00, 0xFD, 0xE3, 0x3C, 0x1F, 0xF0, 0xCF, 0xCF, 0x8C, 0x1E, 0x0F, 0x03, 0x1E, 0x78, 0x31, 0xFC, 218 | 0x40, 0x7D, 0xE0, 0x3E, 0x0F, 0xF0, 0xC7, 0xCF, 0x06, 0x1E, 0x0F, 0x03, 0x1E, 0x78, 0x30, 0xFC, 219 | 0x60, 0x3D, 0xE0, 0x3E, 0x07, 0xE0, 0x67, 0xDF, 0x06, 0x1E, 0x0F, 0x03, 0x1E, 0x3C, 0x30, 0x7C, 220 | 0x30, 0x3D, 0xE0, 0x7E, 0x07, 0xE0, 0x63, 0xFE, 0x03, 0x1E, 0x07, 0x87, 0x1E, 0x3E, 0x30, 0x3C, 221 | 0x3E, 0xF9, 0xF3, 0xFF, 0x0F, 0xE0, 0x73, 0xFE, 0x07, 0xBE, 0x07, 0xFE, 0x1F, 0x1F, 0x30, 0x0C, 222 | 0x1F, 0xE3, 0xFF, 0xE3, 0xFF, 0xE0, 0xFB, 0xFE, 0x07, 0xFF, 0x01, 0xF8, 0x3F, 0x87, 0xF0, 0x04, 223 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x00, 224 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 225 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 226 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 227 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 228 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 229 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 230 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 231 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 232 | }; 233 | // End Boot Logo 234 | 235 | 236 | // Boot Logo and Screen Setup 237 | // ========================== 238 | void bootLogo(){ 239 | //Check ESP Core Boot Logo is running on 240 | //Serial.print("BootLogo Core "); 241 | //Serial.println(xPortGetCoreID()); 242 | 243 | if(!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) { 244 | Serial.println(F("SSD1306 allocation failed")); 245 | for(;;); // Don't proceed, loop forever 246 | } 247 | // Rotate 248 | display.setRotation(2); 249 | // Clear the Buffer 250 | display.clearDisplay(); 251 | // Draw Animation 252 | // Draw Bitmap syntax 253 | // display.drawBitmap(x position, y position, bitmap data, bitmap width, bitmap height, color) 254 | display.drawBitmap(0, 32, myBitmap, 128, 32, WHITE); 255 | display.display(); 256 | // Set Delay for Boot Animation 257 | delay(1500); 258 | // Reset Display 259 | display.display(); 260 | display.setRotation(2); 261 | delay(10); 262 | // Clear the buffer 263 | display.clearDisplay(); 264 | delay(2000); 265 | //End Screen Setup 266 | } 267 | 268 | 269 | // Battery Related Checks 270 | // ====================== 271 | void getBatStat(){ 272 | // Check ESP Core 273 | //Serial.print("Battery Status Core "); 274 | //Serial.println(xPortGetCoreID()); 275 | 276 | adcRead = analogRead(batPin); 277 | batVolt = adcRead; 278 | // Multiply by 2 to correct reading for Featherwing Huzzah32 279 | //batVolt = adcRead * 2; 280 | 281 | int BatteryPct; 282 | int oldBatPct = 0; 283 | // Map Battery voltage range to a percentage 284 | BatteryPct = map (batVolt, 1200, 4200, 0, 100); 285 | // Make sure its populated on initial run 286 | if (oldBatPct < 1){ 287 | oldBatPct = BatteryPct; 288 | } 289 | // Super hack check to omit negative values from OLED Updates 290 | if (BatteryPct < 0 ){ 291 | BatteryPct = oldBatPct; 292 | } 293 | 294 | // Build and send screen update 295 | // ============================ 296 | String lcdBat; 297 | lcdBat = "Battery: " + String(BatteryPct) + "% "; 298 | //display.setCursor(0,50); 299 | //display.print("Battery "); 300 | //display.setCursor(0,50); 301 | //display.print(lcdBat); 302 | //display.display(); 303 | // End Battery Updates 304 | } 305 | 306 | // OLED Updates 307 | // ============ 308 | void oled_Updates(){ 309 | if (curMode== 0 ) { 310 | curMode = 0; 311 | display.setTextSize(1); // Normal 1:1 pixel scale 312 | display.setTextColor(SSD1306_WHITE, BLACK); // Draw white text 313 | display.setCursor(0,1); // Start at top-left corner 314 | display.println(F("Mode: Analog ")); 315 | display.display(); 316 | } else { 317 | curMode = 1; 318 | display.setTextSize(1); // Normal 1:1 pixel scale 319 | display.setTextColor(SSD1306_WHITE, BLACK); // Draw white text 320 | display.setCursor(0,1); // Start at top-left corner 321 | display.println(F("Mode: Digital ")); 322 | display.display(); 323 | } 324 | // End OLED Updates 325 | } 326 | 327 | 328 | // Controller related items 329 | // ======================== 330 | void pollController(){ 331 | //Check ESP Core Controller Polling Occurs on 332 | //Serial.print("Controller Polling Core "); 333 | //Serial.println(xPortGetCoreID()); 334 | 335 | //Make sure that we sync timestamp and last timestamp on first run 336 | if (lastTimeStamp = 0){ 337 | lastTimeStamp = timeStamp; 338 | //hacktest MPU initialization on first run 339 | //initializeSensor(); 340 | } 341 | 342 | // Start The Data Read 343 | // Is the Controller ready to send the data? 344 | TLACK = digitalRead(TLPin); 345 | 346 | // Have we read all the bytes needed for each mode? 347 | if (byteCounter > modeCounter) { 348 | // Set Not ready for Data Coms 349 | digitalWrite(THPin, HIGH); 350 | digitalWrite(TRPin, HIGH); 351 | 352 | // Are we using Analogue Mode or Digital? 353 | if (modeCounter == AnalogMode) { 354 | curMode = 0; 355 | currentButtonStates[0] = 0; 356 | } else { 357 | curMode = 1; 358 | currentButtonStates[0] = 1; 359 | } 360 | 361 | // Decoded Controller Data Successfully 362 | // Proceed based on Analog or Digital Mode 363 | // 364 | // Note that BLEgamepad check occurs in combination with successfully decoded data 365 | // This avoids a bug in NIMBLE where blegamepad could under some circumstances attempt button send prior to connecting and cause abort 366 | if (DecodeData == 1 && bleGamepad.isConnected()) { 367 | //Analog 368 | if (modeCounter == AnalogMode) { 369 | //Analog Mode - Process Input 370 | //Begin all Analog Mode data processing 371 | 372 | //DPAD 373 | if (dataArrayBit6[1] != 0 && dataArrayBit7[1] != 0 && dataArrayBit5[1] != 0 && dataArrayBit4[1] != 0){ 374 | bleGamepad.setHat1(DPAD_CENTERED); 375 | currentButtonStates[1] = 0; 376 | } 377 | if (dataArrayBit4[1] == 0 && (dataArrayBit7[1] != 0 && dataArrayBit6[1] != 0)) { 378 | //Serial.print(" U"); 379 | bleGamepad.setHat1(DPAD_UP); 380 | currentButtonStates[1] = 1; 381 | } 382 | if (dataArrayBit5[1] == 0 && (dataArrayBit7[1] != 0 && dataArrayBit6[1] != 0)) { 383 | //Serial.print(" D"); 384 | bleGamepad.setHat1(DPAD_DOWN); 385 | currentButtonStates[1] = 2; 386 | } 387 | if (dataArrayBit6[1] == 0 && (dataArrayBit4[1] != 0 && dataArrayBit5[1] != 0)) { 388 | //Serial.print(" L"); 389 | bleGamepad.setHat1(DPAD_LEFT); 390 | currentButtonStates[1] = 3; 391 | } 392 | if (dataArrayBit7[1] == 0 && (dataArrayBit4[1] != 0 && dataArrayBit5[1] != 0)) { 393 | //Serial.print(" R"); 394 | bleGamepad.setHat1(DPAD_RIGHT); 395 | currentButtonStates[1] = 4; 396 | } 397 | if (dataArrayBit4[1] == 0 && dataArrayBit6[1] == 0) { 398 | //Serial.print(" U/L"); 399 | bleGamepad.setHat1(DPAD_UP_LEFT); 400 | currentButtonStates[1] = 5; 401 | } 402 | if (dataArrayBit4[1] == 0 && dataArrayBit7[1] == 0) { 403 | //Serial.print(" U/R"); 404 | bleGamepad.setHat1(DPAD_UP_RIGHT); 405 | currentButtonStates[1] = 6; 406 | } 407 | if (dataArrayBit5[1] == 0 && dataArrayBit6[1] == 0) { 408 | //Serial.print(" D/L"); 409 | bleGamepad.setHat1(DPAD_DOWN_LEFT); 410 | currentButtonStates[1] = 7; 411 | } 412 | if (dataArrayBit5[1] == 0 && dataArrayBit7[1] == 0) { 413 | //Serial.print(" D/R"); 414 | bleGamepad.setHat1(DPAD_DOWN_RIGHT); 415 | currentButtonStates[1] = 8; 416 | } 417 | // End DPAD 418 | 419 | //Buttons 420 | if (dataArrayBit2[2] == 0) { 421 | Serial.print(" A"); 422 | bleGamepad.press(BUTTON_4); 423 | currentButtonStates[3] = 1; 424 | } else { 425 | //Serial.print(" "); 426 | bleGamepad.release(BUTTON_4); 427 | currentButtonStates[3] = 0; 428 | } 429 | if (dataArrayBit0[2] == 0) { 430 | //Serial.print(" B"); 431 | bleGamepad.press(BUTTON_1); 432 | currentButtonStates[4] = 1; 433 | } else { 434 | //Serial.print(" "); 435 | bleGamepad.release(BUTTON_1); 436 | currentButtonStates[4] = 0; 437 | } 438 | if (dataArrayBit1[2] == 0) { 439 | //Serial.print(" C"); 440 | bleGamepad.press(BUTTON_2); 441 | currentButtonStates[5] = 1; 442 | } else { 443 | //Serial.print(" "); 444 | bleGamepad.release(BUTTON_2); 445 | currentButtonStates[5] = 0; 446 | } 447 | if (dataArrayBit6[2] == 0) { 448 | //Serial.print(" X"); 449 | bleGamepad.press(BUTTON_7); 450 | currentButtonStates[6] = 1; 451 | } else { 452 | //Serial.print(" "); 453 | bleGamepad.release(BUTTON_7); 454 | currentButtonStates[6] = 0; 455 | } 456 | if (dataArrayBit5[2] == 0) { 457 | // Serial.print(" Y"); 458 | bleGamepad.press(BUTTON_5); 459 | currentButtonStates[7] = 1; 460 | } else { 461 | //Serial.print(" "); 462 | bleGamepad.release(BUTTON_5); 463 | currentButtonStates[7] = 0; 464 | } 465 | if (dataArrayBit4[2] == 0) { 466 | //Serial.print(" Z"); 467 | bleGamepad.press(BUTTON_8); 468 | currentButtonStates[8] = 1; 469 | } else { 470 | //Serial.print(" "); 471 | bleGamepad.release(BUTTON_8); 472 | currentButtonStates[8] = 0; 473 | } 474 | // Uncomment for L/R triggers to send digital in addition to analog values 475 | // May have wrong button_number defined here in the blegamepad.press setting 476 | //if (dataArrayBit3[3] == 0) { 477 | //Serial.print(" LT"); 478 | // bleGamepad.press(BUTTON_5); 479 | // currentButtonStates[9] = 1; 480 | //} else { 481 | //Serial.print(" "); 482 | // bleGamepad.release(BUTTON_5); 483 | // currentButtonStates[9] = 0; 484 | //} 485 | //if (dataArrayBit7[2] == 0) { 486 | //Serial.print(" RT"); 487 | // bleGamepad.press(BUTTON_6); 488 | // currentButtonStates[10] = 1; 489 | //} else { 490 | //Serial.print(" "); 491 | // bleGamepad.release(BUTTON_6); 492 | // currentButtonStates[10] = 1; 493 | //} 494 | if (dataArrayBit3[2] == 0) { 495 | //Serial.print(" Start "); 496 | bleGamepad.press(BUTTON_12); 497 | currentButtonStates[2] = 1; 498 | } else { 499 | bleGamepad.release(BUTTON_12); 500 | currentButtonStates[2] = 0; 501 | } 502 | // Latency Test Pin 503 | // Intended for BlueRetro latency button 504 | // Can be used for other additional buttons 505 | // 506 | // Note this sends "Start" but can be adjusted to press any other button 507 | if (digitalRead(latPin) == LOW) { 508 | //Serial.print("LAT"); 509 | bleGamepad.press(BUTTON_3); 510 | currentButtonStates[3] = 1; 511 | } else { 512 | bleGamepad.release(BUTTON_3); 513 | currentButtonStates[3] = 0; 514 | } 515 | 516 | // Up / Down 517 | if (dataArrayBit7[4] == 1) { 518 | upDownValue = dataArrayBit0[5] + (dataArrayBit1[5]<<1) + (dataArrayBit2[5]<<2) + (dataArrayBit3[5]<<3) + (dataArrayBit4[4]<<4) + (dataArrayBit5[4]<<5) + (dataArrayBit6[4]<<6); 519 | //bleAnalogX = map(upDownValue, 0, 127, 1, 32767); 520 | bleAnalogX = map(upDownValue, 0, 127, 16384, 32767); 521 | 522 | } else { 523 | upDownValue = !dataArrayBit0[5] + (!dataArrayBit1[5]<<1) + (!dataArrayBit2[5]<<2) + (!dataArrayBit3[5]<<3) + (!dataArrayBit4[4]<<4) + (!dataArrayBit5[4]<<5) + (!dataArrayBit6[4]<<6); 524 | //bleAnalogX = map(upDownValue, 1, 127, -1, -32767); 525 | bleAnalogX = map(upDownValue, 0, 127, 16383, 1); 526 | } 527 | // Deadzone fix - X Axis 528 | // Value is checked based on map output 529 | //=======COMMENTED THIS OUT TEMPORARILY 530 | //if ((bleAnalogX < 0 && bleAnalogX > -500)||(bleAnalogX < 0 && bleAnalogX > -500)){ 531 | // bleAnalogX = 0; 532 | // } 533 | 534 | 535 | //Analog Debugs 536 | //Serial.print(" bleAnalogX "); 537 | //Serial.println(bleAnalogX); 538 | // Format Value to 3 Chars long 539 | //char buf[] = "000"; 540 | //sprintf(buf, "%03i", upDownValue); 541 | 542 | // Left / Right 543 | if (dataArrayBit7[3] == 1) { 544 | //Serial.print(" R = "); 545 | leftRightValue = dataArrayBit0[4] + (dataArrayBit1[4]<<1) + (dataArrayBit2[4]<<2) + (dataArrayBit3[4]<<3) + (dataArrayBit4[3]<<4) + (dataArrayBit5[3]<<5) + (dataArrayBit6[3]<<6); 546 | //bleAnalogY = map(leftRightValue, 1, 127, 1, 32767); 547 | bleAnalogY = map(leftRightValue, 0, 127, 16384, 32767); 548 | } else { 549 | leftRightValue = !dataArrayBit0[4] + (!dataArrayBit1[4]<<1) + (!dataArrayBit2[4]<<2) + (!dataArrayBit3[4]<<3) + (!dataArrayBit4[3]<<4) + (!dataArrayBit5[3]<<5) + (!dataArrayBit6[3]<<6); 550 | 551 | bleAnalogY = map(leftRightValue, 0, 127, 16383, 1); 552 | } 553 | //=======COMMENTED THIS OUT TEMPORARILY 554 | // Deadzone fix - Y Axis 555 | // Value is checked based on map output 556 | //if ((bleAnalogY > 0 && bleAnalogY < 500)||(bleAnalogY < 0 && bleAnalogY > -500)){ 557 | // bleAnalogY = 0; 558 | //} 559 | // Set Analog Pad/Left Thumb Stick X Y Values in Blegamepad 560 | // This is an easy place to flip the X and Yresults if backwards for your use case 561 | bleGamepad.setX(bleAnalogY); 562 | bleGamepad.setY(bleAnalogX); 563 | // Track State of Analog Sticks X and Y Values 564 | currentButtonStates[11] = bleAnalogX; 565 | currentButtonStates[12] = bleAnalogY; 566 | //unused at this time 567 | //currentButtonStates[13] = 0; 568 | //currentButtonStates[14] = 0; 569 | 570 | // Decode Triggers 571 | char trbuf[] = "00"; 572 | //Left Trigger 573 | ltValue = (dataArrayBit7[6] * 8) + (dataArrayBit6[6] * 4) + (dataArrayBit5[6] * 2) + (dataArrayBit4[6] * 1); 574 | //bleLTrigger = map(ltValue, 0, 15, -32767, 32767); 575 | bleLTrigger = map(ltValue, 0, 15, 1, 32767); 576 | bleGamepad.setLeftTrigger(bleLTrigger); 577 | //Track State of L trigger 578 | currentButtonStates[9] = bleLTrigger; 579 | 580 | //sprintf(trbuf, "%02i", ltValue); 581 | //Serial.print(" LT = "); 582 | //Serial.print(trbuf); 583 | 584 | //Right Trigger 585 | rtValue = (dataArrayBit7[5] * 8) + (dataArrayBit6[5] * 4) + (dataArrayBit5[5] * 2) + (dataArrayBit4[5] * 1); 586 | //bleRTrigger = map(rtValue, 0, 15, 32767, -32767); 587 | bleRTrigger = map(rtValue, 0, 15, 1, 32767); 588 | bleGamepad.setRightTrigger(bleRTrigger); 589 | //Track State of R trigger 590 | currentButtonStates[10] = bleRTrigger; 591 | //sprintf(trbuf, "%02i", rtValue); 592 | //Serial.print(" RT = "); 593 | //Serial.print(trbuf); 594 | 595 | //Debug 596 | unsigned int wMark1 = uxTaskGetStackHighWaterMark(nullptr); 597 | printf("Watermark before SendReportA loop is is %u\n", wMark1); 598 | 599 | //All Input states gathered Send Update if current and previous states do not match 600 | //bleGamepad.sendReport(); 601 | if (currentButtonStates != previousButtonStates) 602 | { 603 | for (byte currentIndex = 0; currentIndex < numOfButtons; currentIndex++) 604 | { 605 | previousButtonStates[currentIndex] = currentButtonStates[currentIndex]; 606 | } 607 | 608 | bleGamepad.sendReport(); 609 | } 610 | //End all Analog Mode data 611 | 612 | 613 | } else { 614 | // Digital Mode - Process Input 615 | // ============================ 616 | 617 | // DPAD 618 | if (dataArrayBit6[1] != 0 && dataArrayBit7[1] != 0 && dataArrayBit5[1] != 0 && dataArrayBit4[1] != 0){ 619 | bleGamepad.setHat1(DPAD_CENTERED); 620 | currentButtonStates[1] = 0; 621 | } 622 | if (dataArrayBit4[0] == 0 && (dataArrayBit7[0] != 0 || dataArrayBit6[0] != 0)) { 623 | //Serial.print(" U "); 624 | bleGamepad.setHat1(DPAD_UP); 625 | currentButtonStates[1] = 1; 626 | } 627 | if (dataArrayBit5[0] == 0 && (dataArrayBit7[0] != 0 || dataArrayBit6[0] != 0)) { 628 | //Serial.print(" D "); 629 | bleGamepad.setHat1(DPAD_DOWN); 630 | currentButtonStates[1] = 2; 631 | } 632 | if (dataArrayBit6[0] == 0 && (dataArrayBit4[0] !=0 || dataArrayBit5[0] != 0)) { 633 | //Serial.print(" L "); 634 | bleGamepad.setHat1(DPAD_LEFT); 635 | currentButtonStates[1] = 3; 636 | } 637 | if (dataArrayBit7[0] == 0 && (dataArrayBit4[0] !=0 || dataArrayBit5[0] != 0)) { 638 | //Serial.print(" R "); 639 | bleGamepad.setHat1(DPAD_RIGHT); 640 | currentButtonStates[1] = 4; 641 | } 642 | if (dataArrayBit4[0] == 0 && dataArrayBit6[0] == 0) { 643 | //Serial.print(" U/L "); 644 | bleGamepad.setHat1(DPAD_UP_LEFT); 645 | currentButtonStates[1] = 5; 646 | } 647 | if (dataArrayBit4[0] == 0 && dataArrayBit7[0] == 0) { 648 | //Serial.print(" U/R "); 649 | bleGamepad.setHat1(DPAD_UP_RIGHT); 650 | currentButtonStates[1] = 6; 651 | } 652 | if (dataArrayBit5[0] == 0 && dataArrayBit6[0] == 0) { 653 | //Serial.print(" D/L "); 654 | bleGamepad.setHat1(DPAD_DOWN_LEFT); 655 | currentButtonStates[1] = 7; 656 | } 657 | if (dataArrayBit5[0] == 0 && dataArrayBit7[0] == 0) { 658 | //Serial.print(" D/R "); 659 | bleGamepad.setHat1(DPAD_DOWN_RIGHT); 660 | currentButtonStates[1] = 8; 661 | } 662 | 663 | //Buttons 664 | if (dataArrayBit2[1] == 0) { 665 | //Serial.print(" A "); 666 | bleGamepad.press(BUTTON_4); 667 | currentButtonStates[3] = 1; 668 | } else { 669 | bleGamepad.release(BUTTON_4); 670 | currentButtonStates[3] = 0; 671 | } 672 | if (dataArrayBit0[1] == 0) { 673 | //Serial.print(" B "); 674 | bleGamepad.press(BUTTON_1); 675 | currentButtonStates[4] = 1; 676 | } else { 677 | bleGamepad.release(BUTTON_1); 678 | currentButtonStates[4] = 0; 679 | } 680 | if (dataArrayBit1[1] == 0) { 681 | //Serial.print(" C "); 682 | bleGamepad.press(BUTTON_2); 683 | currentButtonStates[5] = 1; 684 | } else { 685 | bleGamepad.release(BUTTON_2); 686 | currentButtonStates[5] = 0; 687 | } 688 | if (dataArrayBit6[1] == 0) { 689 | //Serial.print(" X "); 690 | bleGamepad.press(BUTTON_7); 691 | currentButtonStates[6] = 1; 692 | } else { 693 | bleGamepad.release(BUTTON_7); 694 | currentButtonStates[6] = 0; 695 | } 696 | if (dataArrayBit5[1] == 0) { 697 | //Serial.print(" Y "); 698 | bleGamepad.press(BUTTON_5); 699 | currentButtonStates[7] = 1; 700 | } else { 701 | bleGamepad.release(BUTTON_5); 702 | currentButtonStates[7] = 0; 703 | } 704 | if (dataArrayBit4[1] == 0) { 705 | //Serial.print(" Z "); 706 | bleGamepad.press(BUTTON_8); 707 | currentButtonStates[8] = 1; 708 | } else { 709 | bleGamepad.release(BUTTON_8); 710 | currentButtonStates[8] = 0; 711 | } 712 | if (dataArrayBit3[2] == 0) { 713 | //Serial.print(" LT "); 714 | bleGamepad.press(BUTTON_11); 715 | currentButtonStates[9] = 1; 716 | } else { 717 | bleGamepad.release(BUTTON_11); 718 | currentButtonStates[9] = 0; 719 | } 720 | if (dataArrayBit7[1] == 0) { 721 | //Serial.print(" RT "); 722 | bleGamepad.press(BUTTON_13); 723 | currentButtonStates[10] = 1; 724 | } else { 725 | bleGamepad.release(BUTTON_13); 726 | currentButtonStates[10] = 0; 727 | } 728 | if (dataArrayBit3[1] == 0) { 729 | //Serial.println(" Start "); 730 | bleGamepad.press(BUTTON_12); 731 | currentButtonStates[2] = 1; 732 | } else { 733 | ////Serial.println(" "); 734 | bleGamepad.release(BUTTON_12); 735 | currentButtonStates[2] = 0; 736 | } 737 | // Latency Test Pin 738 | // Intended for BlueRetro latency test pad to measure BLEGamepad update speeds without modifying the Controller 739 | if (digitalRead(latPin) == LOW) { 740 | Serial.print("Latency Test Press"); 741 | bleGamepad.press(BUTTON_3); 742 | currentButtonStates[2] = 1; 743 | } else { 744 | bleGamepad.release(BUTTON_3); 745 | currentButtonStates[2] = 0; 746 | } 747 | 748 | // unsigned int wMark2 = uxTaskGetStackHighWaterMark(nullptr); 749 | // printf("Watermark - SendReport B loop %u\n", wMark2); 750 | 751 | // All Input states gathered Send Update if state has changed 752 | if (currentButtonStates != previousButtonStates){ 753 | for (byte currentIndex = 0; currentIndex < numOfButtons; currentIndex++) 754 | { 755 | previousButtonStates[currentIndex] = currentButtonStates[currentIndex]; 756 | } 757 | bleGamepad.sendReport(); 758 | } 759 | } 760 | // End Digital Mode - Process Input 761 | // ================================ 762 | 763 | 764 | // Invalid Data or no connection error state 765 | // ========================================= 766 | // Communication or wiring issue 767 | } else { 768 | for (int printByteCounter = 0; printByteCounter < modeCounter + 1; printByteCounter++) { 769 | int derpvar; 770 | derpvar=0; 771 | //Serial.println("Error: Gamepad may not be paired OR Pinout/Timing Incorrect"); 772 | //Uncomment lines for additional debug output 773 | //Serial.print(" -- Byte "); 774 | //Serial.print(printByteCounter); 775 | //Serial.print(" - "); 776 | //Serial.print(dataArrayBit0[printByteCounter]); 777 | //Serial.print(dataArrayBit1[printByteCounter]); 778 | //Serial.print(dataArrayBit2[printByteCounter]); 779 | //Serial.print(dataArrayBit3[printByteCounter]); 780 | //Serial.print(dataArrayBit4[printByteCounter]); 781 | //Serial.print(dataArrayBit5[printByteCounter]); 782 | //Serial.print(dataArrayBit6[printByteCounter]); 783 | //Serial.print(dataArrayBit7[printByteCounter]); 784 | } 785 | // End Invalid Data 786 | //================= 787 | } 788 | // End Polling 789 | // =========== 790 | 791 | // Reset variables ready for data read. 792 | byteCounter = 0; 793 | nibble0Read = 0; 794 | 795 | // Setting Current Time for data read 796 | timeStamp = millis(); 797 | } 798 | 799 | 800 | // Check to see if the Controller is ready to send data and if we have read any data yet 801 | if (TLACK == HIGH and nibble0Read == 0) { 802 | digitalWrite(THPin, LOW); 803 | delayMicroseconds(4); // I don't think I need this 804 | digitalWrite(TRPin, LOW); 805 | delayMicroseconds(4); 806 | //delayMicroseconds(1); 807 | } 808 | 809 | 810 | // If the 1st nibble is being sent and we havn't read the 1st nibble yet get the data from the pins 811 | if (TLACK == LOW and nibble0Read == 0) { 812 | // Sets ready to read the 1st nibble of data 813 | digitalWrite(TRPin, HIGH); 814 | delayMicroseconds(8); //For ESP32 with lower setting get random button press 815 | 816 | // Read the Data for the 1st Nibble 817 | dataArrayBit0[byteCounter] = digitalRead(dataPinD0); 818 | dataArrayBit1[byteCounter] = digitalRead(dataPinD1); 819 | dataArrayBit2[byteCounter] = digitalRead(dataPinD2); 820 | dataArrayBit3[byteCounter] = digitalRead(dataPinD3); 821 | 822 | if (dataArrayBit0[byteCounter] > 1) { 823 | dataArrayBit0[byteCounter] = 0; 824 | } 825 | if (dataArrayBit1[byteCounter] > 1) { 826 | dataArrayBit1[byteCounter] = 0; 827 | } 828 | if (dataArrayBit2[byteCounter] > 1) { 829 | dataArrayBit2[byteCounter] = 0; 830 | } 831 | if (dataArrayBit3[byteCounter] > 1) { 832 | dataArrayBit3[byteCounter] = 0; 833 | } 834 | 835 | // 1st Nibble has been read 836 | nibble0Read = 1; 837 | 838 | // Check if we are reading data for Analogue mode or Digital Mode 839 | // The mode defines the number of Bytes we need to read 840 | if (byteCounter == 0) { 841 | if ((dataArrayBit0[0] == 1 and dataArrayBit1[0] == 0 and dataArrayBit2[0] == 0 and dataArrayBit3[0] == 0) or (dataArrayBit0[0] == 0 and dataArrayBit1[0] == 1 and dataArrayBit2[0] == 1 and dataArrayBit3[0] == 0) or (dataArrayBit0[0] == 1 and dataArrayBit1[0] == 1 and dataArrayBit2[0] == 1 and dataArrayBit3[0] == 0) ) { 842 | modeCounter = AnalogMode; 843 | } else { 844 | modeCounter = DigitalMode; 845 | } 846 | } 847 | } 848 | 849 | // Check if the 2nd Nibble is being sent and if we have already read the 1st Nibble 850 | if (TLACK == HIGH and nibble0Read == 1) { 851 | // Sets ready to read the 2nd Nibble 852 | digitalWrite(TRPin, LOW); 853 | delayMicroseconds(8); 854 | // Read the data for the 2nd Nibble 855 | dataArrayBit4[byteCounter] = digitalRead(dataPinD0); 856 | dataArrayBit5[byteCounter] = digitalRead(dataPinD1); 857 | dataArrayBit6[byteCounter] = digitalRead(dataPinD2); 858 | dataArrayBit7[byteCounter] = digitalRead(dataPinD3); 859 | if (dataArrayBit4[byteCounter] > 1) { 860 | dataArrayBit4[byteCounter] = 0; 861 | } 862 | if (dataArrayBit5[byteCounter] > 1) { 863 | dataArrayBit5[byteCounter] = 0; 864 | } 865 | if (dataArrayBit6[byteCounter] > 1) { 866 | dataArrayBit6[byteCounter] = 0; 867 | } 868 | if (dataArrayBit7[byteCounter] > 1) { 869 | dataArrayBit7[byteCounter] = 0; 870 | } 871 | 872 | // Reset the 1st Nibble read variable, ready for the next Byte of data 873 | nibble0Read = 0; 874 | 875 | // Increase the Byte counter by 1 so we are ready to read the next Byte of data 876 | byteCounter++; 877 | } 878 | 879 | // Time Stamp Routines to check if data has paused. 880 | currentMillis = millis(); 881 | 882 | if (currentMillis - timeStamp >= interval) { 883 | // Resetting All Variables 884 | timeStamp = millis(); 885 | digitalWrite(THPin, HIGH); 886 | digitalWrite(TRPin, HIGH); 887 | byteCounter = 0; 888 | nibble0Read = 0; 889 | Serial.println("Resetting"); 890 | } 891 | } 892 | //============================= 893 | // End Controller related items 894 | 895 | // Pin anything that isn't the controller or bluetooth to core 0 896 | //============================================================== 897 | 898 | void Task1code( void * pvParameters ){ 899 | //Serial.print("OLED Update ESP Core "); 900 | //Serial.println(xPortGetCoreID()); 901 | 902 | for(;;){ 903 | //Serial.print("timeStamp val" ); 904 | if (timeStamp == 0){ 905 | //bootLogo(); 906 | timeStamp = 1; 907 | } 908 | //oled_Updates(); 909 | //getBatStat(); 910 | Serial.print(" "); 911 | } 912 | } 913 | // End Core Pinning 914 | //================= 915 | 916 | 917 | // Controller and Bluetooth Jobs here 918 | void Task2code( void * pvParameters ){ 919 | //Serial.print("Task2code running on core "); 920 | //Serial.println(xPortGetCoreID()); 921 | 922 | for(;;){ 923 | //debug 924 | // unsigned int wMark3 = uxTaskGetStackHighWaterMark(nullptr); 925 | // printf("Watermark before FOR Loop is %u\n", wMark3); 926 | 927 | pollController(); 928 | //debug 929 | // unsigned int wMark = uxTaskGetStackHighWaterMark(nullptr); 930 | // printf("Watermark after FOR Loop is %u\n", wMark); 931 | } 932 | } 933 | 934 | 935 | // This is the main loop due to threading in arduino + core tasking in RTOS 936 | //========================================================================== 937 | void setup() { 938 | // Check ESP Core 939 | //Serial.print("Setup Core"); 940 | //Serial.println(xPortGetCoreID()); 941 | 942 | 943 | // Start BLE Gamepad 944 | BleGamepadConfiguration bleGamepadConfig; 945 | bleGamepadConfig.setControllerType(CONTROLLER_TYPE_GAMEPAD); // CONTROLLER_TYPE_JOYSTICK, CONTROLLER_TYPE_GAMEPAD (DEFAULT), CONTROLLER_TYPE_MULTI_AXIS 946 | bleGamepadConfig.setButtonCount(numOfButtons); 947 | bleGamepadConfig.setWhichAxes(enableX, enableY, enableZ, enableRX, enableRY, enableRZ, enableSlider1, enableSlider2); // Can also be done per-axis individually. All are true by default 948 | bleGamepadConfig.setHatSwitchCount(numOfHatSwitches); // 1 by default 949 | bleGamepadConfig.setAxesMin(0x8001); // -32767 --> int16_t - 16 bit signed integer - Can be in decimal or hexadecimal 950 | // 951 | //bleGamepadConfig.setAxesMin(0x0001); // -32767 --> int16_t - 16 bit signed integer - Can be in decimal or hexadecimal 952 | bleGamepadConfig.setAxesMax(0x7FFF); // 32767 --> int16_t - 16 bit signed integer - Can be in decimal or hexadecimal 953 | // Other Available Special Button Options 954 | // bleGamepadConfig.setIncludeStart(true); 955 | // bleGamepadConfig.setIncludeSelect(true); 956 | // bleGamepadConfig.setIncludeMenu(true); 957 | // bleGamepadConfig.setIncludeHome(true); 958 | // bleGamepadConfig.setIncludeBack(true); 959 | // bleGamepadConfig.setIncludeVolumeInc(true); 960 | // bleGamepadConfig.setIncludeVolumeDec(true); 961 | // bleGamepadConfig.setIncludeVolumeMute(true); 962 | // Use or disable BLEGampead AutoReporting 963 | // AutoReport can impact latency negatively with high speed of updates 964 | bleGamepadConfig.setAutoReport(false); // This is true by default 965 | bleGamepad.begin(&bleGamepadConfig); 966 | 967 | 968 | // Setting input and output pins for Comms with Controller 969 | // Use Heavy and Lightwing pinout settings instead of changing this 970 | //==========================// Pin 1 = +5V 971 | pinMode(dataPinD1, INPUT); // Pin 2 = Data D1 972 | pinMode(dataPinD0, INPUT); // Pin 3 = Data D0 973 | pinMode(THPin, OUTPUT); // Pin 4 = TH Select 974 | pinMode(TRPin, OUTPUT); // Pin 5 = TR Request 975 | pinMode(TLPin, INPUT); // Pin 6 = TL Response 976 | pinMode(dataPinD3, INPUT); // Pin 7 = Data D3 977 | pinMode(dataPinD2, INPUT); // Pin 8 = Data D2 978 | //==========================// Pin 9 = GND 979 | 980 | // Set pin state to begin reading controller state 981 | digitalWrite(THPin, HIGH); 982 | digitalWrite(TRPin, HIGH); 983 | 984 | // Define Latency test pin 985 | pinMode(latPin,INPUT_PULLUP ); 986 | 987 | 988 | // Setup Serial comms for Serial Monitor 989 | Serial.begin(115200); 990 | Serial.print("Device Booting...."); 991 | 992 | // Creates Tasks for Core 0 993 | xTaskCreatePinnedToCore( 994 | Task1code, /* Task function. */ 995 | "Task1", /* name of task. */ 996 | 5000, /* Stack size of task */ //originally 10k 997 | NULL, /* parameter of the task */ 998 | 3, /* priority of the task */ 999 | &Task1, /* Task handle to keep track of created task */ 1000 | 0); /* pin task to core 0 */ 1001 | 1002 | //Creates Tasks for Core 0 1003 | xTaskCreatePinnedToCore( 1004 | Task2code, /* Task function. */ 1005 | "Task2", /* name of task. */ 1006 | 5000, /* Stack size of task */ 1007 | NULL, /* parameter of the task */ 1008 | 2, /* priority of the task */ 1009 | &Task2, /* Task handle to keep track of created task */ 1010 | 1); /* pin task to core 1 */ 1011 | 1012 | } 1013 | 1014 | 1015 | // Loop will always run on Core 1 1016 | void loop(){ 1017 | // Putting stuff here will break some automatic aspects of RTOS task cleanup 1018 | // All tasks/jobs should be performed from setup loop 1019 | } 1020 | 1021 | //Currently unused setup for Motion Controls 1022 | // All this code is trashed and intermixed (sorry) but the idea is 1023 | // initializeSensor -> setupMPU -> GetTiltMpu 1024 | void initializeSensor(){ 1025 | // Perfrom full reset as per MPU-6000/MPU-6050 Register Map and Descriptions, Section 4.28, pages 40 to 41. 1026 | // performing full device reset, disables temperature sensor, disables SLEEP mode 1027 | Wire.beginTransmission(0x68); // Device address. 1028 | Wire.write(0x6B); // PWR_MGMT_1 register. 1029 | Wire.write(0b10001000); // DEVICE_RESET, TEMP_DIS. 1030 | Wire.endTransmission(); 1031 | delay(100); // Wait for reset to complete. 1032 | 1033 | Wire.beginTransmission(0x68); // Device address. 1034 | Wire.write(0x68); // SIGNAL_PATH_RESET register. 1035 | Wire.write(0b00000111); // GYRO_RESET, ACCEL_RESET, TEMP_RESET. 1036 | Wire.endTransmission(); 1037 | delay(100); // Wait for reset to complete. 1038 | 1039 | // Disable SLEEP mode because the reset re-enables it. Section 3, PWR_MGMT_1 register, page 8. 1040 | Wire.beginTransmission(MPU_addr); // Device address. 1041 | Wire.write(0x6B); // PWR_MGMT_1 register. 1042 | Wire.write(0b00001000); // SLEEP = 0, TEMP_DIS = 1. 1043 | Wire.endTransmission(); 1044 | } 1045 | // ============= 1046 | // End Start MPU 1047 | 1048 | void setupMPU() { 1049 | //Setup Accelerometer 1050 | //Wire.begin(); 1051 | 1052 | //Configure MPU Connectivity 1053 | //Wire.beginTransmission(MPU_addr); 1054 | //Wire.write(0x68); 1055 | //Wire.write(0x00); // Make reset - place a 0 into the 6B register 1056 | //Wire.endTransmission(true); 1057 | //Wire.write(0); 1058 | //Wire.endTransmission(true); 1059 | 1060 | 1061 | //Adafruit_MPU6050 mpu; 1062 | //if (!Wire.begin()) { 1063 | // Serial.println("Failed to find MPU6050 chip"); 1064 | // while (1) { 1065 | // delay(10); 1066 | // } 1067 | //} 1068 | //Serial.println("MPU6050 Found!"); 1069 | //Setup Accelerometer End 1070 | } 1071 | 1072 | 1073 | 1074 | void getTiltMPU() { 1075 | //sensors_event_t a, g, temp; 1076 | //mpu.getEvent(&a, &g, &temp); 1077 | //Wire.begin(); 1078 | 1079 | //Configure MPU Connectivity 1080 | //Wire.beginTransmission(MPU_addr); 1081 | //Wire.write(0x68); 1082 | //Wire.write(0); 1083 | //Wire.endTransmission(true); 1084 | 1085 | //Serial.begin(115200); 1086 | //void loop(){ 1087 | 1088 | //Get Data from MPU 1089 | //Wire.beginTransmission(MPU_addr); 1090 | //Wire.write(0x3B); 1091 | //Wire.endTransmission(false); 1092 | //Wire.requestFrom(MPU_addr,14,true); 1093 | 1094 | //Store MPU Values 1095 | //AcX=Wire.read()<<8|Wire.read(); 1096 | //AcY=Wire.read()<<8|Wire.read(); 1097 | //AcZ=Wire.read()<<8|Wire.read(); 1098 | 1099 | Serial.print("MPU X Y Z"); 1100 | //Serial.print(a.acceleration.x, 1); 1101 | //Serial.print(a.acceleration.y, 1); 1102 | //Serial.print(a.acceleration.z, 1); 1103 | Serial.print("MPU GYRO"); 1104 | //Serial.print(g.gyro.x, 1); 1105 | //Serial.print(g.gyro.y, 1); 1106 | //Serial.print(g.gyro.z, 1); 1107 | 1108 | //Convert stored values from Rads to Degrees 1109 | //int xAng = map(AcX,minVal,maxVal,-90,90); 1110 | //int yAng = map(AcY,minVal,maxVal,-90,90); 1111 | //int zAng = map(AcZ,minVal,maxVal,-90,90); 1112 | //x= RAD_TO_DEG * (atan2(-yAng, -zAng)+PI); 1113 | //y= RAD_TO_DEG * (atan2(-xAng, -zAng)+PI); 1114 | //z= RAD_TO_DEG * (atan2(-yAng, -xAng)+PI); 1115 | 1116 | //Print current angles 1117 | //Serial.print("AngleX "); 1118 | //Serial.println(x); 1119 | //Serial.print("AngleY "); 1120 | //Serial.println(y); 1121 | //display.clearDisplay(); 1122 | //display.setTextSize(1); // Normal 1:1 pixel scale 1123 | //display.setTextColor(SSD1306_WHITE, BLACK); // Draw white text 1124 | //display.setCursor(0,10); 1125 | //display.println("Tilt: On"); 1126 | //display.setCursor(0,20); // r 1127 | //String derp1 = " X Axis: " + String(x); 1128 | //String derp2 = " Y Axis: " + String(y); 1129 | //display.clearDisplay(); 1130 | //display.println(derp1); 1131 | //display.println(F("X Axis " + String(x))); 1132 | //display.setCursor(0,30); 1133 | //display.println(derp2); 1134 | //display.println(F("y Axis " + String(y))); 1135 | //display.display(); 1136 | // 1137 | 1138 | //Limit Degrees of angle based on comfort prior to converting to BLEGamepad Vals 1139 | //Limit X 1140 | if (x < 160){ 1141 | x=160; 1142 | } 1143 | if (x > 200){ 1144 | x=200; 1145 | } 1146 | //Limit Y 1147 | if (y < 160){ 1148 | y=160; 1149 | } 1150 | if (y > 200){ 1151 | y=200; 1152 | } 1153 | 1154 | //Define DeadZones 1155 | //Will require calibration based on your install 1156 | // 1157 | //Create X deadzone 1158 | if ((x >= 170) && (x <= 190)){ 1159 | x=180; 1160 | } 1161 | // 1162 | //Create Y deadzone 1163 | if ((y >= 170) && (y <= 190)){ 1164 | y=180; 1165 | } 1166 | 1167 | //Map Values to Gamepad Ranges 1168 | xmap = map (x, 160, 200, 0, 32767); 1169 | ymap = map (y, 160, 200, 0, 32767); 1170 | //Print Mapped values 1171 | //Serial.print("Xmap BleG: "); 1172 | //Serial.println(xmap); 1173 | //Serial.print("Ymap BleG: "); 1174 | //Serial.println(ymap); 1175 | 1176 | Serial.println("-----------------------------------------"); 1177 | 1178 | } 1179 | --------------------------------------------------------------------------------