Description
Using a photocell and an FSR as analog input, it's the desktop roadtrip!
The photocell provides input on the surrounding light, and my Processing program depicts a day/night scene appropriate to the amount of light provided. In a high-light setting, the scene appears with clear blue skies and a bright yellow sun. In medium light, the sun is lower in the horizon and the sky turns to an orange/red color. As the sun goes down, the sky becomes more purple and the moon begins to rise. In a dark room, the moon (with craters!) rises and stars come out in the sky.
The FSR controls the spedometer in the lower right corner. I set it up to work with a "gas pedal" to apply pressure to the FSR. I initially planned to create an animation so that the yellow lane line would move slowly or quickly according to the acceleration established by the FSR, but it proved more difficult than expected.
Compenents
- breadboard
- 2 10k Ohm resistors, 1 220 Ohm resistor
- wires (black/red for ground, green for input/output, orange for 5V)
- photocell
- FSR (with wires)
- Arduino board
- cardboard, fedex envelope, and old geometry book for gas pedal
- lots of lights to test the photocell
- green LED for testing
- as always, rubber bands for a secure Arduino
Arduino Code
/*
* Resistive Sensor Input
* Takes the input from a resistive sensor, e.g., FSR or photocell
* Dims the LED accordingly, and sends the value (0-255) to the serial port
*/
// Input Pins
int photocellPin = 2;
int fsrPin = 0;
int ledPin = 11; // select the output pin for the LED, if used
int photocellVal;
int fsrVal;
void setup() {
Serial.begin(9600);
}
void loop() {
photocellVal = analogRead(photocellPin);
fsrVal = analogRead(fsrPin);
analogWrite(ledPin, fsrVal/4); // analogWrite (dimming the LED) can be between 0-255
Serial.print(photocellVal);
Serial.print(",");
Serial.print(fsrVal);
delay(100); // rest a little...
}
Processing Code
/*
* Desktop Roadtripping
* ----------------------
* Isaac Salier-Hellendag
* dish@ischool.berkeley.edu
* TUI - Assignment 4
*
* Using a photocell and an FSR, a user can "drive" down the highway.
*
* The photocell responds to nearby light -- a darker room is reflected
* by showing nighttime (featuring stars and moon with craters!) and a lighter
* room is reflected by either clear blue skies or a beautiful red sunset.
*
* Pressing down on the FSR causes the spedometer to jump. Thankfully, there
* don't seem to be any cops on this highway.
*
*/
import processing.serial.*;
// Change this to the portname your Arduino board
String portname = "/dev/tty.usbserial-A4001lSn"; // or "COM5"
Serial port;
String buf="";
int cr = 13; // ASCII return == 13
int lf = 10; // ASCII linefeed == 10
int radius = 25;
int curTime = 0;
int curAcceleration = 0;
int curDirection = 0;
int windowHeight = 600;
int windowWidth = 600;
float leftMost = windowWidth * .15;
float rightMost = windowWidth * .75;
float horizonIndent = 55;
float paintWidth = 10;
float maxColor = 255;
float earthHeight = 100;
float orbApex = 100;
float orbRadius = 100;
float orbYChange = (((windowHeight - earthHeight) + orbRadius) - orbApex);
float bestPhotoInput = 800;
float orbSlope = orbYChange / (bestPhotoInput / 2);
float orbX = 100;
float orbY = 0;
float timeFloat;
float accelFloat;
int lineLength = 4;
int meterRadius = 200;
int centerX = windowWidth - 30;
int centerY = windowHeight - 30;
float fastDegrees = 80;
float slowDegrees = 190;
float degreeVal;
void setup() {
size(windowHeight, windowWidth);
frameRate(100);
smooth();
background(40,40,40);
noStroke();
port = new Serial(this, portname, 9600);
}
void draw() {
}
void keyPressed() {
if(key == ' ') {
background(40,40,40); // erase screen
}
else {
//drawball(radius, radius, 0);
}
}
void drawBall(float x, float y, int sunMoon) {
int r = 255;
int g = 255;
int b = 255;
switch(sunMoon) {
case 0: r=255; g=255; b=0; break;
case 1: r=255; g=255; b=255; break;
}
fill(r,g,b);
ellipse(x, y, orbRadius, orbRadius);
if(sunMoon == 1) {
fill(255,255,255);
ellipse(windowWidth/4,windowHeight/6,3,3);
ellipse(windowWidth/5,windowHeight/3,3,3);
ellipse(windowWidth - 150,windowHeight/7,3,3);
ellipse(windowWidth/7,windowHeight/2,3,3);
ellipse(windowWidth/10,windowHeight/6,3,3);
ellipse(windowWidth/2,windowHeight/4,3,3);
ellipse(windowWidth-75,windowHeight/2,3,3);
fill(200,200,200);
ellipse(x-(orbRadius/5),y-(orbRadius/5),(orbRadius/5),(orbRadius/5));
ellipse(x+(orbRadius/7),y+(orbRadius/7),(orbRadius/3),(orbRadius/3));
}
}
void drawTime(int timeVal) {
if(timeVal != curTime) {
drawSky(timeVal);
drawOrb(timeVal);
drawEarth(timeVal);
//curTime = timeVal;
}
}
void drawSky(int timeVal) {
timeFloat = float(timeVal);
float r = getSkyRed(timeFloat);
float g = getSkyGreen(timeFloat);
float b = getSkyBlue(timeFloat);
background(r,g,b);
}
float getSkyRed(float timeFloat) {
if(timeFloat <= (bestPhotoInput * .25))
return 0;
if(timeFloat > (bestPhotoInput * .25) && timeFloat <= (bestPhotoInput * .5))
return (maxColor * -1) + ((maxColor / (bestPhotoInput * .25)) * timeFloat);
if(timeFloat > (bestPhotoInput * .5) && timeFloat <= (bestPhotoInput * .75))
return maxColor;
if(timeFloat > (bestPhotoInput * .75) && timeFloat < (bestPhotoInput * .875))
return ((maxColor * 4) - ((maxColor / (bestPhotoInput * .25)) * timeFloat));
else
return maxColor * .5;
}
float getSkyBlue(float timeFloat) {
if(timeFloat >= (bestPhotoInput * .75))
return (maxColor * -3) + (((maxColor / (bestPhotoInput * .25)) * timeFloat));
if(timeFloat > (bestPhotoInput * .5) && timeFloat < (bestPhotoInput * .75))
return (maxColor * -3) - ((maxColor / (bestPhotoInput * .25)) * timeFloat);
if(timeFloat > (bestPhotoInput * .25) && timeFloat <= (bestPhotoInput * .5))
return (maxColor * -1) + (((maxColor / (bestPhotoInput * .25)) * timeFloat));
else
return 0;
}
float getSkyGreen(float timeFloat) {
if(timeFloat <= (bestPhotoInput * .5))
return 0;
if(timeFloat > (bestPhotoInput * .5) && timeFloat < (bestPhotoInput * .75))
return (maxColor * -2) + ((maxColor / (bestPhotoInput * .25)) * timeFloat);
if(timeFloat >= (bestPhotoInput * .75) && timeFloat <= (bestPhotoInput * .875))
return (maxColor * 4) - ((maxColor / (bestPhotoInput * .25)) * timeFloat);
else
return maxColor * .5;
}
void drawOrb(int timeVal) {
timeFloat = float(timeVal);
if(timeVal < (bestPhotoInput/2)) {
orbY = (orbSlope * timeFloat) + orbApex;
drawBall(orbX, orbY, 1);
} else {
orbY = (orbSlope * timeVal * -1) + ((orbYChange * 2) + orbApex);
drawBall(orbX, orbY, 0);
}
}
void drawEarth(int timeVal) {
fill(40,40,40);
rect(0, (windowHeight - earthHeight), windowWidth, earthHeight);
drawHighway();
}
void drawHighway() {
fill(255,255,255);
quad(leftMost + horizonIndent, windowHeight - earthHeight,
leftMost + horizonIndent + paintWidth, windowHeight - earthHeight,
leftMost + paintWidth + 5, windowHeight,
leftMost, windowHeight);
quad(rightMost - paintWidth, windowHeight - earthHeight,
rightMost, windowHeight - earthHeight,
rightMost + horizonIndent + paintWidth + 5, windowHeight,
rightMost + horizonIndent, windowHeight);
drawDashedLine();
}
void drawDashedLine() {
fill(255,220,0);
quad((windowWidth/2) - (paintWidth/2) + 1, windowHeight - earthHeight,
(windowWidth/2) + (paintWidth/2) - 1, windowHeight - earthHeight,
(windowWidth/2) + (paintWidth/2), windowHeight - earthHeight + 20,
(windowWidth/2) - (paintWidth/2), windowHeight - earthHeight + 20);
quad((windowWidth/2) - (paintWidth/2) - 1, windowHeight - earthHeight + 40,
(windowWidth/2) + (paintWidth/2) + 1, windowHeight - earthHeight + 40,
(windowWidth/2) + (paintWidth/2) + 3, windowHeight - earthHeight + 60,
(windowWidth/2) - (paintWidth/2) - 3, windowHeight - earthHeight + 60);
quad((windowWidth/2) - (paintWidth/2) - 4, windowHeight - earthHeight + 82,
(windowWidth/2) + (paintWidth/2) + 4, windowHeight - earthHeight + 82,
(windowWidth/2) + (paintWidth/2) + 6, windowHeight,
(windowWidth/2) - (paintWidth/2) - 6, windowHeight);
}
void drawSpedometer(int accelerationVal) {
accelFloat = float(accelerationVal);
drawMeter();
drawSpeedLine(accelFloat);
}
void drawMeter() {
fill(255,255,255);
ellipse(centerX, centerY, meterRadius, meterRadius);
fill(0,0,0);
ellipse(centerX, centerY, meterRadius - 2, meterRadius - 2);
}
void drawSpeedLine(float accelFloat) {
degreeVal = (((fastDegrees - slowDegrees)/1024) * accelFloat) + slowDegrees;
float rad = radians(degreeVal);
float lineX = (cos(rad) * meterRadius * .45) + centerX;
float lineY = centerY - (sin(rad) * meterRadius * .45);
stroke(255,0,0);
strokeWeight(3);
line(lineX, lineY, centerX, centerY);
noStroke();
}
// called whenever serial data arrives
void serialEvent(Serial p) {
int c = port.read();
if (c != lf && c != cr) {
buf += char(c);
}
if (c == lf) {
String[] inputs = buf.split(",");
int timeVal = int(inputs[0]);
int accelerationVal = int(inputs[1]);
drawTime(timeVal);
drawSpedometer(accelerationVal);
buf = "";
}
}
Photos & Video
Photos available at:
http://www.flickr.com/photos/46877131@N00/sets/72157602189563095/
See it in action (YouTube, featuring Chuck Berry):
http://www.youtube.com/v/WQ_S_O-NX0Y
Comments
isaac, really fun demo.
isaac, really fun demo. Great job! I like how you said "more difficult than expected" though it's not the intention of the course, it's unfortunately true a lot of the times, isn't it?