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.

Wednesday, June 6, 2012

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...


CODE FOR BOARD ONE (NO MP3 SHIELD):

/* Smart Shelf Program:
Uses an FSR sensor to sense the presence of an object. When an object is removed 
from the shelf, the timeLED lights up to indicate that the object is gone (mostly 
for test purposes). After 15 seconds, if the object is still missing, a second light
(weightLED) lights up to indicate that it's time to return the object.

Charissa Lind, May 2012
For a project by Charissa Lind and Erin Murphy
Including Metro library by Thomas Ouellet Fredericks and Easy Transfer Library by Bill Porter*/



#include <Metro.h> //Call Metro (timer) library
#include <EasyTransfer.h> //Call EasyTransfer Library

//create object
EasyTransfer ET;

struct SEND_DATA_STRUCTURE{
  //for EasyTransfer
  //put your variable definitions here for the data you want to send
  //THIS MUST BE EXACTLY THE SAME ON THE OTHER ARDUINO
  int timeup; //variables used to send signals that a particular song should play
  int timeupB;
  int timeupAB;
  int timeupBC;
  int timeupC;
  int timeupAC;
  int timeupABC;
  int stopmusic;
};

//give a name to the group of data (EasyTransfer)
SEND_DATA_STRUCTURE mydata;

//variables for sensor A 
const int fsrAnalogPin = A0; //Sensor A goes to analog pin 0.
int fsrReading; //variable that will be used to store values from FSR A.
int aHigh; //will be the highest acceptable reading when the toy is in the shelf.
int aLow; //will be the lowest acceptable reading when the toy is in the shelf.
const int timeLED = 11;  //purple LED

//variables for sensor B 
const int fsrAnalogPinB = A1; //Sensor B goes to analog pin 2
int fsrReadingB; //variable that will be used to store values from FSR B.
int bHigh;
int bLow;
const int timeLEDB = 10; //blue LED

//variables for sensor C/button C
const int fsrAnalogPinC = 8; //this is a digital pin, since our FSR broke, but I kept the label consistent.
int fsrReadingC;
int pinStateC = 0;
int cHigh; //used for an FSR sensor...it won't show up in this code, because our sensor broke and we used a button instead
int cLow; //ditto.
const int timeLEDC = 7;  //green LED

//variables for button
const int buttonPin = 2;
int buttonState= 0;

//variables for light sensor
const int lightSensor=A2;
int light= 0;

int metroPot=A3; //potentiometer on analog pin 3 (will be used to set time)
int metroVal= 15000; //by default, toy can be gone for 15 seconds (for real use, as opposed to demo use, this would be much longer)
int metroInt = 15000; //will change with the potentiometer's position.  
//variables for timer
Metro ledMetro = Metro(metroInt, 1); //Sensor A has an interval of 15 seconds.
//this interval will be used when waiting for the object to return.
Metro ledMetroB = Metro(metroInt, 1); //Sensor B has an interval of 15 seconds.
//this interval will be used when waiting for the object to return.
Metro ledMetroC = Metro(metroInt, 1); //Sensor C has an interval of 15 seconds.
//this interval will be used when waiting for the object to return.

void reset(){//Sets up a command that can be called in loop() and will reset Metro's timer.
}

void setup()
{
  Serial.begin(9600); //set up a serial output for testing 
  pinMode(timeLED, OUTPUT); //set up the LED on pin 11 
  pinMode(timeLEDB, OUTPUT); //set up the LED on pin 10
  pinMode(timeLEDC, OUTPUT); //set up the LED on pin 7
  pinMode(fsrAnalogPinC, INPUT); //the button on pin 8 is an input
  pinMode(buttonPin, INPUT); //the button on pin 2 is an input 
  
  //start the EasyTransfer library, pass in the data details and the name of the serial port. Can be Serial, Serial1, Serial2, etc.
  ET.begin(details(mydata), &Serial);
  randomSeed(analogRead(0));
}

void sensorA(){   //this section will set up actions for sensor A
  metroVal = analogRead(metroPot); //get reading from rotary potentiometer on pin A3
  metroInt = map(metroVal, 0, 1023, 5000, 30000); //map that value to a time value (in milliseconds)
  light= analogRead(lightSensor); //get reading from photocell
  if(light<500){ //if the light is off, turn everything off.
    Serial.println("off");
   digitalWrite(timeLED, LOW); //all lights off
   ledMetro.reset(); 
   mydata.stopmusic = 1;
   ET.sendData();
  }
  else{ 
  fsrReading= analogRead(fsrAnalogPin); //get reading from sensor A
  if (fsrReading>aLow && fsrReading<aHigh){ //if the object is present
   digitalWrite(timeLED, LOW); //all lights off 
   ledMetro.reset(); //reset the timer
   mydata.timeup = 0; //music off
   mydata.timeupAB = 0;
    ET.sendData(); //send "off" signal to Mp3 shield
   }
     
  else{ //if object is not present
    
    digitalWrite(timeLED, HIGH); //turn on time LED to indicate program is waiting for object
 mydata.timeup = 0; //music is still off.
 mydata.timeupAB = 0;
  ET.sendData();
  
    if(ledMetro.check() == 1) //if 15 seconds have passed (and no object is present)
    {
     if ((fsrReadingB<bLow || fsrReadingB>bHigh) && (fsrReading<aLow || fsrReading>aHigh) && (pinStateC == LOW)){ 
       //if A,B, and C are missing.
       digitalWrite(timeLED, HIGH);
       mydata.timeupABC= 1; //play "all toys are missing" sound
       mydata.timeupAB=0;
       mydata.timeupAC=0;
       mydata.timeupBC=0;
       mydata.timeup=0;
       ET.sendData();//send the signal to play "all toys are missing" sound
     }  
     
     if ((fsrReadingB<bLow || fsrReadingB>bHigh) && (fsrReading<aLow || fsrReading>aHigh) && (pinStateC == HIGH)){
       //if A and B are missing.
       digitalWrite(timeLED, HIGH);
       mydata.timeupAB = 1; //send signal to play "Blue and Purple are missing" sound
       mydata.timeup = 0;
       mydata.timeupB = 0;
       mydata.timeupBC = 0;
       mydata.timeupABC = 0;
       ET.sendData();
      }      
      
      if ((fsrReadingB>bLow && fsrReadingB<bHigh) && (fsrReading<aLow || fsrReading>aHigh) && (pinStateC ==LOW)){
       //if A and C are missing
       digitalWrite(timeLED, HIGH);
       mydata.timeupAC = 1;
       mydata.timeup = 0;
       mydata.timeupC = 0;
       mydata.timeupAB = 0;
       mydata.timeupABC = 0;
       ET.sendData();
      }
      
      if ((fsrReading<aLow || fsrReading>aHigh) && (fsrReadingB>bLow && fsrReadingB<bHigh) && (pinStateC ==HIGH)){
       digitalWrite(timeLED, HIGH); //keep the timer LED on
       //if only A is missing.
       mydata.timeup= 1;
       mydata.timeupABC= 0;
       mydata.timeupAB = 0;
       mydata.timeupBC = 0;
       mydata.timeupAC=0;
       ET.sendData();
      }            
  }
   else{ //otherwise, don't tell the Mp3 shield to play anything.
       mydata.timeup=0;
       mydata.timeupAB = 0;
       mydata.timeupABC= 0;
       mydata.timeupAC=0;

        ET.sendData();
    }
  
  }
  }
}

void sensorB(){//the same thing, but set up with variables for sensor B
  if(light<500){
    Serial.println("off");
   digitalWrite(timeLEDB, LOW); //all lights off
   ledMetroB.reset();
   mydata.timeupB = 0;
   ET.sendData();
  }
  else{
  //Begin second sensor
  fsrReadingB= analogRead(fsrAnalogPinB);
  if (fsrReadingB>bLow && fsrReadingB<bHigh){
   digitalWrite(timeLEDB, LOW); 
   ledMetroB.reset();
   mydata.timeupB = 0;
   mydata.timeupBC = 0;
   ET.sendData();
   }
     
  else{
    
    
    digitalWrite(timeLEDB, HIGH);
    mydata.timeupB = 0;
    mydata.timeupBC =0;
    mydata.timeupAB = 0;
    ET.sendData();
  
    if(ledMetroB.check() == 1)
    { 
      if ((fsrReadingB<bLow || fsrReadingB>bHigh) && (pinStateC==LOW) && (fsrReading>aLow && fsrReading<aHigh)){
      digitalWrite(timeLEDB, HIGH);
   mydata.timeupBC = 1;
   mydata.timeupB = 0;
   mydata.timeupAB = 0;
   mydata.timeupABC = 0;
   ET.sendData();
      }
  
      
      if((fsrReadingB<bLow || fsrReadingB>bHigh) && (pinStateC == HIGH) && (fsrReading>aLow && fsrReading<aHigh)){
        digitalWrite(timeLEDB, HIGH);
   mydata.timeupB = 1;
   mydata.timeupAB = 0;
   mydata.timeupBC = 0;
   mydata.timeupABC = 0;
   ET.sendData();
      }
     }
  }
 }
}

void sensorC(){   //same as above, but for sensor/button C.
  metroVal = analogRead(metroPot);
  metroInt = map(metroVal, 0, 1023, 5000, 30000);
  light= analogRead(lightSensor);
  if(light<500){
    Serial.println("off");
   digitalWrite(timeLEDC, LOW); 
   ledMetroC.reset();
   mydata.timeupC = 0;
   ET.sendData();
  }
  else{
  pinStateC = digitalRead(fsrAnalogPinC); 
  if (pinStateC == HIGH){ 
   digitalWrite(timeLEDC, LOW); 
   ledMetroC.reset(); 
   mydata.timeupC = 0;
   mydata.timeupAC = 0;
   mydata.timeupABC = 0;
    ET.sendData();
   }
     
  else{ //if object is not present
    
    digitalWrite(timeLEDC, HIGH); 
 mydata.timeupC = 0;
 mydata.timeupAC = 0;
 mydata.timeupBC = 0;
 mydata.timeupABC = 0;
  ET.sendData();
  
    if(ledMetroC.check() == 1) 
    { 
       if ((pinStateC == LOW) && (fsrReading<aLow || fsrReading>aHigh) && (fsrReadingB>bLow && fsrReadingB<bHigh)){
      digitalWrite(timeLEDC, HIGH);
   mydata.timeupAC = 1;
   mydata.timeupC = 0;
   mydata.timeupABC = 0;
   mydata.timeupBC = 0;
   ET.sendData();
      }
      if ((pinStateC == LOW) && (fsrReading>aLow && fsrReading<aHigh) && (fsrReadingB>bLow && fsrReadingB<bHigh)){
      digitalWrite(timeLEDC, HIGH); 
   mydata.timeupC= 1;
   mydata.timeupAC = 0;
   mydata.timeupABC = 0;
   mydata.timeupBC = 0;
    ET.sendData();
      }  
  }  
  }
  }
}
  

void interval(); //for Metro library

void button (){ //this section sets up the button to calibrate the shelf on button press.
  if(light<500){
    Serial.println("off");
  }
  else{
   buttonState = digitalRead(buttonPin); //read push button state
  if (buttonState == HIGH) {     
      fsrReading= analogRead(fsrAnalogPin); //get reading from sensor A
      fsrReadingB= analogRead(fsrAnalogPinB); //get reading from sensor B
      aHigh= (fsrReading+ 150); //set high end of acceptable weight range for sensor A
      aLow= (fsrReading- 150); //set low end of acceptable weight range for sensor A
      bHigh= (fsrReadingB+ 150); //set high end of acceptable weight range for sensor B
      bLow= (fsrReadingB- 150); //set low end of acceptable weight range for sensor B
      
  } 
  }
}

void stopmusic(){ //this section sets up the shelf to stop playing music when the objects are all present.
  if ((pinStateC == HIGH) && (fsrReading>aLow && fsrReading<aHigh) && (fsrReadingB>bLow && fsrReadingB<bHigh)){
    mydata.stopmusic = 1;
    ET.sendData();
    ledMetro.reset();
    ledMetroB.reset();
    ledMetroC.reset();
  }
  else{
    mydata.stopmusic = 0;
    ET.sendData();
  }
}
  
void loop(){ // this is where Arduino actually runs through the commands above.
  button();
  ledMetro.interval(metroInt);
  ledMetroB.interval(metroInt);
  ledMetroC.interval(metroInt);
  sensorA();
  sensorB();
  sensorC();
  stopmusic();
  
  //send the data
  ET.sendData();
  delay(250);
  
  

  
}


CODE FOR BOARD TWO (MP3 SHIELD):

/* Smart Shelf Program Part 2:
Uses an FSR sensor to sense the presence of an object. When an object is removed 
from the shelf, the timeLED lights up to indicate that the object is gone (mostly 
for test purposes). After 15 seconds, if the object is still missing, a second light
(weightLED) lights up to indicate that it's time to return the object.

Charissa Lind, May 2012
For a project by Charissa Lind and Erin Murphy
Including Metro library by Thomas Ouellet Fredericks and Mp3 Shield Library and Easy Transfer Library by Bill Porter*/

#include <EasyTransfer.h>
#include <SPI.h>
#include <SdFat.h>
#include <SdFatUtil.h> 
#include <SFEMP3Shield.h>
//call necessary libraries

SFEMP3Shield MP3player; //create object

//create object
EasyTransfer ET; 

struct RECEIVE_DATA_STRUCTURE{
  //for EasyTransfer
  //put your variable definitions here for the data you want to receive
  //THIS MUST BE EXACTLY THE SAME ON THE OTHER ARDUINO
  int timeup; //variables used to send signals that a particular song should play
  int timeupB;
  int timeupAB;
  int timeupBC;
  int timeupC;
  int timeupAC;
  int timeupABC;
  int stopmusic;
};

//give a name to the group of data
RECEIVE_DATA_STRUCTURE mydata;

void setup(){
  Serial.begin(9600);
  MP3player.begin(); //start the shield
 //start the EasyTransfer library, pass in the data details and the name of the serial port. Can be Serial, Serial1, Serial2, etc. 
  ET.begin(details(mydata), &Serial);  
}

void musicA(){
     ET.receiveData(); //receive data from the other board
    if(mydata.timeup ==1 && mydata.timeupB !=1){ //if signal is received to play song for object A. (purple shelf)
      MP3player.playTrack(1);
      Serial.println (mydata.timeup);     
    }
  }

  
void musicB(){
     ET.receiveData();
    if(mydata.timeupB==1 && mydata.timeup !=1){
      MP3player.playTrack(2);    
    }
  }
  
  
void musicC(){
     ET.receiveData();
    if(mydata.timeupC ==1 && mydata.timeupB !=1 && mydata.timeup != 1){
      MP3player.playTrack(3);   
    }
  }
    
void musicAB(){
     ET.receiveData();
    if(mydata.timeupAB ==1 && mydata.timeupABC==0){
      MP3player.playTrack(4);      
    }
  }

  void musicAC(){
     ET.receiveData();
    if(mydata.timeupAC ==1 && mydata.timeupABC ==0){
      MP3player.playTrack(5);          
    }
  }
  
  void musicBC(){
     ET.receiveData();
    if(mydata.timeupBC ==1 && mydata.timeupABC==0){
      MP3player.playTrack(6);
    }
  }
  
  void musicABC(){
     ET.receiveData();
    if(mydata.timeupABC ==1){
      MP3player.playTrack(7);      
    }
  }
  
   void stopmusic(){
     ET.receiveData();
    if(mydata.stopmusic ==1){
      MP3player.stopTrack();       
    }
  }
  //Voila!


void loop(){ //run the functions above
   ET.receiveData();
   musicA();
   musicB();
   musicC();
   musicAB();
   musicAC();
   musicBC();
   musicABC();
   stopmusic();
   
   
  //you should make this delay shorter then your transmit delay or else messages could be lost
delay(10);
}

No comments:

Post a Comment