Sunday, December 27, 2009

Clint's Addendum: Midterm-Taxi Light

The midterm-taxi light project had several lessons on how things can go wrong with lighting. We spent a lot of time and money on lighting solutions that broke within the system. So I made many trips to the lighting store on 14th Street between 5th and Broadway.

From our first mishaps with the florescent light enclosure we learned that certain circuits don't respond well to rapid on and off switching. We later learned from the relay incident that when you overload a relay it may fuse open and allow current through.

I helped plan the housing structures of both prototypes, and the behavior of the system. Through most of the last iteration, I focused on getting lighting hardware in place for Finley's code.

Final-InterFACE

This project was a good group effort. Each member of the team had a useful skill set and the work was spread across the group.

Photos: http://www.flickr.com/photos/45806699@N05/
Videos: http://www.vimeo.com/8334405 , http://www.vimeo.com/8334560

I handled most of the programming, and I learned a lot about Processing for handling screens, states, and timing. We went through a lot of interface iterations as we got feedback. Here's the last version of the code, for the blue cloud interface in the flickr photos:

import processing.serial.*;

Serial port; // Serial port variable
int serialIn = 0;

int stagew = 1920;
int stageh = 1156;

int state = 1;

PFont font; // Define font variable

int homeFlag = 1;
int homeDelay = 4000;
int startTime, currentTime, diffTime;
int endFlag = 1;
int endDelay = 6000;
int fromEnd = 0;

String selectedQuestion;
String gAdvice;
String mouth;

int randQ;
int randGAdvice;
int randAdvice;
String happyAdvice;
String sadAdvice;

int[][] answerArray = new int[2][500]; // Array to store answers
int answerCount = 0;
int happyCount = 0;
int qNumber = 1;
int qBuffer = 1;
int n = 0;
int happyTotal = 0; // Total of happy answers
int noneTotal = 0; // Total of neutral answers
int sadTotal = 0; // Total of sad answers
float sadRows = 0;
float happyRows = 0;
float noneRows = 0;

int sadBuffer, noneBuffer, happyBuffer;
String selectedQuestionBuffer;

int faceZeroW, faceZeroH, sumfaceZeroW, sumfaceZeroH, sadX, sadY, sadW, sadH, noneX, noneY, noneW, noneH, happyX, happyY, happyW, happyH;
int moodhappyTotal, moodnoneTotal, moodsadTotal, socialhappyTotal, socialnoneTotal, socialsadTotal, energyhappyTotal, energynoneTotal, energysadTotal;
int moodTotal, moodPoints, moodPossible, socialTotal, socialPoints, socialPossible, energyTotal, energyPoints, energyPossible;
float moodSpread, socialSpread, energySpread;

int eyeflag = 0;
int mouthflag = 0;
int switchflag = 0;
int pressedflag = 0;
int eyeval, mouthval, switchval;
int eyevalBuffer, eyevalDiff;


// *************************** SETUP LOOP ***************************
void setup() {
size(stagew, stageh); // Stage size
background(44);
noStroke();
smooth();

// Print a list of the serial ports and select first:
println(Serial.list());
port = new Serial(this, Serial.list()[0], 9600);
}


// *************************** DRAW LOOP ***************************
void draw() {
background(#8ccaea);

// *************************** Home State
if (state == 1) {

answerCount = 0;
happyCount = 0;
fromEnd = 0;

moodTotal = 0;
moodPoints = 0;
moodhappyTotal = 0;
moodnoneTotal = 0;
moodsadTotal = 0;
socialTotal = 0;
socialPoints = 0;
socialhappyTotal = 0;
socialnoneTotal = 0;
socialsadTotal = 0;
energyTotal = 0;
energyPoints = 0;
energyhappyTotal = 0;
energynoneTotal = 0;
energysadTotal = 0;

if (homeFlag == 1) {
startTime = millis();
}
if (homeFlag == 0) {
currentTime = millis();
}
diffTime = currentTime - startTime;

// Home Screen 1
if (diffTime < homea =" loadImage(" homeb="loadImage(" font =" loadFont(" font =" loadFont(" homec="loadImage(" font =" loadFont(" homeflag =" 0;" randgadvice =" int(random(3));" vizbkgd="loadImage(" linea="loadImage(" lineb="loadImage(" linec="loadImage(" vizdotted="loadImage(" vizfaces="loadImage(" font =" loadFont(" font =" loadFont(" font =" loadFont(" font =" loadFont(" i="0;" if="">= 130) {
moodhappyTotal++;
moodPoints = moodPoints + 2;
}
else if ((answerArray[1][i] >= 120) && (answerArray[1][i] < moodpoints =" moodPoints">= 130) {
socialhappyTotal++;
socialPoints = socialPoints + 2;
}
else if ((answerArray[1][i] >= 120) && (answerArray[1][i] < socialpoints =" socialPoints">= 130) {
energyhappyTotal++;
energyPoints = energyPoints + 2;
}
else if ((answerArray[1][i] >= 120) && (answerArray[1][i] < energypoints =" energyPoints"> (2*homeDelay)) {
homeFlag = 1;
}
}

}

// *************************** Question State
if (state == 2) {

//load cloud background image
PImage QBkgd;
QBkgd=loadImage("cloud_qbkgd.jpg");
image(QBkgd, 0, 0);

//load static face image
PImage QFace;
QFace=loadImage("face_nomouth.jpg");
image(QFace, 994, 244);

//question

font = loadFont("Strada-Light-70.vlw"); //load font file
textFont(font);
fill(#ffffff); //font color
textAlign(LEFT);
text(selectedQuestion, 208, 244, 696, 594); //text display, color
textAlign(CENTER);

//text instructions part a
font = loadFont("Strada-Light-40.vlw"); //load font file
textFont(font);
fill(#000000); //font color
textAlign(LEFT);
text("Choose your mood with the mouth,", 308, 1082, 612, 40); //text display, color

//text instructions part b
font = loadFont("Strada-Light-40.vlw"); //load font file
textFont(font);
fill(#0b920b); //font color
text("press the button to submit your answer.", 912, 1082, 696, 40); //text display, color
textAlign(CENTER);

smileyMouth(1089, 527, 537, 101, 1); // Face showing input values

progBars(answerCount);

}

// *************************** End State
if (state == 5) {

if (endFlag == 1) {
startTime = millis();
randAdvice = int(random(5));
}
if (endFlag == 0) {
currentTime = millis();
}
diffTime = currentTime - startTime;

if (diffTime < rbkgd="loadImage(" font =" loadFont(" randadvice ="=" happyadvice = "If you smile while you talk, you make people happy." sadadvice = "A happy person is not a person in a certain set of circumstances, but rather a person with a certain set of attitudes. ~ Hugh Downs" randadvice ="=" happyadvice = "A good word to a friend or stranger can bring light into their day." sadadvice = "Look at the happy side of life, and you will develop the happiness habit." randadvice ="=" happyadvice = "Whoever is happy will make others happy, too. ~ Mark Twain" sadadvice = "Learn to laugh. This will make you feel better." randadvice ="=" happyadvice = "Be kind whenever possible. It is always possible. ~ Dalai Lama" sadadvice = "Each day do something to make you feel happy, until this becomes a habit." randadvice ="=" happyadvice = "Let us be grateful to people who make us happy, they are the charming gardeners who make our souls blossom. ~ Marcel Proust" sadadvice = "Choose happiness, concentrate on happiness, and you will find it everywhere."> 1) {
font = loadFont("Strada-Light-70.vlw"); //load font file
textFont(font);
fill(#000000); //font color
textAlign(LEFT);
text(happyAdvice, 234, 474, 1462, 320); //text display, color
textAlign(CENTER);
}
else {
font = loadFont("Strada-Light-70.vlw"); //load font file
textFont(font);
fill(#000000); //font color
textAlign(LEFT);
text(sadAdvice, 234, 474, 1462, 320); //text display, color
textAlign(CENTER);
}

for (int i=0; i= 130) {
moodhappyTotal++;
moodPoints = moodPoints + 2;
}
else if ((answerArray[1][i] >= 120) && (answerArray[1][i] < moodpoints =" moodPoints">= 130) {
socialhappyTotal++;
socialPoints = socialPoints + 2;
}
else if ((answerArray[1][i] >= 120) && (answerArray[1][i] < socialpoints =" socialPoints">= 130) {
energyhappyTotal++;
energyPoints = energyPoints + 2;
}
else if ((answerArray[1][i] >= 120) && (answerArray[1][i] < energypoints =" energyPoints" endflag =" 0;" state =" 1;" homeflag =" 1;" endflag =" 1;" fromend =" 1;" serialin =" port.read();" mouthflag ="=" mouthval =" serialIn;" switchflag ="=" switchval =" serialIn;" serialin ="=" mouthflag =" 0;" switchflag =" 1;" serialin ="=" mouthflag =" 1;" switchflag =" 0;" mouthflag =" 0;" switchflag =" 0;" answercount ="=" selectedquestion = "How do you feel about SVA IXD?" qnumber =" 1;" selectedquestion = "1/4 of your degree is almost done." qnumber =" 4;" selectedquestion = "Today is a great day." qnumber =" 7;" answercount ="=" selectedquestion = "Celebrating the holidays with friends and family?" qnumber =" 2;" selectedquestion = "I feel like hanging out with friends." qnumber =" 5;" selectedquestion = "Want to play pictionary?" qnumber =" 8;" answercount ="=" selectedquestion = "I'm getting enough sleep." qnumber =" 3;" selectedquestion = "Are you motivated today?" qnumber =" 6;" selectedquestion = "I feel adventurous." qnumber =" 9;" state ="=">= 130) {
//case 1
mouth = "happy";
}
else if ((mouthval >= 120) && (mouthval < mouth = "none" mouth = "sad" switchval ="=" pressedflag ="=" sadtotal =" 0;" nonetotal =" 0;" happytotal =" 0;" happycount =" 0;" i="0;" if="">= 130) {
happyTotal++;
happyCount++;
}
else if ((answerArray[1][i] >= 120) && (answerArray[1][i] < randq =" int(random(3));"> 2) {
state = 5;
endFlag = 1;
}
}

}

// *************************** Home State

if ((state == 1) && (fromEnd == 0)) {

if ((switchval == 0) && (pressedflag == 1)) {
state = 2;
randQ = int(random(3));
}
}

// setting flags for switch pressed
if (switchval == 1) {
pressedflag = 1;
}
else {
pressedflag = 0;
}

}


// *************************** SMILEY MOUTH FUNCTION ***************************
void smileyMouth(int x, int y, int w, int h, int active) {

PImage e, f, g;
e = loadImage("happy-mouth.jpg");
f = loadImage("none-mouth.jpg");
g = loadImage("sad-mouth.jpg");

if ((mouth == "happy") || (active == 0)) {
image(e, x, y, w, h);
}
else if (mouth == "none") {
image(f, x, y, w, h);
}
else if (mouth == "sad") {
image(g, x, y, w, h);
}
}

// *************************** PROGRESS BARS FUNCTION ***************************
void progBars(int answers) {

PImage onbar, offbar;
onbar = loadImage("onbar.jpg");
offbar = loadImage("offbar.jpg");

if (answers < answers ="=" answers ="="> 2) {
image(onbar, 20, 20);
image(onbar, 655, 20);
image(onbar, 1290, 20);
}
}

// *************************** GROUP STATS FUNCTION ***************************
void groupStats(int mPoints, int sPoints, int ePoints, int mTotal, int sTotal, int eTotal) {

int groupBarWidth = 1404;
int groupBarX = 286;
int mY = 388;
int sY = 564;
int eY = 740;
float mSpread = 0;
float sSpread = 0;
float eSpread = 0;
int mPossible, sPossible, ePossible;

int gAdviceX = 286;
int gAdviceY = 1030;

PImage mMarker, sMarker, eMarker;
mMarker = loadImage("green_triangle.png");
sMarker = loadImage("purple_triangle.png");
eMarker = loadImage("orange_triangle.png");

mPossible = mTotal * 2;
sPossible = sTotal * 2;
ePossible = eTotal * 2;

if ((mPoints != 0) && (mPossible != 0)) {
mSpread = float(mPoints) / float(mPossible);
}
if ((sPoints != 0) && (sPossible != 0)) {
sSpread = float(sPoints) / float(sPossible);
}
if ((ePoints != 0) && (ePossible != 0)) {
eSpread = float(ePoints) / float(ePossible);
}

float mOffset = mSpread * float(groupBarWidth);
float sOffset = sSpread * float(groupBarWidth);
float eOffset = eSpread * float(groupBarWidth);

image(mMarker, groupBarX + int(mOffset), mY);
image(sMarker, groupBarX + int(sOffset), sY);
image(eMarker, groupBarX + int(eOffset), eY);

float avSpread = (mSpread + sSpread + eSpread)/3;
if (avSpread > 0.5) {
switch(randGAdvice) {
case 0:
gAdvice = "Your happiness is showing!";
break;
case 1:
gAdvice = "Smiling is contagious.";
break;
case 2:
gAdvice = "Share the joy, it's satisfying.";
break;
}
}
else if (avSpread <= 0.5) { switch(randGAdvice) { case 0: gAdvice = "Happiness can overcome all challenges."; break; case 1: gAdvice = "Happiness equals good health."; break; case 2: gAdvice = "Extract happiness in all things!"; break; } } font = loadFont("Strada-Bold-40.vlw"); // Load font file textFont(font); fill(#000733); //font color textAlign(LEFT); text(gAdvice, gAdviceX, gAdviceY, 1500, 90); }

Saturday, December 26, 2009

Midterm-Taxi Light

Our idea for the project was to create a new taxi light notification system. Here's a diagram of the current system:


After observing many tourists and NYC newcomers waving in confusion at cabs in various states, we decided this is something worth improving with physical computing. We also interviewed several cab drivers to understand the system from their point of view. We found these were the initial problems:
  • Unclear meaning to customers (especially tourists)
  • Visually unclear when far away or moving fast
  • Drivers often break rules and solicit passengers while “Off Duty”
For our first pass, we came up with a very early prototype. It was mostly LED's in a clear box with a sign at the front saying "Busy" or "Vacant". The sign was attached to a servo motor to rotate between Busy and Vacant. When the sign was on Busy, the orange LED's in the box glowed. When the sign was Vacant, the green LED's in the box glowed.

Attached to the light box was a chair meant to simulate the seats of a cab. The chair had a force resistor attached, and when a user sat down the Busy mode would trigger, and when a user got up the Vacant mode would trigger. A few seconds of delay were added before both state changes to allow for users shifting in their seats. This system meant the cab driver would no longer need to manually keep track of (or manipulate) what was going on. The system would be easier for both drivers and passengers to understand. Our first solution worked in the following ways:
  • Easy to see at any distance or speed
  • Uses green “Go” and orange “Warning” conventions
  • Customers see what they care about - Yes or No
  • Taxi company sees details, not customer
  • Drivers will not lure customers while “Off Duty”
After presenting this first prototype, we learned a lot from group feedback. There were some additional problems the class brought up:
  • Not language agnostic
  • Not visible from all sides
  • Not as visible in day time
So we made another iteration on the design. We created a modular light box that addressed the previous issues. The new design uses icons so it's language agnostic, and the modular design allows for a 4-per-cab setup that is clearly viewable from all sides.




The photos show the prototype with a portable florescent bulb inside, because this iteration had several problems with lighting. We first tried wiring into small florescent bulb enclosures to put into the light box. These blew after a few rapid on and off switches. We thought it was the bulbs, so we bought more and tested. Then it turned out the enclosure units were burning out. So we bought more enclosure units and they burned out again. The units couldn't handle the rapid on and off from the wire-to-wire vibration of testing.

We decided to switch to incandescent bulbs and use relays in a circuit to allow AC current from the wall power outlet to flow to the bulbs.

The schematic for this approach:


The code for this approach:

#define RELAY_PIN_ONE 3 //double relay action
#define RELAY_PIN_TWO 4
int relayVal = 0;

int fsrVal = 0; //force-sensitive resistor
int fsrPin = 0;

void setup()
{
pinMode(RELAY_PIN_ONE, OUTPUT);
pinMode(RELAY_PIN_TWO, OUTPUT);
Serial.begin(9600); // open serial
}

void loop(){
fsrVal = analogRead(fsrPin); // read the input pin

// if force resistor is pressed
if(fsrVal > 10){ //sitting
delay(4000);
relayVal = 1;
Serial.println("1");
}
else{ // empty
delay(4000);
relayVal = 0;
Serial.println("2");
}

// switch the relays
if(relayVal == 0){
digitalWrite(RELAY_PIN_TWO, LOW);
digitalWrite(RELAY_PIN_ONE, HIGH);
}
else{
digitalWrite(RELAY_PIN_ONE, LOW);
digitalWrite(RELAY_PIN_TWO, HIGH);
}
}

The 120V AC from the wall overloaded our relays so the current stayed on. Our prototype video shows the timing is working but the relays are overloaded:
http://vimeo.com/7446264

Unfortunately, we did not have time to build a new iteration this semester.

Tuesday, December 22, 2009

Lab10-Wireless

XBee Breakout Board
The XBee breakout board was soldered with male headers on one side and female sockets on the other. The XBee was then mounted to the breakout board, which in turn was mounted to the breadboard.


XBee Connections
The XBee was connected to 3.3V power and ground. The XBee DOUT was connected to Arduino RX, and the XBee DIN was connected to Arduino TX.

Switch Input
A momentary switch and 10K Ohm pull down resistor were added between power and Arduino digital input 2.


Buzzer Output
A speaker was connected to work as a buzzer between Arduino digital pin 5 and ground.


Configure XBees
Both XBee radios were configured with the XBeeConfigTerminal software. Using ATID, ATMY, and ATDL commands, the radios were paired reciprocally.

Program the Arduino Doorbell
The provided code was uploaded to both Arduino boards. When the momentary switch was pressed, the speaker outputted a tone. This verified the wireless connection was made.

Program the Arduino Feedback Doorbell
An LED and 200 Ohm resistor were added to the Switch Input board. The provided code was uploaded to both boards. The updated code allowed the newly added LED to light up when the Buzzer board received a signal and replied. This was tested and confirmed.