Transforming solitary waiting at bus stops into a collaborative and engaging experience through gameplay and ambient light.

0

Intention

The primary goal of our project is to create an environment that encourages interactions among strangers in public spaces.

One such space we identified is the bus stop. It is a place we visit multiple times every day. Most of our time is spent waiting for the bus. It can be very frustrating as it feels like a waste of time. However, it is unavoidable. While waiting for the bus, we encounter the same people almost every day, yet we do not interact with them or even acknowledge their presence. Instead, we occupy ourselves with our phones to avoid eye contact or any type of interaction.

Our project aims to transform solitary waiting at bus stops into a collaborative and engaging experience through game-play and ambient light.

0

Community

We are focusing on the community of bus users in Pittsburgh. More specifically the familiar strangers we encounter while waiting for the bus.

"Familiar strangers are people that we regularly observe but never interact with." - Stanley Milgram (1)

Unlike people that we know and the others who are complete strangers we encounter once and never see again, familiar strangers are somewhere in between. "You know each other - even if you don't really know each other." (2) With the people that we know, we are bound through social reciprocity; and with complete strangers, no such bond exists. However, with familiar strangers, since we encounter them regularly in familiar settings, they establish our connection to individual places (3). The relationship we have with familiar strangers is indeed real wherein both parties mutually agree to ignore each other without any implications of hostility.

Through our project, we aim to explore how might we break this pattern of ignorance to create a positive experience by facilitating interactions among this group of people in a non-intrusive way. We want to make people more aware of each other and their environment.

Some of the points that we must keep in mind while designing for this group of people are:

  • How might we generate interest among these people?
  • How can we create a safe space where people come forward to interact with the system and one another?

0

Inspiration and Context

Some of the projects that acted as inspiration and a guide for our project were:

21 Swings:  A musical installation in Montreal that plays certain melodies only through cooperation between the users. Thus, creating a sense of community and ownership of the space. 

Heated Bus Stop: A bus stop in Montreal that heats up when the people at the bus stop hold hands and touch the sensor to complete the circuit. Although it is a marketing campaign for Duracell, it spreads a positive message that "you are not alone". You are part of a much larger community and together you can spread warmth. At first, people look at the poster quizzically, but once they participate, you can see that they start smiling and have a fun experience while waiting for the bus.

Coca-Cola Friendship Machine: A tall vending machine that can be used only when people work together to reach the keypad. From the video we can see how the machine attracts a lot of attention, making people more aware of their everyday surroundings. Soon we see more and more people crowd around the machine looking at each other with surprise and broad smiles. We then see how people come together to buy soda from the machine. It shows how we can overcome any obstacle if we work together as a community and that no obstacle is big enough if we have each other. 

All these projects highlight how collaboration through a shared activity can bring people together even if they may not know each other, it has the ability to create a positive experience for people. Our project builds on this ideology. 

1
Key observations of bus riders' behaviors at the bus stops around CMU campus
0

Process

Our project started with the initial idea of wanting to make the CMU campus more lively. To create a space where students can relax and interact with one another. However, we wanted to share our project with the greater community of Pittsburgh and hence looked at other spaces. Once we identified bus stops as our creative space we moved on to ideating the experience.

Some of the themes that emerged from ideation were:

  • Interaction 
  • Collaboration 
  • Competition 
  • Exploration 
  • Safety 

Playing games was central to our idea since it facilitates interaction. While gaming can be competitive, we wanted to focus on collaboration as the primary method of interaction. We looked at the different ways in which we could incorporate the gaming experience at a bus stop.  

Since our idea revolved around collaboration rather than competition, we looked at ways in which people could come together to light up the bus stop stand with their combined efforts. We considered having bus stops light up differently across the city to reflect the character of the area and promote exploration. We also considered interactions between bus stops on opposite sides of the road. In all these instances, we faced a few challenges. 

  • How can we make it intuitive?
  • How can we clearly and easily communicate to the user the functionality of the system/game?
  • How can we make it interesting for the user to want to interact with our system not once but multiple times?

Our final concept design takes these challenges into consideration and works at creating a system that facilitates interactions in an intuitive, engaging and simplified manner.   

0

Conceptual Design 

Our system is installed at the bus stop, it comprises of an enchanted bus stop sign and multiple tiles embedded into the floor around the stand.

The sign is wrapped with LED lights to create an ambient effect. In its resting state, it glows white with two dots, one blue and one orange, running through it.

The tiles have footprints on them to indicate that the user must step on it. It ties into the fact that if there is an outline of a foot, it is a human tendency that we want to put our foot on it/stand there. This makes our system very intuitive. Once the user steps on it, the panel will glow either blue or orange to indicate the user's team color. At first, they may not fully understand the meaning of the color but it registers in their mind that they are either orange or blue.

The user then starts to move from foot to foot as a natural behavior. This prompts two changes to take place simultaneously. Firstly, the panel on the tile will start blinking based on the movement. This encourages the user to keep moving to try to discover what is happening. Secondly, the bus stop sign glows white and shows the points for each team in the form of LED lights glowing in the team's color. As the user increases the pace of their footsteps the LED count grows until a team wins. Having the team color on the tile seamlessly allows the user to make the connection between the color on the tile and sign.

When either team wins, the entire bus stop sign displays the winning team's color in a running pattern. 

0

Storyboard

The storyboard below shows the contexts and how community members interact with the BusCade.

0

Prototype

Prototype version 1.0:

Initially, after considering FSR (too unstable) and the push buttons (too fragile) as the sensors to be implemented in the tiles at the bus stop to detect motions of the bus riders, we decided to use pairs of panels covered by aluminium foil instead.

To test if the idea would work, we built a quick first prototype using cardboard and aluminium foil:

0

Prototype version 2.0:

Since prototype version 1.0 worked out, we proceeded to build a set of tiles with foam core boards and also prototyped the ambient bus stop display to communicate the status of the game-play. 

We tried several combinations of the signals used on the ambient bus stop display and the colors to be used (while avoiding traffic signal colors.)

0

Prototype version 3.0 (Final):

Using prototype version 2.0, we performed user testings and collected feedback from the TA and other teams.

Based on the feedback, we re-deigned the gamification part to make the rules clearer, aiming at communicating effectively with the user while remaining as non-intrusive as possible. And added the reset function so the game will automatically go back to the starting stage when it hasn't been stepped on for a while.

We also visited TechSpark for wood working to build more durable prototypes of the tiles.

0

The key outcomes of the project are summarized in the following two videos:

0
1. Product concept video: (Final, with captions)
0
2. Users having fun interacting with the prototypes:
0

Codes and circuits:

0
The Bus Stop Sign code
// Bus Stop Sign - version 4.0

// This #include statement was automatically added by the Particle IDE.
#include <neopixel.h>

// IMPORTANT: Set pixel COUNT, PIN and TYPE
#define PIXEL_COUNT 30
#define PIXEL_PIN D5
#define PIXEL_TYPE WS2812B

// Define color codes
#define WHITE 150,150,150
#define CYAN 10,150,70
#define PEACH 200,50,5

Adafruit_NeoPixel strip = Adafruit_NeoPixel( PIXEL_COUNT, PIXEL_PIN, PIXEL_TYPE );

int waitTime = 25; // number of the delay time
int i; // number of the LED

// The stepping frequency of team A (top)
int top_brightness = 0; 
// The stepping frequency of team B (bottom)
int bottom_brightness = 0;

void setup() {
    
    // initiate the LED strip
    strip.begin();
    strip.show();
    
    // for testing and debugging - set the progress data of team A
    Particle.function( "Set_Top_Brightness", set_top_brightness ); // top
    // for testing and debugging - set the progress data of team B
    Particle.function( "Set_Bottom_Brightness", set_bottom_brightness ); // bottom
    
    // listen to the progress data of team A from the tiles
    Particle.subscribe( "SpringPandaIsCallingMe", handleSharedEventTop ); // top
    // listen to the progress data of team B from the tiles
    Particle.subscribe( "AutumnPandaIsCallingMe", handleSharedEventBottom ); // bottom
}

void loop() {
    
    // if team A or team B wins, display the running effect
    if( top_brightness == 255 || bottom_brightness == 255 )
    {
        if( top_brightness == 255 )
        {
            show_all( PEACH, 25 );
        }
        else
        {
            show_all( CYAN, 25 );
        }
    }
    // if no team has won yet
    else
    {
        // if no one has stepped on the tile
        if( top_brightness == 0 && bottom_brightness == 0 )
        {
            show_prepare( WHITE, CYAN, PEACH, 125 );
        }
        // if someone has stepped on the tile
        else
        {
            // if only team A has stepped on the tile
            if( top_brightness > 0 && bottom_brightness == 0 )
            {
                show_top( CYAN );
                show_bottom( WHITE );
            }
            
            // if only team B has stepped on the tile
            if( bottom_brightness > 0 && top_brightness == 0 )
            {
                show_top( WHITE );
                show_bottom( PEACH );
            }
            
            // if both team has started playing
            if( top_brightness > 0 && bottom_brightness > 0 )
            {
                show_top( CYAN );
                show_bottom( PEACH );
            }
        }
    }
}

// make the top part of the bus stop sign display the progress of team A
void show_top( int R, int G, int B ) {
    
    int j = map( top_brightness, 0, 255, 0, 15 );
    
    for( i = 0; i < j; i++ )
    {
        strip.setPixelColor( 0, R, G, B );
        strip.setPixelColor( i, R, G, B );
        strip.show();
    }
    for( i = j; i < 15; i++ )
    {
        strip.setPixelColor( i, 150, 150, 150 );
        strip.show();
    }
}

// make the bottom part of the bus stop sign display the progress of team B
void show_bottom( int R, int G, int B ) {

    int j = map( bottom_brightness, 0, 255, 15, 30 );
    
    for( i = 15; i < j; i++ )
    {
        strip.setPixelColor( 15, R, G, B );
        strip.setPixelColor( i, R, G, B );
        strip.show();
    }
    for( i = j; i < 30; i++ )
    {
        strip.setPixelColor( i, 150, 150, 150 );
        strip.show();
    }
}

// before any team has started playing, display (running effect) cyan and peach color pixel on the top and bottom part of the bus stop sign
void show_prepare( int R, int G, int B, int R1, int G1, int B1, int R2, int G2, int B2, int waitTime )
{
    for( i = 0; i <= 15; i++ )
    {
        strip.setPixelColor( i - 1, R, G, B );
        strip.setPixelColor( i, R1, G1, B1 );
        
        strip.setPixelColor( i + 14, R, G, B );
        strip.setPixelColor( i + 15, R2, G2, B2 );
        
        strip.show();
        delay( waitTime );
    }
}

// if team A or team B wins, display the running effect on the bus stop sign, showing the color of the winning team
void show_all( int R, int G, int B, int waitTime )
{
    for( i = 0; i < 30; i++ )
    {
        strip.setPixelColor( i, R, G, B );
        strip.show();
        delay( waitTime );
    }
    
    for( i = 0; i < 30; i++ )
    {
        strip.setPixelColor( i, 0, 0, 0 );
        strip.show();
        delay( waitTime );
    }
}

// for testing and debugging - set the progress data of team A
int set_top_brightness( String arg )
{
    top_brightness = arg.toInt();
    
    return top_brightness;
}

// for testing and debugging - set the progress data of team B
int set_bottom_brightness( String arg )
{
    bottom_brightness = arg.toInt();
    
    return bottom_brightness;
}

// get the progress data of team A from the tiles
void handleSharedEventTop(const char *event, const char *data)
{
    set_top_brightness( data );
}

// get the progress data of team B from the tiles
void handleSharedEventBottom(const char *event, const char *data)
{
    set_bottom_brightness( data );
}
Click to Expand
0
The Bus Stop Sign circuit
Screen shot 2019 12 11 at 3.49.18 pm
0
The BusCade Tile code
// This #include statement was automatically added by the Particle IDE.
#include <neopixel.h>

//set up for NeoPixel
#define PIXEL_COUNT 7
#define PIXEL_TYPE WS2812
#define PIXELR1_PIN A1
#define PIXELR2_PIN A5
#define PIXELB1_PIN A3
#define PIXELB2_PIN A4
Adafruit_NeoPixel stripR1 = Adafruit_NeoPixel(PIXEL_COUNT, PIXELR1_PIN, PIXEL_TYPE);
Adafruit_NeoPixel stripR2 = Adafruit_NeoPixel(PIXEL_COUNT, PIXELR2_PIN, PIXEL_TYPE);
Adafruit_NeoPixel stripB1 = Adafruit_NeoPixel(PIXEL_COUNT, PIXELB1_PIN, PIXEL_TYPE);
Adafruit_NeoPixel stripB2 = Adafruit_NeoPixel(PIXEL_COUNT, PIXELB2_PIN, PIXEL_TYPE);

//define input pins for stepping tiles
int buttonPinR1L = D0;
int buttonPinR1R = D1;
int buttonPinB1L = D2;
int buttonPinB1R = D3;
int buttonPinR2L = D4;
int buttonPinR2R = D5;
int buttonPinB2L = D6;
int buttonPinB2R = D7;

long timeStartedAt = 0;
long timeElapsed = 0;
long now = 0;
long lastRecord = 0;

int frequencyR = 0;
int frequencyB = 0;

int stepBrightnessR = 0;
int stepBrightnessB = 0;

int frequencyR1LA = 0;
int frequencyR1LB = 0;
int frequencyR1RA = 0;
int frequencyR1RB = 0;

int frequencyB1LA = 0;
int frequencyB1LB = 0;
int frequencyB1RA = 0;
int frequencyB1RB = 0;

int frequencyR2LA = 0;
int frequencyR2LB = 0;
int frequencyR2RA = 0;
int frequencyR2RB = 0;

int frequencyB2LA = 0;
int frequencyB2LB = 0;
int frequencyB2RA = 0;
int frequencyB2RB = 0;

int frequencyAddA = 0;
int frequencyAddB = 0;

long lastPublishedAt = 0;

//the time delay before we should publish a new event from this device
int publishAfter = 100;

uint32_t b = stripB1.Color(10,150,70); //Cyan
uint32_t r = stripR1.Color(200, 50, 5); //Peach

void setup() {
    //setup input pins for stepping tiles
    pinMode( buttonPinR1L , INPUT_PULLUP);
    pinMode( buttonPinR1R , INPUT_PULLUP);
    pinMode( buttonPinB1L , INPUT_PULLUP);
    pinMode( buttonPinB1R , INPUT_PULLUP);
    pinMode( buttonPinR2L , INPUT_PULLUP); 
    pinMode( buttonPinR2R , INPUT_PULLUP);
    pinMode( buttonPinB2L , INPUT_PULLUP);
    pinMode( buttonPinB2R , INPUT_PULLUP);

    //setup NeoPixels for tile signal lights
    stripR1.begin();
    stripR1.show();
    stripR2.begin();
    stripR2.show();
    stripB1.begin();
    stripB1.show();
    stripB2.begin();
    stripR2.show();
}

void loop() {
    //count the steps
    howFrequent();
    
    //reset the game if not stepping for 10 seconds
    if(lastRecord==0){
        lastRecord = millis();
        frequencyAddA = frequencyR + frequencyB;
    }
    else if(now - lastRecord >= 10*1000){
        frequencyAddB = frequencyR + frequencyB;
        if (frequencyAddA == frequencyAddB){
            frequencyR = 0;
            frequencyB = 0;
            lastRecord = millis();
        }
        else{
            frequencyAddA = frequencyR + frequencyB;
            lastRecord = millis();
        }
    }
    
    //setup each team's NeoPixels for tile signal lights 
    for( int i = 0; i < stripR1.numPixels(); i++ ){
        stripR1.setPixelColor(i, r);
        stripR2.setPixelColor(i, r);
        stripB1.setPixelColor(i, b);
        stripB2.setPixelColor(i, b);
    }
    stripR1.show();
    stripR2.show();
    stripB1.show();
    stripB2.show();
    
    //count time
    now = millis();
}

void howFrequent(){
    if( timeStartedAt == 0 ){
        timeStartedAt = millis();
    }
        
    timeElapsed = millis() - timeStartedAt;
    
    //count the amount of steps using 3 seconds as a unit
    if( timeElapsed <= 3*1000 ){
        
	//Count frequency for team Red
        frequencyR1LA = digitalRead( buttonPinR1L );
        frequencyR1RA = digitalRead( buttonPinR1R );
        frequencyR2LA = digitalRead( buttonPinR2L );
        frequencyR2RA = digitalRead( buttonPinR2R );
        
        delay( 100 );
        
        frequencyR1LB = digitalRead( buttonPinR1L );
        frequencyR1RB = digitalRead( buttonPinR1R );
        frequencyR2LB = digitalRead( buttonPinR2L );
        frequencyR2RB = digitalRead( buttonPinR2R );
        
        if( frequencyR1LA > frequencyR1LB){
            frequencyR++;
        }
        if( frequencyR1RA > frequencyR1RB){
            frequencyR++;
        }
        if( frequencyR2LA > frequencyR2LB){
            frequencyR++;
        }
        if( frequencyR2RA > frequencyR2RB){
            frequencyR++;
        }
        
	//Counting frequency for team Blue
        frequencyB1LA = digitalRead( buttonPinB1L );
        frequencyB1RA = digitalRead( buttonPinB1R );
	    frequencyB2LA = digitalRead( buttonPinB2L );
        frequencyB2RA = digitalRead( buttonPinB2R );        
    
        delay( 100 );
        
        frequencyB1LB = digitalRead( buttonPinB1L );
        frequencyB1RB = digitalRead( buttonPinB1R );
        frequencyB2LB = digitalRead( buttonPinB2L );
        frequencyB2RB = digitalRead( buttonPinB2R );
        
        if( frequencyB1LA > frequencyB1LB){
            frequencyB++;
        }
        if( frequencyB1RA > frequencyB1RB){
            frequencyB++;
        }
        if( frequencyB2LA > frequencyB2LB){
            frequencyB++;
        }
        if( frequencyB2RA > frequencyB2RB){
            frequencyB++;
        }
    }
    
    //map the amount of steps to brightness/light behavior
    else
    {
        timeStartedAt = 0;
        timeElapsed = 0;
        if (frequencyR == 0 && frequencyB == 0){
            stepBrightnessR = 0;
            stepBrightnessB = 0;
        }
        else{
            stepBrightnessR = map(frequencyR, 0, 20, 0,270);
            stepBrightnessB = map(frequencyB, 0, 20, 0,270);
        }
        
        if (stepBrightnessR >255){
            stepBrightnessR = 255;
        }
        if (stepBrightnessB >255){
            stepBrightnessB = 255;
        }
        
        //publish the value to The Bus Sign
        publishRedEvent();
        delay(110);
        publishBlueEvent();
        
        //change all the tile signal lights to the winning team's color for 10 seconds
        if (stepBrightnessR == 255){
            stepBrightnessR = 0;
            stepBrightnessB = 0;
            frequencyR = 0;
            frequencyB = 0;
            uint32_t c = stripR2.Color(200, 50, 5); //Peach
 
            for( int i = 0; i < stripR2.numPixels(); i++ ){
                stripR1.setPixelColor(i, c);
                stripR2.setPixelColor(i, c);
                stripB1.setPixelColor(i, c);
                stripB2.setPixelColor(i, c);
            }
            stripR1.show();
            stripR2.show();
            stripB1.show();
            stripB2.show();
            delay(10000);
            
            c = stripR2.Color(0,0,0); // Off
 
            for( int i = 0; i < stripR2.numPixels(); i++ ){
                stripR1.setPixelColor(i, c);
                stripR2.setPixelColor(i, c);
                stripB1.setPixelColor(i, c);
                stripB2.setPixelColor(i, c);
            }
            stripR1.show();
            stripR2.show();
            stripB1.show();
            stripB2.show();
        }
        else if (stepBrightnessB ==255){
           stepBrightnessR = 0;
           stepBrightnessB = 0;
           frequencyR = 0;
           frequencyB = 0;
            uint32_t c = stripR2.Color(10,150,70); // Cyan
 
            for( int i = 0; i < stripR2.numPixels(); i++ ){
                stripR1.setPixelColor(i, c);
                stripR2.setPixelColor(i, c);
                stripB1.setPixelColor(i, c);
                stripB2.setPixelColor(i, c);
            }
            stripR1.show();
            stripR2.show();
            stripB1.show();
            stripB2.show();
            delay(10000);
            
            c = stripR2.Color(0,0,0); // Off
 
            for( int i = 0; i < stripR2.numPixels(); i++ ){
                stripR1.setPixelColor(i, c);
                stripR2.setPixelColor(i, c);
                stripB1.setPixelColor(i, c);
                stripB2.setPixelColor(i, c);
            }
            stripR1.show();
            stripR2.show();
            stripB1.show();
            stripB2.show();
        }
    }
}

//publish the value for red team to The Bus Sign
void publishRedEvent()
{
  // check that it's been 0.1 secondds since the last publish
  if( lastPublishedAt + publishAfter < millis() )
  {
      String eventName = "SpringPandaIsCallingMe" + System.deviceID();
      Particle.publish( eventName, String(stepBrightnessR) );

      //capture publish time
      lastPublishedAt = millis();
  }
}

//publish the value for blue team to The Bus Sign
void publishBlueEvent()
{
  // check that it's been 0.1 secondds since the last publish
  if( lastPublishedAt + publishAfter < millis() )
  {
      String eventName = "AutumnPandaIsCallingMe" + System.deviceID();
      Particle.publish( eventName, String(stepBrightnessB) );

      //capture publish time
      lastPublishedAt = millis();
  }
}
Click to Expand
0
The BusCade Tile circuit - each pedal is an individual piece of aluminum foil connected with 1 wire, noted as Pedal-[Blue/Red team] [Player 1/2] [Right/Left foot] [A/B] (2 pedals form 1 pair)
Pedals
0

Some technical challenges we've encountered and overcame:

- To design and make the LED strip to display the game-play information in an effective and stable manner

- To communicate the game-play information between the tiles and the ambient bus stop display via Particle cloud instantly and stably

- To build durable tiles for testing and demo purposes

- To organize the wire "jungles" in an elegant manner and hide the complexity from the users

List of parts used to build the final prototype:

- Particle Argon x 2

- Breadboard x 2

- Adafruit NeoPixel Digital RGB LED Strip - Black 30 LED (1m) x 1

- NeoPixel Digital RGB LED (circular-shaped) x 4

- Jump wires

- Aluminium foil

- Craft wood boards (1/8 inch thick)

- Foams (0.8cm thick)

- Foam core boards

- Double-sided tape

- Duct tape

- Medical tape (for the front panel of the ambient bus stop sign display)

- Acrylic sheets (clear)

- Power banks

0

Next Steps

If we were to take this project forward, we would consider the following -

Making the system self-sufficient - The current system is required to be linked to the power grid to function. However, on further development, we hope to use the energy generated by the user's movement to power the ambient light.

Inclusive - Our current design allows users to interact with the system by stepping on the tiles. This may prevent people with disabilities (e.g a person in a wheelchair) from using our system. Going forward we want to incorporate a design solution that would allow everyone to be able to use it without any limitations.

More varieties - While our current game is engaging, going forward we want to find ways in which we can keep updating the games to ensure that the system remains engaging over a longer period of time. We can consider having different games at different bus stops, different colors of teams etc. This will generate more conversations, motivate bus riders to explore more of the city, and encourage more citizens to use public transportation.

Larger surface area - At the moment we are using tiles as our sensors for the input. This leads to having only limited number of tiles and also limited surface area on which the user can stand. Going forward we envision that the sensors are embedded into the ground and there are no limitations of standing spots.

Materials - For the purpose of this project, we used materials such as foam boards, foil and wood. However, if this project were to be implemented in the real world, durable and weatherproof materials would be required.

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

Transforming solitary waiting at bus stops into a collaborative and engaging experience through gameplay and ambient light.

Created

December 5th, 2019