21 April 2025

Team 5: Fish Controller


Description 

Our controller takes the playful, unexpected form of a fish—transforming the caught object into the control mechanism itself. This inversion lies at the heart of our conceptual model: instead of wielding a tool to catch a fish, the player interacts with the fish to control the act of fishing. This ironic, self-aware design ties directly into the game's humorous tone.

Inputs are gathered through flex sensors embedded along the fish’s body and light sensors positioned in its eyes. Flex sensors capture pressure and bending from player touches, squeezes, or twists—allowing the system to detect where and how the fish is being held or manipulated that is then mapped into the four cardinal directions. Meral detectors in the mouth act as a unique form of interaction detection: if the player taps them with a provided hook, the controller interprets that as a distinct command, as two contect buttons. There's also a tail linked to a potentiometer that's required to be constantly moving in order to keep reading inputs. Outputs, then, are actions within the game—casting the line, reeling in, and even interacting with in-game menus—mapped creatively to these gestures.

Feedback is primarily visual and in-game, with exaggerated animations or haptic pulses when certain thresholds are hit. This closed loop—physical input yielding immediate, meaningful feedback—reinforces the affordance of the fish-shaped controller as a usable, learnable interface.

The absurdity of the form is balanced by the consistency of the interaction logic, and despite lacking traditional buttons, the tactile and spatial input model helps make the experience cohesive.

Open-ended question for peer feedback: Does the form of the controller lend itself well to prolonged use, or does its novelty risk overshadowing ergonomic function?

Contributions

Chris:
- Created casing
- Drew schematic
- Soldered sensors
- Programmed most code

Gavin:
- Secondary coder
- Soldered wires and CPE

Schematic

Code

//Keyboard layouts for various languages
#include <Keyboard.h>
#include <KeyboardLayout.h>
#include <Keyboard_da_DK.h> //Danish
#include <Keyboard_de_DE.h> //German
#include <Keyboard_es_ES.h> //Spanish
#include <Keyboard_fr_FR.h> //French
#include <Keyboard_hu_HU.h> //Hungarian
#include <Keyboard_it_IT.h> //Italian
#include <Keyboard_pt_PT.h> //Portuguese
#include <Keyboard_sv_SE.h> //Swedish

#include <Adafruit_CircuitPlayground.h>
#include <Adafruit_Circuit_Playground.h>

// Variables
int rawPot;
int lastPot;

// Raw Data Of Flex Sensors
int flexUp;
int flexDown;
int flexLeft;
int flexRight;

// Mapped Data of Flex Sensors
int goUp;
int goDown;
int goLeft;
int goRight;

// Is the tail moving?
bool swimming = false;
byte potPercentage;
byte oldPercentage;

// Detects if Hook Circuit is Complete
int leftHook;
int rightHook;

void setup() 
{
  pinMode(CPLAY_SLIDESWITCHPIN, INPUT_PULLUP); // Detects if CPE Switch is turned on
  CircuitPlayground.begin();
  Serial.begin(9600);

  pinMode(10, INPUT_PULLDOWN); // Read for Left Hook (Pin A3/D10)
  pinMode(3, INPUT_PULLDOWN); // Read for Right Hook (Pin A4/D3)
}

void loop() {
  if(digitalRead(CPLAY_SLIDESWITCHPIN)) 
  {
    //Reads if Potentiometer is moving, double read pot
    rawPot = analogRead(A0);
    rawPot = analogRead(A0);
    rawPot = constrain(rawPot, 16, 1008); //Prevents most bounce
    
    if(rawPot < (lastPot - 4) || rawPot > (lastPot + 4))
    {
      lastPot = rawPot;
      potPercentage = map(lastPot, 16, 1000, 1, 50);

      if(oldPercentage != potPercentage)
      {
        swimming = true;
        // Serial.print("Pot percentage: ");
        // Serial.print(potPercentage);
        // Serial.println("%");
        oldPercentage = potPercentage;
      }
    }
    
    leftHook = digitalRead(10); //Read if Left Hook is active
    //Action of Left Hook
    if(leftHook == HIGH) 
    {
      Keyboard.press(' ');
      Serial.println("Space Bar");
    } 
    Keyboard.release(' ');
    
    rightHook = digitalRead(3); //Reads if Right Hook is active
    //Action of Right Hook
    if(rightHook == HIGH) 
    {
      Keyboard.press('C');
      Serial.println("Cancel");
    } 
    Keyboard.release('C');
  
    //Only Active when tail is moving
    if(swimming) 
    {
      flexUp = analogRead(A6);
      goUp = map(flexUp, 1023, 1, 100, 1);
      if(goUp <= 50)
        Keyboard.press(KEY_UP_ARROW);

      flexLeft = analogRead(A5);
      goLeft = map(flexLeft, 1023, 1, 100, 1);
      if(goLeft <= 50)
        Keyboard.press(KEY_LEFT_ARROW);

      flexDown = analogRead(A2);
      goDown = map(flexDown, 1023, 1, 100, 1);
      if(goDown <= 50)
        Keyboard.press(KEY_DOWN_ARROW);

      flexRight = analogRead(A1);
      goRight = map(flexRight, 1023, 1, 100, 1);
      if(goRight <= 50)
        Keyboard.press(KEY_RIGHT_ARROW);

      Serial.println("Swimming");
    }

  delay(100);
  swimming = false;
  }
}

Demonstration



No comments:

Post a Comment

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