Someone Somewhere

Take a break. Sit. Make new friends.

Made by Jeremy Jiang, Rajlakshmee Rajlakshmee, fskuok and Cheng (Vanessa) Li

Connected seating platforms, at different parts of the world, connecting strangers. In this fast paced life, where loneliness is increasing day by day, Someone Somewhere brings an enchanted world of friendship and fun for everyone. Each time a person sits on a platform he or she connects randomly with new people who are also sitting on platforms. The connections are made through voice connections allowing people to talk to new people and develop new relationships.

Created: February 3rd, 2015

0


0

Process

0

For our demo, we set up several Spark Cores with force sensors and RGB LEDs to show the established connections. There are three states for a platform:

State 0 (Red): No one sitting on platform

State 1 (Purple): Someone sitting on platform waiting for a connection

State 2 (Green): Connected with another person on another platform

All platforms start in state 0. When a person sits on a platform, they enter state 1. When another person sits on a another platform, a connection is made and both platforms enter state 2. When a person leaves, their platform returns to state 0, and their partner’s platform either enters state 1 if that person is waiting for another connection or state 0 if that person leaves as well. 

0

Components

For the hardware portion of this demonstration, the following materials will be needed for each chair that you want to include in the system:

Spark Core x1

RGB LED x1

Force Sensitive Resistor Sensor x1

1K Ohm Resistor x3

10K Ohm Resistor x1

Breadboard x1

Jumper Wires

0

Final circuit

0

Outcome

First Try: Only Spark Cores

The first attempt that we made at controlling the system was solely through the Spark Cores themselves using Spark.publish() and Spark.subscribe() to transmit and receive all of the information. The biggest challenge we faced here was designing a matching system that needed several Spark.publish() and Spark.subscribe() events to convey information and data. Starting with two cores, this was fairly easy, but when we tried to expand to three cores and beyond, the matching algorithm got much more complicated. In addition to all of these cloud events, there needed to be a great deal of internal logic to decipher the data and establish and confirm a match. Through many iterations of the code as well as considering all the possible matching situations, even just with three cores, we were still unable to transmit, receive, and decipher all the data needed to establish a successful match. The final iteration of the failed code can be found below:

0

Failed Code

0
//pin allocations
int redPin = A0;
int greenPin = D0;
int bluePin = D1;
int frsPin = A1;

//four chair states:
//0 - no person sitting
//1 - person sitting in chair but waiting for a person to sit in another chair
//2 - person in another chair but waiting for a person to sit in this chair
//3 - connection established
int state = 0;

const char *selfID = "J";
char targetID[5]; //create a buffer with more than enough space to hold a character
char requestID[5]; //create a buffer with more than enough space to hold a character
char confirmID[5]; //create a buffer with more than enough space to hold a character

int frsThreshold = 2000;

void setup() {
  pinMode(redPin, OUTPUT);
  pinMode(greenPin, OUTPUT);
  pinMode(bluePin, OUTPUT);
  pinMode(frsPin, INPUT);

  Spark.subscribe("SDAT2015/chair-occupied", sitDownEventHandler);
  Spark.subscribe("SDAT2015/connection-request", connectionRequestHandler);
  Spark.subscribe("SDAT2015/connection-confirm", connectionConfirmHandler);
  Spark.subscribe("SDAT2015/chair-empty", quitConnectionHandler);

  setLEDColor(state);
}

void loop() {
  switch (state) {
    case 0: //no person sitting
      //keep checking for a person to sit down
      if (isSat()) {
        state = 1;
      }
      break;

    case 1: //waiting for another person to sit down in another chair
      Spark.publish("SDAT2015/chair-occupied", selfID);
      //if the person leaves
      if (!isSat()) {
        state = 0;
        //publish an event here
        Spark.publish("SDAT2015/chair-empty", selfID);
      }
      break;

    case 2: //waiting for person to sit in this chair
      //a person sits down
      if (isSat()) {
        Spark.publish("SDAT2015/connection-request", requestID);
      }
      break;

    case 3: //connection built
      //one or both people leave
      if (!isSat()) {
        state = 0;
        Spark.publish("SDAT2015/chair-empty", selfID);
      }
  }

  setLEDColor(state);
  delay(100);
}

void sitDownEventHandler(const char *event, const char *id) {
  //if this event is from another chair
  if ((id[0] != *selfID) && ((state == 1) || (state == 0))) {
    //record ID of other chair
    strcpy(targetID, id);
    requestID[0] = *targetID;
    requestID[1] = *selfID;
    state = 2;
  }
}

void connectionRequestHandler(const char *event, const char *id) {
  //if the request is for this chair
  if ((id[0] == *selfID) && (id[1] == requestID[0]) && (state != 3) && (state != 0)) {
    strcpy(confirmID, id);
    Spark.publish("SDAT2015/connection-confirm", confirmID);
    state = 3;

  //if the request is not for this chair
  //which means two other chairs have been connected
  } else if ((id[0] != *selfID)) {
    state = 1;
  }
}

void connectionConfirmHandler(const char *event, const char *id) {
  //if correct confirmation code received
  if (((id[0] == *selfID) || (id[1] == *selfID)) && (state != 3) && (state != 0)) {
    state = 3;
  } else {
    state = 0;
  }
}

void quitConnectionHandler(const char *event, const char *id) {
  if ((state == 3) && ((id[0] == confirmID[0]) || (id[0] == confirmID[1]))) {
    state = 0;
  }
}

bool isSat() {
  return analogRead(frsPin) > frsThreshold ? true : false;
}

//set color output for RGB LED
void setLEDColor(int state) {
  int r, g, b;
  switch (state) {
    case 0:
      r = 0;
      g = 255;
      b = 255;
      break;
    case 2:
      r = 255;
      g = 255;
      b = 0;
      break;
    case 1:
      r = 0;
      g = 255;
      b = 0;
      break;
    case 3:
      r = 255;
      g = 0;
      b = 255;
      break;
  }

  analogWrite(redPin, r);
  analogWrite(greenPin, g);
  analogWrite(bluePin, b);
}
Click to Expand
0

We determined that the biggest obstacle to the matching system was that there was no central controller to handle all of the matching. Each Spark Core in the system was trying to handle its own matching, but this created a lot of redundant and jumbled data in the cloud. As such, in our next iteration, we decided construct a handler to which all of the Spark Cores would report for matching. The handler alone would run the matching algorithm and deal with the matching data, and all it would report to the Spark Cores was whether or not they had been matched. 

0

Improvement: Adding a coordinator

We added a webpage as an “coordinator”, which’s job is managing a waiting list and sending connect and disconnect order to those individual chairs.

When a person sits down on a chair, the chair publish a ‘sitdown’ event, and the coordinator put this chair to the end of the waiting list.

When a person leaves a chair, the chair publish a “leave” event, and the coordinator send a order to disconnect the chair if it is connected, or clear it from waiting list if it is waiting.

Every time an event is published, the coordinator will send a order to build a connection between the first and second chair in the waiting list(if there is more than one chair in the waiting list) and clear them from the waiting list. The coordinator will keep repeat this step until there is less than 2 chairs in the waiting list.

0

Coordinator Code

0
<!DOCTYPE HTML>
<html>
<head>
    <!--used jquery to send AJAX requests-->
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
</head>
<body>
    <script type="text/javascript">
    //chair's name : accessToken;
    
    var chairsLogInfo = {
            "53ff6e066667574822262067": "15916cf78641fbabdec9c7766212ad846a9799de", //fan sai
            "53ff6f066667574826352167": "f36986b13669d9499004aed7de5af9c96a871fac", //jeremy
            "53ff68066667574851512367": "e31a1e42deb194ee4cfdfde6bfec16c417cfb935", //vanessa
            "53ff6d066667574815382067": "b142ca27e40c939294795da923ee4a98376ed7be"  //raj

        },
        chairStatus = {},
        waitingChairs = [],
        connectedChairs = [],
        chair,
        loc = {
            "53ff6e066667574822262067": "San Francisco", 
            "53ff6f066667574826352167": "Chicago", 
            "53ff68066667574851512367": "Pittsburgh", 
            "53ff6d066667574815382067": "New York"  
        };

    for (chair in chairsLogInfo ){
        if(chairsLogInfo.hasOwnProperty(chair)){

            start(chair, chairsLogInfo[chair]);
            chairStatus[chair] = {
                "status": "unconnected",
                "partner": undefined,
                "init": function(){
                    this.status = "unconnected";
                    this.partner = undefined;
                }
            }
        }
        console.log("Watch chair: ", loc[chair]);
        
    }


    //build connections with spark cores
    function start(deviceID, accessToken) {
        var eventSource = new EventSource("https://api.spark.io/v1/devices/" + deviceID + "/events/?access_token=" + accessToken);

        eventSource.addEventListener('error', function(e) {
                console.log("Errored!"); 
            },false);

        eventSource.addEventListener('sdnt/sitdown', sitDownHandler, false);
        eventSource.addEventListener('sdnt/leave', leaveHandler, false);
    };


    function sitDownHandler(e){
        
        var id = JSON.parse(e.data).coreid;

        if(waitingChairs.indexOf(id) === -1 && chairStatus[id].status === "unconnected"){
            chairStatus[id].status = "waiting";
            waitingChairs.push(id);
            console.log("Chair ", loc[id], " added to waiting list");
        }

        matcher();
    }


    function leaveHandler(e){
        
        var id = JSON.parse(e.data).coreid;

        console.log("Chair ", id, " leaves");

        //if is a waiting chair, remove from the waiting list
        if(chairStatus[id].status === "waiting"){
            waitingChairs = waitingChairs.filter(function(v){
                return v !== id;
            })
            chairStatus[id].init();
        //if is a connected chair, disconect it
        }else if(chairStatus[id].status === "connected"){
            disconnect(id);
        }
    }

    //match waiting chairs in pair
    function matcher(){
        console.log('Matcher executed, waiting chairs:', waitingChairs);
        if(waitingChairs.length < 2) return;
        connect(waitingChairs.shift(), waitingChairs.shift());
        if(waitingChairs.length >= 2) matcher();
    }


    //connect two chairs
    function connect(chair1, chair2){
        console.log("Connect ", loc[chair1], " and ", loc[chair2])

        //connect chair1
        chairStatus[chair1].status = "connected";
        chairStatus[chair1].partner = chair2;
        callFn(chair1, "connect", 1);

        //connect chair2
        chairStatus[chair2].status = "connected";
        chairStatus[chair2].partner = chair1;
        callFn(chair2, "connect", 1);
    }

    //disconnect a chair, put its partner into waiting list
    function disconnect(chair){
        var partner = chairStatus[chair].partner;
        console.log("Disconnected ", loc[chair]);

        //put its partner to "waiting" status,
        if(partner){
            console.log("Disconnected ", loc[chair], "'s partner ", loc[partner]);
            waitingChairs.push(partner);
            chairStatus[partner].status = "waiting";
            callFn(partner, "disconnect", 1);
        }

        callFn(chair, "disconnect", 1);
        chairStatus[chair].init();
        matcher();
    }

    //a short hand function for send POST request
    function callFn(chairId, fnName, arg) {
        $.post( "https://api.spark.io/v1/devices/" + chairId + "/" + fnName + 
                        "?access_token=" + chairsLogInfo[chairId], 
                        { "args": arg });
    }
    </script>
</body>
</html>
Click to Expand
0

Chair Code

0
int redPin = A0;
int greenPin = D0;
int bluePin = D1;
int frsPin = A1;

int state = 0;

int frsThreshold = 2000;

bool alreadySitting = false;
bool isConnected = false;

void setup() {
  pinMode(frsPin, INPUT);
  pinMode(redPin, OUTPUT);
  pinMode(greenPin, OUTPUT);
  pinMode(bluePin, OUTPUT);

  Spark.function("connect", connect);
  Spark.function("disconnect", disconnect);

  setLEDColor(state);
}

void loop() {
  if (isSat() && !alreadySitting) {
    Spark.publish("sdnt/sitdown");
    alreadySitting = true;
  } else if (!isSat() && alreadySitting) {
    Spark.publish("sdnt/leave");
    alreadySitting = false;
  }

  if (isConnected) {
    state = 2;
  } else {
    if (isSat()) {
      state = 1;
    } else {
      state = 0;
    }
  }

  setLEDColor(state);
}

bool isSat() {
  return analogRead(frsPin) > frsThreshold;
}

void setLEDColor(int state) {
  int r, g, b;
  switch (state) {
    case 0:
      r = 0;
      g = 255;
      b = 255;
      break;
    case 1:
      r = 0;
      g = 255;
      b = 0;
      break;
    case 2:
      r = 255;
      g = 0;
      b = 255;
      break;
  }

  analogWrite(redPin, r);
  analogWrite(greenPin, g);
  analogWrite(bluePin, b);
}

int connect(String command) {
  isConnected = true;
  return 1;
}

int disconnect(String command) {
  isConnected = false;
  return 1;
}
Click to Expand
0

Coordinator Console Log

x
Share this Project


About

Connected seating platforms, at different parts of the world, connecting strangers.
In this fast paced life, where loneliness is increasing day by day, Someone Somewhere brings an enchanted world of friendship and fun for everyone. Each time a person sits on a platform he or she connects randomly with new people who are also sitting on platforms. The connections are made through voice connections allowing people to talk to new people and develop new relationships.