Our Balan Wonderworld controller is based on the iconic hat found in the game. The hat is a signature piece for both the game and the character, found on a majority of its marketing and game collectibles. We thought controlling Balan’s hat would be the best way to experience the game and feel like a showman character. The hat is separated into 3 pieces: the inner hat, the printed out Ball Bearings, and the outer hat. The outer hat is shaped and sewn using foam and fabric to create the curvature of the sides and the brim of the hat. Creating the inner hat was also another focus to house the wires and Arduino without tangling the cables. The ball bearings were an interesting component to both conceive and print, but we found the concept fun to control the character’s attire.
The design of the hat controller uses an unconventional button to use the character’s costume action by pressing the top of the hat. Along with the button, the player tilts their head forward to automatically move forward and turn their head left or right to turn the character in either direction. And finally, to change costumes, the player spins the hat in a complete 360. This design allows you to feel more connected to Balan and his whimsical world of props and sets.
Inputs - Outputs:
(Magnetometer Z axis) - W key (Forward movement)
(Unconventional Button) - Space Bar (Ability)
(Magnetometer Y axis) - Mouse Movement (Turn camera)
(Hall Effect Sensor) - C key (Change costumes)
Video:
Code:
// Basic demo for magnetometer readings from Adafruit LIS3MDL
#include <Adafruit_CircuitPlayground.h>
#include <Adafruit_Circuit_Playground.h>
#include <Wire.h>
#include <Adafruit_LIS3MDL.h>
#include <Adafruit_Sensor.h>
#include <Keyboard.h>
#include <KeyboardLayout.h>
#include <Keyboard_da_DK.h>
#include <Keyboard_de_DE.h>
#include <Keyboard_es_ES.h>
#include <Keyboard_fr_FR.h>
#include <Keyboard_hu_HU.h>
#include <Keyboard_it_IT.h>
#include <Keyboard_pt_PT.h>
#include <Keyboard_sv_SE.h>
#include <Mouse.h>
Adafruit_LIS3MDL lis3mdl;
#define LIS3MDL_CLK 13
#define LIS3MDL_MISO 12
#define LIS3MDL_MOSI 11
#define LIS3MDL_CS 10
bool on = true;
bool switchTrigger=false;
float initialCalibrationx;
float initialCalibrationy;
float initialCalibrationz;
float vectorx;
float vectorz;
float localizeddirection;
int incomingByte = 0; // Variable to store the received byte
void setup(void) {
Serial.begin(115200);
while (!Serial) delay(10); // will pause Zero, Leonardo, etc until serial console opens
//pinMode(A1, INPUT_PULLUP); // Enable pull-up for button press
//pinMode(CPLAY_SLIDESWITCHPIN, INPUT_PULLUP);
Serial.println("Adafruit LIS3MDL test?");
// Try to initialize!
if (! lis3mdl.begin_I2C()) { // hardware I2C mode, can pass in address & alt Wire
//if (! lis3mdl.begin_SPI(LIS3MDL_CS)) { // hardware SPI mode
//if (! lis3mdl.begin_SPI(LIS3MDL_CS, LIS3MDL_CLK, LIS3MDL_MISO, LIS3MDL_MOSI)) { // soft SPI
Serial.println("Failed to find LIS3MDL chip");
while (1) { delay(10); }
}
Serial.println("LIS3MDL Found!");
lis3mdl.setPerformanceMode(LIS3MDL_MEDIUMMODE);
Serial.print("Performance mode set to: ");
switch (lis3mdl.getPerformanceMode()) {
case LIS3MDL_LOWPOWERMODE: Serial.println("Low"); break;
case LIS3MDL_MEDIUMMODE: Serial.println("Medium"); break;
case LIS3MDL_HIGHMODE: Serial.println("High"); break;
case LIS3MDL_ULTRAHIGHMODE: Serial.println("Ultra-High"); break;
}
lis3mdl.setOperationMode(LIS3MDL_CONTINUOUSMODE);
Serial.print("Operation mode set to: ");
// Single shot mode will complete conversion and go into power down
switch (lis3mdl.getOperationMode()) {
case LIS3MDL_CONTINUOUSMODE: Serial.println("Continuous"); break;
case LIS3MDL_SINGLEMODE: Serial.println("Single mode"); break;
case LIS3MDL_POWERDOWNMODE: Serial.println("Power-down"); break;
}
lis3mdl.setDataRate(LIS3MDL_DATARATE_155_HZ);
// You can check the datarate by looking at the frequency of the DRDY pin
Serial.print("Data rate set to: ");
switch (lis3mdl.getDataRate()) {
case LIS3MDL_DATARATE_0_625_HZ: Serial.println("0.625 Hz"); break;
case LIS3MDL_DATARATE_1_25_HZ: Serial.println("1.25 Hz"); break;
case LIS3MDL_DATARATE_2_5_HZ: Serial.println("2.5 Hz"); break;
case LIS3MDL_DATARATE_5_HZ: Serial.println("5 Hz"); break;
case LIS3MDL_DATARATE_10_HZ: Serial.println("10 Hz"); break;
case LIS3MDL_DATARATE_20_HZ: Serial.println("20 Hz"); break;
case LIS3MDL_DATARATE_40_HZ: Serial.println("40 Hz"); break;
case LIS3MDL_DATARATE_80_HZ: Serial.println("80 Hz"); break;
case LIS3MDL_DATARATE_155_HZ: Serial.println("155 Hz"); break;
case LIS3MDL_DATARATE_300_HZ: Serial.println("300 Hz"); break;
case LIS3MDL_DATARATE_560_HZ: Serial.println("560 Hz"); break;
case LIS3MDL_DATARATE_1000_HZ: Serial.println("1000 Hz"); break;
}
lis3mdl.setRange(LIS3MDL_RANGE_4_GAUSS);
Serial.print("Range set to: ");
switch (lis3mdl.getRange()) {
case LIS3MDL_RANGE_4_GAUSS: Serial.println("+-4 gauss"); break;
case LIS3MDL_RANGE_8_GAUSS: Serial.println("+-8 gauss"); break;
case LIS3MDL_RANGE_12_GAUSS: Serial.println("+-12 gauss"); break;
case LIS3MDL_RANGE_16_GAUSS: Serial.println("+-16 gauss"); break;
}
lis3mdl.setIntThreshold(500);
lis3mdl.configInterrupt(false, false, true, // enable z axis
true, // polarity
false, // don't latch
true); // enabled!
Mouse.begin();
// on start, get current direction and set that as forward
sensors_event_t event;
lis3mdl.getEvent(&event);
initialCalibrationx = event.magnetic.x;
initialCalibrationy = event.magnetic.y;
initialCalibrationz = event.magnetic.z;
pinMode(A1, INPUT_PULLUP); // Enable pull-up for button press
}
void loop() {
lis3mdl.read(); // get X Y and Z data at once
sensors_event_t event;
lis3mdl.getEvent(&event);
if (on == true){
Serial.print("running");
int buttonState = digitalRead(A1); // Read the state of the external button
/////////////////////////// JUMP //////////////////////////////
int A1sensorValue = analogRead(A1); // Read the analog value from A1
float voltage = A1sensorValue * (3.3 / 1023.0); // Convert to voltage
if (voltage<0.5){
Keyboard.press(' ');
}else{
Keyboard.release(' ');
}
///////////////////////////////////////////////////////////////
/////////////////////////// COSTUME CHANGE ///////////////////////////////
int A2sensorValue = analogRead(A2); // Read the analog value from pin A2
if (A2sensorValue==1 && switchTrigger == false){
switchTrigger = true;
Keyboard.press('c');
delay(10);
Keyboard.release('c');
delay(1000); //debounce so costume doesn't switch multiple times while hat is going back to position
switchTrigger = false;
}
////////////////////////////////////////////////////////////////////////////
/* Display the results (magnetic field is measured in uTesla) */
//Serial.print("\tX: "); Serial.print(event.magnetic.x-initialCalibrationx);
//Serial.print(" \tY: "); Serial.print(event.magnetic.y-initialCalibrationy);
//Serial.print(" \tZ: "); Serial.print(event.magnetic.z-initialCalibrationz);
///////////////////////// CALCULATE INPUT DIRECTION //////////////////////////
//using the magnometer's orientation in 3d space and atan2, get the LOCAL yaw direction of the hat (allows the head turn input to be unaffected by any imbalance)
localizeddirection = atan2(event.magnetic.x-initialCalibrationx,event.magnetic.z-initialCalibrationz);//vectorx,vectorz);
Mouse.move(localizeddirection*10, 0, 0);
//////////////////////////////////////////////////////////////////////////////
/////////////////////////////// START / STOP WALKING ///////////////////////////////
// if player nods forward, toggle holding w. if they nod backwards, let go of w
if (event.magnetic.z-initialCalibrationz>=10) {
Keyboard.press('w');
}
if (event.magnetic.z-initialCalibrationz<=10) {
Keyboard.release('w');
}
//////////////////////////////////////////////////////////////////////////////////////
} else
{Serial.println("currently turned off, send 'x' inyo the serial monitor to turn on");delay(2000); }
///////////////////////////// EXTERNAL ON OFF SWITCH FOR CIRCUIT PLAYGROUND ////////////////////////////
// allows the arduino loop to be toggled off/on without having to touch the circuit playground by sending "x" into the serial monitor
// also recalibrates the origin of the magnometer to the current facing direction when turned back on
if (Serial.available() > 0) {
float key = Serial.read();
if (key==120){ // Check for key "x" being sent into the serial monitor
on = !on; //flipflop bool
if (on == true){
Serial.print("starting arduino back up an recalibrating the magnometer");
initialCalibrationx = event.magnetic.x;
initialCalibrationy = event.magnetic.y;
initialCalibrationz = event.magnetic.z;
}
}else{}
delay(2);//quickly but not instantly iterate through serial monitor input
Serial.println();
}else{ // if nothing is sending into the monitor then do usual loop delay
delay(200);
Serial.println();
}
//////////////////////////////////////////////////////////////////////////////////////////////////////
}
Schematic:
No comments:
Post a Comment
Note: Only a member of this blog may post a comment.
No comments:
Post a Comment
Note: Only a member of this blog may post a comment.