Physical Computing Blog 7

The Magic Detection Device

Task: Show that you can make a simple, engaging, interactive system with physical controls with a Halloween theme.

For this assignment, I worked with Tianyi in order to create a Harry Potter themed game. We started off by brainstorming ideas, T wanted to incorporate all of her classes (fabrication, ICM, and physical computing which all had harry potter themes). We initially decided on a physical interaction that utilized a stretch sensor to measure when the user extends their elbow. The elbow movement would then control a p5.js sketch by controlling the speed of the boat. After sharing our idea in class last week, we decided to switch to a triple axis accelerometer (ADXL345).

We started by hooking up the accelerometer and testing it out. We used this tutorial in order to get the accelerometer to work and modified the code for our project. After testing out the accelerometer, we soldered it onto longer wires and sewed it onto an elbow brace. We mounted the arduino onto a piece of wood and attached a rod in order to stabilize our wires. We modified an existing sketch that T had on p5.js in order to control the movement of the boat. We used an array in order to take the average of our output numbers and stabilize the numbers that were moving the boat forward or backward.

The accelerometer produces an acceleration, which I don’t fully understand what the output of the accelerometer is, in m/s^2. I chose an arbitrary range in order to gather data (4 G), where lower ranges have higher sensitivity and higher ranges have less sensitivity. The data rate, rate that the output data is updated, was kept to the default (10 HZ). We decided to use the absolute value of the y acceleration only because the results looked the most stable and fitting to our needs. We ran into some problems with our p5.js code as well, where it worked very well in the beginning with a simple sketch, but after we added some media, the boat movement was a bit wonky. Sometimes the boat moved with a low threshold, other times the boat didn’t move, and sometimes the boat moved even without movement. We set the threshold of movement to be 5 or 10 (depending on how the accelerometer was working) because that seemed to be the lowest we could go without making the “game” too easy. If the user is moving below the threshold speed, the boat starts to move backwards and a “hurry up” message pops up. Once the user reaches the end of the screen, a "congratulations” message pops up, but only for as long as you keep swinging, if the user stops or slows down, the boat starts to move backwards again. Below you will find the code that we used and some process photos. Videos of user testing can be found here.

Process photos

Code used for p5.js

COde used for arduino

#include <Wire.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_ADXL345_U.h>

/* Assign a unique ID to this sensor at the same time */
Adafruit_ADXL345_Unified accel = Adafruit_ADXL345_Unified(12345);

void displaySensorDetails(void)
{
  sensor_t sensor;
  accel.getSensor(&sensor);
  delay(500);
}

void displayDataRate(void)
{  
  switch(accel.getDataRate())
  {
    case ADXL345_DATARATE_3200_HZ:
      break;
    case ADXL345_DATARATE_1600_HZ:
      break;
    case ADXL345_DATARATE_800_HZ:
      break;
    case ADXL345_DATARATE_400_HZ:
      break;
    case ADXL345_DATARATE_200_HZ:
      break;
    case ADXL345_DATARATE_100_HZ:
      break;
    case ADXL345_DATARATE_50_HZ:
      break;
    case ADXL345_DATARATE_25_HZ:
      break;
    case ADXL345_DATARATE_12_5_HZ:
      break;
    case ADXL345_DATARATE_6_25HZ:
      break;
    case ADXL345_DATARATE_3_13_HZ:
      break;
    case ADXL345_DATARATE_1_56_HZ:
      break;
    case ADXL345_DATARATE_0_78_HZ:
      break;
    case ADXL345_DATARATE_0_39_HZ:
      break;
    case ADXL345_DATARATE_0_20_HZ:
      break;
    case ADXL345_DATARATE_0_10_HZ:
      break;
    default:
      break;
  }  
}

void displayRange(void)
{  
  switch(accel.getRange())
  {
    case ADXL345_RANGE_16_G:
      break;
    case ADXL345_RANGE_8_G:
      break;
    case ADXL345_RANGE_4_G:
      break;
    case ADXL345_RANGE_2_G:
      break;
    default:
      break;
  }  
}

void setup(void) 
{
#ifndef ESP8266
  while (!Serial); // for Leonardo/Micro/Zero
#endif
  Serial.begin(9600);  
  /* Initialise the sensor */
  if(!accel.begin())
  {
    /* There was a problem detecting the ADXL345 ... check your connections */
    Serial.println("Ooops, no ADXL345 detected ... Check your wiring!");
    while(1);
  }

  /* Set the range to whatever is appropriate for your project */
//  accel.setRange(ADXL345_RANGE_16_G);
//   accel.setRange(ADXL345_RANGE_8_G);
   accel.setRange(ADXL345_RANGE_4_G);
  // accel.setRange(ADXL345_RANGE_2_G);
  
  /* Display some basic information on this sensor */
  displaySensorDetails();
  
  /* Display additional settings (outside the scope of sensor_t) */
  displayDataRate();
  displayRange();
//  Serial.println("");
}

void loop(void) 
{
  /* Get a new sensor event */ 
  sensors_event_t event; 
  accel.getEvent(&event);
 
  /* Display the results (acceleration is measured in m/s^2) */
  Serial.println(int(abs(event.acceleration.y)));//+event.acceleration.y+event.acceleration.z));
//  Serial.print("X: "); Serial.print(event.acceleration.x); Serial.print("  ");
//  Serial.print("Y: "); Serial.print(event.acceleration.y); Serial.print("  ");
//  Serial.print("Z: "); Serial.print(event.acceleration.z); Serial.print("  ");Serial.println("m/s^2 ");
  delay(100);
}
//var angle = 0.0;
//var offset = 200;
//var scalar = 30; //数量
//var speed = 0.05;
var angle = 0.0;
var offset = 220;
var scalar = 20; //数量
var speed = 0.06;
var speedWave1 = 0.01;
var speedWave2 = 0.03;

var boatrace = 1;
var y1;
var x = 0;
var cx;

var serial; // variable to hold an instance of the serial port library
var fromSerial = 0; //variable to hold the data

var welcomeText;

let snowFlakes = []; // array to hold snowflake objects
let snowSpeed = 100000; // Larger value means slower snowfall
var snowOn = false;

//array code
let array = [],
    count = 0,
    total = 0,
    avg = 0;

function preload() {
    imgBoat = loadImage("IMG_7256.png");
    imgNami = loadImage("nami_back.png");
    imgNami2 = loadImage("nami_front.png");
    imgSky = loadImage("sky.png");
    imgHogwarts = loadImage("Hogwarts.png");
}

function setup() {
    winText = createP();

    /*Set up canvas information */
    var canvas = createCanvas(windowWidth, windowHeight);
    //canvas.position(0, 0);
    canvas.style('z-index', '-1'); //set the canvas as the background 

    //link Ardurino
    serial = new p5.SerialPort(); // make a new instance of  serialport librar  
    serial.on('list', printList); // callback function for serialport list event
    serial.on('data', serialEvent); // callback for new data coming in  
    serial.list(); // list the serial ports
    serial.open("/dev/cu.usbmodem1441"); // open a port

    /*Set up boat height as responsive to canvas*/
    offset = height * 3 / 5;
}

//press ESC on keyboard to delete current text and clear the snowflakes.
// function keyPressed() {
//   if (keyCode === ESCAPE) {
//     welcomeText.html('');
//     snowOn = false;
//     snowFlakes = [];
//   }
// }

function draw() {
    cx = constrain(x, 0, width * 4 / 5);
    /* pint serial input at the console*/
    //print(fromSerial);
    /*setup backgound img*/
    image(imgSky, 0, 0, width, height);
    image(imgHogwarts, width * 3 / 5, height * 1 / 3, width * 0.3, height * 0.6);
    /*var y can not be put in setup, else it will not show*/
    var y1 = offset + sin(angle) * scalar;
    var y2 = offset - sin(angle) * 10;
    var y3 = offset + sin(angle) * 5;
    angle += speed;
    /* placeholder x, if serial input not avaliable*/
    //x += boatrace;

    //array code
    total = 0;
    count = ((count++) + 5) % 5;
    if (count < 5) {
        array[count] = fromSerial;
        for (let i = 0; i < array.length; i++) {
            total += array[i];
        }
        avg = total / array.length;
        console.log(avg);
    }
    /* make "x" as associate with serial input*/
    if (avg > 5) {
        x+=5;
    } else if (avg < 5) {
        x-=5;
        if (x <= width * 4 / 5) {
            hurryUp();
        }
    }
    if (x >= width * 4 / 5) {
        winSlogan();
    }
    /*draw waves on canvas*/
    image(imgNami, 0, y3 + 50, width, height * 2 / 5);
    /*draw the boat on canvas*/
    image(imgBoat, cx, y1 + 60, width * 1 / 10, height * 1 / 8);
    /*draw waves on canvas*/
    image(imgNami2, 0, y2 + 40, width, height * 2 / 5);
    //drawing the flakes.
    if (snowOn == true) {
        fill(240);
        let t = frameCount / snowSpeed;
        for (let i = 0; i <= random(3); i++) {
            snowFlakes.push(new snowFlake()); //append a new snowflake to the snowFlake container
        }
        for (let thisFlake of snowFlakes) {
            thisFlake.reFresh(t);
            thisFlake.draw();
        }
    }
}


function snowFlake() {
    this.x = random(width / 2, width);
    this.y = 0;
    this.size = random(2, 5);
    this.originalAngle = random(0, 2 * PI);
    this.transparency = 255;
    //define the angle speed
    this.wSpeed = random(0, 2 * PI);
    //define the moving radius
    this.radius = random(width / 200);
    this.reFresh = function(currentTime) {
        this.x = this.x + this.radius * sin(currentTime * this.wSpeed + this.originalAngle);
        this.y = this.y + sqrt(this.size);
        //if the snowflake moves out of the frame, remove it from the array.
        if (this.y > height) {
            let thisIndex = snowFlakes.indexOf(this);
            snowFlakes.splice(thisIndex, 1);
        }
    };
    //drawing the flake
    this.draw = function() {
        noStroke();
        ellipse(this.x, this.y, this.size, this.size);
    }
}

function winSlogan() {
    fill(255);
    textSize(36);
    textFont('Righteous');
    text("Now You've Proved Your Magic Power, Welcome to Hogwarts!", width * 1 / 8, height * 1 / 5, width * 3 / 8, height * 3 / 5);
    snowOn = true;
}

function hurryUp() {
    fill(255);
    textSize(36);
    textFont('Righteous');
    text("You have to swing faster to get into Hogwarts", width * 1 / 8, height * 1 / 5, width * 3 / 8, height * 3 / 5);
    snowOn = false;
}

function printList(portList) {
    for (var i = 0; i < portList.length; i++) {
        // Display the list the console:
        print(i + " " + portList[i]);
    }
}

function serialEvent() {
    // this is called when data is recieved, data will then live in fromSerial  
    var stringFromSerial = serial.readLine();
    if (stringFromSerial.length > 0) {
        var trimmedString = trim(stringFromSerial);
        fromSerial = Number(trimmedString);
    }
}
GIF of T testing out our contraption.

GIF of T testing out our contraption.