As computing becomes more ubiquitous in our objects, designers need to be more aware of how to design meaningful interactions into electronically enhanced objects. At the University of Washington, a class of junior Interaction Design majors is exploring this question. These pages chronicle their efforts.

Friday, June 8, 2012

Ryan & Dave: Tailwind - Giving Cyclists a Boost

Well, we've arrived at the end of the quarter but not the end of the project. Tailwind will continue to be developed into a functional product. As we have interviewed people and explained our concept to the people who assisted us, we encountered a lot of interest in seeing the finished prototype and requests for plans. It will take several days just to keep our promises to show off the working prototype!

So here's a summary of the project:

The problem as we see it: A significant number of avid cyclists and would be cyclists don't ride as often as they might like, particularly in a 'utility' capacity, running errands, commuting, etc. Contributing factors include age, physical conditioning, persistent injuries, hilly terrain, and the distance or loads involved. This is unfortunate since this is the type of cycling that would have the most direct positive impact on congestion and fossil fuel consumption.

Power assisted bikes and kits have been on the market for a while now, and would appear to be a good solution to these problems except for one thing: they don't hold much appeal to experienced cyclists. Our research indicated several reasons for this, including bike quality, complexity, efficiency, ego, and the type of bike or bike fit.

Our Solution: We wanted to design a power assist that would appeal to a potential utility cyclist, which as we saw it, was the group most likely to adopt the behavior patterns mentioned above. To do this, we recognized that this group has an established mental model of what the cycling experience is, and we needed to develop a solution that functions within that framework.

We quickly realized that the best way to accomplish this was to keep the power assist as unobtrusive as possible so the interaction continued to be a 'cycling experience' and not a 'power assist experience'. This is were the power of the Arduino comes into play, allowing us to use an array of sensors to monitor ride conditions rather than requiring input from the rider via a throttle or switches. Since the rider no longer has to worry about monitoring and integrating the power assist with their ride, they are free to appreciate the cycling experience - without worrying about the big hill ahead!

Implementation: The only direct interface the cyclist needs to worry about is a set and forget dial used to set the point where the rider would like to start getting some help from Tailwind. Once this is set, Tailwind uses an array of sensors and micro-controllers to monitor and adapt to what the cyclist is experiencing. Idealy, the cyclist would not even be aware of the system engaging and would always feel as though they were ridding unencumbered.

Our algorithm begins by ensuring that the brake lever is not engaged using a momentary switch. If the brake is engaged, the boost is immediately turned off. The sketch then checks the rider position with a pressure sensor in the saddle, reducing the crank RPM if the rider is out of the saddle. The force exerted on the pedals is constantly monitored and sent to the central controller by a wireless data link.

Once the pedal forces are read, the main controller has to decide what to do with the data. 1) The pedal force is assessed to determine if it exceeds the value set by the rider. 2) If the force of either pedal exceeds the threshold value, the force on both pedals is then compared. Approximately equal readings would indicate the rider is 'jockeying' (standing on both pedals but coasting or attempting to balance) and the assist would be disengaged. 3) The amount of effort the rider is exerting on the pedals and the riders position on the saddle are then used to determine the duty cycle of the Pulse Width Modulation (PWM) sent to the motor.

Ideally, the assist provided to the crankset would reduce the force the rider exerts on the pedals and a state of equilibrium is reached between the rider and the assist. If the cyclist wishes to increase the cadence (crank rpm) a momentary increase in effort would cause the amount of boost to increase until equilibrium is again established. If the system is well set up, the rider would not be aware of these changes, focusing only on the ride.

Our Code:  This is the code for the main controller using the VirtualWire library. While it compiles, we are not yet getting good data from the pedals and are still attempting to isolate the cause. The serial.print statements are included for debugging purposes.


#include <VirtualWire.h>

//Declair I/O Pins
const int assist_pin = A0;//threshhold
const int boost_pin = 9; //pwm out to mofset
const int pedalLeft_pin = 11;//left FSR via virtual_wire
const int pedalRight_pin = 12;//right FSR via virtual_wire
const int saddle_pin = A1;//saddle FSR
const int brake_pin = 8;//NC micro-switch on rear brake lever
const int transmit_en_pin = 3;

//Declair variables
int assist = 0; //amoount of desired assistance
int boost = 0; //PWM duty cycle 0-255
int pedalLeft = 0; //FSR value from left pedal
int pedalRight = 0; //FSR value from right pedal
int saddle = 0; //FSR value from saddle
int saddleOut = 0; //Threshold value for determing out of saddle
int standing = 30; //if the difference in pedal readings is below this value, assume the rider is jockeying

void setup() {
  delay(500);
  Serial.begin(9600); // Debugging only
  Serial.println("setup");
  vw_set_tx_pin( pedalRight_pin);
  vw_set_rx_pin(pedalLeft_pin);
  vw_set_ptt_pin(transmit_en_pin);
  vw_set_ptt_inverted(true); // Required for DR3100
  vw_setup(2000); // Bits per sec
  vw_rx_start();       // Start the receiver PLL running
  pinMode(boost_pin, OUTPUT); // set  the boost_pin as output:
}

void loop() {
  delay(2000);
  int saddleFactor = 100;   //default percentage for saddle corection
  assist = analogRead(assist_pin);  //read assist level
  map(assist, 0, 1023, 0, 255);
  saddle = analogRead(saddle_pin);  //read saddle state
  map(saddle, 0, 1023, 0, 255);
  if (digitalRead(brake_pin) == HIGH) {  //if the brake is off
    uint8_t buf[VW_MAX_MESSAGE_LEN];
    uint8_t buflen = VW_MAX_MESSAGE_LEN;
    vw_set_rx_pin(pedalLeft_pin);   //set the recieve pin to left side
    pedalLeft = vw_get_message(buf, &buflen);
    vw_set_rx_pin(pedalRight_pin); //set the recieve pin to right side
    pedalRight = vw_get_message(buf, &buflen);
    if (pedalLeft > assist || pedalRight > assist) {   //if either pedal exceedes the assist threshold
      if (abs(pedalLeft - pedalRight) > standing) {   //if the difference in pedal forces is greater than the standing test
        if (saddle < saddleOut)saddleFactor = 80;   //if rider is out of the saddle then seat = 0.80
        boost = 180 * (pedalLeft + pedalRight) * (saddleFactor / 100) / assist;
        analogWrite (boost_pin, boost);
      }
    }
  }
  Serial.print("Assist is set at ");
  Serial.println(assist);
  Serial.print("Brake is ");
  Serial.println(brake_pin);
  Serial.print("Left pedal is at ");
  Serial.println(pedalLeft);
  Serial.print("Right pedal is at ");
  Serial.println(pedalRight);
  Serial.print("Saddle is ");
  Serial.println(saddle);
}



The build: As mentioned, the code is not yet functional and this is perhaps our takeaway for the course. We attempted a very ambitious project and by all accounts, one that would be successful when fully realized. But it also involved a sophisticated mechanical system AND multiple sensors controlled by three Arduinos communicating over two wireless links. In the end, this was too much to resolve in the time available to us. We had hoped to have a ride-able bike for our presentation. Our success with each individual component and small groups of components had encouraged us and led us to believe this was an attainable goal, even as our deadline approached. While we were able to demonstrate individual components, in retrospect, we should have reduced the complexity of our prototype to something less than fully functional at an earlier stage or have settled on an intermediate goal from the outset.

Thanks: We would like to extend our thanks to John Martin and Robert Beyer for their cycling expertise and encouragement and to Maia Gower-Opincarne for the generous use of her bike.

No bicycles were harmed in the making of Tailwind.

Wednesday, June 6, 2012

Sheema & Claire's Design: tilt: the bed that gets you going.


Problem: Snoozing

We identified snoozing as a major problem amongst people today. We were intrigued by this fascination with the snooze button, and decided that tilt could be the first step in mediating this snoozing trend.  People rely on the alarm snooze button to cram in a few more minutes of sleep, but end up wasting time in a state of limbo, neither awake nor asleep. This ability to snooze gives people a false sense of security. It is there to encourage people to lie in bed a little bit longer because there will be another reminder to get up in the near future.


Our Design: tilt

Our bed frame design will help those who are dependent or in the snooze button get up and out of bed in the morning and ultimately change their getting-up habits, thus making a more efficient use of their time.


Process:

In order to get to the end result we went through countless ideation on form and technical mechanics on how best to physically get people out of bed. Tilt was our final solution. The mechanical and physical aspects of tilt consists of plywood, long wood blocks, inflatable tube, hose, air pump, alarm, air mattress, arduino, relay, and microphone sensor. 


Close up form and construction
Sketches and tube
Relay in case
Soldered relay
Testing bed

Materials
Tools


Inflatable bed coming along

tilt in environment


Final Video: 




tilt_Art387 from Sheema Nezam-Tehrani on Vimeo.

Project Overview:



Shelby and Bridget: Q-Jumped

Often while standing in line—to pay at the grocery store, to purchase concert tickets, or to order coffee at a cafe—people experience frustration when others cut into the line. While it is not socially acceptable to cut into a line, there is also no appropriate way to respond if you are behind a person who has cut.


Q-Jumped enables people to react in a socially acceptable and anonymous manner to the frustrating situation of getting cut in line. Twitter, Processing, Arduino, and a seven-segment display work together to generate a physical response. The seven-segment display shows the number of tweets that report an incident of line-cutting.


CONCEPT BOARDS

















































BRIDGET AND SHELBY'S REFLECTIONS
We really enjoyed doing this project, and feel we learned a lot, both about coding and about basic electronics. Mostly, we learned how to learn, using the resources available to us (the internet, our peers, Dominic, and the community). It was really empowering to be able to not know how to do something very well, but to learn enough to figure out how to make a working prototype.
During our final presentation, we got some helpful feedback; some people suggested we offer more context within the prototype. This would create better interest in the project, as well as give people a sense of what the project is about. We plan on acting on this feedback this summer to improve our prototype.
Overall, we feel happy about the way that the project addresses the situation, and we really enjoyed the process.


RESOURCES WE USED FOR THIS PROJECT
Metrix Create:Space was an incredible resource, and really helped us with deciding on components and the overall setup.
A few online resources were also really helpful, and they are highlighted in these two blog posts: (1) (2).


ELECTRONIC COMPONENTS USED
84 red LEDs
14 75-ohm Resistors
2 SN7447 7-segment display drivers
wire wrap, jumper cables
1 Arduino Uno


CODE
processing:
import processing.serial.*;

import twitter4j.conf.*;
import twitter4j.internal.async.*;
import twitter4j.internal.org.json.*;
import twitter4j.internal.logging.*;
import twitter4j.json.*;
import twitter4j.internal.util.*;
import twitter4j.management.*;
import twitter4j.auth.*;
import twitter4j.api.*;
import twitter4j.util.*;
import twitter4j.internal.http.*;
import twitter4j.*;
import twitter4j.internal.json.*;

static String OAuthConsumerKey = "CONSUMER KEY";
static String OAuthConsumerSecret = "CONSUMER SECRET";
static String AccessToken = "ACCESS TOKEN";
static String AccessTokenSecret = "ACCESS TOKEN SECRET";

Serial arduino;
Twitter twitter = new TwitterFactory().getInstance();

String oldID = "";

void setup() {
size(125, 125);
frameRate(10);
background(0);
println(Serial.list());
String arduinoPort = Serial.list()[0];
arduino = new Serial(this, arduinoPort, 9600);
loginTwitter();
}

void loginTwitter() {
twitter.setOAuthConsumer(OAuthConsumerKey, OAuthConsumerSecret);
AccessToken accessToken = loadAccessToken();
twitter.setOAuthAccessToken(accessToken);
}

private static AccessToken loadAccessToken() {
return new AccessToken(AccessToken, AccessTokenSecret);
}

void draw() {
background(0);
text("@cut_in_line", 35, 65);
getMention();
delay(15000); // wait 15 seconds to avoid Twitter Rate Limit
}


void getMention() {
List mentions = null;
try {
mentions = twitter.getMentions();
}
catch(TwitterException e) {
println("Exception: " + e + "; statusCode: " + e.getStatusCode());
}
Status status = (Status)mentions.get(0);
String newID = str(status.getId());
if (oldID.equals(newID) == false){
oldID = newID;
println(status.getText()+", by @"+status.getUser().getScreenName());
arduino.write(1); // arduino gets 1
}
}



arduino:
int seven_seg_digits[10][4] = {  { 0,0,0,0 },  // = 0
                                 { 0,0,0,1 },  // = 1
                                 { 0,0,1,0 },  // = 2
                                 { 0,0,1,1 },  // = 3
                                 { 0,1,0,0 },  // = 4
                                 { 0,1,0,1 },  // = 5
                                 { 0,1,1,0 },  // = 6
                                 { 0,1,1,1 },  // = 7
                                 { 1,0,0,0 },  // = 8
                                 { 1,0,0,1 }   // = 9
                               };
int tweetCount = 0;

void setup()
{
  Serial.begin(9600);
  pinMode(6, OUTPUT);
  pinMode(7, OUTPUT);
  pinMode(8, OUTPUT);
  pinMode(9, OUTPUT);
  pinMode(10, OUTPUT);
  pinMode(11, OUTPUT);
  pinMode(12, OUTPUT);
  pinMode(13, OUTPUT);
}

void loop()
{
  listenToSerial();
}

void listenToSerial()
{
  int serialMsg = 0;
  if (Serial.available())
  {
    serialMsg = Serial.read();
    if (serialMsg == 1)
    {
      Display();
    }
  }
}

void Display()
{
  tweetCount++;
  int ones = 0;
  int tens = 0;
  ones = tweetCount % 10;
  tens = tweetCount / 10;
  int pinOnes = 6;
  for (int disp1 = 0; disp1 < 4; disp1++)
  {
    digitalWrite(pinOnes, seven_seg_digits[ones][disp1]);
    ++ pinOnes;
  }
  int pinTens = 10;
  for (int disp10 = 0; disp10 < 4; disp10++)
  {
    digitalWrite(pinTens, seven_seg_digits[tens][disp10]);
    ++ pinTens;
  }
}


VIDEO


Q-Jumped: Physical Prototype from Shelby Li on Vimeo.

Robin Carly | Shame Bathroom

VIDEO
https://vimeo.com/43688195

CONCEPT BOARDS


THE SETUP
HOW IT WORKS
THE CODE

unsigned long startTime; 
boolean start =false;
unsigned long elapsedTime;
boolean didntWash = false;
boolean flash = true;

int speakerPin = 9; //Set speaker to pin 9
int length = 15; // the number of notes
char notes[] = "ccggaagffeeddc "; // a space represents a rest
int beats[] = { 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 2, 4 };
int tempo = 300;


void setup() {
  pinMode(4, OUTPUT);
 pinMode(speakerPin, OUTPUT);
  pinMode(13, OUTPUT);
  
}

void playTone(int tone, int duration) {
  for (long i = 0; i < duration * 1000L; i += tone * 2) {
    digitalWrite(speakerPin, 1000);
    delayMicroseconds(tone);
    digitalWrite(speakerPin, 0);
    delayMicroseconds(tone);
  }
}

void playNote(char note, int duration) {
  char names[] = { 'c', 'd', 'e', 'f', 'g', 'a', 'b', 'C' };
  int tones[] = { 1915, 1700, 1519, 1432, 1275, 1136, 1014, 956 };

  // play the tone corresponding to the note name
  for (int i = 0; i < 8; i++) {
    if (names[i] == note) {
      playTone(tones[i], duration);
    }
  }
}






void loop() {
  //digitalWrite(4,HIGH); 
 if(didntWash){
   if(millis() - startTime < 5000){
       for (int i = 0; i < length; i++) {
          
          if (notes[i] == ' ') {
            
            delay(beats[i] * tempo); // rest
          
          } else {
            
            if(flash){
              digitalWrite(13, HIGH);
              flash = !flash;
            } else {
              digitalWrite(13, LOW);
              flash = !flash;
            }
          
            playNote(notes[i], beats[i] * tempo);
          }
      
          // pause between notes
          delay(tempo / 2); 
        }

   } else {
    digitalWrite(13, LOW);
    elapsedTime = 0;
    didntWash = false;
    start = false; 
   }
 } 
  if(!start && !didntWash){    
    if(analogRead(0) < 300){
      digitalWrite(13, HIGH);
      startTime = millis();
      start = true;       
    }
  } else if (start && !didntWash) {     
     elapsedTime = millis() - startTime;       
     if(elapsedTime < 3000){
       if(analogRead(1) < 300){
         didntWash = true;
         startTime = millis();
       }
     } else {
       start = false;
       elapsedTime = 0;  
       digitalWrite(13, LOW);
     }    
  }
}

Toy Store: Smart Shelf

OUR CONCEPT




Our project is a shelf that helps instill organizational habits in children by reminding them to put their toys back after use. Amidst the excitement of playtime and make-believe, toys are often pulled out and then forgotten as the children move onto their next game. "Toy Store" provides a gentle reminder to children to put toys away when they finish playing.

The shelf can be calibrated to sense the normal weight of the objects in the shelf. A parent can then turn a dial to set the amount of time the toys can be out. When a toy is removed, a colored LED turns on to indicate that it is missing. After the set amount of time has passed, a voice says, "The (shelf color/s) shelf is empty! Time to put the toys away!", and a cheerful song plays. The music stops when the toys are replaced. A light sensor turns off the shelf when the room is dark, ensuring that children are not woken by the sound or light.





THE PROCESS

We began working with an Arduino Uno. To play a song, we needed a larger amount of memory than the Uno had on board, so we added a Sparkfun Mp3 Shield. To run it, we used the Sparkfun Mp3 Shield Arduino Library by Bill Porter. What we soon realized is that, between the library and the shield itself, most of the pins on the board were being used. 
Pins used by the Mp3 shield and
Mp3 shield library (black)

We considered a couple of options to mitigate this problem. The first was to use a shift register to increase the number of outputs from the board (tutorial here). However, the shift register uses pins  8, 11 and 12, which are used by the shield. 

Bill Porter wrote a library to make it easier to use serial communication between two Arduino boards. We borrowed a second Arduino Uno and then used the EasyTransfer Arduino Library to send information between them. 

We used Force Sensitive Resistors to sense the approximate force of the objects on the shelf. These were not extremely precise, but with some experimenting, we were able to generate results reliable enough for our purposes. We used a layer of felt and a layer of foam core over each sensor to distribute the weight. Two days ago, one of our FSRs failed, so the code below is written for two FSRs, and one button. 

There is a button for calibrating the weights on the shelf, a rotary potentiometer for setting the playtime length, and a mini photocell to sense when the lights in the room are turned off. We used a battery-powered Mp3 player speaker to play the sound. 

We used the Metro library by Thomas Ouellet Fredericks to handle time-based functions, like triggering sound. 

View the code after the break...

Aaron and Kelly Final Project: The Hiatus Chair


We propose a chair that gets tired, just like you do. Too much sitting at one time makes the Hiatus Chair weary, and it begins to lean forward, encouraging you to stand up. If you persist in sitting, the chair will tilt further, forcing out its occupant so it can take a well-deserved five minute break. After this five minute pause, the chair will reset to its regular, comfortable position. As its occupant returns, the timer starts over and will once again encourage a break in two hours.

The Hiatus Chair is intended to prevent common office injuries as well as form habits that increase long-term health and reduce the risk of sitting-induced health problems. In addition, it has long been known by many productive individuals that a quick break in the midst of hard work can spur creativity, insight, and help combat writer’s block. The Hiatus Chair provides a regulated schedule for these mental breaks as well. With the knowledge that no work can be done in that five minute window, you are free to take a walk, eat a snack, or just relax and get a new perspective on your work.

CONCEPT BOARDS:




PROCESS:

We began our process by assessing a variety of existing social problems, and considering the elements of situation and response we discussed in class. After generating three situations of interest, we soon became more intrigued by the idea of deterring people from unhealthy behavior, and through discussion narrowed down on the idea of preventing office injuries and overly sedentary lifestyles. Our next step was to determine the best method of deterring people from being sedentary. We came upon the idea of modifying a chair itself to make it grow weary of a person sitting in it for too long. This was both a direct way of combatting the biggest source of sedentary time (sitting) and an interesting social interaction between a chair and its user, with a chair responding back for the first time.


We generated a concept diagram of how we envisioned our responsive chair. This diagram allowed us to plan out our next steps, and assess what electrical components and Arduino programming we might need. For the next several weeks, we worked hard to develop a working code that let the Arduino run a timer, and based on that timer, activate a motor which would shut off and reverse when tapping microswitches we placed when it reached the tilted position and the regular seated position.


At the same time, we planned out our hardware solution, rigging an Arduino Uno up with a four-way relay, car window motor, and a motorcycle battery to run the system. We cut (using a band saw, table saw, and laser cutter) an arm for the motor to move that would tilt the chair when necessary, and pull it back. Finally, we assembled the entire operation into what we now call the Hiatus Chair.


PROCESS VIDEOS:







Despite adversity (and our chair totally breaking right before the final), we persevered. Meet the Hiatus Concept Chair.






CODE:

/*
PROJECT TITLE: Anti-Office Injury Chair
CODE NAME: [Frank]enstein (Tentative)
DESIGNERS: Aaron Calzado, Kelly Graham
CLASS: ART 387 Physical Interaction Design Sping 2012
ADVISOR: Dominic Muren


*/


//Define the sensors
const int seat = 12;
const int fwdStop = 2;
const int revStop = 4;

//Define the motors
const int fwd = 7;
const int rev = 8;


//Define the stages in the process
int seatButton = 0;
int stopButton = 0;
int finishButton = 0;


//This activates the moter and keeps it on
boolean movingBackwards = false;
boolean movingForwards = false;

//seatButton "holding" stuff
int seatFirstTime = 1;
unsigned long seatStartTime;
unsigned long seatPressTime;


//stopButton "holding" stuff
int stopFirstTime = 1;
unsigned long stopStartTime;
unsigned long stopPressTime;

//===============================================

void setup() {

//Set pin modes

pinMode(seat, INPUT);
pinMode(fwdStop, INPUT);
pinMode(revStop, INPUT);
pinMode(fwd, OUTPUT);
pinMode(rev, OUTPUT);
Serial.begin(9600);


}


//===============================================


void loop(){

seatButton = digitalRead(seat);
stopButton = digitalRead(fwdStop);
finishButton = digitalRead(revStop);


/*
stopButton and finishButton: LOW == IN and HIGH == OUT
seatButton: HIGH == IN and LOW == OUT
*/


//Start and waiting process
if (movingForwards){
motorOnFwd();
} else {
motorOff();
}
//seatButton IN, finishButton IN
if (seatButton == HIGH && finishButton == LOW) {
if (seatFirstTime == 1) {
seatStartTime = millis();
seatFirstTime = 0;
}
seatPressTime = millis() - seatStartTime;
if (seatPressTime >= 1) {
}
seatPressTime = millis() - seatStartTime;
if(seatPressTime >= 1){
Serial.print("Time: ");
Serial.print(seatPressTime);
Serial.print(" milliseconds ");
Serial.print(int(seatPressTime/1000));
Serial.println(" seconds");
}
if (seatPressTime > 10000) {
movingForwards = true;
}
} else if (seatFirstTime == 0) {
seatFirstTime = 1;
movingForwards = true;
}

//Forward and stop process
//Wait and reverse process
if (movingBackwards){
motorOnRev();
} else {
motorOff();
}
//stopButton IN
if (seatButton == LOW && stopButton == LOW && finishButton == HIGH || stopButton == LOW) {
if (stopFirstTime == 1) {
stopStartTime = millis();
stopFirstTime = 0;
}
stopPressTime = millis() - stopStartTime;
if (stopPressTime >= 1) {
}
stopPressTime = millis() - stopStartTime;
if(stopPressTime >= 1){
Serial.print("Time: ");
Serial.print(stopPressTime);
Serial.print(" milliseconds ");
Serial.print(int(stopPressTime/1000));
Serial.println(" seconds");
}
if (stopPressTime > 10000) {
movingBackwards = true;
}
} else if (stopFirstTime == 0) {
stopFirstTime = 1;
movingBackwards = true;
}
/* IMPORTANT! DO NOT DELETE THIS SECTION
seatButton and finishButton act as killswitches so
the motor and battery does not commit suicide */
if (seatButton == HIGH && movingBackwards == true || finishButton == LOW) {
movingBackwards = false;
}
if (stopButton == LOW) {
movingForwards = false;
}
/* End of important section */
}

//===============================================

void motorOff() {
digitalWrite(fwd, HIGH);
digitalWrite(rev, HIGH);
}
void motorOnFwd() {
digitalWrite(fwd, LOW);
digitalWrite(rev, HIGH);
}
void motorOnRev() {
digitalWrite(fwd, HIGH);
digitalWrite(rev, LOW);
}
/*
Much mahalos: Dae, John, Sara Jo, Tom C.
Holding code taken from: Jeremy1998 via
http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1280511595/all
*/




Brushing Your Teeth with Pause

By Miles Koons and Kim Lewis



Our Concept 

"The average person in the U.S. spends only 17 seconds each time they brush their teeth." -Dr. Harold Katz. This number may sound a little appalling at first, when the recommended time is two to three minutes, but when I began to notice how long I brush my own teeth it started to make more sense. For most people, getting ready in the morning is a rushed process as they are on their way to work, school, or some other important function. Brushing your teeth drops down in priorities when you are  late. Even if you are not in a hurry,  standing around for several minutes while you are brushing your teeth is boring. Not brushing your teeth can lead to many unfortunate consequences, however, with the most obvious including plague, tooth decay, gum disease and overall poor oral health.

Our goal was to design an experience and prototype it with Arduino that would change a person's toothbrushing habits through a fun and positive interaction. When researching, we discovered that the average American spends four to five hours a day watching TV. (mashable.com) What was that you were saying about not having time to brush your teeth? What if we could take some of the enjoyment and entertainment of watching t.v. and incorporate it into the toothbrushing experience?

For our solution we created Pause, an interactive device that encourages users to brush their teeth for the recommended three minutes. It accomplishes this by showing a curated set of puppy videos to entertain the user while they are brushing. When the three minutes have completed, the last opened video plays till the end and a congratulations screen appears letting the user know they have completed the suggested time. By using fun to change behavior, users will look forward to brushing their teeth and try to incorporate it into their daily routines.



How It Works



Pause works by interfacing the Arduino with Processing. A sketch is uploaded to the Arduino that sends serial information over to Processing through the values of 0 or 1. A microswitch is located in the bottom of the toothbrush holder, attached to the breadboard through wires, which is then attached the Arduino through pins 2, GND and 5v. When the microswitch is pressed, this sends a value of 1 to Processing, telling it to wait. Once the toothbrush is removed, the value becomes 0 which then triggers the Pause program.

Pause is set up to call a different puppy video at random using an array. Each video is made up of embedded video codes in an html file that is pulled up locally. It will call videos consecutively once they are done playing for the duration of two minutes and thirty seconds. Once the timer in the Processing sketch reaches this time, it will call the last video and allow it to play out in its entirety before displaying the completed screen. The Processing sketch checks every second to see if the serial value is one or zero, and once the toothbrush is placed back in the holder it is aware to not pull any more videos if the brusher has stopped the brushing process early.

Videos are displayed behind a two way mirror, which has an Ipad behind it.



















Electronic Components Used

Software Used


The Code

Arduino:

//Interfacing an Arduino with Processing
 //Sketch by Kim Lewis and Miles Koons, 2012

 //built off of Button sketch
 //created 2005
 //by DojoDave <http://www.0j0.org>
 //modified 30 Aug 2011
 //by Tom Igoe

 //This sketch works on the Arduino end, to help Processing change a square
 //different colors depending on the input of a button
 //The circuit:
 //* LED attached from pin 13 to ground
 //* pushbutton attached to pin 2 from +5V
 //* 10K resistor attached to pin 2 from ground

 //setting the pin numbers for the LED and button to connect with Arduino

int buttonPin = 2;     // the number of the pushbutton pin
const int ledPin =  13;      // the number of the LED pin
int i = 0;
int buttonState = LOW;  //variable that reads the state of the pushbutton
int val = 0;
void setup()
 {
   Serial.begin(9600);         //initialize the serial communication
   pinMode(ledPin, OUTPUT);    //initialize the LED pin as an output
   pinMode(buttonPin, INPUT);  //initialize the pushbutton pin as an input
 
 
 }

 void loop(){
  val = digitalRead(buttonPin);           // read the state of the pushbutton value:
 
   if (val == HIGH){
     //buttonState = digitalRead(buttonPin); //check if the pushbutton is pressed, if it is the buttonState is HIGH:
   
     if (buttonState == LOW) {            //turn LED off:
   
       Serial.println(0);                 //Send 0 to Processing
       digitalWrite(ledPin, HIGH);
       buttonState = HIGH;
       delay(50);
     }
    } else {
       //turn LED on:
     
      if (buttonState == HIGH) {
   
       Serial.println(1);                 //send 0 to Processing
       digitalWrite(ledPin, LOW);
       buttonState = LOW;
       delay(50);
       //for (i = 0; i < 500; i++) {
       //Serial.println(i);
       //}
     }
   }
 }

Processing:

import processing.serial.*;

Serial myPort; //The serial port
int val;
String [] puppy = new String [57]; //the puppy video string
int [] time = new int [57];
int lastRun = 0;
int countTime = 0;
int videoTime = 0;
int intro = 0;
void setup() {

  String portName = Serial.list()[0];
  myPort = new Serial(this, portName, 9600);

  //here are all of the local puppy files
  puppy[0] = "file:///Users/kimlewis/Documents/UW%20Work/07_Spring%20Qtr%202012/Art%20387_Physical/06_Final%20Code/video_0.html"; //41 seconds
  puppy[1] = "file:///Users/kimlewis/Documents/UW%20Work/07_Spring%20Qtr%202012/Art%20387_Physical/06_Final%20Code/video_1.html"; //36 seconds
  puppy[2] = "file:///Users/kimlewis/Documents/UW%20Work/07_Spring%20Qtr%202012/Art%20387_Physical/06_Final%20Code/video_2.html"; //18 seconds
  puppy[3] = "file:///Users/kimlewis/Documents/UW%20Work/07_Spring%20Qtr%202012/Art%20387_Physical/06_Final%20Code/video_3.html"; //99 seconds
  puppy[4] = "file:///Users/kimlewis/Documents/UW%20Work/07_Spring%20Qtr%202012/Art%20387_Physical/06_Final%20Code/video_4.html"; //68 seconds
  puppy[5] = "file:///Users/kimlewis/Documents/UW%20Work/07_Spring%20Qtr%202012/Art%20387_Physical/06_Final%20Code/video_5.html"; //87 seconds
  puppy[6] = "file:///Users/kimlewis/Documents/UW%20Work/07_Spring%20Qtr%202012/Art%20387_Physical/06_Final%20Code/video_6.html"; //49 seconds
  puppy[7] = "file:///Users/kimlewis/Documents/UW%20Work/07_Spring%20Qtr%202012/Art%20387_Physical/06_Final%20Code/video_7.html"; //33 seconds
  puppy[8] = "file:///Users/kimlewis/Documents/UW%20Work/07_Spring%20Qtr%202012/Art%20387_Physical/06_Final%20Code/video_8.html"; //60 seconds
  puppy[9] = "file:///Users/kimlewis/Documents/UW%20Work/07_Spring%20Qtr%202012/Art%20387_Physical/06_Final%20Code/video_9.html"; //60 seconds
  puppy[10] = "file:///Users/kimlewis/Documents/UW%20Work/07_Spring%20Qtr%202012/Art%20387_Physical/06_Final%20Code/video_10.html"; //95 seconds
  puppy[11] = "file:///Users/kimlewis/Documents/UW%20Work/07_Spring%20Qtr%202012/Art%20387_Physical/06_Final%20Code/video_11.html"; //105 seconds
  puppy[12] = "file:///Users/kimlewis/Documents/UW%20Work/07_Spring%20Qtr%202012/Art%20387_Physical/06_Final%20Code/video_12.html"; //136 seconds
  puppy[13] = "file:///Users/kimlewis/Documents/UW%20Work/07_Spring%20Qtr%202012/Art%20387_Physical/06_Final%20Code/video_13.html"; //104 seconds
  puppy[14] = "file:///Users/kimlewis/Documents/UW%20Work/07_Spring%20Qtr%202012/Art%20387_Physical/06_Final%20Code/video_14.html"; //37 seconds
  puppy[15] = "file:///Users/kimlewis/Documents/UW%20Work/07_Spring%20Qtr%202012/Art%20387_Physical/06_Final%20Code/video_15.html"; //49 seconds
  puppy[16] = "file:///Users/kimlewis/Documents/UW%20Work/07_Spring%20Qtr%202012/Art%20387_Physical/06_Final%20Code/video_16.html"; //90 seconds
  puppy[17] = "file:///Users/kimlewis/Documents/UW%20Work/07_Spring%20Qtr%202012/Art%20387_Physical/06_Final%20Code/video_17.html"; //42 seconds
  puppy[18] = "file:///Users/kimlewis/Documents/UW%20Work/07_Spring%20Qtr%202012/Art%20387_Physical/06_Final%20Code/video_18.html"; //133 seconds
  puppy[19] = "file:///Users/kimlewis/Documents/UW%20Work/07_Spring%20Qtr%202012/Art%20387_Physical/06_Final%20Code/video_19.html"; //6 seconds
  puppy[20] = "file:///Users/kimlewis/Documents/UW%20Work/07_Spring%20Qtr%202012/Art%20387_Physical/06_Final%20Code/video_20.html"; //111 seconds
  puppy[21] = "file:///Users/kimlewis/Documents/UW%20Work/07_Spring%20Qtr%202012/Art%20387_Physical/06_Final%20Code/video_21.html"; //60 seconds
  puppy[22] = "file:///Users/kimlewis/Documents/UW%20Work/07_Spring%20Qtr%202012/Art%20387_Physical/06_Final%20Code/video_22.html"; //142 seconds
  puppy[23] = "file:///Users/kimlewis/Documents/UW%20Work/07_Spring%20Qtr%202012/Art%20387_Physical/06_Final%20Code/video_23.html"; //72 seconds
  puppy[24] = "file:///Users/kimlewis/Documents/UW%20Work/07_Spring%20Qtr%202012/Art%20387_Physical/06_Final%20Code/video_23.html"; //231 seconds Fix Dis! (video_24 was changed to video_23) need to find a new video
  puppy[25] = "file:///Users/kimlewis/Documents/UW%20Work/07_Spring%20Qtr%202012/Art%20387_Physical/06_Final%20Code/video_25.html"; //59 seconds
  puppy[26] = "file:///Users/kimlewis/Documents/UW%20Work/07_Spring%20Qtr%202012/Art%20387_Physical/06_Final%20Code/video_26.html"; //113 seconds
  puppy[27] = "file:///Users/kimlewis/Documents/UW%20Work/07_Spring%20Qtr%202012/Art%20387_Physical/06_Final%20Code/video_27.html"; //60 seconds
  puppy[28] = "file:///Users/kimlewis/Documents/UW%20Work/07_Spring%20Qtr%202012/Art%20387_Physical/06_Final%20Code/video_28.html"; //49 seconds
  puppy[29] = "file:///Users/kimlewis/Documents/UW%20Work/07_Spring%20Qtr%202012/Art%20387_Physical/06_Final%20Code/video_29.html"; //119 seconds
  puppy[30] = "file:///Users/kimlewis/Documents/UW%20Work/07_Spring%20Qtr%202012/Art%20387_Physical/06_Final%20Code/video_30.html"; //4 seconds
  puppy[31] = "file:///Users/kimlewis/Documents/UW%20Work/07_Spring%20Qtr%202012/Art%20387_Physical/06_Final%20Code/video_31.html"; //48 seconds
  puppy[32] = "file:///Users/kimlewis/Documents/UW%20Work/07_Spring%20Qtr%202012/Art%20387_Physical/06_Final%20Code/video_32.html"; //13 seconds
  puppy[33] = "file:///Users/kimlewis/Documents/UW%20Work/07_Spring%20Qtr%202012/Art%20387_Physical/06_Final%20Code/video_33.html"; //17 seconds
  puppy[34] = "file:///Users/kimlewis/Documents/UW%20Work/07_Spring%20Qtr%202012/Art%20387_Physical/06_Final%20Code/video_34.html"; //82 seconds
  puppy[35] = "file:///Users/kimlewis/Documents/UW%20Work/07_Spring%20Qtr%202012/Art%20387_Physical/06_Final%20Code/video_35.html"; //90 seconds
  puppy[36] = "file:///Users/kimlewis/Documents/UW%20Work/07_Spring%20Qtr%202012/Art%20387_Physical/06_Final%20Code/video_36.html"; //101 seconds
  puppy[37] = "file:///Users/kimlewis/Documents/UW%20Work/07_Spring%20Qtr%202012/Art%20387_Physical/06_Final%20Code/video_37.html"; //26 seconds
  puppy[38] = "file:///Users/kimlewis/Documents/UW%20Work/07_Spring%20Qtr%202012/Art%20387_Physical/06_Final%20Code/video_38.html"; //125 seconds
  puppy[39] = "file:///Users/kimlewis/Documents/UW%20Work/07_Spring%20Qtr%202012/Art%20387_Physical/06_Final%20Code/video_39.html"; //43 seconds
  puppy[40] = "file:///Users/kimlewis/Documents/UW%20Work/07_Spring%20Qtr%202012/Art%20387_Physical/06_Final%20Code/video_40.html"; //31 seconds
  puppy[41] = "file:///Users/kimlewis/Documents/UW%20Work/07_Spring%20Qtr%202012/Art%20387_Physical/06_Final%20Code/video_41.html"; //72 seconds
  puppy[42] = "file:///Users/kimlewis/Documents/UW%20Work/07_Spring%20Qtr%202012/Art%20387_Physical/06_Final%20Code/video_42.html"; //75 seconds
  puppy[43] = "file:///Users/kimlewis/Documents/UW%20Work/07_Spring%20Qtr%202012/Art%20387_Physical/06_Final%20Code/video_43.html"; //63 seconds
  puppy[44] = "file:///Users/kimlewis/Documents/UW%20Work/07_Spring%20Qtr%202012/Art%20387_Physical/06_Final%20Code/video_44.html"; //72 seconds
  puppy[45] = "file:///Users/kimlewis/Documents/UW%20Work/07_Spring%20Qtr%202012/Art%20387_Physical/06_Final%20Code/video_45.html"; //22 seconds
  puppy[46] = "file:///Users/kimlewis/Documents/UW%20Work/07_Spring%20Qtr%202012/Art%20387_Physical/06_Final%20Code/video_46.html"; //51 seconds
  puppy[47] = "file:///Users/kimlewis/Documents/UW%20Work/07_Spring%20Qtr%202012/Art%20387_Physical/06_Final%20Code/video_47.html"; //49 seconds
  puppy[48] = "file:///Users/kimlewis/Documents/UW%20Work/07_Spring%20Qtr%202012/Art%20387_Physical/06_Final%20Code/video_48.html"; //119 seconds
  puppy[49] = "file:///Users/kimlewis/Documents/UW%20Work/07_Spring%20Qtr%202012/Art%20387_Physical/06_Final%20Code/video_49.html"; //22 seconds
  puppy[50] = "file:///Users/kimlewis/Documents/UW%20Work/07_Spring%20Qtr%202012/Art%20387_Physical/06_Final%20Code/video_50.html"; //47 seconds
  puppy[51] = "file:///Users/kimlewis/Documents/UW%20Work/07_Spring%20Qtr%202012/Art%20387_Physical/06_Final%20Code/video_51.html"; //119 seconds
  puppy[52] = "file:///Users/kimlewis/Documents/UW%20Work/07_Spring%20Qtr%202012/Art%20387_Physical/06_Final%20Code/video_52.html"; //65 seconds
  puppy[53] = "file:///Users/kimlewis/Documents/UW%20Work/07_Spring%20Qtr%202012/Art%20387_Physical/06_Final%20Code/video_53.html"; //47 seconds
  puppy[54] = "file:///Users/kimlewis/Documents/UW%20Work/07_Spring%20Qtr%202012/Art%20387_Physical/06_Final%20Code/video_54.html"; //62 seconds
  puppy[55] = "file:///Users/kimlewis/Documents/UW%20Work/07_Spring%20Qtr%202012/Art%20387_Physical/06_Final%20Code/video_55.html"; //59 seconds
  puppy[56] = "file:///Users/kimlewis/Documents/UW%20Work/07_Spring%20Qtr%202012/Art%20387_Physical/06_Final%20Code/video_56.html"; //59 seconds

  time[0] = 41; //confirmed
  time[1] = 36; //confirmed
  time[2] = 18; //confirmed
  time[3] = 99; //confirmed
  time[4] = 68; //confirmed
  time[5] = 87; //confirmed
  time[6] = 49; //confirmed
  time[7] = 33; //confirmed
  time[8] = 60; //confirmed 
  time[9] = 60; //confirmed 
  time[10] = 95; //confirmed
  time[11] = 105; //confirmed
  time[12] = 136; //confirmed
  time[13] = 104; //confirmed 
  time[14] = 37; //confirmed 
  time[15] = 49; //confirmed
  time[16] = 90; //confirmed 
  time[17] = 42; //confirmed
  time[18] = 133; //confirmed
  time[19] = 6; //confirmed
  time[20] = 111; //confirmed 
  time[21] = 60; //confirmed
  time[22] = 142; //confirmed 
  time[23] = 18; //confirmed
  time[24] = 22; //confirmed
  time[25] = 59; //confirmed
  time[26] = 113; //confirmed 
  time[27] = 60; //confirmed
  time[28] = 49; //confirmed 
  time[29] = 119; //confirmed
  time[30] = 4; //confirmed 
  time[31] = 48; //confirmed 
  time[32] = 13; //confirmed 
  time[33] = 17; //confirmed 
  time[34] = 82; //confirmed 
  time[35] = 90; //confirmed  
  time[36] = 101; //confirmed 
  time[37] = 26; //confirmed 
  time[38] = 125; //confirmed 
  time[39] = 43; //confirmed 
  time[40] = 31; //confirmed 
  time[41] = 72; //confirmed 
  time[42] = 75; //confirmed 
  time[43] = 63; //confirmed 
  time[44] = 72; //confirmed 
  time[45] = 22; //confirmed 
  time[46] = 51; //confirmed 
  time[47] = 49; //confirmed 
  time[48] = 119; //confirmed 
  time[49] = 22; //confirmed 
  time[50] = 47; //confirmed 
  time[51] = 119; //confirmed 
  time[52] = 65; //confirmed 
  time[53] = 47; //confirmed 
  time[54] = 62; //confirmed 
  time[55] = 59; //confirmed 
  time[56] = 59; //confirmed 
}

void draw() {
  if (myPort.available() > 0) {   // If data is available
    val = myPort.read();          // read it and store it in val
  }
  if (val == '1') {
    intro = 1;
  }
  
  if (val == '0') {
    intro = 0;
    lastRun = 0;
    countTime = 0;
    videoTime = 0;
  }

  if (intro == 1) {    // If the serial value is 1   
    if (lastRun == 0) {
      link("file://localhost/Users/kimlewis/Documents/UW%20Work/07_Spring%20Qtr%202012/Art%20387_Physical/06_Final%20Code/pauseloading.html"); // 11 seconds
      println("intro");
    }

    if (lastRun % 60 == 0) {
      countTime += 1;
      println(countTime);
    }  

    if (countTime <= 150) {          
      int randInt = int(random(56));

      if (lastRun % 60 == 0) {

        if (countTime == videoTime || countTime == 13) {  
          String yayPuppies = puppy[randInt];
          videoTime = ((time[randInt]) + (countTime));
          println("video time" + ' ' + videoTime);
          println("yay");
          link (yayPuppies, "_self");
        }
      }
    }

    if (countTime >= 150) {

      if (lastRun % 60 == 0) {

        if (countTime == videoTime) {
          link("file://localhost/Users/kimlewis/Documents/UW%20Work/07_Spring%20Qtr%202012/Art%20387_Physical/06_Final%20Code/completescreen.html");
          println("we have reached the time");
        }
      }
    }

    lastRun += 1;
  }

  val = myPort.read();
}

*** In the array listed above, each link is calling a file on my computer which contains an embed code for the specified video. Here is an example of what one of those embed codes looks like:

<iframe id="ytplayer" type="text/html" width="1024" height="768"
src="http://www.youtube.com/embed/F8sIG9Q4aak?autoplay=1&controls=0&modestbranding=1&rel=0&showinfo=0"
frameborder="0" allowfullscreen>

Previous Unsuccessful Attempts

As much as the successes of a project are important, we thought we would also list some of the things we tried before reaching our final project and why they didn't work for us.


  • Originally we wanted to set up a playlist on youtube that could be embedded into a single html file. The problem with this is YouTube has made it impossible to alter that code or include code to play those videos at random since they have removed their randomize feature. We researched as much as we possibly could and found nothing on how to accomplish this
  • Before we considered screen sharing, we were going to connect the Ipad directly to the Arduino and use Processing.js to run our sketch directly off of the Ipad. There was only one tutorial online that we found for accomplishing this without purchasing the Red Park Serial Cable but it was unclear. We found a gadget which is helpful for connecting the Arduino to the Ipad, which is the Ipad Camera Connection Kit, however it would not let us send serial data which is necessary for our project.
  • For getting the videos onto the Ipad we also considered using a bluetooth usb, but they were either too large, too expensive or too ineffective. We also considered purchasing a bluetooth shield for the Arduino, but it would have been a lot of added cost and wouldn't have shipped in time for our project. 
  • We originally tried another screen sharing program called iTap VNC which was absolutely horrible with trying to figure out setup.