Advertisement
phoenixdigital

Pinsim Updated

Aug 18th, 2019
497
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 26.31 KB | None | 0 0
  1. /*
  2.     PinSim Controller v20190511
  3.     Controller for PC Pinball games
  4.     https://www.youtube.com/watch?v=18EcIxywXHg
  5.    
  6.     Based on the excellent MSF_FightStick XINPUT project by Zack "Reaper" Littell
  7.     https://github.com/zlittell/MSF-XINPUT
  8.    
  9.     Uses the Teensy-LC
  10.  
  11.     IMPORTANT PLUNGER NOTE:
  12.     You MUST calibrate the plunger range at least once by holding down "A"
  13.     when plugging in the USB cable. LED-1 should flash rapidly, and then you should
  14.     pull the plunger all the way out and release it all the way back in. The LED1 should
  15.     flash again, and normal operation resumes. The setting is saved between power cycles.
  16.  
  17.     If you're not using a plunger, ground Pin 15.
  18. */
  19.  
  20. //Includes
  21. #include <Wire.h>
  22. #include <Adafruit_Sensor.h>
  23. #include <Adafruit_ADXL345_U.h>
  24. #include <Bounce.h>
  25. #include "xinput.h"
  26. #include <Average.h>
  27. #include <EEPROMex.h>
  28.  
  29. int numSamples = 20;
  30. Average<int> ave(numSamples);
  31.  
  32. /* Assign a unique ID to this sensor at the same time */
  33. Adafruit_ADXL345_Unified accel = Adafruit_ADXL345_Unified(12345);
  34.  
  35. // GLOBAL VARIABLES
  36. // configure these
  37. boolean flipperL1R1 = true; //
  38. boolean fourFlipperButtons = false; // FLIP_L & FLIP_R map to L1/R1 and pins 13 & 14 map to analog L2/R2 100%
  39. boolean doubleContactFlippers = false; // FLIP_L & FLIP_R map to analog L2/R2 10% and pins 13 & 14 map to L2/R2 100%
  40. boolean analogFlippers = false; // use analog flipper buttons
  41. boolean coinAcceptor = true; // enable the coin acceptor detection
  42. boolean coinAcceptorUsesInterrupts = false; // have coin input use interrupts instead of standard button push detection
  43. boolean leftStickJoy = false; // joystick moves left analog stick instead of D-pad
  44. boolean accelerometerInstalled = true;
  45. boolean accelerometerEnabled = false; // Disabled on startup but can be activated. Only added this because primary use of panel will be MAME
  46. boolean plungerEnabled = true;
  47. boolean currentlyPlunging = false;
  48. int16_t nudgeMultiplier = 7500; // accelerometer multiplier (higher = more sensitive)
  49. int16_t plungeTrigger = 60; // threshold to trigger a plunge (lower = more sensitive)
  50. boolean backButtonCanOnlyWorkWithShiftButton = true; // required on my pinsim due to placement of back button under control panel. Wanted to have back (which is quit for mame) harder to accidentally press.
  51.  
  52. // probably leave these alone
  53. int16_t zeroValue = 0; // xbox 360 value for neutral analog stick
  54. int16_t zeroValueBuffer = 0; // save zero value during plunge
  55. int16_t plungerReportDelay = 17; // delay in ms between reading ~60hz plunger updates from sensor
  56. int16_t plungerMin = 200; // min plunger analog sensor value
  57. int16_t plungerMax = 550; // max plunger analog sensor value
  58. int16_t plungerMaxDistance = 0; // sensor value converted to actual distance
  59. int16_t plungerMinDistance = 0;
  60. uint32_t plungerReportTime = 0;
  61. uint32_t tiltEnableTime = 0;
  62. int16_t lastReading = 0;
  63. int16_t lastDistance = 0;
  64. int16_t distanceBuffer = 0;
  65. float zeroX = 0;
  66. float zeroY = 0;
  67.  
  68. ////Pin Declarations
  69. #define pinDpadL 0  //Left on DPAD
  70. #define pinDpadR 1  //Right on DPAD
  71. #define pinDpadU 2  //Up on DPAD
  72. #define pinDpadD 3  //Down on DPAD
  73. #define pinB1 4  //Button 1 (A)
  74. #define pinB2 5  //Button 2 (B)
  75. #define pinB3 6  //Button 3 (X)
  76. #define pinB4 7  //Button 4 (Y)
  77. #define pinLB 8  //Button 5 (LB)
  78. #define pinRB 9  //Button 6 (RB)
  79. #define pinSHIFT 10  // Shift Button (Allows for additional inputs with combinations from joystick/buttons) cannot be used with Analog Trigger mode
  80. #define pinBK 11  //Button 7 (Back)
  81. #define pinST 12  //Button 8 (Start)
  82. #define pinB9 13  //Button 9 (Left Upper Flipper)
  83. #define pinB10 14  //Button 10 (Right Upper Flipper)
  84. #define pinPlunger 15 //IR distance for plunger
  85. #define pinLED1 16  //Onboard LED 1
  86. #define pinLED2 17  //Onboard LED 2
  87. #define rumbleSmall 20 // Large Rumble Motor
  88. #define rumbleLarge 22 // Large Rumble Motor
  89. #define pinLT 21 // Left Analog Trigger
  90. #define pinRT 23 // Right Analog Trigger
  91. #define pinCOIN 23 // Pin for coin acceptor (only valid if not using analog triggers)
  92.  
  93. #define NUMBUTTONS 16  //Number of all buttons
  94. #define MILLIDEBOUNCE 20  //Debounce time in milliseconds
  95.  
  96. //Position of a button in the button status array
  97. #define POSUP 0
  98. #define POSDN 1
  99. #define POSLT 2
  100. #define POSRT 3
  101. #define POSB1 4
  102. #define POSB2 5
  103. #define POSB3 6
  104. #define POSB4 7
  105. #define POSLB 8
  106. #define POSRB 9
  107. #define POSB9 10
  108. #define POSB10 11
  109. #define POSST 12
  110. #define POSBK 13
  111. #define POSSHIFT 14
  112. #define POSCOIN 15
  113.  
  114. // Dual flippers
  115. uint8_t leftTrigger = 0;
  116. uint8_t rightTrigger = 0;
  117.  
  118. //Global Variables
  119. byte buttonStatus[NUMBUTTONS];  //array Holds a "Snapshot" of the button status to parse and manipulate
  120.  
  121. // coin interrupt variables
  122. volatile int coinPulse = 0;
  123. boolean coinInserted = false;
  124. unsigned long coinLastStartTime = 0;
  125. int coinSendOnDelayMilliseconds = 200;
  126. int coinSendOffDelayMilliseconds = 200;
  127.  
  128. //LED Toggle Tracking Global Variables
  129. uint8_t LEDState = LOW; //used to set the pin for the LED
  130. uint32_t previousMS = 0; //used to store the last time LED was updated
  131. uint8_t LEDtracker = 0; //used as an index to step through a pattern on interval
  132.  
  133. //LED Patterns
  134. uint8_t patternAllOff[10] = {0,0,0,0,0,0,0,0,0,0};
  135. uint8_t patternBlinkRotate[10] = {1,0,1,0,1,0,1,0,1,0};
  136. uint8_t patternPlayer1[10] = {1,0,0,0,0,0,0,0,0,0};
  137. uint8_t patternPlayer2[10] = {1,0,1,0,0,0,0,0,0,0};
  138. uint8_t patternPlayer3[10] = {1,0,1,0,1,0,0,0,0,0};
  139. uint8_t patternPlayer4[10] = {1,0,1,0,1,0,1,0,0,0};
  140.  
  141. //Variable to hold the current pattern selected by the host
  142. uint8_t patternCurrent[10] = {0,0,0,0,0,0,0,0,0,0};
  143.  
  144. //Setup Button Debouncing
  145. Bounce dpadUP = Bounce(pinDpadU, MILLIDEBOUNCE);
  146. Bounce dpadDOWN = Bounce(pinDpadD, MILLIDEBOUNCE);
  147. Bounce dpadLEFT = Bounce(pinDpadL, MILLIDEBOUNCE);
  148. Bounce dpadRIGHT = Bounce(pinDpadR, MILLIDEBOUNCE);
  149. Bounce button1 = Bounce(pinB1, MILLIDEBOUNCE);
  150. Bounce button2 = Bounce(pinB2, MILLIDEBOUNCE);
  151. Bounce button3 = Bounce(pinB3, MILLIDEBOUNCE);
  152. Bounce button4 = Bounce(pinB4, MILLIDEBOUNCE);
  153. Bounce buttonLB = Bounce(pinLB, MILLIDEBOUNCE);
  154. Bounce buttonRB = Bounce(pinRB, MILLIDEBOUNCE);
  155. Bounce button9 = Bounce(pinB9, MILLIDEBOUNCE);
  156. Bounce button10 = Bounce(pinB10, MILLIDEBOUNCE);
  157. Bounce buttonSTART = Bounce(pinST, MILLIDEBOUNCE);
  158. Bounce buttonBACK = Bounce(pinBK, MILLIDEBOUNCE);
  159. Bounce buttonSHIFT = Bounce(pinSHIFT, MILLIDEBOUNCE);
  160. Bounce buttonCOIN = Bounce(pinCOIN, MILLIDEBOUNCE);
  161.  
  162. //Initiate the xinput class and setup the LED pin
  163. XINPUT controller(LED_ENABLED, pinLED1);
  164.  
  165. //void Configure Inputs and Outputs
  166. void setupPins()
  167. {
  168.     //Configure the direction of the pins
  169.     //All inputs with internal pullups enabled
  170.     pinMode(pinDpadU, INPUT_PULLUP);
  171.     pinMode(pinDpadD, INPUT_PULLUP);
  172.     pinMode(pinDpadL, INPUT_PULLUP);
  173.     pinMode(pinDpadR, INPUT_PULLUP);
  174.     pinMode(pinB1, INPUT_PULLUP);
  175.     pinMode(pinB2, INPUT_PULLUP);
  176.     pinMode(pinB3, INPUT_PULLUP);
  177.     pinMode(pinB4, INPUT_PULLUP);
  178.     pinMode(pinLB, INPUT_PULLUP);
  179.     pinMode(pinRB, INPUT_PULLUP);
  180.     pinMode(pinB9, INPUT_PULLUP);
  181.     pinMode(pinB10, INPUT_PULLUP);
  182.     pinMode(pinST, INPUT_PULLUP);
  183.     pinMode(pinBK, INPUT_PULLUP);
  184.     pinMode(pinSHIFT, INPUT_PULLUP);
  185.     pinMode(pinCOIN, INPUT_PULLUP);
  186.     pinMode(pinLED1, OUTPUT);
  187.     pinMode(pinLED2, OUTPUT);
  188.  
  189.     //Set the LED to low to make sure it is off
  190.     digitalWrite(pinLED1, LOW);
  191.     //Set the LED to high to turn it on
  192.     digitalWrite(pinLED2, HIGH);
  193.     //Rumble
  194.     pinMode(rumbleSmall, OUTPUT);
  195.     pinMode(rumbleLarge, OUTPUT);
  196. }
  197.  
  198. //Update the debounced button statuses
  199. //We are looking for falling edges since the boards are built
  200. //for common ground sticks
  201. void buttonUpdate()
  202. {
  203.   if (dpadUP.update()) {buttonStatus[POSUP] = dpadUP.fallingEdge();}
  204.   if (dpadDOWN.update()) {buttonStatus[POSDN] = dpadDOWN.fallingEdge();}
  205.   if (dpadLEFT.update()) {buttonStatus[POSLT] = dpadLEFT.fallingEdge();}
  206.   if (dpadRIGHT.update()) {buttonStatus[POSRT] = dpadRIGHT.fallingEdge();}
  207.   if (button1.update()) {buttonStatus[POSB1] = button1.fallingEdge();}
  208.   if (button2.update()) {buttonStatus[POSB2] = button2.fallingEdge();}
  209.   if (button3.update()) {buttonStatus[POSB3] = button3.fallingEdge();}
  210.   if (button4.update()) {buttonStatus[POSB4] = button4.fallingEdge();}
  211.   if (buttonLB.update()) {buttonStatus[POSLB] = buttonLB.fallingEdge();}
  212.   if (buttonRB.update()) {buttonStatus[POSRB] = buttonRB.fallingEdge();}
  213.   if (button9.update()) {buttonStatus[POSB9] = button9.fallingEdge();}
  214.   if (button10.update()) {buttonStatus[POSB10] = button10.fallingEdge();}
  215.   if (buttonSTART.update()) {buttonStatus[POSST] = buttonSTART.fallingEdge();}
  216.   if (buttonBACK.update()) {buttonStatus[POSBK] = buttonBACK.fallingEdge();}
  217.   if (buttonSHIFT.update()) {buttonStatus[POSSHIFT] = buttonSHIFT.fallingEdge();}
  218.   if (buttonCOIN.update()) {buttonStatus[POSCOIN] = buttonCOIN.fallingEdge();}
  219.  
  220. }
  221.  
  222. //ProcessInputs
  223. void processInputs()
  224. {
  225.  
  226.  
  227.   // Check for SHIFT KEY Combinations
  228.   if (buttonStatus[POSSHIFT]) {
  229.  
  230.     // SHIFT + Left Flipper set new plunger dead zone
  231.     // If SHIFT and Left Flipper pressed simultaneously, set new plunger dead zone
  232.     // Compensates for games where the in-game plunger doesn't begin pulling back until
  233.     // the gamepad is pulled back ~half way. Just pull the plunger to the point just before
  234.     // it begins to move in-game, and then press BACK & LB.
  235.     if (buttonStatus[POSLB]) {
  236.       deadZoneCompensation();
  237.     }
  238.  
  239.     // SHIFT + Right Flipper toggle accelleromter + delay of 1 second
  240.     if (buttonStatus[POSRB]) {
  241.       if (accelerometerEnabled) {
  242.         accelerometerEnabled = false;
  243.       }
  244.       else {
  245.         accelerometerEnabled = true;
  246.       }
  247.  
  248.       // delay and blink LED for accellerometer status
  249.       delay(1000);
  250.      
  251.     }
  252.    
  253.     // If SHIFT and joystick Up pressed simultaneously, map joystick to Xbox Left Stick
  254.     // If SHIFT and joystick Down pressed, map joystick to D-pad
  255.     if (leftStickJoy && buttonStatus[POSDN])
  256.     {
  257.       leftStickJoy = false;
  258.     }
  259.     else if (!leftStickJoy && buttonStatus[POSUP])
  260.     {
  261.       leftStickJoy = true;
  262.     }
  263.  
  264.     // If SHIFT and joystick Left pressed simultaneously, use normal L1 & R1
  265.     // If SHIFT and joystick Right pressed, use analog L2 & R2
  266.     if (!flipperL1R1 && buttonStatus[POSLT])
  267.     {
  268.       flipperL1R1 = true;
  269.     }
  270.     else if (flipperL1R1 && buttonStatus[POSRT])
  271.     {
  272.       flipperL1R1 = false;
  273.     }
  274.  
  275.     // SHIFT + POSBK = POSBK to make it harder to accidentally quit MAME games
  276.     if (backButtonCanOnlyWorkWithShiftButton) {
  277.       if (buttonStatus[POSBK]) {controller.buttonUpdate(BUTTON_BACK, 1);}
  278.       else {controller.buttonUpdate(BUTTON_BACK, 0);}
  279.     }
  280.  
  281.     // SHIFT + A = Left stick click
  282.     if (buttonStatus[POSB1]) {controller.buttonUpdate(BUTTON_L3, 1);}
  283.     else {controller.buttonUpdate(BUTTON_L3, 0);}
  284.  
  285.     // SHIFT + B = Right stick click
  286.     if (buttonStatus[POSB2]) {controller.buttonUpdate(BUTTON_R3, 1);}
  287.     else {controller.buttonUpdate(BUTTON_R3, 0);}
  288.  
  289.     // SHIFT + X = Xbox Logo
  290.     if (buttonStatus[POSB3]) {controller.buttonUpdate(BUTTON_LOGO, 1);}
  291.     else {controller.buttonUpdate(BUTTON_LOGO, 0);}
  292.  
  293.     // SHIFT + Y = Rumble Test
  294.     if (buttonStatus[POSB4]) {
  295.       for (int str=0; str < 256; str++){
  296.         analogWrite(rumbleSmall, str);
  297.         delay(10);
  298.       }
  299.       for (int str=255; str > 0; str--){
  300.         analogWrite(rumbleSmall, str);
  301.         delay(10);
  302.       }
  303.    
  304.       for (int str=0; str < 256; str++){
  305.         analogWrite(rumbleLarge, str);
  306.         delay(10);
  307.       }
  308.       for (int str=255; str > 0; str--){
  309.         analogWrite(rumbleLarge, str);
  310.         delay(10);
  311.       }
  312.     }
  313.  
  314.   }
  315.  
  316.   // only check for other buttons when shift key is not pressed.
  317.   // For example if SHIFT + A is pressed without this check both Left stick click and button A will be registered.
  318.   else {
  319.    
  320.     if (leftStickJoy)
  321.     {
  322.       int leftStickX = buttonStatus[POSLT] * -30000 + buttonStatus[POSRT] * 30000;
  323.       int leftStickY = buttonStatus[POSDN] * -30000 + buttonStatus[POSUP] * 30000;
  324.       controller.stickUpdate(STICK_LEFT, leftStickX, leftStickY);    
  325.       controller.dpadUpdate(0, 0, 0, 0);
  326.     }
  327.     else
  328.     {
  329.       //Update the DPAD
  330.       controller.dpadUpdate(buttonStatus[POSUP], buttonStatus[POSDN], buttonStatus[POSLT], buttonStatus[POSRT]);
  331.     }
  332.    
  333.     //Buttons
  334.     if (buttonStatus[POSB1]) {controller.buttonUpdate(BUTTON_A, 1);}
  335.     else  {controller.buttonUpdate(BUTTON_A, 0);}
  336.     if (buttonStatus[POSB2]) {controller.buttonUpdate(BUTTON_B, 1);}
  337.     else {controller.buttonUpdate(BUTTON_B, 0);}
  338.     if (buttonStatus[POSB3]) {controller.buttonUpdate(BUTTON_X, 1);}
  339.     else {controller.buttonUpdate(BUTTON_X, 0);}
  340.     if (buttonStatus[POSB4]) {controller.buttonUpdate(BUTTON_Y, 1);}
  341.     else {controller.buttonUpdate(BUTTON_Y, 0);}
  342.  
  343.     //Middle Buttons
  344.     if (buttonStatus[POSST]){controller.buttonUpdate(BUTTON_START, 1);}
  345.     else {controller.buttonUpdate(BUTTON_START, 0);}
  346.  
  347.     // this is useful to for back button placement which is too easy to press. Makes it harder to accidentally quit a game in MAME
  348.     if (!backButtonCanOnlyWorkWithShiftButton) {
  349.       if (buttonStatus[POSBK]){controller.buttonUpdate(BUTTON_BACK, 1);}
  350.       else {controller.buttonUpdate(BUTTON_BACK, 0);}
  351.     }
  352.  
  353.     // coin mechanism behaves like a normal button (L3 - Left stick click)
  354.     if (coinAcceptor && !analogFlippers) {
  355.       if (buttonStatus[POSCOIN]){controller.buttonUpdate(BUTTON_L3, 1);}
  356.       else {controller.buttonUpdate(BUTTON_L3, 0);}
  357.     }
  358.  
  359.     ///////////////////////////////////////////////////////////////////
  360.     // This section monitors for upper flippers configured on GPIO 13 & 14
  361.     // These can be added as additional buttons or double contact leaf switches
  362.     //
  363.     // detect double contact flipper switches tied to GPIO 13 & 14
  364.     if (!doubleContactFlippers && !fourFlipperButtons)
  365.     {
  366.       if (buttonStatus[POSB9] && buttonStatus[POSLB])
  367.       {
  368.         flipperL1R1 = false;
  369.         doubleContactFlippers = true;
  370.       }
  371.       if (buttonStatus[POSB10] && buttonStatus[POSRB])
  372.       {
  373.         flipperL1R1 = false;
  374.         doubleContactFlippers = true;
  375.       }
  376.     }
  377.  
  378.     // detect four flipper buttons, second pair tied to GPIO 13 & 14
  379.     // detection occurs if GPIO 13 is pressed WITHOUT FLIP_L being pressed
  380.     // or GPIO 14 without FLIP_R being pressed (not possible with double contact switch)
  381.     if (!fourFlipperButtons)
  382.     {
  383.       if (buttonStatus[POSB9] && !buttonStatus[POSLB])
  384.       {
  385.         flipperL1R1 = true;
  386.         fourFlipperButtons = true;
  387.         doubleContactFlippers = false;
  388.       }
  389.       if (buttonStatus[POSB10] && !buttonStatus[POSRB])
  390.       {
  391.         flipperL1R1 = true;
  392.         fourFlipperButtons = true;
  393.         doubleContactFlippers = false;
  394.       }
  395.     }
  396.     ///////////////////////////////////////////////////////////////////
  397.  
  398.     // Bumpers
  399.     // Standard mode: FLIP_L and FLIP_R map to L1/R1, and optionally GPIO 13 & 14 map to L2/R2
  400.     if (flipperL1R1)
  401.     {
  402.       uint8_t leftTrigger = 0;
  403.       uint8_t rightTrigger = 0;
  404.       if (buttonStatus[POSLB]) {controller.buttonUpdate(BUTTON_LB, 1);}
  405.       else {controller.buttonUpdate(BUTTON_LB, 0);}
  406.       if (buttonStatus[POSRB]) {controller.buttonUpdate(BUTTON_RB, 1);}
  407.       else {controller.buttonUpdate(BUTTON_RB, 0);}
  408.       if (buttonStatus[POSB9]) leftTrigger = 255;
  409.       else leftTrigger = 0;
  410.       if (buttonStatus[POSB10]) rightTrigger = 255;
  411.       else rightTrigger = 0;
  412.       controller.triggerUpdate(leftTrigger, rightTrigger);
  413.     }
  414.  
  415.     // L2/R2 Flippers (standard mode swapped)
  416.     else if (!flipperL1R1 && !doubleContactFlippers)
  417.     {
  418.       uint8_t leftTrigger = 0;
  419.       uint8_t rightTrigger = 0;
  420.       if (buttonStatus[POSB9]) {controller.buttonUpdate(BUTTON_LB, 1);}
  421.       else {controller.buttonUpdate(BUTTON_LB, 0);}
  422.       if (buttonStatus[POSB10]) {controller.buttonUpdate(BUTTON_RB, 1);}
  423.       else {controller.buttonUpdate(BUTTON_RB, 0);}
  424.       if (buttonStatus[POSLB]) {leftTrigger = 255;}
  425.       else {leftTrigger = 0;}
  426.       if (buttonStatus[POSRB]) {rightTrigger = 255;}
  427.       else {rightTrigger = 0;}
  428.       controller.triggerUpdate(leftTrigger, rightTrigger);
  429.     }
  430.  
  431.     // Double Contact Flippers
  432.     else if (!flipperL1R1 && doubleContactFlippers)
  433.     {
  434.       uint8_t leftTrigger = 0;
  435.       uint8_t rightTrigger = 0;
  436.       if (buttonStatus[POSLB] && buttonStatus[POSB9]) {leftTrigger = 255;}
  437.       else if (buttonStatus[POSLB] && !buttonStatus[POSB9]) {leftTrigger = 25;}
  438.       else if (!buttonStatus[POSLB] && !buttonStatus[POSB9]) {leftTrigger = 0;}
  439.       if (buttonStatus[POSRB] && buttonStatus[POSB10]) {rightTrigger = 255;}
  440.       else if (buttonStatus[POSRB] && !buttonStatus[POSB10]) {rightTrigger = 25;}
  441.       else if (!buttonStatus[POSRB] && !buttonStatus[POSB10]) {rightTrigger = 0;}
  442.       controller.triggerUpdate(leftTrigger, rightTrigger);
  443.     }
  444.  
  445.     //Experimental Analog Input
  446.     //Analog flippers
  447.     if (analogFlippers && !coinAcceptor)
  448.     {
  449.       uint8_t leftTrigger = map(analogRead(pinLT), 0, 512, 0, 255);
  450.       uint8_t rightTrigger = map(analogRead(pinRT), 0, 512, 0, 255);
  451.       controller.triggerUpdate(leftTrigger, rightTrigger);
  452.     }
  453.    
  454.     //Tilt
  455.     if (accelerometerEnabled && !leftStickJoy)
  456.     {
  457.       /* Get a new sensor event */
  458.       sensors_event_t event;
  459.       accel.getEvent(&event);
  460.  
  461.       // zero accelerometer whenever START is pushed (PinSim Yellow Start Button)
  462.       if (buttonStatus[POSST])
  463.       {
  464.         zeroX = event.acceleration.x * nudgeMultiplier * -1;
  465.         zeroY = event.acceleration.y * nudgeMultiplier * -1;
  466.       }
  467.    
  468.       int leftStickX = zeroX + (event.acceleration.x * nudgeMultiplier);
  469.       int leftStickY = zeroY + (event.acceleration.y * nudgeMultiplier);
  470.       if (millis() > tiltEnableTime)
  471.       {
  472.         controller.stickUpdate(STICK_LEFT, leftStickX, leftStickY);
  473.       }
  474.     }
  475.  
  476.     // Plunger
  477.     // This is based on the Sharp GP2Y0A51SK0F Analog Distance Sensor 2-15cm
  478.     if (plungerEnabled)
  479.     {
  480.       int reading = analogRead(pinPlunger);
  481.    
  482.       if (((reading - lastReading) > -10 && (reading - lastReading) < 10) || (reading - lastReading > 75) || (reading - lastReading < -75))
  483.       {
  484.         ave.push(reading);
  485.       }
  486.       lastReading = reading;
  487.       int16_t averageReading = ave.mean();
  488.  
  489.       // it appears the distance sensor updates at about 60hz, no point in checking more often than that
  490.       if (millis() > plungerReportTime)
  491.       {
  492.         // restore zero value after a plunge
  493.         if (zeroValueBuffer)
  494.         {
  495.           zeroValue = zeroValueBuffer;
  496.           zeroValueBuffer = 0;
  497.         }
  498.         plungerReportTime = millis() + plungerReportDelay;
  499.         int16_t currentDistance = readingToDistance(averageReading);
  500.         distanceBuffer = currentDistance;
  501.  
  502.         // if plunger is pulled
  503.         if (currentDistance + 50 < plungerMaxDistance && currentDistance > plungerMinDistance + 50)
  504.         {
  505.          
  506.           currentlyPlunging = true;
  507.           // Attempt to detect plunge
  508.          
  509.           int16_t adjustedPlungeTrigger = map(currentDistance, plungerMaxDistance, plungerMinDistance, plungeTrigger/2, plungeTrigger);
  510.           if (currentDistance - lastDistance >= adjustedPlungeTrigger)
  511.           {
  512.               // we throw STICK_RIGHT to 0 to better simulate the physical behavior of a real analog stick
  513.               controller.stickUpdate(STICK_RIGHT, 0, 0);
  514.               // disable plunger momentarily to compensate for spring bounce
  515.               plungerReportTime = millis() + 1000;
  516.               distanceBuffer = plungerMaxDistance;
  517.               lastDistance = plungerMaxDistance;
  518.               if (zeroValue)
  519.               {
  520.                 zeroValueBuffer = zeroValue;
  521.                 zeroValue = 0;
  522.               }
  523.               return;
  524.           }
  525.           lastDistance = currentDistance;
  526.  
  527.           // Disable accelerometer while plunging and for 1 second afterwards.
  528.           if (currentDistance < plungerMaxDistance - 50) tiltEnableTime = millis() + 1000;
  529.         }
  530.  
  531.         // cap max
  532.         else if (currentDistance <= plungerMinDistance + 50)
  533.         {
  534.           currentlyPlunging = true;
  535.           controller.stickUpdate(STICK_RIGHT, 0, -32768);
  536.           distanceBuffer = plungerMinDistance;
  537.           tiltEnableTime = millis() + 1000;
  538.         }
  539.        
  540.         // cap min
  541.         else if (currentDistance > plungerMaxDistance)
  542.         {
  543.           currentlyPlunging = false;
  544.           distanceBuffer = plungerMaxDistance;
  545.         }
  546.  
  547.         else if (currentlyPlunging) currentlyPlunging = false;
  548.       }
  549.        
  550.       if (distanceBuffer < plungerMinDistance) distanceBuffer = plungerMinDistance;
  551.       if (distanceBuffer > plungerMaxDistance) distanceBuffer = plungerMaxDistance;
  552.  
  553.       if (currentlyPlunging)
  554.       {
  555.         controller.stickUpdate(STICK_RIGHT, 0, map(distanceBuffer, plungerMaxDistance, plungerMinDistance, zeroValue, -32767));
  556.       }
  557.       else controller.stickUpdate(STICK_RIGHT, 0, map(distanceBuffer, plungerMaxDistance, plungerMinDistance, 0, -32767));
  558.     }
  559.  
  560.   }
  561.  
  562.   // Rumble
  563.   analogWrite(rumbleSmall, controller.rumbleValues[1]);
  564.   analogWrite(rumbleLarge, controller.rumbleValues[0]);
  565.  
  566.   // Duplicate rumble signals on both motors (causes unacceptable current draw)
  567. //  if (controller.rumbleValues[0] > 0 && controller.rumbleValues[1] == 0x00)
  568. //  {
  569. //    analogWrite(rumbleSmall, controller.rumbleValues[0]);
  570. //  }
  571. //  if (controller.rumbleValues[1] > 0 && controller.rumbleValues[0] == 0x00)
  572. //  {
  573. //    analogWrite(rumbleLarge, controller.rumbleValues[1]);
  574. //  }
  575.  
  576. }
  577.  
  578. uint16_t readingToDistance(int16_t reading)
  579. {
  580.     // The signal from the IR distance detector is curved. Let's linearize. Thanks for the help Twitter!
  581.     float voltage = reading / 310.0f;
  582.     float linearDistance = ((0.1621f * voltage) + 1.0f) / (0.1567f * voltage);
  583.     return linearDistance * 100;
  584. }
  585.  
  586. uint16_t getPlungerAverage()
  587. {
  588.   for (int i=0; i<numSamples; i++)
  589.   {
  590.     int reading = analogRead(pinPlunger);
  591.  
  592.     if ((reading - lastReading) > -10 && (reading - lastReading) < 10)
  593.     {
  594.       ave.push(reading);
  595.     }
  596.     lastReading = reading;
  597.   }
  598.   int averageReading = ave.mean();
  599.   return averageReading;
  600. }
  601.  
  602. // Finally you need to configure the plunger.
  603. // With the Teensy LC USB cable unplugged, hold down the Start button (Teensy Pin 12) and plug in the USB cable.
  604. // After a moment the LED-1 should flash rapidly. Slowly pull the plunger all the way out and then slowly allow it to retract all the way back in.
  605. // The LED should flash rapidly again, and then blink normally.
  606. // You can repeat this process as necessary, but the settings are permanently stored between power cycles.
  607.  
  608. void getPlungerMax()
  609. {
  610.   flashStartButton();
  611.   plungerMax = plungerMin + 1;
  612.   int averageReading = ave.mean();
  613.   while (averageReading < plungerMin + 100)
  614.   {
  615.     // wait for the plunger to be pulled
  616.     int reading = analogRead(pinPlunger);
  617.     if ((reading - lastReading) > -10 && (reading - lastReading) < 10)
  618.     {
  619.       ave.push(reading);
  620.     }
  621.     lastReading = reading;
  622.     averageReading = ave.mean();
  623.   }
  624.  
  625.   while (averageReading > plungerMin)
  626.   {
  627.     // start recording plungerMax
  628.     int reading = analogRead(pinPlunger);
  629.     if ((reading - lastReading) > -10 && (reading - lastReading) < 10)
  630.     {
  631.       ave.push(reading);
  632.     }
  633.     lastReading = reading;
  634.     averageReading = ave.mean();
  635.     if (averageReading > plungerMax) plungerMax = averageReading;
  636.   }
  637.  
  638.   EEPROM.writeInt(0,plungerMax);
  639.   flashStartButton();
  640. }
  641.  
  642. void deadZoneCompensation()
  643. {
  644.   zeroValue = map(distanceBuffer, plungerMaxDistance, plungerMinDistance, 0, -32768) + 10;
  645.   if (zeroValue > 0) zeroValue = 0;
  646.   flashStartButton();
  647.   buttonUpdate();
  648.   // ensure just one calibration per button press
  649.   while (digitalRead(POSSHIFT) == LOW)
  650.   {
  651.     // wait...
  652.   }
  653. }
  654.  
  655. void flashStartButton()
  656. {
  657.   for (int i=0; i<10; i++)
  658.   {
  659.     digitalWrite(pinLED1, HIGH);
  660.     delay(50);
  661.     digitalWrite(pinLED1, LOW);
  662.     delay(50);
  663.   }
  664. }
  665.  
  666.  
  667. //Setup
  668. void setup()
  669. {
  670.   setupPins();
  671.   delay(500);
  672.  
  673.   // rumble test (hold Left Flipper on boot)
  674.   if (digitalRead(pinLB) == LOW)
  675.   {
  676.     for (int str=0; str < 256; str++)
  677.     {
  678.       analogWrite(rumbleSmall, str);
  679.       delay(10);
  680.     }
  681.     for (int str=255; str > 0; str--)
  682.     {
  683.       analogWrite(rumbleSmall, str);
  684.       delay(10);
  685.     }
  686.  
  687.     for (int str=0; str < 256; str++)
  688.     {
  689.       analogWrite(rumbleLarge, str);
  690.       delay(10);
  691.     }
  692.     for (int str=255; str > 0; str--)
  693.     {
  694.       analogWrite(rumbleLarge, str);
  695.       delay(10);
  696.     }
  697.   }
  698.  
  699.   // Hold Right Flipper on boot to disable accelerometer
  700.   if (digitalRead(pinRB) == LOW)
  701.   {
  702.     accelerometerEnabled = false;
  703.     leftStickJoy = true;
  704.   }
  705.  
  706.   /* Initialise the sensor */
  707.   if (accelerometerInstalled)
  708.   {
  709.     if(!accel.begin())
  710.     {
  711.       /* There was a problem detecting the ADXL345 ... check your connections */
  712.       accelerometerEnabled = false;
  713.       flashStartButton();
  714.     }
  715.   }
  716.  
  717.   if (accelerometerInstalled)
  718.   {
  719.     /* Set the range to whatever is appropriate for your project */
  720.     // accel.setRange(ADXL345_RANGE_16_G);
  721.     // accel.setRange(ADXL345_RANGE_8_G);
  722.     // accel.setRange(ADXL345_RANGE_4_G);
  723.     accel.setRange(ADXL345_RANGE_2_G);
  724.    
  725.     delay(2500); // time to lower the cabinet lid
  726.     sensors_event_t event;
  727.     accel.getEvent(&event);
  728.     zeroX = event.acceleration.x * nudgeMultiplier * -1;
  729.     zeroY = event.acceleration.y * nudgeMultiplier * -1;
  730.   }
  731.   else delay(1000);
  732.  
  733.   // plunger setup
  734.   plungerMin = getPlungerAverage();
  735.   if (plungerEnabled) plungerMax = EEPROM.readInt(0);
  736.  
  737.   // to calibrate, hold A or START when plugging in the Teensy LC
  738.   if (digitalRead(pinB1) == LOW) getPlungerMax();
  739.   else if (digitalRead(pinST) == LOW) getPlungerMax();
  740.  
  741.   // linear conversions
  742.   if (plungerEnabled)
  743.   {
  744.      plungerMaxDistance = readingToDistance(plungerMin);
  745.      plungerMinDistance = readingToDistance(plungerMax);
  746.      lastDistance = plungerMaxDistance;
  747.   }
  748. }
  749.  
  750. void loop()
  751. {
  752.   //Poll Buttons
  753.   buttonUpdate();
  754.  
  755.   //Process all inputs and load up the usbData registers correctly
  756.   processInputs();
  757.  
  758.   //Update the LED display
  759.   controller.LEDUpdate();
  760.  
  761.   //Send data
  762.   controller.sendXinput();
  763.  
  764.   //Receive data
  765.   controller.receiveXinput();
  766. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement