16 April 2022

Gauntlet: A Motion Controller

 


What was originally called the Knight Motion Controller™ is now called the Gauntlet™ -  a wireless motion controller. Instead of having the original design of being a handheld motion controller, I'm now utilizing a glove with compartments that stores the CPB and the battery pack. I came to the realization that this would be much more comfortable and intuitive to control. I have to preface the rest of the description by acknowledging why it's called the Gauntlet. The game I chose to design the controller around is one I made called Knightborn, it's set in medieval times and plated armor pieces for your hands are called gauntlets. The Gauntlet utilizes the accelerometer on the CPB to control everything from keyboard to mouse controls. Because I had to map 4 inputs and 8 directions of the mouse, I had to be a little creative for it to work adequately enough. The Gauntlet has 2 control options, one controls the character and left mouse button (LMB), the other controls mouse movement, I'll discuss how that works later. Knightborn uses the A/D keys for left/right character movement. Simply titling the glove (your hand) left/right will control the character. LMB is triggered when the controller is tilted forward and the escape key, which is used to open the menu, is triggered when the controller is tilted far enough backwards (towards you). The trickiest part about this controller was mouse motion inputs. After hours of tinkering, I decided that a toggle between character and mouse control was the best option. To switch from key input to mouse control, the glove must be rotated until your palm is face up and rotated back to switch between the two. When you are in the mouse control state, you have 8 directions to control the mouse cursor. In other words, I mapped the X and Y axes to a radian (a unit of angle) variable which divided a 360 degree radius into 8 segments, each segment mapped to their specific mouse direction. When the state of your controller is switched, a lower octave sound cue plays to signify character control, higher octave for mouse control. The name of the controller and the reason behind it connects to the game and some of the motions are similar to in-game actions, attacking is a downward controller movement and pausing the game requires a stop hand gesture.




/* PROJECT: GAME CONTROLLER
 *  
 * KNIGHTBORN MOTION GLOVE
 * BY MATTHEW POWELL
 * 
 * TO BE USED WITH THE GAME KNIGHTBORN
 */

//**********************************************************************************
//                                PRE-INITIALIZE
//**********************************************************************************

// LIBRARIES
  #include <Adafruit_CircuitPlayground.h>
  #include <Adafruit_Circuit_Playground.h>
  #include <bluefruit.h>
  #include "notes.h"

// BLUEFRUIT BLUETOOTH
  BLEDis bledis;
  BLEHidAdafruit blehid;

// HID (MOUSE)
  uint8_t const report_id = 0;
  int8_t const VERT = -5; // MOUSE MOVEMENT
  int8_t const HORZ = 5; // MOUSE MOVEMENT

// TOGGLE CHARACTER/MOUSE CONTROL
   float M = 1;

// BLUETOOTH CONNECTION
   float B = 0;

//**********************************************************************************
//                                   SETUP
//**********************************************************************************

void setup() {

  // BEGIN BLUEFRUIT/BLUETOOTH
  Bluefruit.begin();
  Bluefruit.setTxPower(4);
  bledis.setManufacturer("Adafruit Industries");
  bledis.setModel("Bluefruit Feather 52");
  bledis.begin();
  blehid.begin();
  startAdv(); // STARTS BLUETOOTH FUNCTION

  // INPUTS/OUTPUTS
  pinMode(7, INPUT_PULLDOWN);
  
  // TESTING
  /*
  pinMode(4, INPUT_PULLDOWN);
  pinMode(5, INPUT_PULLDOWN);
  */
  
  // DEBUGGING SETUP
  /*
  Serial.begin(9600);
  delay(1000);
  */

  // BEGIN CIRCUIT PLAYGROUND 
  CircuitPlayground.begin();

  // SET SPEAKER VOLUME
  analogWrite (A0, 75);

  // BLUETOOTH IS SEARCHING & CONTROLLER IS ON AUDIO CUE
  CircuitPlayground.playTone(C5, 80);
  CircuitPlayground.playTone(E5, 80);
  CircuitPlayground.playTone(G5, 80);
}

//**********************************************************************************
//                              BLUETOOTH FUNCTION
//**********************************************************************************

void startAdv(void)
{  
  // ADVERTISING PACKET
  Bluefruit.Advertising.addTxPower();
  Bluefruit.Advertising.addAppearance(BLE_APPEARANCE_HID_MOUSE);
  
  // ADD BLE HID SERVICES
  Bluefruit.Advertising.addService(blehid);

  // ADD NAME TO ADVERTISING PACKET
  Bluefruit.Advertising.addName();
  
  // ADVERTISE IF DISCONNECTED
  Bluefruit.Advertising.restartOnDisconnect(true);
  
  // ADVERTISING INTERVAL
  Bluefruit.Advertising.setInterval(32, 244);
  
  // ADVERTISING FAST MODE TIMEOUT
  Bluefruit.Advertising.setFastTimeout(30);
  
  // START ADVERTISING
  Bluefruit.Advertising.start(0); 
}

//**********************************************************************************
//                                RUNTIME FUNCTION
//**********************************************************************************

void loop() {  

  // IF BLUEOOTH IS CONNECTED, PLAY AUDIO CUE
  if(Bluefruit.connected() && B == 0 ) {
    CircuitPlayground.playTone(G5, 80);
    CircuitPlayground.playTone(G5, 80);
    B = 1;
    }
  
  // X, Y, Z
  float x = CircuitPlayground.motionX();
  float y = CircuitPlayground.motionY();
  float z = CircuitPlayground.motionZ();
  float rad = atan2(y, x);
  
  // DEBUGGING
  /*
  Serial.print(x);
  Serial.print("   ");
  Serial.print(y);
  Serial.print("   ");
  Serial.print(z);
  Serial.print("   ");
  Serial.print(M);
  Serial.print("  ");
  Serial.print(rad);
  Serial.println("   ");
  */

  // SWITCH BETWEEN KEYBOARD AND MOUSE CONTROL
  if(digitalRead(7) && M == 0) {
    keyboard();
  } 
  if(digitalRead(7) && M == 1) {
    mouse();
    blehid.keyRelease();
  }

  // CONTROLLER FUNCTIONALITY IS OFF AND NULL KEYBOARD/MOUSE INPUT
  // PREVENTS FEEDBACK LOOPS AND ACTS AS AN EMERGENCY SHUTOFF
    if(!digitalRead(7)) {
    blehid.keyRelease();
    blehid.mouseButtonRelease(); 
   }
  }  

  //----------------------------------------------------------------------------------
  //                                   KEYBOARD CONTROLS
  //----------------------------------------------------------------------------------

void keyboard() {

  // X, Y, Z, RAD FLOATS
  float x = CircuitPlayground.motionX();
  float y = CircuitPlayground.motionY();
  float z = CircuitPlayground.motionZ();

  // IF Z IS LESS THAN -9 & MOUSE VARIABLE IS SET TO 0, SWITCH TO MOUSE CONTROLS
  if(z < -9 && M == 0) {
    M = 1;
    CircuitPlayground.playTone(G4, 80);
    CircuitPlayground.playTone(G4, 80);
    blehid.keyRelease();
    blehid.mouseButtonRelease();
    delay(100);
    }
 
  // IF X AXIS VALUE IS GREATER THAN 6.5, INPUT KEY
  if(x > 4.5) {
    blehid.keyPress('a');
    } 
    
  // IF X AXIS VALUE IS LESS THAN -6.5, INPUT KEY D
  if(x < -7.5) {
    blehid.keyPress('d'); 
    }

  // IF X AXIS VALUE IS GREATER THAN 6.5, INPUT KEY ESCAPE
  if(y > 9) {
    uint8_t const modifier = 0;
    uint8_t keycode[6] = {0};
    keycode[0] = HID_KEY_ESCAPE;
    blehid.keyboardReport(report_id, modifier, keycode);
    delay(100);
    blehid.keyRelease();
    }

  // IF Y IS LESS THAN -8, TRIGGER LEFT MOUSE BUTTON
  if(y < -8) {
    blehid.mouseButtonPress(MOUSE_BUTTON_LEFT);
    delay(100);
    blehid.mouseButtonRelease();
    }

  // RELEASE A,D KEYS IF X AXIS IS BETWEEN 6.5,-6.5
  if(x < 4.5 && x > -7.5) {
    blehid.keyRelease();
    }
  }
  
  //----------------------------------------------------------------------------------
  //                                    MOUSE CONTROLS
  //----------------------------------------------------------------------------------

// WONKY MOUSE CONTROLS, REQUIRES FURTHER OPTIMIZATION
void mouse() {

  // X, Y, Z, RAD FLOATS
  float x = CircuitPlayground.motionX();
  float y = CircuitPlayground.motionY();
  float z = CircuitPlayground.motionZ();
  float rad = atan2(y, x);

  // IF Z AXIS IS LESS THAN -9 & MOUSE VARIABLES IS SET TO 1, CHANGE TO KEYBOARD CONTROLS
  if(z < -9 && M == 1) {
    M = 0;
    CircuitPlayground.playTone(C4, 80);
    CircuitPlayground.playTone(C4, 80);
    delay(100);
    }

  // IF Z AXIS IS LESS THAN 0, STOP MOUSE MOVEMENT, THAT WAY IF YOU'RE TRYING TO SWITCH TO
  // KEYBOARD CONTROLS TO USE LMB, THE MOUSE WON'T CHANGE POSITIONS.
  if(z < 0) {
    blehid.mouseMove(0, 0);
    }
  
  // UP
  if(rad < -1 && rad > -2) {
    blehid.mouseMove(0, VERT);
    } 

  // UP/RIGHT
  if(rad < -2 && rad > -2.75) {
    blehid.mouseMove(HORZ, VERT);
    } 
    
  // RIGHT
  if((rad < -2.75 && rad > -3) || (rad < 3 && rad > 2)) {
    blehid.mouseMove(HORZ, 0);
    }

  // DOWN/RIGHT
  if(rad < 2.75 && rad > 2) {
    blehid.mouseMove(HORZ, -VERT);
    }
    
  // DOWN
  if(rad < 2 && rad > 1) {
    blehid.mouseMove(0, -VERT);
    } 

  // DOWN/LEFT
  if(rad < 1 && rad > .25) {
    blehid.mouseMove(-HORZ, -VERT);
    }
    
  // LEFT
  if(rad < .25 && rad > -.25) {
    blehid.mouseMove(-HORZ, 0);
    } 

  // UP/LEFT
  if(rad < -.25 && rad > -1) {
    blehid.mouseMove(-HORZ, VERT);
    }
  } 




No comments:

Post a Comment

Note: Only a member of this blog may post a comment.