The conceptual model for our controller was a glow stick that you can dance with at a concert reminiscent of the high energy pop start era featuring lots of holographic projections. The main affordance of the controller are the movement featuring four inputs sent by rotating the controller left, right, and up with the accompaniment of a potentiometer not only mapped to a gradient in the LED stick, but also dictating wether a vertical shake is sending up or down input based on the positioning of the slider. The color of the LEDs is also mapped to the external accelerometer/gyroscope that makes the LEDs blink a greenish color when shaken enough to trigger an input. This gives the player visual feedback to tell whether they’re doing the correct movement. This all connects back to the rhythm aspect of the game since timing is very important and light feedback makes the activity more engaging and visually striking which parallels the striking visuals presented by the game during each song.How well do you think our conceptual model is portrayed through the design choices made, and how would you improve the design to help facilitate engaging gameplay?
Marcus was the programmer/stress tester and Sarah was the designer and modeler. We both worked on the wiring and schematic, as well the concept and mechanics
#include <Keyboard.h>
#include <KeyboardLayout.h>
// Marcus Werner
// Sarah Walenciak
// include libraries
#include <Adafruit_CircuitPlayground.h>
#include <Adafruit_MPU6050.h>
#define NEOPIX_PIN A2
#define NUM_PIXELS 10
#define DEBUG_MODE 1
Adafruit_CPlay_NeoPixel strip = Adafruit_CPlay_NeoPixel(NUM_PIXELS, NEOPIX_PIN, NEO_GRB + NEO_KHZ800);
Adafruit_MPU6050 mpu;
int sensorPin = A0; // select the input pin for the potentiometer
int sensorValue = 0;
int r = 255;
int b = 0;
int gPix = 0;
// Booleans for the directional input (WASD) and the slide inputs
bool up = false;
bool down = false;
bool left = false;
bool right = false;
bool slideLeft = false;
bool slideRight = false;
float x, y, z; // Track accel values continuously
float x2, y2, z2; // Update accel values when needed to use in conditionals
const int btnA = 4;
bool lock = false;
float filter_coeff = 0.1;
float filtered_shake = 0;
float threshold = 10;
float thresholdColor = 10;
float thresholdSlide = 10;
void setup()
{
#if defined DEBUG_MODE
Serial.begin(115200);
#endif
CircuitPlayground.begin();
strip.begin();
Keyboard.begin();
pinMode(btnA, INPUT_PULLDOWN);
while (!Serial) {
delay(10); // will pause Zero, Leonardo, etc until serial console opens
}
// Try to initialize!
if (!mpu.begin()) {
Serial.println("Failed to find MPU6050 chip");
while (1) {
delay(10);
}
}
mpu.setAccelerometerRange(MPU6050_RANGE_16_G);
mpu.setGyroRange(MPU6050_RANGE_250_DEG);
mpu.setFilterBandwidth(MPU6050_BAND_21_HZ);
Serial.println("");
delay(100);
}
void loop()
{
// Initialize values for the potentiometer
sensors_event_t a, g, temp;
mpu.getEvent(&a, &g, &temp);
// Continuously tracks accel values on x, y, and z axes
x = a.acceleration.x;
y = a.acceleration.y;
z = a.acceleration.z;
// Sets secondary accel values when not actively shaking (to use in conditionals)
if (filtered_shake < threshold)
{
x2 = x;
y2 = y;
z2 = z;
}
// Determine standardized value for shake detection
float shake = sqrt(x*x + y*y + z*z);
shake -= 9.81; // subtract gravity
shake *= shake;
filtered_shake = (shake * filter_coeff) + (filtered_shake * (1 - filter_coeff));
#if defined DEBUG_MODE
if (filtered_shake > 1)
{
Serial.println(filtered_shake);
}
delay(10);
#endif
/* Print out the values */
Serial.print("AccelX:");
Serial.print(x);
Serial.print(",");
Serial.print("AccelY:");
Serial.print(y);
Serial.print(",");
Serial.print("AccelZ:");
Serial.print(z);
Serial.print(", ");
//CircuitPlayground.clearPixels();
// Sets external LEDs to on
strip.show();
// read the value from the potentiometer:
sensorValue = analogRead(sensorPin);
// Map the red and blue value of the LEDs to each extreme of the potentiometer
r = map(sensorValue, 0, 1023, 255, 0);
b = map(sensorValue, 0, 1023, 0, 255);
strip.setPixelColor(0, r, gPix, b);
strip.setPixelColor(1, r, gPix, b);
strip.setPixelColor(2, r, gPix, b);
strip.setPixelColor(3, r, gPix, b);
strip.setPixelColor(4, r, gPix, b);
strip.setPixelColor(5, r, gPix, b);
strip.setPixelColor(6, r, gPix, b);
strip.setPixelColor(7, r, gPix, b);
strip.setPixelColor(8, r, gPix, b);
strip.setPixelColor(9, r, gPix, b);
// Compares secondary accel x value to threshold of 9.0 and ensures left or right is not also chosen already
// If all is satisfied, up is set to true
if (x2 > 8.0 && b > 200 && !left && !right)
{
up = true;
}
else
{
up = false;
}
// Compares secondary accel x value to threshold of -8.0 and ensures left or right is not also chosen already
// If all is satisfied, down is set to true
if (x2 > 8.0 && r > 200 && !left && !right)
{
down = true;
}
else
{
down = false;
}
// Compares secondary accel y value to threshold of 9.0 and ensures up or down is not also chosen already
// If all is satisfied, right is set to true
if (y2 > 5.0 && !up && !down)
{
right = true;
}
else
{
right = false;
}
// Compares secondary accel y value to threshold of -9.0 and ensures up or down is not also chosen already
// If all is satisfied, left is set to true
if (y2 < -5.0 && !up && !down)
{
left = true;
}
else
{
left = false;
}
// If the potentiometer is slid to the blue extreme, slide left input is stored
/*if (b = 255)
{
slideLeft = true;
}
else
{
slideRight = false;
//Keyboard.release('A');
}*/
// The filtered shake value is compared to the predetermined threshold to sense if the controller is being shaken enough
if (filtered_shake > threshold)
{
if (up) // If the controller is aimed up and shaken, 'W' is sent as an input
{
if (!lock)
{
Keyboard.press('W');
//Keyboard.press(KEY_UP_ARROW);
}
}
if (down) // If the controller is aimed down and shaken, 'S' is sent as an input
{
if (!lock)
{
Keyboard.press('S');
//Keyboard.press(KEY_DOWN_ARROW);
}
}
if (left) // If the controller is tilted left and shaken, 'A' is sent as an input
{
if (!lock)
{
Keyboard.press('A');
//Keyboard.press(KEY_LEFT_ARROW);
}
}
if (right) // If the controller is tilted right and shaken, 'D' is sent as an input
{
if (!lock)
{
Keyboard.press('D');
//Keyboard.press(KEY_RIGHT_ARROW);
}
}
}
else // In between shakes, all inputs are released
{
Keyboard.release('W');
Keyboard.release('A');
Keyboard.release('S');
Keyboard.release('D');
}
// Compared filtered shake to the threshold determined for the LEDs
if (filtered_shake > thresholdColor)
{
if (up) // If shaken enough (less than needed for input to be sent) the green value of LEDs is set to 255
{
gPix = 255;
}
if (down) // If shaken enough (less than needed for input to be sent) the green value of LEDs is set to 255
{
gPix = 255;
}
if (left) // If shaken enough (less than needed for input to be sent) the blue value of LEDs is set to 255
{
b = 255;
}
if (right) // If shaken enough (less than needed for input to be sent) the blue value of LEDs is set to 255
{
b = 255;
}
}
else
{
gPix = 0;
b = 0;
}
// If the controller is aimed forward then it's free to send the input for the slides
if (!up && !down && !left && !right)
{
if (filtered_shake > thresholdSlide) // Compares filtered shake to the threshold of the slides
{
if (slideLeft) // If shaken and slid to the left/blue extreme, the left slide input is sent
{
if (!lock)
{
//Keyboard.press('A');
}
}
else if (slideRight) // If shaken and slid to the left/blue extreme, the right slide input is sent
{
if (!lock)
{
//Keyboard.press('D');
}
}
}
else
{
//Keyboard.release('A');
//Keyboard.release('D');
}
}
https://youtu.be/n8kHLZAKKR4