Ping Sensor Carrot Karate!
Some time ago, I purchased a Parallax Ping sensor during a RadioShack sale just for the heck of it, without any idea of what to do with it. Well, one day, this assignment came along and I decided to make use of it. I plugged it in an Arduino Mega, and it was surprisingly easy to get working – the interesting bit was that it uses the signal pin as both input and output. It first sends a ping out as digital output, and then the same pin switches into input to receive the pulse that bounces back. Ok, that’s great for technology, but now what to do with it…
Hmm… aha! The idea hit me when I was munching on some carrots. Why not use build a carrot chopping simulator using the ping sensor to detect the position of the chop? And that is exactly what I did. While the project did feel somewhat rushed (I was initially intending on adding 4 different types of carrots but time got the better of me), the system works fairly well along with a very simple circuit that could be dismantled and rebuilt very fast. I think the sound effects are a nice addition, and I will be looking forward to seeing what else I could achieve with this ultrasonic rangefinder.
Here is a concept sketch:
Here is a Fritzing Diagram of the Ping Sensor (I could not find the exact model):
Some pics of the hardware setup:
Some Processing Screens:
The Arduino Code:
/* Simple ping sensor carrot chopping game
/* --- PRE SETUP --- */
int pingPin = 31;
/* ----- SETUP ----- */
void setup() {
Serial.begin(57600);
}
/* --- MAIN LOOP --- */
void loop() {
//send a ping out
pingOut();
//get the ping back as duration in microseconds
unsigned int duration = pingIn();
//convert the duration to a distance
unsigned int cm = microsecondsToCm(duration);
unsigned int in = microsecondsToIn(duration);
//print the data
//Serial.print(in);
//Serial.print("in, ");
//send the data to processing
Serial.write(in);
//delay a bit
delay(100);
}
/* --- Ping Functions --- */
//sends a ping out
void pingOut() {
pinMode(pingPin, OUTPUT);
digitalWrite(pingPin, LOW);
delayMicroseconds(2);
digitalWrite(pingPin, HIGH);
delayMicroseconds(5);
digitalWrite(pingPin, LOW);
}
//getting the time it takes for the ping to return (in microseconds)
unsigned int pingIn() {
pinMode(pingPin, INPUT);
return pulseIn(pingPin, HIGH);
}
//get cm from microseconds
int microsecondsToCm(int microseconds)
{ return microseconds / 29 / 2; }
//get in from microseconds
int microsecondsToIn(int microseconds)
{ return microseconds / 74 / 2; }
The Processing Code:
/* Simple ping sensor carrot chopping game
* by John Choi. */
/* --- PRE SETUP --- */
//screen width and height
int w = 800;
int h = 600;
//pre setup serial:
import processing.serial.*;
Serial serial;
boolean useSerial = true;
//pre setup minim:
import ddf.minim.*;
Minim minim;
AudioPlayer gong;
AudioPlayer chop;
//pre setup game:
PImage wood;
PImage title;
PImage[] carrot = new PImage[10];
int[] chops = {0,0,0,0,0,0,0,0,0,0};
int carrotSize = w/12;
int carrotPos = w+100;
//game vars
boolean started = false;
boolean chopStarted = false;
int chopEndTime = 5000;
int chopStartTime = 0;
int chopTime = 0;
float fade = 255;
int oldIn = 42;
int in = 42;
int time = 0;
int score = 0;
/* ----- SETUP ----- */
void setup() {
//prepare the screen
size(w,h);
//setup serial
if(useSerial == true) {
println(Serial.list());
serial = new Serial(this, Serial.list()[5], 57600);
}
//setup minim
minim = new Minim(this);
gong = minim.loadFile("gong.wav");
chop = minim.loadFile("chop.wav");
//load all the images
wood = loadImage("wood.png");
//wood.resize(w,h);
title = loadImage("title.png");
title.resize(0,h);
for(int i = 0; i < 10; i++) { carrot[i] = loadImage("c1_"+i+".png"); carrot[i].resize(carrotSize,0); } } /* --- MAIN LOOP --- */ void draw() { //update the time. time = millis(); //refresh the screen: background(0); /* --- GET INPUT: --- */ //get serial input: if(useSerial == true && serial.available() > 0) {
in = serial.read()/2-2;
}
//key press override:
if(keyPressed == true) {
switch(key) {
case '0': in = 0; break;
case '1': in = 1; break;
case '2': in = 2; break;
case '3': in = 3; break;
case '4': in = 4; break;
case '5': in = 5; break;
case '6': in = 6; break;
case '7': in = 7; break;
case '8': in = 8; break;
case '9': in = 9; break;
}
}
/* --- GAME DISPLAY --- */
//if the game started:
if(started == true) {
//draw the wood:
tint(255,255);
image(wood,0,0);
//move the carrot into position:
if(carrotPos > 0) {
carrotPos -= 4;
} else if (carrotPos == 0) {
if(chopStarted == false) {
chopStarted = true;
chopStartTime = time;
}
}
//update the chop time:
chopTime = time - chopStartTime;
//move the carrot out when time runs out:
if(chopTime > chopEndTime && chopStarted == true) {
if(carrotPos > -w) {
carrotPos -= 4;
} else {
chopStarted = false;
carrotPos = w+100;
for(int i = 0; i < 10; i++) { chops[i] = 0; }
}
}
//chop the carrot based on input:
if(in != oldIn && chopStarted == true) {
if(0 <= in && in < 10) {
//if the spot was not already chopped:
if(chops[in] == 0) {
chops[in] = parseInt(random(15))+5;
chop.rewind();
chop.play();
score += 1;
}
}
}
//draw the carrot:
for(int i = 0; i < 10; i++) {
//get the offset up to the top spot:
int offset = carrotPos;
for(int c = 0; c < i; c++) { offset+=chops[c]; } //draw the carrot piece with the offset. image(carrot[i],carrotSize*(i)+offset,h/2-carrotSize/2); } //draw the score: fill(255,255); textSize(32); text("Chops: "+score,60,60); } /* --- TITLE SCREEN --- */ //if we haven't started yet: if (started == false) { //if input found, then start: if(in != oldIn && time > 5000) {
started = true;
gong.play();
}
}
//we have started, so fade the title out.
else {
if(fade > 0) { fade -= 1.2; }
else { fade = 0; }
}
drawTitle();
//update oldIn:
oldIn = in;
//draw info about oldIn and in
fill(255,255);
textSize(32);
text(in,700,60);
}
//this function draws the title screen and slowly fades it out:
void drawTitle() {
//draw the black title background:
fill(0,fade);
rect(0,0,w,h);
//draw the title:
tint(255,constrain(fade*2.5,0,255));
image(title,0,0); //title text
//draw the chop to begin flickering text:
fill(255, (fade/255.0) * (sin(time*0.002)*255 +128));
textSize(24);
text("Chop to begin",w/2-90,h*9/10);
}
And if you read all that, go ahead and take a look at this stupid picture of me holding a carrot and pretending to be a chef (by Golan’s request): Enjoy!