It is an ambient device to recreate the feeling of a candle lit dinner for a couple in a long distance relationship.

0

Purpose:

Dinner for Two is an exploration of a connect device that promotes intimacy and companionship for couples in a long distance relationship. Dinner is one of the few times a day where couples have the opportunity to unwind from their day and connect with their significant others. Setting aside time for dinner is especially important for the health and success of a relationship. According to a study by a team of Dutch and Canadian psychologists (aan het Rot et al., 2015), the simple act of eating a meal together may be all it takes to bring you and your partner to an emotionally better place

Dinner is an especially lonely and challanging time for couples that are in a long distance relationship and may lead to relationship strain over time. Dinner for Two allows two individuals to have a shared dining experience at a distances, through an interconnected  series of candles. Dinner for Two allows couples to have a synchronized date night even though they are in a long distance relationship.

0

Problem Statement & Goal:

Dinner for two allows couples to sit at two separate dinner tables,  and using the visual imagery of a candlelit dinner,  individuals  are able to coordinate a shared dining experience. The setup consists of  two identical candles. When Partner A sits down at their dinner table, an IR distance sensor, built into their candle,  perceives that Partner A is seated for dinner, and the corresponding candle at Partner B's table lights up to indicate that their partner is seated for dinner. When Partner B takes his/her seat for dinner, a candle on Partner A's table illuminates.   When both  partner are seated, both candles are illuminated, and dinner is ready to commence. Our goal is that seeing this illuminated candle flickering on one's table, brings the user a little bit closer to the comfort of a shared dining experience with their partner, which is fundamental for the health and happiness of a long distance relationship.

0

Design and Development Process:

The major consideration of our design and development process centered around the goal of collecting and transmitting the information that a person is seated at their dining room table. Initially we focused on using Both a an FSR sensor as well as a pressure plate switch integrated into the cushion of a dining room chair to register that the user is seated to trigger the partners candle. We determined that wiring from the candle to the chair would impede both the aesthetics and functionality of this design choice, and that all chairs at a dining room table would need to be outfitted with these cushions, if the user did not regularly sit in the same chair. 

We settled on using an IR distance sensor for two reason, Firstly, it would allow for the integration of the sensing mechanism directly into the candle, which would remove the needs for wires between the candle in the chair. Secondly, a user would be able to sit in any chair at their table, and just turn the candle to the direction of where they were siting. They could also move the candle to different environments in the house, such as a coffee table, or a bar table, where on might also have dinner. This led to the process of constructing of a housing for the sensor that would be integrated into the bottom of the candle. 

Bill of Materials:

  1. Breadboard (generic) x 2
  2. Particle Photon x 2
  3. IR Distance Sensor x 2
  4. Neopixel x2
  5. Jumper Wires x 16
0

Fritzing Diagram:


0

Code Development Process:

1. Set up the code for the IR sensor. To do this, you must declare integer for the IR proximity, distance variable and transition time, and digital pin for the ir sensor pin.  

2. Set up the code for the neopixel. To do this, you must define pixel pin, count and type, and include neopixel library.

3. Initialize all pixels to 'off'. 

4. Write an if loop in order to lit the candle according to the distance variable.

5. Complete the setup for the neopixel and functions that you will call later.

6. Connect the two photons and publish events by referring to the template code provided



0
// CODE FOR IR DISTANCE SENSOR + NEOPIXEL WITH FLICKERING EFFECT

#include "neopixel.h"
#include <math.h>
// IMPORTANT: Set pixel COUNT, PIN and TYPE
#define PIXEL_PIN D2
#define PIXEL_COUNT 24
#define PIXEL_TYPE WS2812
Adafruit_NeoPixel strip = Adafruit_NeoPixel(PIXEL_COUNT, PIXEL_PIN, PIXEL_TYPE);
int ledPin = D0;
int irPin = A0;
int irProximity = 0;
int transitionTime = 5000; // Transition Time in ms
const int sampleWindow = 50;
int distance = 0; // initialize distance variable
int unsigned long start;
int unsigned long timeElapsed;
// color variables: mix RGB (0-255) for desired yellow
int redPx = 255;
int grnHigh = 100; //110-120 for 5v, 135 for 3.3v
int bluePx = 10; //10 for 5v, 15 for 3.3v
// animation time variables, with recommendations
int burnDepth = 10; //10 for 5v, 14 for 3.3v -- how much green dips below grnHigh for normal burn -
int flutterDepth = 25; //25 for 5v, 30 for 3.3v -- maximum dip for flutter
int cycleTime = 250; //120 -- duration of one dip in milliseconds
int fDelay;
int fRep;
int flickerDepth;
int burnDelay;
int burnLow;
int flickDelay;
int flickLow;
int flutDelay;
int flutLow;
int state = 0;
void setup() {
  pinMode( PIXEL_PIN, OUTPUT );
  flickerDepth = (burnDepth + flutterDepth) / 2.4;
  burnLow = grnHigh - burnDepth;
  burnDelay = (cycleTime / 2) / burnDepth;
  flickLow = grnHigh - flickerDepth;
  flickDelay = (cycleTime / 2) / flickerDepth;
  flutLow = grnHigh - flutterDepth;
  flutDelay = ((cycleTime / 2) / flutterDepth);
  strip.begin();
  strip.show();
  RGB.control(true);
  Serial.begin(9600);
  Serial.println( 9600 ); //enable serial monitor
  strip.begin();
  strip.show(); // Initialize all pixels to 'off'
  pinMode( PIXEL_PIN, OUTPUT );
  pinMode(ledPin, OUTPUT);
  start = millis();
  timeElapsed = 0;
  RGB.control(TRUE);
}
void loop() {
  distance = sampleProximity();
  Serial.println( distance );
  if(distance > 300){
    for(int i=0; i<strip.numPixels(); i++) {
      strip.setPixelColor(i, 0,0,0);
      strip.show();
    RGB.color(0,0,0);
      }
    }
  if(distance <= 300){
    state += 1;
    state = state % 8;
  switch (state) {
    case 0:
      burn(10);
      break;
    case 1:
      flicker(5);
      break;
    case 2:
      burn(8);
      break;
    case 3:
      flutter(6);
      break;
    case 4:
      burn(3);
      break;
    case 5:
      on(10);
      break;
    case 6:
      burn(10);
      break;
    case 7:
      flicker(10);
      break;
    }
  }
  
}
int sampleProximity( )
{
  unsigned long startMillis = millis(); // Start of sample window
  int farthest_sample = 0;
  int closest_sample = 1000;
  // collect data for 50 mS
  while (millis() - startMillis < sampleWindow)
  {
    int sample = analogRead( irPin );
    // invert the range, and convert it to a percent
    sample = map( sample, 0, 4095, 1000, 0 );
    // now see if the sample is the lowest;
    if ( sample > farthest_sample ){
    farthest_sample = sample ;
    }
    if ( sample < closest_sample ){
    closest_sample = sample;
    }
  }
  Serial.print( "Farthest = " );
  Serial.println( farthest_sample );
  Serial.print( "Closest = " );
  Serial.println( closest_sample );
  int proximityAverage = (farthest_sample + closest_sample)/2 ;
  return proximityAverage;
}
/*
    METHODS
*/

  Particle.publish("timeUp");
}
/**
 * Scale a value returned from a trig function to a byte value.
 * [-1, +1] -> [0, 254]
 * Note that we ignore the possible value of 255, for efficiency,
 * and because nobody will be able to differentiate between the
 * brightness levels of 254 and 255.
 */
byte trigScale(float val) {
  val += 1.0; // move range to [0.0, 2.0]
  val *= 127.0; // move range to [0.0, 254.0]
  return int(val) & 255;
}
/**
 * Map an integer so that [0, striplength] -> [0, 2PI]
 */
float map2PI(int i) {
  return M_PI*2.0*float(i) / float(strip.numPixels());
}
// Input a value 0 to 255 to get a color value.
// The colours are a transition r - g - b - back to r.
uint32_t Wheel(byte WheelPos) {
  if(WheelPos < 85) {
   return strip.Color(WheelPos * 3, 255 - WheelPos * 3, 0);
  } else if(WheelPos < 170) {
   WheelPos -= 85;
   return strip.Color(255 - WheelPos * 3, 0, WheelPos * 3);
  } else {
   WheelPos -= 170;
   return strip.Color(0, WheelPos * 3, 255 - WheelPos * 3);
  }
}
void colorWipe(uint32_t c, int wait) {
  for(uint16_t i=0; i<strip.numPixels(); i++) {
    strip.setPixelColor(i, c);
    strip.show();
    delay(wait);
  }
}
// basic fire functIon - not called in main loop
void fire(int grnLow) {
  for (int grnPx = grnHigh; grnPx > grnLow; grnPx--) {
    for (int i = 0; i < PIXEL_COUNT; i++ ){
      strip.setPixelColor(i,redPx, grnPx, bluePx); // THIS IS WHAT WE EDITED
      strip.show();
  }
    delay(fDelay);
  }
  for (int grnPx = grnLow; grnPx < grnHigh; grnPx++) {
    for (int i = 0; i < PIXEL_COUNT; i++ ){
      strip.setPixelColor(i,redPx, grnPx, bluePx); // THIS IS WHAT WE EDITED
      strip.show();
  }
    delay(fDelay);
  }
}
// fire animation
void on(int f) {
  fRep = f * 1000;
  int grnPx = grnHigh - 5;
  for (int i = 0; i < PIXEL_COUNT; i++ ){
    strip.setPixelColor(i,redPx, grnPx, bluePx); // THIS IS WHAT WE EDITED
    strip.show();
}
  delay(fRep);
}
void burn(int f) {
  fRep = f * 8;
  fDelay = burnDelay;
  for (int var = 0; var < fRep; var++) {
    fire(burnLow);
  }
}
void flicker(int f) {
  fRep = f * 8;
  fDelay = burnDelay;
  fire(burnLow);
  fDelay = flickDelay;
  for (int var = 0; var < fRep; var++) {
    fire(flickLow);
  }
  fDelay = burnDelay;
  fire(burnLow);
  fire(burnLow);
  fire(burnLow);
}
void flutter(int f) {
  fRep = f * 8;
  fDelay = burnDelay;
  fire(burnLow);
  fDelay = flickDelay;
  fire(flickLow);
  fDelay = flutDelay;
  for (int var = 0; var < fRep; var++) {
    fire(flutLow);
  }
  fDelay = flickDelay;
  fire(flickLow);
  fire(flickLow);
  fDelay = burnDelay;
  fire(burnLow);
  fire(burnLow);
}
void checkturnoff(){
  Particle.connect();
  distance = sampleProximity();
  Serial.println( distance );
  if(distance > 500){
    colorWipe(strip.Color(0, 0, 0), 25);
  }
}
Click to Expand
0
// CODE FOR IR DISTANCE SENSOR + NEOPIXEL WITH FLICKERING EFFECT AFTER CONNECTING BOTH PHOTONS AND PUBLISHING EVENTS

// This #include statement was automatically added by the Particle IDE.

#include "neopixel.h"
#include <math.h>
// IMPORTANT: Set pixel COUNT, PIN and TYPE
#define PIXEL_PIN D2
#define PIXEL_COUNT 24
#define PIXEL_TYPE WS2812

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

int ledPin = D0;
int irPin = A0;
int irProximity = 0;
int transitionTime = 5000; // Transition Time in ms

const int sampleWindow = 50;

int distance = 0; // initialize distance variable
int unsigned long start;
int unsigned long timeElapsed;


// color variables: mix RGB (0-255) for desired yellow
int redPx = 255;
int grnHigh = 100; //110-120 for 5v, 135 for 3.3v
int bluePx = 10; //10 for 5v, 15 for 3.3v

// animation time variables, with recommendations
int burnDepth = 10; //10 for 5v, 14 for 3.3v -- how much green dips below grnHigh for normal burn -
int flutterDepth = 25; //25 for 5v, 30 for 3.3v -- maximum dip for flutter
int cycleTime = 250; //120 -- duration of one dip in milliseconds

int fDelay;
int fRep;
int flickerDepth;
int burnDelay;
int burnLow;
int flickDelay;
int flickLow;
int flutDelay;
int flutLow;

int state = 0;

int lastPublishedAt = 0;
int publishAfter = 1000;
String a = "b";
String neopixel = "";

void setup() {

  pinMode( PIXEL_PIN, OUTPUT );

  flickerDepth = (burnDepth + flutterDepth) / 2.4;
  burnLow = grnHigh - burnDepth;
  burnDelay = (cycleTime / 2) / burnDepth;
  flickLow = grnHigh - flickerDepth;
  flickDelay = (cycleTime / 2) / flickerDepth;
  flutLow = grnHigh - flutterDepth;
  flutDelay = ((cycleTime / 2) / flutterDepth);

  strip.begin();
  strip.show();
  RGB.control(true);
  Serial.begin(9600);
  Serial.println( 9600 ); //enable serial monitor
  strip.begin();
  strip.show(); // Initialize all pixels to 'off'
  pinMode( PIXEL_PIN, OUTPUT );
  pinMode(ledPin, OUTPUT);
  start = millis();
  timeElapsed = 0;
  RGB.control(TRUE);
  //Particle.variable("is3",state);

}

void loop() {
  Particle.subscribe("imhome/CMUIoT", handlesharedevent); //"210032000d47343438323536"
  distance = sampleProximity();
  Serial.println( distance );

  if(distance > 300){
    a = 'h';
    publishmyEvent(a);
  }

  if(distance <= 300){
   
    a = 'l';
    publishmyEvent(a);

void colorWipe(uint32_t c, int wait) {
  for(uint16_t i=0; i<strip.numPixels(); i++) {
    strip.setPixelColor(i, c);
    strip.show();
    delay(wait);
  }
}

// basic fire funciton - not called in main loop
void fire(int grnLow) {
  for (int grnPx = grnHigh; grnPx > grnLow; grnPx--) {
    for (int i = 0; i < PIXEL_COUNT; i++ ){
      strip.setPixelColor(i,redPx, grnPx, bluePx); // THIS IS WHAT WE EDITED
      strip.show();
  }
    delay(fDelay);
  }
  for (int grnPx = grnLow; grnPx < grnHigh; grnPx++) {
    for (int i = 0; i < PIXEL_COUNT; i++ ){
      strip.setPixelColor(i,redPx, grnPx, bluePx); // THIS IS WHAT WE EDITED
      strip.show();
  }
    delay(fDelay);
  }
}

// fire animation
void on(int f) {
  fRep = f * 1000;
  int grnPx = grnHigh - 5;
  for (int i = 0; i < PIXEL_COUNT; i++ ){
    strip.setPixelColor(i,redPx, grnPx, bluePx); // THIS IS WHAT WE EDITED
    strip.show();
}
  delay(fRep);
}

void burn(int f) {
  fRep = f * 8;
  fDelay = burnDelay;
  for (int var = 0; var < fRep; var++) {
    fire(burnLow);
  }
}

void flicker(int f) {
  fRep = f * 8;
  fDelay = burnDelay;
  fire(burnLow);
  fDelay = flickDelay;
  for (int var = 0; var < fRep; var++) {
    fire(flickLow);
  }
  fDelay = burnDelay;
  fire(burnLow);
  fire(burnLow);
  fire(burnLow);
}

void flutter(int f) {
  fRep = f * 8;
  fDelay = burnDelay;
  fire(burnLow);
  fDelay = flickDelay;
  fire(flickLow);
  fDelay = flutDelay;
  for (int var = 0; var < fRep; var++) {
    fire(flutLow);
  }
  fDelay = flickDelay;
  fire(flickLow);
  fire(flickLow);
  fDelay = burnDelay;
  fire(burnLow);
  fire(burnLow);
}

void checkturnoff(){
  Particle.connect();
  distance = sampleProximity();
  Serial.println( distance );
  if(distance > 500){
    colorWipe(strip.Color(0, 0, 0), 25);
  }
}
Click to Expand
0
//CODE FOR IR DISTANCE SENSOR + NEOPIXEL ( NO FLICKERING EFFECT)

#include "neopixel.h"
#include <math.h>
// IMPORTANT: Set pixel COUNT, PIN and TYPE
#define PIXEL_PIN D2
#define PIXEL_COUNT 24
#define PIXEL_TYPE WS2812

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

int irPin = A0;
int irProximity = 0;
int transitionTime = 5000; // Tranition Time in ms

const int sampleWindow = 50;

int distance = 0; // initialize distance variable
int unsigned long start;
int unsigned long timeElapsed;

void setup() {
  Serial.println( 9600 ); //enable serial monitor
  strip.begin();
  strip.show(); // Initialize all pixels to 'off'
  pinMode( PIXEL_PIN, OUTPUT );
  pinMode(ledPin, OUTPUT);
  start = millis();
  timeElapsed = 0;
}

void loop() {
  distance = sampleProximity();
  Serial.println( distance );

  if(distance > 300){
   for(int i=0; i<strip.numPixels(); i++) {
     strip.setPixelColor(i, 0,0,0);
     strip.show();
   RGB.color(0,0,0);
 }
  if(distance <= 300){
    for(int i=0; i<strip.numPixels(); i++) {
         strip.setPixelColor(i, 255,173,0);
         strip.show();
        RGB.color(255,173,0);
  }
}



int sampleProximity( )
{
  unsigned long startMillis = millis(); // Start of sample window
  int farthest_sample = 0;
  int closest_sample = 1000;
  // collect data for 50 mS
  while (millis() - startMillis < sampleWindow)
  {
    int sample = analogRead( irPin );
    // invert the range, and convert it to a percent
    sample = map( sample, 0, 4095, 1000, 0 );
    // now see if the sample is the lowest;
    if ( sample > farthest_sample ){
    farthest_sample = sample ;
    }
    if ( sample < closest_sample ){
    closest_sample = sample;
    }
  }
  Serial.print( "Farthest = " );
  Serial.println( farthest_sample );
  Serial.print( "Closest = " );
  Serial.println( closest_sample );
  int proximityAverage = (farthest_sample + closest_sample)/2 ;
  return proximityAverage;
}
/*
    METHODS
*/

/**
 * Scale a value returned from a trig function to a byte value.
 * [-1, +1] -> [0, 254]
 * Note that we ignore the possible value of 255, for efficiency,
 * and because nobody will be able to differentiate between the
 * brightness levels of 254 and 255.
 */
byte trigScale(float val) {
  val += 1.0; // move range to [0.0, 2.0]
  val *= 127.0; // move range to [0.0, 254.0]
  return int(val) & 255;
}
/**
 * Map an integer so that [0, striplength] -> [0, 2PI]
 */
float map2PI(int i) {
  return M_PI*2.0*float(i) / float(strip.numPixels());
}
// Input a value 0 to 255 to get a color value.
// The colours are a transition r - g - b - back to r.
uint32_t Wheel(byte WheelPos) {
  if(WheelPos < 85) {
   return strip.Color(WheelPos * 3, 255 - WheelPos * 3, 0);
  } else if(WheelPos < 170) {
   WheelPos -= 85;
   return strip.Color(255 - WheelPos * 3, 0, WheelPos * 3);
  } else {
   WheelPos -= 170;
   return strip.Color(0, WheelPos * 3, 255 - WheelPos * 3);
  }
}
void colorWipe(uint32_t c, int wait) {
  for(uint16_t i=0; i<strip.numPixels(); i++) {
    strip.setPixelColor(i, c);
    strip.show();
    delay(wait);
  }
}
Click to Expand
0
// FINAL CODE AFTER CONNECTING BOTH PHOTONS AND PUBLISHING EVENTS ( NO FLICKERING EFFECT )

#include "neopixel.h"
#include <math.h>
// IMPORTANT: Set pixel COUNT, PIN and TYPE
#define PIXEL_PIN D2
#define PIXEL_COUNT 24
#define PIXEL_TYPE WS2812

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

int irPin = A0;
int irProximity = 0;
int transitionTime = 5000; // Transition Time in ms

const int sampleWindow = 50;

int distance = 0; // initialize distance variable
int unsigned long start;
int unsigned long timeElapsed;

int lastPublishedAt = 0;
int publishAfter = 1000;
String a = "b";
String neopixel = "";

unsigned long lightedafter = 0;


void handle( const char *event, const char *data){
  for(int i=0; i<strip.numPixels(); i++) {
    strip.setPixelColor(i, 255,173,0);
    strip.show();
}
}




void setup() {
  Serial.println( 9600 ); //enable serial monitor
  strip.begin();
  strip.show(); // Initialize all pixels to 'off'
  pinMode( PIXEL_PIN, OUTPUT );
  //pinMode(ledPin, OUTPUT);
  start = millis();
  timeElapsed = 0;
  RGB.control(TRUE);
}

void loop() {
  int sample = analogRead( irPin );
  sample = map( sample, 0, 4095, 1000, 0 );
  Particle.subscribe("imhome/CMUIoT", handlesharedevent); //"210032000d47343438323536"

  if (millis() - lightedafter > 2000){
  if(distance > 300){
    for(int i=0; i<strip.numPixels(); i++) {
      strip.setPixelColor(i, 0,0,0);
      strip.show();
    RGB.color(0,0,0);
    a = 'h';
    publishmyEvent(a);
      }
    }
  }

   distance = sampleProximity();
  Serial.println( distance );
  if(distance <= 300){


    a = 'l';
    publishmyEvent(a);
  // strip.Color(8, 255,173,0);
  // strip.show();
  }


}

void publishmyEvent(String a)
{
  if( lastPublishedAt + publishAfter < millis() )
{
  neopixel = "imhome/CMUIoT" + System.deviceID();
  if (a=="h"){
    Particle.publish(neopixel, "Motion > 300" );
    lastPublishedAt = millis();
  }else if(a == "l"){
    Particle.publish(neopixel, "Motion < 300" );
    lastPublishedAt = millis();
  }
}
}

void handlesharedevent(const char *event, const char *data)
{
 String neopixel2 = String( event ); // convert to a string object
 String deviceID = System.deviceID();
 if( neopixel2.indexOf( deviceID ) != -1 ){
 // The event was published by MY photon
 }else{
   // The event was published by the OTHER photon
   String whatDoesTheDataSay = String(data);
   if(whatDoesTheDataSay == "Motion < 300"){
     //individualServo1();

       for(int i=0; i<strip.numPixels(); i++) {
         strip.setPixelColor(i, 255,173,0);
         strip.show();
        RGB.color(255,173,0);
       lightedafter = millis();
     }
   }


 }
}


int sampleProximity( )
{
  unsigned long startMillis = millis(); // Start of sample window
  int farthest_sample = 0;
  int closest_sample = 1000;
  // collect data for 50 mS
  while (millis() - startMillis < sampleWindow)
  {
    int sample = analogRead( irPin );
    // invert the range, and convert it to a percent
    sample = map( sample, 0, 4095, 1000, 0 );
    // now see if the sample is the lowest;
    if ( sample > farthest_sample ){
    farthest_sample = sample ;
    }
    if ( sample < closest_sample ){
    closest_sample = sample;
    }
  }

  Serial.print( "Farthest = " );
  Serial.println( farthest_sample );
  Serial.print( "Closest = " );
  Serial.println( closest_sample );
  int proximityAverage = (farthest_sample + closest_sample)/2 ;
  return proximityAverage;
}

void colorWipe(uint32_t c, int wait) {
  for(uint16_t i=0; i<strip.numPixels(); i++) {
    strip.setPixelColor(i, c);
    strip.show();
    delay(wait);
  }
}
Click to Expand
0

Prototype

0
0
Dinner4Two
John Vanderveen - https://youtu.be/-4E1lH21df8
0

Reflection:

The team had a very good grasp of the circuitry requirement to produce the two connected products. The circuitry and the code for each device was tested independently with an activation of the IR distance sensor, and the corresponding output of the Neopixel. The real challenge for the team was creating the connection between the two devices. The first problem the team ran into was that only one Neopixel could be active at a time. After many edits to the code, the team ran into the next problem, where an activation of one light, would trigger both lights to turn on. It wasn't until many edits later, and specifically making changes to the delays in the code that the team had a moderate level of success. The team also needed to remove the flickering function from the neopixel, which caused a lot of delays as well.  This was a very challenging project to execute, despite the fact that it was a relatively simple concept. 

0

Next Steps:

Future iterations of this product would allow for each partner to have two candles at their table. When Partner A sits down, one candle at Partner A's table would illuminate, and one candle on Partner B 's table would illuminate as well.  When Partner B  sits down, the second candle on Parnter A's table would illuminate, and the second candle on Partner B 's table would illuminate as well. We would like to incorporate this in future iterations of our device, because candles are typically displayed in pairs. and each candle would symbolizes one person at the table.

We may explore additional aesthetics with a tall candle stick with an exposed flickering LED, which our typically associated with a more romantic dinner. 

x
Share this Project

Courses

49713 Designing for the Internet of Things

· 25 members

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


About

It is an ambient device to recreate the feeling of a candle lit dinner for a couple in a long distance relationship.

Created

February 14th, 2018