Posted by raghavchandra
Description:
In this lab, we further explore sensors as input devices. This time we use photocells and force sensitive resistors as inputs to a Processing UI application. This introduces us to Processing as a programming language as well.
Components Used:
-
Arduino Micro-controller
-
1 Resistor (1K Ohms)
-
Wires/Bread Board
-
Sensors (FSR and Photocell)
-
Ambient light
Material for FSR:
For increasing the FSR's area of input for use under doormats, hard covered books(encyclopedias) or wood planks performed well. They weight applied on them is uniformly distributed, hence the sensor gets a good estimate of the force being applied. For concentrated forces(hitting at a perticular point), a ball(soccer ball, tennis ball) was quite effective. It spreads the force applied on it evenly and so the sensor can measure the force.
Programming- 'Magical Gravity':
The first thought that came into my mind while using photocells in class was about Motion Sensing. Hence, in my example, I use my hands to control a falling orb.
The orb simulates a real falling ball under gravity and with the damping caused by the ground. The collision with the ground changes the direction in which the orb bounces, depending on how it collides. But using my hands (and of course, a photocell), I control the gravity, hence controlling the movement of the ball. This could be further extended to controlling the horizontal force on the ball as well, simply by adding another photocell. This way, the user could feel like a Jedi, using telekinesis to move the orb.
Click here to see the video.
Ardruino Code:
/*
* Takes input from a sensor(Photocell) and prints it to the Serial as input for Processing code.
*/
int potPin = 0; // select the input pin for the potentiometer
void setup() {
Serial.begin(9600);
}
void loop() {
Serial.println(analogRead(potPin)); // read the value from the sensor, between 0 - 1024
}
Processing Code:
/**
** Magical Gravity **
* In this example, we have an Orb which has non-orthogonal
* collisions with multiple ground segments. This simulation
* is realistic as the orbs trajectory is not only effected by
* the COLLISIONS, but also by the GRAVITY and the grounds DAMPNIG.
* In this example, a user could control the GRAVITY simply by the
* motion of his hand!
*
* (Taken from the 'Reflection2' Example by Ira Greenberg)
* Code for Ground and Orb can be found in the Example folder in Reflection2 example.
*/
/* Imports and Initiallization for Serial Input */
import processing.serial.*;
String portname = "/dev/tty.usbmodem1d11";
Serial port;
String buf="";
int cr = 13; // ASCII return == 13
int lf = 10; // ASCII linefeed == 10
/** serialEvent()
* Called whenever serial data arrives
*/
void serialEvent(Serial p) {
int c = port.read();
if (c != lf && c != cr) {
buf += char(c);
}
if (c == lf) {
int val = int(buf);
gravity = init_gravity - val/200.0*init_gravity;
println("val="+val+", gravity="+gravity);
buf = "";
}
}
Orb orb;
PVector velocity;
float init_gravity = .1, init_damping = 0.8;
float gravity = init_gravity, damping = init_damping;
int segments = 15;
Ground[] ground = new Ground[segments];
float[] peakHeights = new float[segments+1];
void setup(){
//Initiallizing Port to the serial input port
port = new Serial(this, portname, 9600);
size(700, 700);
smooth();
orb = new Orb(50, 50, 5);
velocity = new PVector(.5, 0);
// Calculate ground peak heights
for (int i=0; i<peakHeights.length; i++){
peakHeights[i] = random(height-40, height-30);
}
/* Float value required for segment width (segs)
calculations so the ground spans the entire
display window, regardless of segment number. */
float segs = segments;
for (int i=0; i<segments; i++){
ground[i] = new Ground(width/segs*i, peakHeights[i],
width/segs*(i+1), peakHeights[i+1]);
}
}
void draw(){
// Background
noStroke();
fill(155,0,0);
rect(0, 0, width, height);
// Move orb
orb.x += velocity.x;
velocity.y += gravity;
orb.y += velocity.y;
// Draw ground
fill(50,50,50);
beginShape();
for (int i=0; i<segments; i++){
vertex(ground[i].x1, ground[i].y1);
vertex(ground[i].x2, ground[i].y2);
}
vertex(ground[segments-1].x2, height);
vertex(ground[0].x1, height);
endShape(CLOSE);
// Draw orb
noStroke();
fill(125,225,255);
ellipse(orb.x, orb.y, orb.r*2, orb.r*2);
// Collision detection
checkWallCollision();
for (int i=0; i<segments; i++){
checkGroundCollision(ground[i]);
}
}
void checkWallCollision(){
if (orb.x > width-orb.r){
orb.x = width-orb.r;
velocity.x *= -1;
velocity.x *= damping;
}
else if (orb.x < orb.r){
orb.x = orb.r;
velocity.x *= -1;
velocity.x *= damping;
}
}
void checkGroundCollision(Ground groundSegment) {
// Get difference between orb and ground
float deltaX = orb.x - groundSegment.x;
float deltaY = orb.y - groundSegment.y;
// Precalculate trig values
float cosine = cos(groundSegment.rot);
float sine = sin(groundSegment.rot);
/* Rotate ground and velocity to allow
orthogonal collision calculations */
float groundXTemp = cosine * deltaX + sine * deltaY;
float groundYTemp = cosine * deltaY - sine * deltaX;
float velocityXTemp = cosine * velocity.x + sine * velocity.y;
float velocityYTemp = cosine * velocity.y - sine * velocity.x;
/* Ground collision - check for surface
collision and also that orb is within
left/rights bounds of ground segment */
if (groundYTemp > -orb.r &&
orb.x > groundSegment.x1 &&
orb.x < groundSegment.x2 ){
// keep orb from going into ground
groundYTemp = -orb.r;
// bounce and slow down orb
velocityYTemp *= -1.0;
velocityYTemp *= damping;
}
// Reset ground, velocity and orb
deltaX = cosine * groundXTemp - sine * groundYTemp;
deltaY = cosine * groundYTemp + sine * groundXTemp;
velocity.x = cosine * velocityXTemp - sine * velocityYTemp;
velocity.y = cosine * velocityYTemp + sine * velocityXTemp;
orb.x = groundSegment.x + deltaX;
orb.y = groundSegment.y + deltaY;
}