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 }