Interaction Lab: Final Project

Partner: Zeyao Li

Brainstorm:

At first, I and Zeyao wanted to work on bettering our midterm robot for our final project. However, we thought that we could maybe build another “robot” but a more interactive one with its own characteristics. So we decided to make a snake robot based on the snake game. We also decided to use the keyboards to control the direction of the snake. Moreover, we wanted to use MakeyMakey to set a route for the snake to go while recording some audios for different images along the route so it would make different sounds when passing different images.

Process:

We first thought the snake was really easy to make because we were not planning on using any servos or wheels like midterm, so that we really focused on writing our codes and the circuits for the first two nights.

IMG_0763-1024x1024

This is our prototype, and this is what we really first planned on making. IMG_9116

 

 

Screen Shot 2016-05-20 at 2.22.05 AM

After we finished coding, we tired to put the motors around the snake head by using a triangle cardboard shown below.

IMG_9117

Unfortunately, although the motors were working, they were impossible to move the snake body fully due to its weight. Then, we tired using this little wheel to see if it would help moving the snake; however, it still would not move a bit. Then, we tried to reduce the weight of the snake, so we reset everything in a smaller breadboard. However, it still would not move.

IMG_9118    IMG_0785-1024x768

Finally, we decided to add more motors and made it into a car. During this process, we met a lot of difficulties. First, we did not know that different motors have different speed so that it was impossible for us to set directions for the car. Second, it actually could not turn either left or right because each of its tires has its own motor and too limited space between the body of the car and the tires so that it was not possible for it to turn.  So, in order for it to move, we had to add two more wheels and also two more motors. We also bought batteries to support the back wheels because there were no more spaces for us to set up two more motors on breadboard. And this is how it looked before its finished.IMG_0805-768x1024

After making it move, we met another problem. The problem was that although it could move, it always turned right due to the faster speed that the right motor had. Because of this problem, we were impossible to make a route for this car because we could no longer control its left and right direction. We could only tell it when to go and when to stop. Due to limited time we had, we chose to change our original plan totally into a different plan. IMG_0820-1024x768

SO THIS IS HOW IT FINALLY LOOKED LIKE. We eventually decided to put ultrasonic sensor, buzzer, and LED lights on it. As you can see, the lights are red and green. So whenever the distance is more than 30 centimeters, the green light will constantly be on, meaning there is no obstacles and it is safe in the front. However, if the distance is less than 30 centimeters, the two red lights will turn on as well as the sound of the buzzer, warning users to stop the car due to obstacles in the front.

Codes:

Arduino Code: 

#define led 2//green
#define led2 3
#define led3 4//red
#define led4 5

int transistorPin = 9; //right; PWM Pin 9 connected to the base of the transistor
int potValue = 0; // value returned from the potentiometer
int secondtransistorPin = 10;//left
int secondpotValue = 0;
int toneVal;
int speakerOut = 11;
const int pingPin = 7;
char ch;

void setup() {
Serial.begin(9600);
// set the transistor pin as output:
pinMode(transistorPin, OUTPUT);
pinMode(secondtransistorPin, OUTPUT);
pinMode(speakerOut, OUTPUT);
pinMode(led, OUTPUT);
pinMode(led2, OUTPUT);
pinMode(led3, OUTPUT);
pinMode(led4, OUTPUT);
}

void loop() {
long duration, cm;

pinMode(pingPin, OUTPUT);
digitalWrite(pingPin, LOW);
delayMicroseconds(2);
digitalWrite(pingPin, HIGH);
delayMicroseconds(5);
digitalWrite(pingPin, LOW);

pinMode(pingPin, INPUT);
duration = pulseIn(pingPin, HIGH);

cm = microsecondsToCentimeters(duration);

Serial.print(cm);
Serial.print(“cm”);
Serial.println();
delay(5);
if (Serial.available()) {
ch = Serial.read();

}

if (cm > 30 ) {//up
digitalWrite(led, HIGH);
digitalWrite(led2, HIGH);
digitalWrite(led3, LOW);
digitalWrite(led4, LOW);
noTone(speakerOut);
}
if (ch == ‘a’){
digitalWrite(transistorPin, 180);
digitalWrite(secondtransistorPin, 180);

}
if (cm <= 30) { //stop
digitalWrite(led3, HIGH);
digitalWrite(led4, HIGH);
digitalWrite(led, LOW);
digitalWrite(led2, LOW);
tone(speakerOut, 3000, 500);

}
if (ch == ‘b’){
digitalWrite(transistorPin, 0);
digitalWrite(secondtransistorPin, 0);

}

}

long microsecondsToCentimeters(long microseconds) {
return microseconds / 29 / 2;
}

Processing code:

import processing.serial.*;
Serial myPort;

int angle=0;
int snakesize=5;
int time=0;
int[] headx= new int[2500];
int[] heady= new int[2500];
int applex=(round(random(47))+1)*8;
int appley=(round(random(47))+1)*8;
boolean redo=true;
boolean stopgame=false;
void setup()
{
restart();
size(800, 600);
textAlign(CENTER);
String portName = Serial.list()[2];
println(Serial.list());
myPort = new Serial(this, portName, 9600);
}

void draw()
{
if (stopgame)
{
//do nothing because of game over (stop playing)
} else
{
//draw stationary stuff
time+=1;
fill(255, 0, 0);
stroke(0);
//rect(applex,appley,8,8);
fill(0);
stroke(0);
rect(0, 0, width, 8);
rect(0, height-8, width, 8);
rect(0, 0, 8, height);
rect(width-8, 0, 8, height);
//my modulating time by 5, we create artificial frames each 5 frames
//(otherwise the game would go WAY too fast!)
if ((time % 5)==0)
{
travel();
display();
checkdead();
}
}
}
//controls:
void keyPressed()
{
if (key == CODED)
{
//what key was pressed? is the previous angle the opposite direction?
//we wouldn’t want to go backwards into our body, that would hurt!
//also, is the previous body segment in front of the direction?
//(based on the previous question, but added for secure turning without suicide)
if (keyCode == UP && angle!=270 && (heady[1]-8)!=heady[2])
{
myPort.clear();
myPort.write(‘a’);
angle=90;
}
if (keyCode == DOWN && angle!=90 && (heady[1]+8)!=heady[2])
{
angle=270;
myPort.clear();
myPort.write(1);
myPort.write(‘b’);
}
if (keyCode == LEFT && angle!=0 && (headx[1]-8)!=headx[2])
{
myPort.clear();
myPort.write(‘c’);
angle=180;
}
if (keyCode == RIGHT && angle!=180 && (headx[1]+8)!=headx[2])
{
myPort.clear();
myPort.write(‘d’);
angle=0;
}
//
if (keyCode == SHIFT)
{
//restart the game by pressing enter
restart();
}
}
}
void travel()
{
for (int i=snakesize; i>0; i–)
{
if (i!=1)
{
//shift all the coordinates back one array
headx[i]=headx[i-1];
heady[i]=heady[i-1];
} else
{
//move the new spot for the head of the snake, which is
//always at headx[1] and heady[1].
switch(angle)
{
case 0:
headx[1]+=8;
break;
case 90:
heady[1]-=8;
break;
case 180:
headx[1]-=8;
break;
case 270:
heady[1]+=8;
break;
}
}
}
}
void display()
{
//is the head of the snake eating the apple?
if (headx[1]==applex && heady[1]==appley)
{
//grow and spawn the apple somewhere away from the snake
//(currently some of the code below might not be working, but the game still works.)
snakesize+=round(random(3)+1);
redo=true;
while (redo)
{
applex=(round(random(47))+1)*8;
appley=(round(random(47))+1)*8;
for (int i=1; i<snakesize; i++)
{

if (applex==headx[i] && appley==heady[i])
{
redo=true;
} else
{
redo=false;
i=1000;
}
}
}
}
//draw the new head of the snake…
stroke(sinecolor(1), sinecolor(0), sinecolor(.5));
fill(0);
rect(headx[1], heady[1], 8, 8);
//…then erase the back end of the snake.
fill(255);
rect(headx[snakesize], heady[snakesize], 8, 8);
}
void checkdead()
{
for (int i=2; i<=snakesize; i++)
{
/*
//is the head of the snake occupying the same spot as any of the snake chunks?
if (headx[1]==headx[i] && heady[1]==heady[i])
{
fill(255);
rect(125,125,160,100);
fill(0);
text(“GAME OVER”,200,150);
text(“Score: “+str(snakesize-1)+” units long”,200,175);
text(“To restart, press Shift.”,200,200);
//stopgame=true;
}
//is the head of the snake hitting the walls?
*/
if (headx[1]>=(width-8))
{
headx[1] = 8;
}
if (headx[1]<=0)
{
headx[1] = width -8;
}
if (heady[1]>=(height-8))
{
heady[1] = 8;
}
if (heady[1]<=0)
{
heady[1] = height – 8;
}
}
}
void restart()
{
//by pressing shift, all of the main variables reset to their defaults.
background(255);
headx[1]=200;
heady[1]=200;
for (int i=2; i<1000; i++)
{
headx[i]=0;
heady[i]=0;
}
stopgame=false;
applex=(round(random(47))+1)*8;
appley=(round(random(47))+1)*8;
snakesize=5;
time=0;
angle=0;
redo=true;
}
float sinecolor(float percent)
{
float slime=(sin(radians((((time +(255*percent)) % 255)/255)*360)))*255;
return slime;
}

Post-mortem:

Although we did not accomplish what we wanted to accomplish in the beginning, I still feel very satisfied with our final project. I also learned a lesson from making our final project, which is to accept all kinds of consequences and outcomes that might result in the end because things do not always come out the way we want them to be. I was really happy that we still got to improve our final project at the last moment before the IMA show so that we could present our projects to the public and explain what they do. These two projects are both very meaningful to me because I certainly tried my best during each of the making process and I am very satisfied on how they all came out eventually. Great experience and great class!IMG_9113

Leave a Reply