It's Love O'Clock

Made by yoolp, Xiaowen Xu and qichengy

Found in DioT 2019: Social Objects - Part 2

We live in bubbles. Some are larger, affluent bubbles. Others are less fortunate, smaller ones. The gap between these two types of bubbles creates social isolation and neglection. The objective of our project is to facilitate the social connection with immigrants and refugees. 'It's Love O'Clock' allows people in public spaces to show their support and care through digital high-fives and displays the number of supporters in a clock format.

0

Intention

The problem area we are interested in is social isolation, particularly the social isolation of immigrants and refugees in the current political climate. Our solution involves an interactive device set up in a public space, which senses the high-five's from people. A pressure sensor on the hand-shaped board would sense and count the handprints of passers-by who volunteer to send a high-five to immigrants. The number count of high-five's would be linked to the clocks, located both in the place with the sensor and another public place with a high number of the immigrant/refugee population. The clock hand, connected to a servo, would point to a certain color programmed on Neopixel based on the number of high-five counts and replicate the sound of a real clock every time it moves. When it reaches the “love O’clock”, which is the maximum number of counts set on the clock, it would play music. This real-time interaction and data shared allow a more human-level connection between people who live in different socioeconomic, political circles. Through our solution, we aim to show social support to people who are unseen and unheard. We hope to push this social campaign forward and reach a larger population. 

0
Process

1. Ideation and planning

We first started off mapping out the interactions among the devices. We drew a diagram of the interaction between the sensor and three devices (Neopixel, Servo and Piezo). Based on the feedback we received, we switched our sensor from a touch sensor to a pressure sensor. For this prototyping stage, we decided to keep the switch we currently have in order to perfect the interaction prototype first. 

2. Build the circuit and pseudocode

The first requirement we fulfilled was planning how the Neopixel, Servo, and Piezo would receive and process the input from the pressure sensor. We discussed several methods we could potentially take; one option was to have every pressure input publish an event which triggers interactions of the devices, and another option was to have one device count the number of high-fives and publish the number to other devices. At last, we came to a conclusion that it is most effective and efficient in terms of the process operation and our team collaboration if we have the Piezo be the only one to receive the signals from the sensor, and publish for other devices to respond. 

3. Coding and build individual devices

We took a 'divide and conquer' approach for this step. We each worked on our device to create the interactions we were envisioning.

Neopixel: When it receives the signal from Piezo, Neopixel counts the number of signals, and based on the counts it displays different colors. We envision that the 'Love O'Clock' would mean having at least 1 million people's high-five's. However, for the convenience of this prototyping process, we decided to reduce the number to 15 to effectively test the interactions. 

Servo: Likewise, Servo would also receive the signals from Piezo and count the number of signals. At every count, Servo would move 24 degrees clockwise to accord with the 15 LED lights on Neopixel (= 360 degrees/15 LED lights). The challenge we faced with Servo, however, is that Servo does not rotate full 360 degrees, and its maximum rotation range is 180 degrees only. Hence, for this stage, we coded the Servo to reverse back to the starting point once it reaches the 180-degree limit.

Piezo: Piezo senses the signals from the pressure sensor (in this current stage, pushdown button). Based on the number of counts, Piezo plays different sounds. At every high-five, Piezo beeps once, and at every quarter it plays different melodies. We utilized the notes of the Westminster clock, which signifies the time progression through the completeness of the melody it plays. 

4. Paired 3 devices 

Our devices were paired from the beginning, but we still had some fine-tuning left to coordinate the three devices' interaction together. We confirmed that each device successfully fulfilled its task individually upon the signal the Piezo receives from the push-down button.

5. Adjust and test code

We have been continuously adjusting our codes to fine-tune the interactions of the devices. We have yet to improve on the consistency of the workings of these devices at an individual level and at a group level. We will continue to work on this for the next week and complete our project.


0

Outcome

List of Parts:

  • 3 Breadboard
  • 3 Particle Argon
  • 1 FSR Sensor
  • 1 Neopixel
  • 1 Piezo
  • 1 FSR Sensor
  • 1 Servos
  • 17 Resistors
  • 2 USB Micro B Cable
  • 21 Jumper Wires

Sketches:

0

Code for Side A - Piezo & FSR

Currently, we are using a pushdown button instead of FSR. 

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 = 1000;

int buttonPin = D3;
int speakerPin = D2;

int count = 0;

void setup()
{

  // We'll want to subscribe to an event thats fairly unique


  // From the Particle Docs

  // A subscription works like a prefix filter.

  // If you subscribe to "foo", you will receive any event

  // whose name begins with "foo", including "foo", "fool",

  // "foobar", and "food/indian/sweet-curry-beans".


  // Basically this will match any event that starts with 'db2018/paired/'

  // This is a feature we'll useto figure out if our event comes from

  // this device or another (see publishMyEvent below)

  pinMode(speakerPin, OUTPUT );
  pinMode( buttonPin , INPUT_PULLUP);

  Particle.subscribe(  "diot/2019/paired/yoolqc7000" , handleSharedEvent );

}

void loop()
{
    // publish my event

    // you'll want some more complex stuff here

    int buttValue = digitalRead(buttonPin);
    if (buttValue == LOW) {
        
        publishMyEvent();
        // delay for a bit

        delay(100);
    }
}


void publishMyEvent()
{
  // 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.

  
  if( lastPublishedAt + publishAfter < millis() )
  {
      // Remember our subscribe is matching  "db2018/paired/"

      // We'll append the device id to get more specific

      // about where the event came from


      // System.deviceID() provides an easy way to extract the device

      // ID of your device. It returns a String object of the device ID,

      // which is used to identify your device.


      String eventName = "diot/2019/paired/yoolqc7000" + 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, "90" );
      
        // check that it's been 10 secondds since our last publish

      
      String eventName1 = "diot/2019/paired/qcyool7000" + System.deviceID();
      Particle.publish( eventName1, "1000" );

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


      // we just pubished so capture this.

      lastPublishedAt = millis();
      
      
  }
   count = count + 1;

}

// Our event handlde requires two bits of information

// This gives us:

// A character array that consists of the event name

// A character array that contains the data published in the event we're responding to.

void handleSharedEvent(const char *event, const char *data)
{
    // Now we're getting ALL events published using "db2018/paired/"

    // This includes events from this device.

    // So we need to ignore any events that we sent.


    // Let's check the event name

    String eventName = String( event ); // convert to a string object

    // This gives us access to a bunch of built in methods

    // Like indexOf()

    // Locates a character or String within another String.

    // By default, searches from the beginning of the String,

    // but can also start from a given index,

    // allowing for the locating of all instances of the character or String.

    // It Returns: The index of val within the String, or -1 if not found.


    // We can use this to check if our event name contains the

    // id of this device


    String deviceID = System.deviceID();

    // device id = 0123456789abcdef

    // event = "diot/2019/paired/0123456789abcdef"


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

      // the event came from this device.

      // so stop doing stuff

      //return;

    }
   
    if (count == 3) {
        playPiezo1();
    } 
    if (count == 6) {
        playPiezo2();
    } 
    if (count == 9) {
        playPiezo3();
    } 
    if (count == 12) {
        playPiezo4();
    } 
    else {
        playBeep();
    }
    
    if(count >= 15) {
        count = 0;
    }
    // otherwise do your stuff to respond to

    // the paired device here


    //motorOn = true;


}

void playPiezo1() {

// create an array for the notes in the melody:

//C4,G3,G3,A3,G3,0,B3,C4


// TONES  ==========================================

// Start by defining the relationship between 

//       note, period, &  frequency. 

// #define  c     3830    // 261 Hz 

// #define  d     3400    // 294 Hz 

// #define  e     3038    // 329 Hz 

// #define  f     2864    // 349 Hz 

// #define  g     2550    // 392 Hz 

// #define  a     2272    // 440 Hz 

// #define  b     2028    // 493 Hz 

// #define  C     1912    // 523 Hz 


int melody[] = {415, 370, 330, 247};

// create an array for the duration of notes.

// note durations: 4 = quarter note, 8 = eighth note, etc.:

int noteDurations[] = {4, 4, 4, 2};


    // iterate over the notes of the melody:

    for (int thisNote = 0; thisNote < 4; thisNote++) {

      // to calculate the note duration, take one second

      // divided by the note type.

      //e.g. quarter note = 1000 / 4, eighth note = 1000/8, etc.

      int noteDuration = 1000/noteDurations[thisNote];
      tone(speakerPin, melody[thisNote],noteDuration);

      // to distinguish the notes, set a minimum time between them.

      // the note's duration + 30% seems to work well:

      int pauseBetweenNotes = noteDuration * 1.30;
      delay(pauseBetweenNotes);
      // stop the tone playing:

      noTone(speakerPin);
    }
}

void playPiezo2() {

// create an array for the notes in the melody:

//C4,G3,G3,A3,G3,0,B3,C4


// TONES  ==========================================

// Start by defining the relationship between 

//       note, period, &  frequency. 

// #define  c     3830    // 261 Hz 

// #define  d     3400    // 294 Hz 

// #define  e     3038    // 329 Hz 

// #define  f     2864    // 349 Hz 

// #define  g     2550    // 392 Hz 

// #define  a     2272    // 440 Hz 

// #define  b     2028    // 493 Hz 

// #define  C     1912    // 523 Hz 


int melody[] = {330, 415, 370, 247, 330, 370, 415, 330};

// create an array for the duration of notes.

// note durations: 4 = quarter note, 8 = eighth note, etc.:

int noteDurations[] = {4, 4, 4, 2, 4, 4, 4, 2};


    // iterate over the notes of the melody:

    for (int thisNote = 0; thisNote < 8; thisNote++) {

      // to calculate the note duration, take one second

      // divided by the note type.

      //e.g. quarter note = 1000 / 4, eighth note = 1000/8, etc.

      int noteDuration = 1000/noteDurations[thisNote];
      tone(speakerPin, melody[thisNote],noteDuration);

      // to distinguish the notes, set a minimum time between them.

      // the note's duration + 30% seems to work well:

      int pauseBetweenNotes = noteDuration * 1.30;
      delay(pauseBetweenNotes);
      // stop the tone playing:

      noTone(speakerPin);
    }
}

void playPiezo3() {


int melody[] = {415, 247, 370, 247, 247, 370, 415, 330, 415, 370, 330, 247};

// create an array for the duration of notes.

// note durations: 4 = quarter note, 8 = eighth note, etc.:

int noteDurations[] = {4, 4, 4, 2, 4, 4, 4, 2, 4, 4, 4, 2};


    // iterate over the notes of the melody:

    for (int thisNote = 0; thisNote < 12; thisNote++) {

      // to calculate the note duration, take one second

      // divided by the note type.

      //e.g. quarter note = 1000 / 4, eighth note = 1000/8, etc.

      int noteDuration = 1000/noteDurations[thisNote];
      tone(speakerPin, melody[thisNote],noteDuration);

      // to distinguish the notes, set a minimum time between them.

      // the note's duration + 30% seems to work well:

      int pauseBetweenNotes = noteDuration * 1.30;
      delay(pauseBetweenNotes);
      // stop the tone playing:

      noTone(speakerPin);
    }
}
void playPiezo4() {


int melody[] = {330, 415, 370, 247, 330, 370, 415, 330 ,415, 247, 370, 247, 247, 370, 415, 330, 415, 370, 330, 247};

// create an array for the duration of notes.

// note durations: 4 = quarter note, 8 = eighth note, etc.:

int noteDurations[] = {4, 4, 4, 2, 4, 4, 4, 2, 4, 4, 4, 2, 4, 4, 4, 2};


    // iterate over the notes of the melody:

    for (int thisNote = 0; thisNote < 16; thisNote++) {

      // to calculate the note duration, take one second

      // divided by the note type.

      //e.g. quarter note = 1000 / 4, eighth note = 1000/8, etc.

      int noteDuration = 1000/noteDurations[thisNote];
      tone(speakerPin, melody[thisNote],noteDuration);

      // to distinguish the notes, set a minimum time between them.

      // the note's duration + 30% seems to work well:

      int pauseBetweenNotes = noteDuration * 1.30;
      delay(pauseBetweenNotes);
      // stop the tone playing:

      noTone(speakerPin);
    }
}

void playBeep() {

// create an array for the notes in the melody:

//C4,G3,G3,A3,G3,0,B3,C4



int melody[] = {415};

// create an array for the duration of notes.

// note durations: 4 = quarter note, 8 = eighth note, etc.:

int noteDurations[] = {4};


    // iterate over the notes of the melody:

    for (int thisNote = 0; thisNote < 1; thisNote++) {

      // to calculate the note duration, take one second

      // divided by the note type.

      //e.g. quarter note = 1000 / 4, eighth note = 1000/8, etc.

      int noteDuration = 1000/noteDurations[thisNote];
      tone(speakerPin, melody[thisNote],noteDuration);

      // to distinguish the notes, set a minimum time between them.

      // the note's duration + 30% seems to work well:

      int pauseBetweenNotes = noteDuration * 1.30;
      delay(pauseBetweenNotes);
      // stop the tone playing:

      noTone(speakerPin);
    }
}
Click to Expand
0

Code for Side B - Servo

0
int servoPin = A3;
Servo myServo;
int servoPos = 0;
int count = 0;
String deviceID; 

void setup() {

 // attaches the servo on the A3 pin to the servo object

  myServo.attach( A3 );

   //Register our Particle to control the servo

   Particle.function("servo", servoControl);
   
   // Keep a cloud variable for the current position

  Particle.variable(  "servoPos" , &servoPos , INT );
  
  Particle.subscribe(  "diot/2019/paired/yoolqc7000" , handleSharedEvent );
  
  Particle.variable("publishCount", count );
  
  Particle.variable("deviceid", deviceID);
}

void loop() {
}

int servoControl(String command)
{
    // Convert

   int newPos = command.toInt();
   // Make sure it is in the right range

   // And set the position

   servoPos = constrain( newPos, 0 , 180);

   // Set the servo

   myServo.write( servoPos );
   
  
   // done

   return 1;
}



void handleSharedEvent(const char *event, const char *data)
{
    // Now we're getting ALL events published using "db2018/paired/"

    // This includes events from this device.

    // So we need to ignore any events that we sent.


    // Let's check the event name

    String eventName = String( event ); // convert to a string object

    // This gives us access to a bunch of built in methods

    // Like indexOf()

    // Locates a character or String within another String.

    // By default, searches from the beginning of the String,

    // but can also start from a given index,

    // allowing for the locating of all instances of the character or String.

    // It Returns: The index of val within the String, or -1 if not found.


    // We can use this to check if our event name contains the

    // id of this device


    deviceID = System.deviceID();

    // device id = 0123456789abcdef

    // event = "diot/2019/paired/0123456789abcdef"


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

      // the event came from this device.

      // so stop doing stuff

      return;
    }
    count = count + 1;
    if (count >= 7){
        count = 0;
    }
    // otherwise do your stuff to respond to

    // the paired device here

    // Convert

//   int newPos = data.toInt();

   // Make sure it is in the right range

   // And set the position

   servoPos = constrain( count * 24, 0 , 180);

   // Set the servo

   myServo.write( servoPos );
   
    // Particle.publish( "diot/2019/paired/seven12345678", "Test hahaha" );


    //motorOn = true;


}
Click to Expand
0

Code for Side C - NeoPixel

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

#include <neopixel.h>

// IMPORTANT: Set pixel COUNT, PIN and TYPE

#define PIXEL_PIN D2
#define PIXEL_COUNT 15
#define PIXEL_TYPE WS2812

int count = 0;

int redValue = 0; // Full brightness for an ANODE RGB LED is 0, and off 255

int greenValue = 0; // Full brightness for an ANODE RGB LED is 0, and off 255

int blueValue = 0; // Full brightness for an ANODE RGB LED is 0, and off 255</td>


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


void setup()
{
  strip.begin();
  strip.show(); // Initialize all pixels to 'off'

  Particle.subscribe( "diot/2019/paired/qcyool7000" , handleSharedEvent );
//   Particle.variable ("publishCount", publishCount);

  neoPixelLight() ;
  
  Particle.variable("publishCount", count);

}

void loop()
{
    
}

// Our event handlde requires two bits of information

// This gives us:

// A character array that consists of the event name

// A character array that contains the data published in the event we're responding to.

void handleSharedEvent(const char *event, const char *data)
{
    // Now we're getting ALL events published using "db2018/paired/"

    // This includes events from this device.

    // So we need to ignore any events that we sent.


    // Let's check the event name

    String eventName = String( event ); // convert to a string object

    // This gives us access to a bunch of built in methods

    // Like indexOf()

    // Locates a character or String within another String.

    // By default, searches from the beginning of the String,

    // but can also start from a given index,

    // allowing for the locating of all instances of the character or String.

    // It Returns: The index of val within the String, or -1 if not found.


    // We can use this to check if our event name contains the

    // id of this device


    String deviceID = System.deviceID();

    // device id = 0123456789abcdef

    // event = "diot/2019/paired/0123456789abcdef"


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

      // the event came from this device.

      // so stop doing stuff

      return;
    }
    
    // otherwise do your stuff to respond to

    // the paired device here


    //motorOn = true;

    count += 1;
    
    if (count > 5){
        neoPixelLight(); 
    }
}



void neoPixelLight() {
    uint16_t i;
    uint32_t off = strip.Color(0, 0, 0);
    uint32_t cyan = strip.Color(0, 255, 255); 

    for(i=0; i< strip.numPixels(); i++) {
        strip.setPixelColor(i, cyan);
		strip.show();
		delay( 100 );
    }
    
    for(i=0; i< strip.numPixels(); i++) {
        strip.setPixelColor(i, off);
		strip.show();
		delay( 100 );
    }
}
Click to Expand
0
x
Share this Project

Courses

49713 Designing for the Internet of Things

· 18 members

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


Focused on
About

We live in bubbles. Some are larger, affluent bubbles. Others are less fortunate, smaller ones. The gap between these two types of bubbles creates social isolation and neglection. The objective of our project is to facilitate the social connection with immigrants and refugees.

'It's Love O'Clock' allows people in public spaces to show their support and care through digital high-fives and displays the number of supporters in a clock format.

Created

February 10th, 2019