0

Summary

Jump! is a fun and competitive game where you compete with rival dorms for bragging rights.  Located in the common room of an undergraduate dorm, it has a matching pair in the neighboring rival dorm.  When you're walking through the common room and see your Jump! display flashing, it means that one of your competitors is doing jumping jacks on the paired device and is racking up points.  Hop on your Jump! pad to complete more jumping jacks and beat the competition.

0
Jump! Designing for the Internet of Things
Anna Krakora - https://www.youtube.com/watch?v=awrU9LzAi3k
0

Intention

 CMU is known to have a diverse, hardworking student body and an intense atmosphere. In some situations, this can lead to students feeling isolated because they focus so much on their coursework. People know that active study breaks can help to do more productive work over longer periods of time, but students often need more motivation to leave their seats and take a break. Our team’s goal is to great a connected device that can encourage students to take quick, fun, and active study breaks that will both reduce their stress and connect to their community. The team will focus on the setting of undergraduate dorm first-floor common areas because this is a semi-public and multi-functional space used for studying, hanging out, eating, and waiting for friends. This space also sees a large quantity of foot traffic because students must pass through the common area to reach their individual rooms. Undergraduate dorms are also known for the rivalries between different buildings, and so the team will focus on creating a device that will add to the competition between dorms and enhance the rivalry.    

1

Community

This team is defining the community as CMU undergraduates who live in dorms. This community is rather large and often looking to connect with others while adjusting to their new living spaces or filling the time between classes. There are about 7000 undergraduates at CMU who come from all over the world (1). About 65%, or 4500, of these students live in university housing (2). The first year of living in a dorm is a big adjustment for most people because they are busy, multi-use spaces without much privacy. Common rooms on the first floors of buildings are gathering places were people study, hang out, play video games, watch movies, or wait for others. People often pass through this space looking for something to do between other activities.

CMU is also known for its intense atmosphere and focus on academic success. Students seem to need to be reminded to take breaks and take care of themselves. While on-campus resources provide some reminders to be active, there’s no consistent activity for undergrads to take a quick, active study break in their dorm. This project will begin looking at the conceptualization and prototyping of an internet-connected device that encourages quick, active breaks while encouraging connections between students living in dorms.

  1. https://admission.enrollment.cmu.edu/pages/undergraduate-admission-statistics

  2. https://admission.enrollment.cmu.edu/pages/ntia-campus-life

  3. https://www.cmu.edu/wellness/

0

Inspiration and Context

The first precedent is a subway station in Moscow that allows passengers to pay for the ride in squats (1). It started during the Sochi Olympic games as a way to bring sports to more people. To get a free ticket, a passenger has to complete 30 squats in two minutes in front of the ticket machine, which counts the squats for the passenger. In videos of crowds using this device, the exercise brought smiles to the passengers doing the exercise and also to onlookers. Friends challenged each other to do the squats and some passengers upped the ante by doing one-legged squats or more squats in less time (2). Generally, the interaction was met with enthusiasm by users and onlookers who seemed to enjoy the novelty and silliness of the interaction. Not only did the device encourage passengers to take a quick break with exercise, but it also created a fun scenario with smiles and laughter that encouraged interactions with others in the community. Our team will build off of this precedent by also making a small station for exercising in place that is located in a public area to create a funny moment for the participants and onlookers. The sensing method will be different to accommodate the technology available in class, and we will connect matching devices through the Particle cloud in order to encourage competition between different parts of the undergraduate community.

The second precedent is the Underarmour online fitness tracker app (3). Many technologies exist that allow users to connect and compete with friends through an app. The apps collect and share fitness data between friend groups and can motivate users to keep active for some time, but the novelty of the experience quickly wears off. The app cannot display data in physical space because it is only displayed on phone screens. This precedent shows one way of displaying fitness data and competitions that can motivate people to exercise more but does not have a direct physical link to the activity. The app can send push notifications to remind someone to exercise, but that is the limit to the interaction. The team would like to build off of this idea of sharing fitness data between people through an internet connection while building this connection through a physical game space and physical display. A physical display of information will act as the attention-grabbing motivation for passerbyers to stop and play the game. It will add an element of entertainment to the interaction to keep players and their friends interested.

The third precedent is an arcade game called Jumping Jackpot which is an arcade game where players try to match their jumps to flashing lights in order to win points. The game is not a connected device, but it is an example of how arcade games draw the attention of players and encourage them to complete a task. We will build off of the style of the game but change it to fit the environment of a dorm. Our device must be interesting enough to draw the attention of students looking for something to do while also not irritation people who do not want to play the game.  This balance will be important to the user interaction and if they accept or decline the invitation to play the game.

  1. https://www.forbes.com/sites/andrewbender/2013/11/14/moscow-subway-station-lets-passengers-pay-fare-in-squats/#318ccf76b0d8

  2. https://www.wired.com/2013/11/squats-train-ticket/

  3. https://www.myfitnesspal.com/

  4. https://www.youtube.com/watch?v=WKx_hMOP9KI

0

Process

The team used a human-centered design process to generate ideas and decide on a concept.  We began by researching the needs of the community and prior work done on this topic.  With the user's needs in mind, we began brainstorming different ways to solve the problem.  We narrowed down the project's focus by eliminating options that we could not execute in the timeframe of the project and with the materials provided by the course.  One challenge the team faced was figuring out how to display scores and player status in a physical, interactive, and robust way.  We went with a combination of leds and an arrow controlled by a servo in order to make the score both eye-catching and easy to understand.  Another challenge was how to ensure a clear connection between the user and their community.  Our approach to this challenge was to intentionally place the devices in semi-public areas where there would be enough people to interact with but not enough to prevent use for fear of embarrassment.  We also chose the location to capitalize on existing connections and rivalries between groups of students.    

1

Conceptual Design 

 Jump! is a fun and competitive game designed to encourage CMU students to take quick and active study breaks, reducing their stress and encouraging connections with others in their community.  It's an internet-connected, life-size, interactive device that allows students to play games against people across campus with real-time, physical feedback on the status of both players.  

Located in the common room of an undergraduate dorm, it grabs students' attention as they walk through the space.  Flashing lights on the Jump! display means that your competitor in a rival dorm is doing jumping jacks and racking up points on their device.  The player then hops on their Jump! pad to complete more jumping jacks and beat the competition.

Below is an illustration of this concept.

0

Storyboard

1

Prototyping

In this device, a force sensor in the floor mat detects whether or not the player has completed a jump.  The display, which shows if you or the other player has more jumps, shows the score with a combination of lights and an arrow.  When you jump, your display lights up and the arrow moves towards your side of the display.  If the other player is winning, the arrow moves towards their side of the display.  You win by completing more jumping jacks than your competitor and moving the arrow all the way towards your side of the scoreboard.  

The first step in prototyping was to create a small scale version of the device to make sure that the force sensor and servo could interact with each other as the team intended.  

We then worked on prototyping better ways to display the score.  This is when we included the led strip and different types of arrows.

The particles were then connected through the particle cloud so that the displays show which player has the most recorded jumps.

Finally, all of the different components of the system were combined for a final round of testing and debugging.

1

First Prototype

Small scale sensing pad and score display.

0
0

Iterating with sensors and displays

0

Connecting responses through the Particle cloud

0
0

Circuit Diagram

0
//Jumper code(applicable for both particle boards)
//Designing for IoT 12/12/2019
// Source for light strip code: https://learn.adafruit.com/rgb-led-strips/arduino-code
 
#define REDPIN A3
#define GREENPIN A4 
#define BLUEPIN A2
#define FADESPEED 5     // make this higher to slow down

//variables for controlling the light strip
int lastFade = 0;//the variable recording the time entering the light pattern function
int timeElapsed=0;//record the current time
int colorValue = 0; //the color we're going to assign to the light strip
int redValue = 0;
int blueValue = 0;
bool ifNotify=false;//controlling if the reminder is on

//these 10 variables are the time intervals for the light pattern when the player wins 
int timerDuration =500;
int timerDuration2 =1000; 
int timerDuration3 = 1500; 
int timerDuration4 = 2000; 
int timerDuration5 = 2500; 
int timerDuration6 = 3000;
int timerDuration7 = 3500; 
int timerDuration8 = 4000; 
int timerDuration9 = 4500;  
int timerDuration10 = 5000;

// Define a pin that we'll place the FSR on
// Remember to add a 10K Ohm pull-down resistor too.
int fsrPin = A0;
int fsrReading = 0;

int countChannel=0;//this variable is part of the counting logic

// Create a variable to store the LED brightness.
int ledBrightness = 0;

int d1 = 1000;//time interval for updating the servo
int highReading=1000;//thresholds of couting the pressure as one jump
int lowReading=100;

int jumpCount=0;//jump count for this player
String JC=" ";//medium to store received data from the other player 
int jumpCount2=0;//jump count for the other player


int servoPin = D6;
Servo myServo;
int servoPos = 90;//initial position of the servo(arrow)
int LastClick=0;//the variable recording the time entering the servo update function
int Subtraction=0;//updated angle of the servo

bool refreshtimeOrNot=false;//part of the logic to make sure cloud event is published once for each time
long lastPublishedAt = 0;// This value will store the last time we published an event
int publishAfter = 1000;// this is the time delay before we should publish a new event from this device
int now=0;//variable recording current time
bool otherSideOn=false;//variable indicating if the other player is there


bool updateOrNot=true;//controlling if we want to update the servo
//(part of the logic to prevent the outdated cloud data from affecting the servo)
int lastUpdate=0;//last time we enable servo update
int updateDelay=3000;//the time span we want to lock the update

void setup() {
 // Set up the LED for output

  pinMode(REDPIN, OUTPUT);
  pinMode(GREENPIN, OUTPUT);
  pinMode(BLUEPIN, OUTPUT);

  Particle.variable("force", &fsrReading, INT);
  Particle.variable("count", &jumpCount, INT);
  Particle.variable("count2", &jumpCount2, INT);
  Particle.variable("Subtraction", &Subtraction, INT);
  Particle.variable( "Last", LastClick);
  Particle.variable( "now", now);
  Particle.variable( "ledBrightness", ledBrightness);
  Particle.variable( "otherSideOn", otherSideOn);
   
   // attaches the servo on the D6 pin to the servo object
  myServo.attach(servoPin);
 
  //cloud function for testing
   Particle.function("servo", servoControl);
   Particle.function("updateservo", outerUpdate2);
   Particle.function("resetCount", resetCount);
   
    
  Particle.variable(  "servoPos" , &servoPos , INT );// Keep a cloud variable for the current position
  Particle.subscribe(  "diot/2019/paired/jumper/update" , handleSharedEvent );//receiving jumpcount data from the other player
   

  Particle.function( "beginRemind", reminder);//a function that enables light strip pattern when this player wins
   
   


   
}

void loop() {
  
 // Use analogRead to read the photo cell reading
  // This gives us a value from 0 to 4095
  fsrReading = analogRead(fsrPin);

  // Map this value into the PWM range (0-255)
  // and store as the led brightness
  ledBrightness = map(fsrReading, 200, 4095, 0, 100);
if(fsrReading<200){
    ledBrightness=0;//prevent the light strip from blinking when there is low pressure on FSR
}

  
  
  if(ifNotify==false){//when we don't want the light strip to show the winning pattern
  lastFade=millis();
  if(otherSideOn){//when the other player is there, show the mixed color on the light strip
  analogWrite(GREENPIN, ledBrightness);
  analogWrite(REDPIN, ledBrightness);
  analogWrite(BLUEPIN, ledBrightness);
  }else{//otherwise we only show green light(or one of the RGB color)
  analogWrite(GREENPIN, ledBrightness);
  analogWrite(REDPIN, 0);
  analogWrite(BLUEPIN,0);
  }
  }else{//when we need the light strip to show the winning pattern
       reminder("");//turn on the winning pattern
  }

  
  //part of the jump counting logic, we need to detect a high to low pressure fluctuation to count as one jump
  if(countChannel==0){
  Channel0();//enter this function when detecting high pressure
  }else if(countChannel==1){
  Channel1();//enter this function when detecting low pressure
  }
  
    //final calculated angle for the servo representing the substraction between the jump counts of 2 players
  Subtraction=90-(jumpCount-jumpCount2)*4;

 
  // update the servo
 outerUpdate();
 
 if(Subtraction<10){//when this player is winning
      updateOrNot=false;//stop updating jump count data from the other player to prevent servo's angle reset failure
    resetCount(" ");//reset the servo
      ifNotify=true;//enable the winning pattern of the light strip
     
 
     
 }else if(Subtraction>170){//when this player loses
 
    resetCount(" ");//reset the servo
   
 }
 
if(updateOrNot==false){//when not updating the servo
    
    lockUpdate();
    
}else{
    
     lastUpdate=millis();//update the timer
}
 
 
}

void Channel0(){
   if(fsrReading>highReading){//when detecting high pressure
       countChannel=1;//entering next channel
   }
}
void Channel1(){
    if(fsrReading<lowReading){//when detecting low pressure after previous high reading
          countChannel=0;//switch back to the previous channel
          jumpCount++;//add up to the jump count
    }
}



void outerUpdate(){//when we want to update the servo
    now=millis();
    
     if(refreshtimeOrNot==false)
    {//if it's false, run the following code for once
        
        LastClick=millis();//initialize the timer of the update function
        refreshtimeOrNot=true;//make sure the codes here only run once each time
    }
    
   
     
 if(now>LastClick+d1){//run these lines every d1 milliseconds
  publishMyEvent();//send the jumpcount of this player to the other player
  outerUpdate2(" ");//update the servo
  LastClick=now;//record the last update time
  }
    
}


int outerUpdate2(String command){//function for servo update
 servoControl( String(Subtraction));
     return 1;
}




int servoControl(String command)//function for servo update
{
    // Convert
   int newPos = command.toInt();
   // Make sure it is in the right range
   // And set the position
   servoPos = constrain( newPos, 9 , 171);

   // Set the servo
   myServo.write( servoPos );
  
   // done
   return 1;
}



void publishMyEvent()//publish event when telling the other friend the person using this board is available
{
  // Remember that a device can publish at rate of about 1 event/sec,
  // with bursts of up to 4 allowed in 1 second.
  // Back to back burst of 4 messages will take 4 seconds to recover.
  // So we want to limit the amount of publish events that happen.

  // check that it's been 10 seconds since our last publish
  
  
  if( lastPublishedAt + publishAfter < millis() )
  {
      // Remember our subscribe is matching  "db2018/paired/"
      // We'll append the device id to get more specific
      // about where the event came from

      // System.deviceID() provides an easy way to extract the device
      // ID of your device. It returns a String object of the device ID,
      // which is used to identify your device.

      String eventName = "diot/2019/paired/jumper/update" + System.deviceID();

      // now we have something like "diot/2019/paired/0123456789abcdef"
      // and that corresponds to this devices info

      // then we share the jump count of this player out
      Particle.publish( eventName, String(jumpCount));

      // And this will get shared out to all devices using this code

      // we just published so capture this.
      lastPublishedAt = millis();
  }

}


void handleSharedEvent(const char *event, const char *data)//when receiving function that the the other friend is available, execute the following codes
{
    // Now we're getting ALL events published using "db2018/paired/"
    // This includes events from this device.
    // So we need to ignore any events that we sent.

    // Let's check the event name
    String eventName = String( event ); // convert to a string object
    // This gives us access to a bunch of built in methods
    // Like indexOf()
    // Locates a character or String within another String.
    // By default, searches from the beginning of the String,
    // but can also start from a given index,
    // allowing for the locating of all instances of the character or String.
    // It Returns: The index of val within the String, or -1 if not found.

    // We can use this to check if our event name contains the
    // id of this device

    String deviceID = System.deviceID();

    // device id = 0123456789abcdef
    // event = "diot/2019/paired/0123456789abcdef"

    if( eventName.indexOf( deviceID ) != -1 ){
      // if we get anything other than -1
      // the event came from this device.
      // so stop doing stuff
      return;
    }else{ // otherwise do your stuff to respond to
    // the paired device here
        
         JC=data;//use JC to transfer the received data to string
        if(JC!="reset"){ //if not informed to reset angle
            if(updateOrNot==true){//and when the update is not locked
          
           jumpCount2=JC.toInt();//accept the other player's jump count
            }
           
           if(jumpCount2>=1 ){//when there is another player out there
               otherSideOn=true;
           }
           
           
        }else{//when this board is told by the other board to reset
            
            //reset the servo
            JC="0";
            jumpCount=0;
            jumpCount2=0;
            updateOrNot=false;//do not receive other oudated data to prevent reset failure
        }
        
        
    }

}


int resetCount(String command)
{
    //reset this servo
    JC="0";
    jumpCount=0;
    jumpCount2=0;
  String eventName = "diot/2019/paired/jumper/update" + System.deviceID();
  Particle.publish( eventName, "reset");//tell the other board to reset as well
  
  return 1;
}




void lockUpdate(){//lock the servo from updating
   if (lastUpdate<millis()-updateDelay){//the lock will continue for a while
       
       updateOrNot=true;
       
       
   }
    
    
}








int reminder(String anystr){
    //enbale winning pattern for the light strip(not a smart way to do it but a quick and dirty way to get the job done)
    
    
    
     timeElapsed = millis() -lastFade; //let the timer begin from 0 
    
    if( timeElapsed < timerDuration ){//First 500 millisec: turn blue
        
        int colorValue = map( timeElapsed, 0, timerDuration, 0 , 255  );
        int blueValue = colorValue;
        analogWrite(BLUEPIN, blueValue); 
      
        
        
    }else if(timeElapsed < timerDuration2){//Middle 10 minutes: It should slowly transition to red over the course of the next 500 millisec
        
          colorValue = map( timeElapsed,  timerDuration, timerDuration2, 0 , 255  );
          int redValue = colorValue;
          blueValue = 255 - colorValue;
          analogWrite(BLUEPIN, blueValue); 
          analogWrite(REDPIN, redValue); 
 
    
        
    }  else if(timeElapsed < timerDuration3){//repeat the process
        
          colorValue = map( timeElapsed,  timerDuration2, timerDuration3, 0 , 255  );
          blueValue = colorValue;
          redValue = 255-colorValue;
           analogWrite(BLUEPIN, blueValue); 
   analogWrite(REDPIN, redValue); 

        
     }else if(timeElapsed < timerDuration4){//repeat the process
        
          colorValue = map( timeElapsed,  timerDuration3, timerDuration4, 0 , 255  );
         redValue = colorValue;
          blueValue = 255 - colorValue;
          analogWrite(BLUEPIN, blueValue); 
          analogWrite(REDPIN, redValue); 
  
        
     }else if(timeElapsed < timerDuration5){//repeat the process
        
          colorValue = map( timeElapsed,  timerDuration4, timerDuration5, 0 , 255  );
           blueValue = colorValue;
          redValue = 255-colorValue;
           analogWrite(BLUEPIN, blueValue); 
   analogWrite(REDPIN, redValue); 
  
        
     }else if(timeElapsed < timerDuration6){//repeat the process
        
          colorValue = map( timeElapsed,  timerDuration5, timerDuration6, 0 , 255  );
           redValue = colorValue;
          blueValue = 255 - colorValue;
          analogWrite(BLUEPIN, blueValue); 
          analogWrite(REDPIN, redValue); 
  
        
     }else if(timeElapsed < timerDuration7){//repeat the process
        
          colorValue = map( timeElapsed,  timerDuration6, timerDuration7, 0 , 255  );
                blueValue = colorValue;
          redValue = 255-colorValue;
           analogWrite(BLUEPIN, blueValue); 
   analogWrite(REDPIN, redValue); 
  

  
        
     }else if(timeElapsed < timerDuration8){//repeat the process
        
          colorValue = map( timeElapsed,  timerDuration7, timerDuration8, 0 , 255  );
            redValue = colorValue;
          blueValue = 255 - colorValue;
          analogWrite(BLUEPIN, blueValue); 
          analogWrite(REDPIN, redValue); 
  

     }else if(timeElapsed < timerDuration9){//repeat the process
        
          colorValue = map( timeElapsed,  timerDuration8, timerDuration9, 0 , 255  );
               blueValue = colorValue;
          redValue = 255-colorValue;
           analogWrite(BLUEPIN, blueValue); 
   analogWrite(REDPIN, redValue); 
  

  

     }else if(timeElapsed < timerDuration10){// fade out to off.
        
          colorValue = map( timeElapsed,  timerDuration9, timerDuration10, 0 , 255  );
           
blueValue = 255-colorValue;
           analogWrite(BLUEPIN, blueValue); 
   
  

  

     }
    
    
    else{//turn off the lights when the routine finishes
             analogWrite(REDPIN, 0); 
             analogWrite(GREENPIN, 0); 
             analogWrite(BLUEPIN, 0);  
      ifNotify=false;//end the winning pattern
      otherSideOn=false;//refresh the other player's state,assume they leave next round(if they do not, this boolean will be true again)
    }
    
    return 0;
        
    
    
}
Click to Expand
0

Outside and inside of the sensing unit

0

Backing to prevent sliding

0

Final Prototype Demonstration

0
Jump! Designing for the Internet of Things
Anna Krakora - https://www.youtube.com/watch?time_continue=2&v=awrU9LzAi3k&feature=emb_logo
0

Next Steps

1. Test with users in context and incorporate feedback.

3. Conceal wires and circuits.  Correct current draw issue where activating the servo dims the lights. Finalize scorekeeping code and game reset function.

4. Make a stable podium or installation for each unit.  Make clear signage so the user understands the game at a glance. 

5. Adapt the game to include more than two players and connect a greater community.



0

References

Source for light strip code: https://learn.adafruit.com/rgb-led-strips/arduino-code  

All other references are cited in that topic's section.

x
Share this Project

Courses

49713 Designing for the Internet of Things

· 16 members

A hands-on introductory course exploring the Internet of Things and connected product experiences.


Focused on
About

~

Created

December 5th, 2019