The Blowing Game (Interface)

The project was going to be an Awesome Final Game, but I’ve got an Awesome Final Toy!

DESCRIPTION
The is an interface for a blowing game (game is imaginary as only the interface has been created thus far). The interface consists of a mask that a player puts on his head and a fan. The mask measures the strength of player’s blowing and tilt of his her head (see photos below). This information is translated into the speed of rotation of the fan and direction in which the fan is pointing (respectively). Current interface comes with a sample processing program, in which the user sets circular objects on screen in motion by controlling the fans speed and orientation. The interface is quite responsive to the tilt and blow strength.

PHOTOS
Blowing Game Side ViewBlowing Game Front ViewBlowing Game Tilt HeadBlowing Game Top View

CIRCUIT DIAGRAM
The Blowing Game Circuit Diagram

CODE
Arduino

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
#include <Servo.h> 

//Accelerometer Tilt measure global variables
int pulseX, pulseY;
int accX, accY;
int xpin = 2;
int ypin = 3;

//Servo global variables
Servo myservo;
int pos = 0;

//Fan variables
int motorPin = 5;
int motorPower = 0;

//Blow Sensor global variables
int photoCell = 0;
int val = 0;
int pval = 0;
int THRESHOLD = 100;
int counter = 0;
int pcounter = 0;

void setup() {
  Serial.begin(9600);
  myservo.attach(9);  // attaches the servo on pin 9 to the servo object
  myservo.write(pos);
  pinMode(xpin, INPUT);
  pinMode(ypin, INPUT);
  pinMode(motorPin, OUTPUT);
}

void loop() {
  getAccXY(); // update acceleration values
  updateServo(); // update servo position based on AccX, AccY
  readBlowSensor(); // update rotation counter
  runFan(); // make the fan spin, if the blower is spinning

  //Communicate position and spin counter to processing via serial
  Serial.print(pos);
  Serial.print(" ");
  Serial.println(counter);
}

void getAccXY() {
  // read pulse from x- and y-axes
  pulseX = pulseIn(xpin,HIGH);
  pulseY = pulseIn(ypin,HIGH);

  // convert the pulse width into acceleration
  // accX and accY are in milli-g's: earth's gravity is 1000.
  accX = ((pulseX / 10) - 500) * 8;
  accY = ((pulseY / 10) - 500) * 8;

  // print the acceleration
 /* Serial.print(" X : ");
  Serial.print(accX);
  Serial.print("   Y : ");
  Serial.print(accY);
  Serial.println();*/
}

void updateServo() {
  int go = constrain(accX, -900, 700);
  go = map(go, -900, 700, 180, 0);
  if(pos-10 > go || pos+10 < go) {
    pos = go;
    /*Serial.println(pos);*/
    myservo.write(pos);
  }
}

void runFan() {
  if (pcounter != counter) {
    motorPower = 255;
  } else {
    motorPower = 0;
  }
  analogWrite(motorPin, motorPower);
  /*if(motorPower < 255) {
    motorPower += 5;
  }
  else motorPower = 200;*/
}

void readBlowSensor() {
  pval = val;
  pcounter = counter;
  val = analogRead(photoCell);
  //Serial.println(val);

  if((val - pval) >= THRESHOLD) {
    counter++;
    //Serial.println(counter);  
  }
}

Processing

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
import processing.serial.*;

Fan[] fans = new Fan[40];
Serial myPort;  // The serial port
int lf = 10;    // Linefeed in ASCII
String myString;
float posx = 0;
float cframe = 0;
float counter = 0;
float pcframe = 0;
float pcounter = 0;
float dc = 0;

void setup() {
  size(1440, 900);
  background(27);
  smooth();
  // List all the available serial ports
  println(Serial.list());
  // I know that the first port in the serial list on my mac
  // is always my  Keyspan adaptor, so I open Serial.list()[0].
  // Open whatever port is the one you're using.
  myPort = new Serial(this, Serial.list()[1], 9600);
  myPort.clear();
  // Throw out the first reading, in case we started reading 
  // in the middle of a string from the sender.
  myString = myPort.readStringUntil(lf);
  myString = null;

  int j = 1;
  for(int i=0; i<fans.length; i++) {
    fans[i] = new Fan(160*(i%8+1), 160*j - 50);
    if(i%8 == 7) {
      j++;
    }
  }
}

void draw() {
  background(27);
  while (myPort.available() > 0) {
    myString = myPort.readStringUntil(lf);
    if (myString != null) {
      myString = trim(myString);
      //println(myString);
    }
  }

  int[] nums = int(split(myString, ' '));

  if(frameCount%20 == 0) {
    pcounter = counter;
    pcframe = cframe;
    if(nums.length == 2) {
      counter = nums[1];
      cframe = frameCount;
    }
  }

  float speed;
  dc = counter - pcounter;
  speed = dc/20;
  println(pcounter + " : " + counter + " : " + speed);

  float x = map(nums[0], 0, 180, 0, width);
  float y = height/2;

  for(int i=0; i<fans.length; i++) {
    fans[i].updateAngle(x, fans[i].y, speed);
    fans[i].draw();
  }

  strokeWeight(4);
  stroke(255, 0, 0);
  point(x,y);
}

class Fan {

  float x;
  float y;
  float r = 100;
  float angle = 0;
  float speed = 0;

  Fan(float tx, float ty) {
    x = tx;
    y = ty;
  }

  boolean over(float tx, float ty) {
    float distance = dist(tx, ty, x, y);
    if(distance < r) return true;
     else return false;
  }

  void updateAngle(float tx, float ty, float tspeed) {
    if(this.over(tx, ty)) {
      if(tspeed != 0) {
        speed += (tspeed-speed);
      }
    }
  }

  void draw() {
    speed *= 0.95;
    angle += 0.5*PI*speed;
    pushMatrix();
    translate(x, y);
    rotate(angle);
    noStroke();
    fill(0);
    arc(0, 0, r, r, 0, PI);
    fill(255);
    arc(0, 0, r, r, PI, 2*PI);
    popMatrix();
  }
}