Ambient Ice Cream Towers

Made by Anushri Gupta, Langley Vogt and Jamie Talbert

Found in DioT 2019: Connected Communities

Storefront display and in-store interaction to help bring together ice cream lovers.

0

Intention

We are targeting people who bond over their love for food, specifically those with a sweet tooth. Communities have formed on platforms such as Yelp and TripAdvisor where they review their experiences at particular restaurants and talk about what they ordered. Information from one member is helpful for all the others when they are deciding where or what to eat.

For Ice-cream stores in particular, there is usually a dilemma on which flavor to pick from among so many, or which is flavor is most popular at that particular store. We also wanted to focus on an experience that brings joy to those who interact with it. Ice cream is admittedly an indulgence, but we wanted to enhance the attraction by making it exciting for anyone to view from the outside and even more fun to interact when inside the shop.

0

Community

Our community revolves around people who love food and are in the neighborhood for this particular ice cream shop. We want to create an ambient way for this community to learn from each other and share their experiences without having to download an app or search through countless reviews. 

0

Inspiration and Context

Yelp and TripAdvisor are commonly used platforms to understand the popularity of restaurants and the food they offer. But these are digital solutions, and do not involve the community on a more social level. Opposite to this, many restaurants display their specials, but there is no communication for how popular these are.

However, there are some precedents to interactive store displays. DeepLocal has created several storefronts that interact with the people around them, like their Nike display in London. In Las Vegas, there are towering M&M containers at M&M’s World, displaying vast quantities of all the different colors. Many toy stores contain gumball machines or marble machines displaying a complex transportation system that is fun to watch no matter your age. We wanted to combine all of these ideas into a fun and interactive solution to display the popularity of ice cream flavors.

0

Process

Initially we began discussing the type of community we wanted to focus on. First narrowing down to foodies, we then narrowed down further to desserts. Within this category, we defined our problem around the difficult choice of selecting an ice cream flavor. We also wanted to enhance the neighborhood connectivity by installing this in a local store instead of a large national chain.

When designing the space, our primary challenge was around the interaction of the ice cream scoop balls themselves. Initially, the balls would just be in a reservoir on top and would be fed down the correct tube. However, we wanted to add additional interaction to the experience. So, we elected to have the customer place a ball on an “elevator” beside the counter. The mechanism would then raise the ball and direct it towards the correct tube. Getting the ball to elevate to the top of the store and falling out was the biggest challenge with unpredictable behavior from the continuously winding servo. Three or four different mechanisms were explored before the final version was selected and proven.

0

Conceptual Design 

The ambient ice cream display is meant to be a storefront display at a neighborhood ice cream shop. As people walk by, they see which flavors are the most popular through the bar-chart effect created by the balls and cones in the window. As a fun interaction, people could observe how the display changes throughout the day, week, or even based on the time of year.

The storefront display could also persuade people to become part of the interaction. Going inside the store, a mechanism that transports the balls is on display for everyone to see. When a customer orders an ice cream, they are allowed to place a ball in the “elevator” and watch it be transported to the storefront display, so every customer feels personally involved.

0
Initial Concept Sketch
Final creative   outside sketch
0
Initial Concept Sketch
Final creative   inside sketch
0

Storyboard & Workflows

Below is a storyboard of how one may interact with the iiice cream store. Following are the workflows of each component of the prototype: Counter (buttons for ordering and neopixel), swiveling servo (direct the ice cream scoop ball to the correct display) and the continuous-winding servo (to elevate the ball to the channel to be distributed).

0
Workflow for Counter
Workflow counter
0
Workflow for Swiveling Servo
Workflow windingservo
0
Workflow for Winding Servo
Workflow rotatingservo
0

Prototype

For this project we used three Particle Argons which communicate with each other. When a flavor is chosen and a corresponding button is pressed from the first particle board, it publishes an event to the other two. The second particle board is connected to a continuous servo which acts as a pulley to elevate the ball of ice cream, and the third particle board is connected to a small servo which directs the ball into the corresponding flavor of ice cream.

Components Used

3x Particle Argon

3x Pushbutton

1x SG51R Servo

1x FS5103R Continuous Servo

1x Neopixel Jewel

Jumper Wires

0
Circuit for Ice-cream counter
Buttons
0
Ice Cream Counter Code
// This #include statement was automatically added by the Particle IDE.
#include <neopixel.h>

//************************************************//
//Button code
//************************************************//

//Declare buttons
int blueButton = D3; //Flavor 2
int redButton = D4; //Flavor 3
int yellowButton = D2; //Flavor 1

int blueButtonState = 0;
int redButtonState = 0;
int yellowButtonState = 0;

// This value will store the last time we published an event
long lastPublishedAt = 0;
// this is the time delay before we should publish a new event
// from this device
int publishAfter = 10000;

//************************************************//
//Neopixel code
//************************************************//

#define PIXEL_PIN D5
#define PIXEL_COUNT 7
#define PIXEL_TYPE WS2812
#include <neopixel.h>
Adafruit_NeoPixel strip = Adafruit_NeoPixel(PIXEL_COUNT, PIXEL_PIN, PIXEL_TYPE); //define neopixel

uint32_t colorOff = strip.Color(0, 0, 0);


void setup() {
    
    Particle.subscribe("langley/2019/iot/eventFinished/", finishedEvent);
    //Set up particle variables for console
    Particle.variable("blueButtonState", blueButtonState);
    Particle.variable("redButtonState", redButtonState);
    Particle.variable("yellowButtonState", yellowButtonState);
    //Particle.variable("completed", completed);

    //Declare button pin modes
    pinMode(blueButton, INPUT_PULLUP);    
    pinMode(redButton, INPUT_PULLUP);    
    pinMode(yellowButton, INPUT_PULLUP);
    
    strip.begin();
    strip.show(); 

    //For steup, test neopixel
    iceCreamFlavor1();
    turnColorOff();
}

void loop() {
    
    //get button states
    blueButtonState = digitalRead( blueButton );
    redButtonState = digitalRead( redButton );
    yellowButtonState = digitalRead( yellowButton );

    //Determine which button is pressed
    //Publish event accordingly and color for Neopixel
    if ( blueButtonState == LOW )
    {
        delay(1000);
        iceCreamFlavor2();
        blueButtonPublish();
        delay(2000);
    }
    else if (redButtonState == LOW) {
        iceCreamFlavor3();
        redButtonPublish();
        delay(2000);
    }
    else if (yellowButtonState == LOW) {
        iceCreamFlavor1();
        yellowButtonPublish();
        delay(2000);
    }
    
}

void blueButtonPublish()
{
  // 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 second  since our last publish
  if( lastPublishedAt + publishAfter < millis() )
  {

      String eventName = "langley/2019/iot/blueButtonPressed/" + System.deviceID();

      // then we share it out
      Particle.publish( eventName, "Blue" );

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

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

}

void redButtonPublish()
{
    // 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 secondds since our last publish
  if( lastPublishedAt + publishAfter < millis() )
  {

      String eventName = "langley/2019/iot/redButtonPressed/" + System.deviceID();

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

      // then we share it out
      Particle.publish( eventName, "Red" );

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

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

}

void yellowButtonPublish()
{
  // 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 secondds since our last publish
  if( lastPublishedAt + publishAfter < millis() )
  {
   
      String eventName = "langley/2019/iot/yellowButtonPressed/" + System.deviceID();

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

      // then we share it out
      Particle.publish( eventName, "Yellow" );

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

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

}

//Yellow Button
void iceCreamFlavor1() {
    
    //setting colour based on the handler event (blue/green/yellow)
    uint32_t colorYellow = strip.Color(2550, 255, 50);
    
    for(int i=0; i< strip.numPixels(); i++) { //light up one bye one 
        strip.setPixelColor(i, colorYellow );
        delay( 1000 );
        strip.show();
    }
    
}

//Blue Button
void iceCreamFlavor2() {
    
    //setting colour based on the handler event (blue/green/yellow)
    uint32_t colorBlue = strip.Color(0, 0, 255);
    
    for(int i=0; i< strip.numPixels(); i++) { //light up one bye one 
        strip.setPixelColor(i, colorBlue );
        delay( 1000 );
        strip.show();
    }
    
}

//Red Button
void iceCreamFlavor3() {
    
    //setting colour based on the handler event (blue/green/yellow)
    uint32_t colorRed = strip.Color(255, 0, 0);
    
    for(int i=0; i< strip.numPixels(); i++) { //light up one bye one 
        strip.setPixelColor(i, colorRed );
        delay( 1000 );
        strip.show();
    }
    
}

void turnColorOff() {
    
    for(int i=0; i< strip.numPixels(); i++) { //light up one bye one 
        strip.setPixelColor(i, colorOff);
        delay( 1000 );
        strip.show();
    }
    
}

void finishedEvent(const char *event, const char *data)
{

    String eventName = String( event ); 
    
    String deviceID = System.deviceID();
    
    if( eventName.indexOf( deviceID ) != -1 ){
      return;
    }
    
    turnColorOff();

}
Click to Expand
0
Circuit for winding servo
Finalcreativeproject windingservo bb
0
Winding Servo Code
// int servoPin = A1;
int servoPin2 = A1;
// Servo myServo;
Servo myServo2;
// int servoPos = 0;
int servoPos2 = 0;

bool run = false;

// uint32_t w = strip.Color(0, 0, 0);
//     //setting colour based on the handler event (blue/green/yellow)
// uint32_t c = strip.Color(255, 255, 255);


// This value will store the last time we published an event
long lastPublishedAt = 0;
// this is the time delay before we should publish a new event
// from this device
int publishAfter = 10000;


void setup() {

    // myServo.attach( servoPin );
    myServo2.attach( servoPin2 );
    
    //myServo2.write ( 93.5 );
    
    //my retrieve (Flavor 1)
    Particle.subscribe("langley/2019/iot/yellowButtonPressed/", handleSharedEvent );
    
    // my retrieve (Flavor 2)
    Particle.subscribe("langley/2019/iot/blueButtonPressed/", handleSharedEvent );
    
    //my retrieve (Flavor 3)
    Particle.subscribe("langley/2019/iot/redButtonPressed/", handleSharedEvent );
    
    Particle.variable("run", run);

    // turnonLight();

}

void loop() {
    
    if (run) {
        
        myServo2.attach( servoPin2 );
        
        delay(200);
        
        //up
        myServo2.write( 115 );
        //for counterclockwise
        delay(8400);
        //delay(8000); //clockwise
        
        //down
        myServo2.write( 72 );
        delay(8350);
        //myServo2.write( 93.5 );
        
        myServo2.detach();
            
        run = false;
        
        finishedEvent();
    }
}

void handleSharedEvent(const char *event, const char *data)
{

    String eventName = String( event ); 
    
    String deviceID = System.deviceID();
    
    if( eventName.indexOf( deviceID ) != -1 ){
      return;
    }
    
    run = true;

}

void finishedEvent()
{
  // 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 secondds since our last publish
  if( lastPublishedAt + publishAfter < millis() )
  {
   
      String eventName = "langley/2019/iot/eventFinished/" + System.deviceID();

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

      // then we share it out
      Particle.publish( eventName, "finished" );

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

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

}
Click to Expand
0
Circuit for direction servo
Finalcreativeproject swivelservov2 bb
0
Directing Servo Code
Servo MyServo;
int current =0;

void setup()
{
    
MyServo.attach(D2);

Particle.subscribe(  "langley/2019/iot/yellowButtonPressed/", handleSharedEventYellow );
Particle.subscribe(  "langley/2019/iot/blueButtonPressed/", handleSharedEventBlue );
Particle.subscribe(  "langley/2019/iot/redButtonPressed/", handleSharedEventRed );


}

void loop()
{


}


//for YELLOW button

void handleSharedEventYellow(const char *event, const char *data)
{
    
    String eventName = String( event );
    String deviceID = System.deviceID();
    
    //servo start

  current=MyServo.read();
//for loop with delays to slow down movement of servo

   for(int i=current; i>45;i--)
   {
       MyServo.write(i);
       delay(50);
   }
   


    if( eventName.indexOf( deviceID ) != -1 ){
      return;
    }

}

//for BLUE button

void handleSharedEventBlue(const char *event, const char *data)
{
    
    String eventName = String( event ); 
    String deviceID = System.deviceID();
    
    //serov start

    
   
   current=MyServo.read();
//for loop with delays to slow down movement of servo

  if(current<60)

   for(int i=current; i<60;i++)
   {
       MyServo.write(i);
       delay(50);
   }
  else  if(current>60)
   for(int i=current; i>60;i--)
   {
       MyServo.write(i);
       delay(50);
   }
    
    //



    if( eventName.indexOf( deviceID ) != -1 ){
      return;
    }
}

//for RED button

void handleSharedEventRed(const char *event, const char *data)
{
    
    String eventName = String( event ); // convert to a string object

    

    String deviceID = System.deviceID();
    
  // MyServo.write(80);

  //  delay(8000);

  //  MyServo.write(4);

    current=MyServo.read();
  //if(current<80)

   for(int i=current; i<80;i++)
   {
       MyServo.write(i);
       delay(50);
   }
// else if(current>80)

//   for(int i=current; i>80;i--)

//   {

//       MyServo.write(i);

//       delay(50);

//   }

 


    if( eventName.indexOf( deviceID ) != -1 ){
      // if we get anything other than -1

      // the event came from this device.

      // so stop doing stuff

      return;
    }

}
Click to Expand
0
Video of functional prototype
0

Next Steps

To take this project forward we would need to refine the mechanism that transports the ball so that it works more smoothly and reliably. Further, we would need to include more ice cream flavors in the display to make it closer to a real-world ice-cream store experience. We could also expand the concept of an ambient storefront towards other popular food items like pizza or donuts to involve individuals from all realms of the food-lover community.
Interesting feedback we received during the presentation was that we should think about other ways that this data could be collected and used, as well as finding ways to increase community engagement and making the product more scalable.  These would definitely be important considerations to make for future iterations of the project. 

0

References

  • http://www.digitalbuzzblog.com/nike-interactive-store-windows-at-selfridges/
  • https://www.vegas.com/attractions/on-the-strip/m-and-m-world/
  • https://vegas.eater.com/2019/5/20/18633132/itsugar-now-open-grand-bazaar-shops
  • https://en.wikipedia.org/wiki/Rolling_ball_sculpture
  • https://diotlabs.daraghbyrne.me/

x
Share this Project

This project is only accessible by signed in users. Be considerate and think twice before sharing.


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

Storefront display and in-store interaction to help bring together ice cream lovers.

Created

December 7th, 2019