Someone Somewhere
Take a break. Sit. Make new friends.
Made by Jeremy Jiang, Rajlakshmee Rajlakshmee, fskuok and Cheng (Vanessa) Li
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
My Movie from Vanessa Li on Vimeo.
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.
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:
//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
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.
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.
<!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
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
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.