/ USB_SNES_Gamepad / teensySNES_Portal / teensySNES_Portal.ino
teensySNES_Portal.ino
  1  // SPDX-FileCopyrightText: 2019 Anne Barela for Adafruit Industries
  2  //
  3  // SPDX-License-Identifier: MIT
  4  
  5  const int pinAnalogXInput = 3;
  6  const int pinAnalogYInput = 1;
  7  const int pinAnalogZInput = 2;
  8  const int pinAnalogDummyInput = 0;
  9  
 10  const int pinBtnA = 0;
 11  const int pinBtnB = 1;
 12  const int pinBtnX = 2;
 13  const int pinBtnY = 3;
 14  
 15  const int pinBtnUp = 4;
 16  const int pinBtnDown = 5;
 17  const int pinBtnLeft = 6;
 18  const int pinBtnRight = 7;
 19  
 20  const int pinBtnTrigLeft = 8;
 21  const int pinBtnTrigRight = 9;
 22  const int pinBtnSelect = 10;
 23  
 24  //For some reason, it is not possible to get a digital read from
 25  //pin 12 or for as far as I can tell, any analog capable pin.
 26  //const int pinBtnStart = 12;
 27  const int pinBtnStart = 23;
 28  
 29  const int pinLEDOutput = 11;
 30  
 31  //Change these values if accelerometer reading are different:
 32  //How far the accerometer is tilted before
 33  //the Teensy starts moving the mouse:
 34  const int cintMovementThreshold = 18;
 35  
 36  //The average zero acceleration values read
 37  //from the accelerometer for each axis:
 38  const int cintZeroXValue = 328;
 39  const int cintZeroYValue = 328;
 40  const int cintZeroZValue = 328;
 41  
 42  //The maximum (positive) acceleration values read
 43  //from the accelerometer for each axis:
 44  const int cintMaxXValue = 396;
 45  const int cintMaxYValue = 396;
 46  const int cintMaxZValue = 396;
 47  
 48  //The minimum (negative) acceleration values read
 49  //from the accelerometer for each axis:
 50  const int cintMinXValue = 256;
 51  const int cintMinYValue = 256;
 52  const int cintMinZValue = 256;
 53  
 54  //The sign of the mouse movement relative to the acceleration.
 55  //If your cursor is going in the opposite direction you think it
 56  //should go, change the sign for the appropriate axis.
 57  const int cintXSign = 1;
 58  const int cintYSign = -1;
 59  const int cintZSign = 1;
 60  
 61  //const float cfloatMovementMultiplier = 1;
 62  
 63  //The maximum speed in each axis (x and y)
 64  //that the cursor should move. Set this to a higher or lower
 65  //number if the cursor does not move fast enough or is too fast.
 66  const int cintMaxMouseMovement = 10;
 67  
 68  //This reduces the 'twitchiness' of the cursor by calling
 69  //a delay function at the end of the main loop.
 70  //There is a better way to do this without delaying the whole
 71  //microcontroller, but that is left for another time or person.
 72  const int cintMouseDelay = 8;
 73  
 74  //Variables for the mouse buttons.
 75  boolean boolLeftMouseBtn;
 76  boolean boolMiddleMouseBtn;
 77  boolean boolRightMouseBtn;
 78  
 79  //Variables for the states of the SNES buttons
 80  boolean boolBtnA;
 81  boolean boolBtnB;
 82  boolean boolBtnX;
 83  boolean boolBtnY;
 84  
 85  boolean boolBtnTrigLeft;
 86  boolean boolBtnTrigRight;
 87  
 88  boolean boolBtnUp;
 89  boolean boolBtnDown;
 90  boolean boolBtnLeft;
 91  boolean boolBtnRight;
 92  
 93  boolean boolBtnSelect;
 94  boolean boolBtnStart;
 95  
 96  void setup()
 97  {
 98    //This is not needed and set to default but can be useful if you
 99    //want to get the full range out of the analog channels when
100    //reading from the 3.3V ADXL335.
101    //If the analog reference is used, the thresholds, zeroes,
102    //maxima and minima will need to be re-evaluated.
103    analogReference( DEFAULT );
104    
105    //Setup the pin modes.
106    pinMode( pinLEDOutput, OUTPUT );
107  
108    pinMode( pinBtnA, INPUT );
109    pinMode( pinBtnB, INPUT );
110    pinMode( pinBtnX, INPUT );
111    pinMode( pinBtnY, INPUT );
112    
113    pinMode( pinBtnUp, INPUT );
114    pinMode( pinBtnDown, INPUT );
115    pinMode( pinBtnLeft, INPUT );
116    pinMode( pinBtnRight, INPUT );
117    
118    pinMode( pinBtnTrigLeft, INPUT );
119    pinMode( pinBtnTrigRight, INPUT );
120    pinMode( pinBtnSelect, INPUT );
121    
122    //Special for the Teensy is the INPUT_PULLUP
123    //It enables a pullup resitor on the pin.
124    pinMode( pinBtnStart, INPUT_PULLUP );
125  
126    //Zero the mouse buttons:
127    boolLeftMouseBtn = false;
128    boolMiddleMouseBtn = false;
129    boolRightMouseBtn = false;
130    
131    //Zero the SNES controller button keys:
132    boolBtnA = false;
133    boolBtnB = false;
134    boolBtnX = false;
135    boolBtnY = false;
136    
137    boolBtnTrigLeft = false;
138    boolBtnTrigRight = false;
139    
140    boolBtnUp = false;
141    boolBtnDown = false;
142    boolBtnLeft = false;
143    boolBtnRight = false;
144    
145    boolBtnSelect = false;
146    boolBtnStart = false;
147  
148    //Uncomment this line to debug the acceleromter values:
149  //  Serial.begin();
150  
151  }
152  
153  
154  void loop()
155  {
156  //  //debugging the start button...
157    digitalWrite ( pinLEDOutput, digitalRead(pinBtnStart));
158  
159    //Process the accelerometer to make the cursor move.
160    //Comment this line to debug the accelerometer values:
161    fcnProcessAccelerometer();
162  
163    //Uncomment these lines to debug the accelerometer values:
164  //  Serial.print("X:\t");
165  //  Serial.print(analogRead(pinAnalogXInput));
166  //  Serial.print("\tY:\t");
167  //  Serial.print(analogRead(pinAnalogYInput));
168  //  Serial.print("\tZ:\t");
169  //  Serial.println(analogRead(pinAnalogZInput));
170  
171    //Progess the SNES controller buttons to send keystrokes.
172    fcnProcessButtons();
173    
174    //Delay to avoid 'twitchiness' and bouncing inputs
175    //due to too fast of sampling.
176    //As said above, there is a better way to do this
177    //than delay the whole MCU.
178    delay(cintMouseDelay);
179    
180  }
181  
182  //Function to process the acclerometer data
183  //and send mouse movement information to the host computer.
184  void fcnProcessAccelerometer()
185  {
186    //Initialize values for the mouse cursor movement.
187    int intMouseXMovement = 0;
188    int intMouseYMovement = 0;
189    
190    //Read the dummy analog channel
191    //This must be done first because the X analog channel was first
192    //and was unstable, it dropped or pegged periodically regardless
193    //of pin or source.
194    analogRead( pinAnalogDummyInput );
195    
196    //Read accelerometer readings  
197    int intAnalogXReading = analogRead(pinAnalogXInput);
198    int intAnalogYReading = analogRead(pinAnalogYInput);
199    int intAnalogZReading = analogRead(pinAnalogZInput);
200  
201    //Calculate mouse movement
202    //If the analog X reading is ouside of the zero threshold...
203    if( cintMovementThreshold < abs( intAnalogXReading - cintZeroXValue ) )
204    {
205      //...calculate X mouse movement based on how far the X acceleration is from its zero value.
206      intMouseXMovement = cintXSign * ( ( ( (float)( 2 * cintMaxMouseMovement ) / ( cintMaxXValue - cintMinXValue ) ) * ( intAnalogXReading - cintMinXValue ) ) - cintMaxMouseMovement );
207      //it could use some improvement, like making it trigonometric.
208    }
209    else
210    {
211      //Within the zero threshold, the cursor does not move in the X.
212      intMouseXMovement = 0;
213    }
214  
215    //If the analog Y reading is ouside of the zero threshold... 
216    if( cintMovementThreshold < abs( intAnalogYReading - cintZeroYValue ) )
217    {
218      //...calculate Y mouse movement based on how far the Y acceleration is from its zero value.
219      intMouseYMovement = cintYSign * ( ( ( (float)( 2 * cintMaxMouseMovement ) / ( cintMaxYValue - cintMinYValue ) ) * ( intAnalogYReading - cintMinYValue ) ) - cintMaxMouseMovement );
220      //it could use some improvement, like making it trigonometric.
221    }
222    else
223    {
224      //Within the zero threshold, the cursor does not move in the Y.
225      intMouseYMovement = 0;
226    }
227   
228    Mouse.move(intMouseXMovement, intMouseYMovement);
229  
230  }
231  
232  //Function to process the buttons from the SNES controller
233  void fcnProcessButtons()
234  {
235    //Assign temporary values for the buttons.
236    //Remember, the SNES buttons are read as active LOW.
237    //Capture their status here:
238    boolean boolTempBtnA = !digitalRead(pinBtnA);
239    boolean boolTempBtnB = !digitalRead(pinBtnB);  
240    boolean boolTempBtnX = !digitalRead(pinBtnX);
241    boolean boolTempBtnY = !digitalRead(pinBtnY);
242    
243    boolean boolTempBtnTrigLeft = !digitalRead(pinBtnTrigLeft);
244    boolean boolTempBtnTrigRight = !digitalRead(pinBtnTrigRight);
245    
246    boolean boolTempBtnUp = !digitalRead(pinBtnUp);
247    boolean boolTempBtnDown = !digitalRead(pinBtnDown);
248    boolean boolTempBtnLeft = !digitalRead(pinBtnLeft);
249    boolean boolTempBtnRight = !digitalRead(pinBtnRight);
250    
251    boolean boolTempBtnSelect = !digitalRead(pinBtnSelect);
252    boolean boolTempBtnStart = !digitalRead(pinBtnStart);
253    
254  
255    //If the A, B, or Y buttons are different from last time,
256    //send an update of the buttons.
257    //This has to be done, otherwise the Teensy constantly sends
258    //an update to the host that a button has been pressed,
259    //which really bogs everything down.
260    //So use this if statment for any buttons that should not be
261    //repeated until released.
262    if( ( boolTempBtnA != boolBtnA ) ||
263        ( boolTempBtnB != boolBtnB ) ||
264        ( boolTempBtnY != boolBtnY ) )
265    {  
266      //Assign temporary values to the real values:
267      boolBtnA = boolTempBtnA;
268      boolBtnB = boolTempBtnB;
269      boolBtnY = boolTempBtnY;
270      
271      //Update the mouse button status:
272      Mouse.set_buttons(boolBtnB,
273                      false,
274                      boolBtnA);
275  
276      
277      //If the Y button was pressed...
278      if ( true == boolBtnY )
279      {
280        //Set the key modifier to the control key
281        Keyboard.set_modifier( MODIFIERKEY_CTRL );
282      }
283      else
284      {
285        //Set the key modifier to the control key
286        Keyboard.set_modifier( 0 );
287      }
288  
289    }
290    
291  
292    
293    boolBtnUp = boolTempBtnUp;
294    boolBtnDown = boolTempBtnDown;
295    boolBtnLeft = boolTempBtnLeft;
296    boolBtnRight = boolTempBtnRight;
297    
298    boolBtnX = boolTempBtnX;
299    
300    boolBtnTrigLeft = boolTempBtnTrigLeft;
301    boolBtnTrigRight = boolTempBtnTrigRight;
302    
303  
304  
305    //Due to the shape of the D-pad only up or down can be pressed
306    //at the same time.
307    //This is useful as we can only send 6 keys at the same time.
308    
309    //These two lines are sickenly cute, but do not make much sense, kept here for posterity.
310    //Keyboard.set_key1((boolBtnUp ^ boolBtnDown) * ((KEY_W * boolBtnUp) + (KEY_S * boolBtnDown)));
311    //Keyboard.set_key2((boolBtnLeft ^ boolBtnRight) * ((KEY_A * boolBtnLeft) + (KEY_D * boolBtnRight)));
312    //Use if statements for clarity.
313  
314    //If the up button was pressed and not the down button...
315    if ( ( true == boolBtnUp ) && ( false == boolBtnDown ) )
316    {
317      //Set key1 to the 'W' key
318      Keyboard.set_key1( KEY_W );
319    }
320    //If the down button was pressed and not the up button...
321    else if ( ( false == boolBtnUp ) && ( true == boolBtnDown ) )
322    {
323      //Set key1 to the 'S' key
324      Keyboard.set_key1( KEY_S );
325    }
326    //All else: both up and down butttons pressed or none pressed.
327    else
328    {
329      //Set key1 to send nothing
330      Keyboard.set_key1( 0 );
331    }
332  
333    //If the left button was pressed and not the right button...
334    if ( ( true == boolBtnLeft ) && ( false == boolBtnRight ) )
335    {
336      //Set key2 to the 'A' key
337      Keyboard.set_key2( KEY_A );
338    }
339    //If the right button was pressed and not the left button...
340    else if ( ( false == boolBtnLeft ) && ( true == boolBtnRight ) )
341    {
342      //Set key2 to the 'D' key
343      Keyboard.set_key2( KEY_D );
344    }
345    //All else: both left and right butttons pressed or none pressed.
346    else
347    {
348      //Set key2 to send nothing
349      Keyboard.set_key2( 0 );
350    }
351    
352    //If the X button was pressed...
353    if ( true == boolBtnX )
354    {
355      //Set key3 to the space key
356      Keyboard.set_key3( KEY_SPACE );
357    }
358  
359    //If the Left trigger button was pressed...
360    if ( true == boolBtnTrigLeft )
361    {
362      //Set key4 to the tab key
363      Keyboard.set_key4( KEY_TAB );
364    }
365  
366    //If the Right trigger button was pressed...
367    if ( true == boolBtnTrigRight )
368    {
369      //Set key5 to the 'E' key
370      Keyboard.set_key5( KEY_E );
371    }
372  
373    //If the Select or Start buttons have changed since last time...
374    if (  ( boolBtnSelect != boolTempBtnSelect ) ||
375          ( boolBtnStart != boolTempBtnStart ) )
376    {
377  
378      //Update the buttons' statuses.
379      boolBtnSelect = boolTempBtnSelect;
380      boolBtnStart = boolTempBtnStart;
381  
382      //Process the start and select buttons, they can only use
383      //one key channel together so pressing both of them will only
384      //send the key assigned to the start button
385      //If the Start button was pressed...
386      if ( true == boolBtnStart )
387      {
388        //Set key6 to the Escape key
389        Keyboard.set_key6( KEY_ESC );
390      }  
391      //If the Start button was not pressed but the Select button
392      //was pressed...
393      else if ( true == boolBtnSelect )
394      {
395        //Set key6 to the Enter key
396        Keyboard.set_key6( KEY_ENTER );
397      }
398      else
399      {
400        //Set key6 to send nothing
401        Keyboard.set_key6( 0 );
402      }
403  
404    }
405  
406  
407  
408    //Send all of the set keys.
409    Keyboard.send_now();
410  
411    //Send an empty set of keys. The modifier key must be kept.
412    if ( true == boolBtnY )
413    {
414      Keyboard.set_modifier( MODIFIERKEY_CTRL );
415    }
416    else
417    {
418      Keyboard.set_modifier( 0 );
419    }
420    
421    Keyboard.set_key1( 0 );
422    Keyboard.set_key2( 0 );
423    Keyboard.set_key3( 0 );
424    Keyboard.set_key4( 0 );
425    Keyboard.set_key5( 0 );
426    Keyboard.set_key6( 0 );
427  
428    Keyboard.send_now();
429  
430  
431  
432  }