/ USB_SNES_Gamepad / teensySNES_stellakey / teensySNES_stellakey.ino
teensySNES_stellakey.ino
  1  // SPDX-FileCopyrightText: 2019 Limor Fried/ladyada for Adafruit Industries
  2  //
  3  // SPDX-License-Identifier: MIT
  4  
  5  #define KEYREPEAT 85 // delay when repeating characters
  6  #define KEYDELAY 200 // delay from first to second character
  7  const int pinAnalogXInput = 3;
  8  const int pinAnalogYInput = 1;
  9  const int pinAnalogZInput = 2;
 10  const int pinAnalogDummyInput = 0;
 11  
 12  const int pinBtnUp = 0;
 13  const int pinBtnRight = 1;
 14  const int pinBtnDown = 2;
 15  const int pinBtnLeft = 3;
 16  
 17  const int pinBtnSelect = 4;
 18  const int pinBtnStart = 5;
 19  
 20  const int pinBtnB = 7;
 21  const int pinBtnA = 8;
 22  const int pinBtnY = 10;
 23  const int pinBtnX = 9;
 24  
 25  const int pinBtnTrigLeft = 6;
 26  const int pinBtnTrigRight = 23;
 27  
 28  // here is where we define the buttons that we'll use. button "1" is the first, button "6" is the 6th, etc
 29  byte buttons[] = {pinBtnUp, pinBtnRight, pinBtnDown, pinBtnLeft, pinBtnSelect, pinBtnStart,
 30  pinBtnB, pinBtnA, pinBtnY, pinBtnX, pinBtnTrigLeft, pinBtnTrigRight}; 
 31  byte keys[] = {KEY_UP, KEY_RIGHT, KEY_DOWN, KEY_LEFT, KEY_F1, KEY_F2, KEY_SPACE, KEY_SPACE, KEY_4, KEY_5, KEY_ESC, KEY_ENTER};
 32  
 33  #define NUMBUTTONS sizeof(buttons)
 34  
 35  // we will track if a button is just pressed, just released, or 'currently pressed' 
 36  volatile byte pressed[NUMBUTTONS], justpressed[NUMBUTTONS], justreleased[NUMBUTTONS];
 37  
 38  
 39  const int pinLEDOutput = 11;
 40  
 41  //Variables for the states of the SNES buttons
 42  boolean boolBtnA;
 43  boolean boolBtnB;
 44  boolean boolBtnX;
 45  boolean boolBtnY;
 46  
 47  boolean boolBtnTrigLeft;
 48  boolean boolBtnTrigRight;
 49  
 50  boolean boolBtnUp;
 51  boolean boolBtnDown;
 52  boolean boolBtnLeft;
 53  boolean boolBtnRight;
 54  
 55  boolean boolBtnSelect;
 56  boolean boolBtnStart;
 57  
 58  //Change these values if accelerometer reading are different:
 59  //How far the accerometer is tilted before
 60  //the Teensy starts moving the mouse:
 61  const int cintMovementThreshold = 18;
 62  
 63  //The average zero acceleration values read
 64  //from the accelerometer for each axis:
 65  const int cintZeroXValue = 328;
 66  const int cintZeroYValue = 328;
 67  const int cintZeroZValue = 328;
 68  
 69  //The maximum (positive) acceleration values read
 70  //from the accelerometer for each axis:
 71  const int cintMaxXValue = 396;
 72  const int cintMaxYValue = 396;
 73  const int cintMaxZValue = 396;
 74  
 75  //The minimum (negative) acceleration values read
 76  //from the accelerometer for each axis:
 77  const int cintMinXValue = 256;
 78  const int cintMinYValue = 256;
 79  const int cintMinZValue = 256;
 80  
 81  //The sign of the mouse movement relative to the acceleration.
 82  //If your cursor is going in the opposite direction you think it
 83  //should go, change the sign for the appropriate axis.
 84  const int cintXSign = 1;
 85  const int cintYSign = -1;
 86  const int cintZSign = 1;
 87  
 88  //const float cfloatMovementMultiplier = 1;
 89  
 90  //The maximum speed in each axis (x and y)
 91  //that the cursor should move. Set this to a higher or lower
 92  //number if the cursor does not move fast enough or is too fast.
 93  const int cintMaxMouseMovement = 10;
 94  
 95  //This reduces the 'twitchiness' of the cursor by calling
 96  //a delay function at the end of the main loop.
 97  //There is a better way to do this without delaying the whole
 98  //microcontroller, but that is left for another time or person.
 99  const int cintMouseDelay = 8;
100  
101  
102  void setup()
103  {
104      //This is not needed and set to default but can be useful if you
105    //want to get the full range out of the analog channels when
106    //reading from the 3.3V ADXL335.
107    //If the analog reference is used, the thresholds, zeroes,
108    //maxima and minima will need to be re-evaluated.
109    analogReference( DEFAULT );
110    
111    //Setup the pin modes.
112    pinMode( pinLEDOutput, OUTPUT );
113  
114    //Special for the Teensy is the INPUT_PULLUP
115    //It enables a pullup resitor on the pin.
116    for (byte i=0; i< NUMBUTTONS; i++) {
117      pinMode(buttons[i], INPUT_PULLUP);
118  
119    }
120    //Uncomment this line to debug the acceleromter values:
121    //Serial.begin(19200);
122  }
123  
124  
125  void loop()
126  {
127  //  //debugging the start button...
128    digitalWrite ( pinLEDOutput, digitalRead(pinBtnStart));
129  
130  
131    //Process the accelerometer to make the cursor move.
132    //Comment this line to debug the accelerometer values:
133    fcnProcessAccelerometer();
134  
135    //Progess the SNES controller buttons to send keystrokes.
136     fcnProcessButtons();
137    
138      
139    //Delay to avoid 'twitchiness' and bouncing inputs
140    //due to too fast of sampling.
141    //As said above, there is a better way to do this
142    //than delay the whole MCU.
143    delay(cintMouseDelay);
144  }
145  
146  
147  
148  //Function to process the acclerometer data
149  //and send mouse movement information to the host computer.
150  void fcnProcessAccelerometer()
151  {
152    //Initialize values for the mouse cursor movement.
153    int intMouseXMovement = 0;
154    int intMouseYMovement = 0;
155    
156    //Read the dummy analog channel
157    //This must be done first because the X analog channel was first
158    //and was unstable, it dropped or pegged periodically regardless
159    //of pin or source.
160    analogRead( pinAnalogDummyInput );
161    
162    //Read accelerometer readings  
163    int intAnalogXReading = analogRead(pinAnalogXInput);
164    int intAnalogYReading = analogRead(pinAnalogYInput);
165    int intAnalogZReading = analogRead(pinAnalogZInput);
166  
167    //Calculate mouse movement
168    //If the analog X reading is ouside of the zero threshold...
169    if( cintMovementThreshold < abs( intAnalogXReading - cintZeroXValue ) )
170    {
171      //...calculate X mouse movement based on how far the X acceleration is from its zero value.
172      intMouseXMovement = cintXSign * ( ( ( (float)( 2 * cintMaxMouseMovement ) / ( cintMaxXValue - cintMinXValue ) ) * ( intAnalogXReading - cintMinXValue ) ) - cintMaxMouseMovement );
173      //it could use some improvement, like making it trigonometric.
174    }
175    else
176    {
177      //Within the zero threshold, the cursor does not move in the X.
178      intMouseXMovement = 0;
179    }
180  
181    //If the analog Y reading is ouside of the zero threshold... 
182    if( cintMovementThreshold < abs( intAnalogYReading - cintZeroYValue ) )
183    {
184      //...calculate Y mouse movement based on how far the Y acceleration is from its zero value.
185      intMouseYMovement = cintYSign * ( ( ( (float)( 2 * cintMaxMouseMovement ) / ( cintMaxYValue - cintMinYValue ) ) * ( intAnalogYReading - cintMinYValue ) ) - cintMaxMouseMovement );
186      //it could use some improvement, like making it trigonometric.
187    }
188    else
189    {
190      //Within the zero threshold, the cursor does not move in the Y.
191      intMouseYMovement = 0;
192    }
193   
194    Mouse.move(intMouseXMovement, intMouseYMovement);
195  }
196  
197  //Function to process the buttons from the SNES controller
198  void fcnProcessButtons()
199  {
200    byte flag = 0;
201    static long currentkey = 0;
202    static long repeat = 0;
203    
204    for (byte i = 0; i < NUMBUTTONS; i++) {
205  
206      if ( !digitalRead(buttons[i])) {
207        flag = 1;
208        if (currentkey != keys[i]) {
209          // unset the old key
210          Keyboard.set_key1(0);
211          Keyboard.send_now();
212          Keyboard.set_key1(keys[i]);
213          currentkey = keys[i];
214          Keyboard.send_now();
215          repeat = 0;
216        } else {
217          if (! repeat) {
218            delay(KEYDELAY);
219          } else {
220           // resend 
221           repeat = 1;
222           Keyboard.set_key1(keys[i]);
223           Keyboard.send_now();
224           delay(KEYREPEAT);
225         }
226        }
227      }
228    }
229     if ( currentkey && !flag) {
230       currentkey = 0;
231       Keyboard.set_key1(0);
232       Keyboard.send_now();
233    }
234  }