Kiss Communicator

Made by Grace Zhu, Yulin Chen and Narayan Ashanahalli

Our project tends to simulate kisses to bridge the gap between physical and digital connection and strengthen long-Distance relationships

Created: December 16th, 2024

0

Intention

As more and more families are living far apart and have less opportunity to interact physically. And currently, many forms of physical interaction cannot effectively convey the emotional message that comes from the actual touch sensation. In this case, kiss communicator can fill the Void in Emotional Communication. The Kiss Communicator introduces tactile interaction, enabling people to send and receive simulated kisses to bridge the gap between physical and digital connection and strengthen long-Distance relationships. Our project tends to explore how can physical device express kiss to each other to build emotional connection.

0

Historical Case

What we Know: The precedent we choose is designed by Duncan Kerr, Heather Martin and Mat Hunte for people separated by physical distance. The only one resource for us to learn about this device is the essay: Buchenau, Marion, and Jane Fulton Suri. "Experience prototyping." Proceedings of the 3rd conference on Designing interactive systems: processes, practices, methods, and techniques. 2000. It shows that the device allowed users to blow into it to send a kiss signal represented by light patterns to their partner's device and create your "message" in the form of an animated light sequence as the device responds to your breath. The "message" shows while you blow and if you are happy with it, you simply relax your grip, and it is sent to the corresponding Communicator.

What don't we know about this precedent: Wireless Connectivity: How many people can be wirelessly connected to each other using this Kiss Communicator? (Reference: The Kiss device and app, which allow users to send different colors to various receivers to convey their mood.) Internal Mechanism: How do the neo pixels respond to the messages received from the blow sensor? Button Functionality: How does the button work? Do users need to press it before blowing into the device?

Reference:  Buchenau, Marion, and Jane Fulton Suri. "Experience prototyping." Proceedings of the 3rd conference on Designing interactive systems: processes, practices, methods, and techniques. 2000.

0

PHASE I: REMAKING

Approach

The original kiss communicator enabled both parties to respond to each other’s breathing and visually represented the interaction through a Neopixel display. Based on our understanding of the references, the device facilitated a single kiss transmission: User 1 would press a button on the side of the device, breathe into it, and release the button to send the kiss. User 2 would then press the same button to receive the transmitted kiss after the signal was received.

At this stage, we aimed to build upon the precedent by preserving the original simple kiss transmission process—utilizing the wind sensor to generate kisses—while introducing a new functionality. This enhancement allows the device to send multiple kisses in a single transmission, expanding the interaction while staying true to the core concept.

0

Process

  1. In the first iteration, we experimented with using a wind sensor on a single device. We designed a simple circuit to understand the mechanism of the wind sensor by changing the state of an LED through blowing air. This helped us learn how the wind sensor performs continuous readings and controls outputs.
  2. In the second iteration, we developed a more complex workflow to refine the concept of our "kiss communicator." Using Particle’s webhook feature, we connected two devices, replaced the LED with a Neopixel, and added a button to simulate the experience of "starting the recording."
  3. In the final iteration, we built on the previous designs and added the functionality to transmit the number of breaths (or kisses). User 1 could send any number of kisses to User 2. In this iteration, the process began with User 1 pressing the button, triggering the Neopixel to flash red, indicating the start of the recording. User 1 would then blow air several times before releasing the button to send the kisses. Upon completion, User 2’s device would display the corresponding number of kisses as transmitted by User 1’s device.

These iterations laid a solid foundation for the future development of our final reinterpretation project.


0

Prototype

Due to the limited number of wind sensors, we divided the two devices into a sender and a receiver. Device 1 (sender) is equipped with a wind sensor, a Neopixel, and a button, while Device 2 (receiver) is equipped with a Neopixel to complete the process.

0
Kiss Communicator Video 1
supermao - https://youtu.be/tzV-4i8LuQw
0
//Device 1
#include "Particle.h"
#include <neopixel.h>
#include <iostream>
#include <string>

#define PIXEL_PIN SPI
#define PIXEL_COUNT 8
#define PIXEL_TYPE WS2812

int windSensorPin = A0;
int windSpeed = 0; //range:1700-2400
int threshold = 2000;
unsigned long lastFade = 0;
int Interval = 3000;
int buttonPin = D1;

int redValue = 255;
int greenValue = 255; 
int blueValue = 255;
int color = 255;

int breathecount = 0;
bool buttonsignal = true;
int delayvalue = 5;
int largerthanthresholdcount = 0;

Adafruit_NeoPixel strip(PIXEL_COUNT, PIXEL_PIN, PIXEL_TYPE);
uint16_t i;
uint32_t c = strip.Color(0, 0, 0);

void setup() {
    Serial.begin( 9600 );

    strip.begin();
    strip.show(); 
    Particle.variable("speed",&windSpeed, INT);
    pinMode( buttonPin , INPUT_PULLUP);

}

void loop() {
    windSpeed = analogRead(windSensorPin); 
    int buttonState = digitalRead( buttonPin );


    while( buttonState == LOW ){
        
        windSpeed = analogRead(windSensorPin); 
        
        if (windSpeed>threshold){
            buttonsignal = false;
            Secondstagebreathe();
        }
        if(buttonsignal == true){
            Firststagesignal();
        }
        buttonState = digitalRead( buttonPin );
        
    }
    
    if(buttonsignal == false){
        if(breathecount>0){
            for(i=0;i<3;i++){
                Sendsuccessfully();
                delay(50);
            }
            buttonsignal = true;
            char breathe[10];
            itoa(breathecount, breathe, 10);
            Particle.publish("kissPublish",breathe);
        }
    }
    
    breathecount = 0;
}

void Firststagesignal(){
    for(i=0; i< strip.numPixels(); i++) {
        c = strip.Color(redValue, 0, 0);
        strip.setPixelColor(i, c );
		strip.show();
		delay( 5 );
    }
    for(i=0; i< strip.numPixels(); i++) {
        c = strip.Color(0, 0, 0);
        strip.setPixelColor(i, c );
		strip.show();
		delay( 5 );
    }
}

void delayvariation(){
    windSpeed = analogRead(windSensorPin);
    delay(5);
    if(windSpeed){
        
    }
}


void Secondstagebreathe(){
    c = strip.Color(255, 255, 255);
    if (windSpeed>threshold){
        unsigned long now = millis();
        c = strip.Color(0, 0, 0);
        if ((now - lastFade) >= Interval) {
            for (int color = 0; color < 255; color++) {
                for (i = 0; i < strip.numPixels(); i++) {
                    c = strip.Color(color, color, color);
                    strip.setPixelColor(i, c);
                }
                strip.show();
                delay(delayvalue);
            }
                
            for (int color = 255; color > 0; color--) {
                for (i = 0; i < strip.numPixels(); i++) {
                    c = strip.Color(color, color, color);
                    strip.setPixelColor(i, c);
                }
                strip.show();
                delay(delayvalue);
            }
        lastFade = now;
        }
    breathecount ++;
    Serial.println(breathecount);
    }
}

void Sendsuccessfully(){
    //blink green lights
    uint16_t i;
    uint32_t c = strip.Color(0, 0, 0);
    for(i=0; i< strip.numPixels(); i++) {
        c = strip.Color(0, greenValue, 0);
        strip.setPixelColor(i, c );
		strip.show();
		delay( 10 );
        }
    for(i=0; i< strip.numPixels(); i++) {
        c = strip.Color(0, 0, 0);
        strip.setPixelColor(i, c );
		strip.show();
		delay( 10 );
    }
}
Click to Expand
0
// Device 2

#include "Particle.h"
#include <neopixel.h>

#define PIXEL_PIN SPI
#define PIXEL_COUNT 8
#define PIXEL_TYPE WS2812

Adafruit_NeoPixel strip(PIXEL_COUNT, PIXEL_PIN, PIXEL_TYPE);
uint16_t i;
uint32_t c = strip.Color(0, 0, 0);
int buttonPin = D2;
unsigned long lastFade = 0;
int Interval = 3000;

bool receive_or_not = false;
bool signall = true;
int breathecountreceive = 0;
int buttonState = digitalRead( buttonPin );

void setup(){
    strip.begin();
    strip.show();
    Particle.subscribe("kissReceive", handlekiss);
    Serial.begin( 9600 );
}

void handlekiss(const char *event, const char *data) {
    breathecountreceive = atoi(data);
    receive_or_not = true;
}

void loop(){
    Serial.println(breathecountreceive);
    Serial.println(receive_or_not);
    Serial.println(buttonState);
    buttonState = digitalRead( buttonPin );
    
    if(receive_or_not == true){
        if (signall == true){
            for(i=0 ;i < breathecountreceive;i++){
                Secondstagebreathe();
            }
            // for( i = 0;i<3;i++){
            //     Receivepinksignal();
            // }
        }
        signall = false;
        // else if (buttonState==LOW){
        //     for(i=0;i<breathecountreceive;i++){
        //         Secondstagebreathe();
        //     }
        //     signall = false; 
        // }
    }
    delay(500);
    
}

void Receivepinksignal(){
    for(i=0; i< strip.numPixels(); i++) {
        c = strip.Color(0, 0, 255);
        strip.setPixelColor(i, c );
		strip.show();
		delay( 5 );
    }
    for(i=0; i< strip.numPixels(); i++) {
        c = strip.Color(0, 0, 0);
        strip.setPixelColor(i, c );
		strip.show();
		delay( 5 );
    }
}

void Secondstagebreathe(){
    c = strip.Color(255, 255, 255);
    unsigned long now = millis();
    c = strip.Color(0, 0, 0);
    if ((now - lastFade) >= Interval) {
        for (int color = 0; color < 255; color++) {
            for (i = 0; i < strip.numPixels(); i++) {
                c = strip.Color(color, color, color);
                strip.setPixelColor(i, c);
            }
            strip.show();
            delay(5);
        }
                
        for (int color = 255; color > 0; color--) {
            for (i = 0; i < strip.numPixels(); i++) {
                c = strip.Color(color, color, color);
                strip.setPixelColor(i, c);
            }
            strip.show();
            delay(5);
        }
    lastFade = now;
    }
}
Click to Expand
0

Reflection

Reconstructing the Kiss Communicator has prompted us to rethink the remote transmission of intimate signals. The accuracy, recognizability, and diversity of these signals are particularly crucial. Exploring ways to create more meaningful and engaging intimate signals will be the focus of our next stage of research. How can a simple act of breathing be used to convey significant love messages?

0

PHASE II: REINTERPRETING

0

Process

Our reinterpretation of the Kiss Communicator aims to retain some original features of the product while addressing certain limitations. The interaction flow and signal design of the original precedent were relatively simple, and its appearance—being overly rigid and closely resembling lips—might cause discomfort for some users due to its mimicry of a body part. This prompted us to rethink improvements at both the signal and product design levels. Our goal is to enhance the interactive functionality and accuracy of love message transmission while making the product’s appearance more innovative and natural compared to the precedent. 

Honestly, we indeed thought we could add more performance like motor or servo which can combine with some cute and lovely stickers to create more emotional experience. And we really have tried to do this during the whole process, however, it is a same level challenge as figuring out very complex multi-layers signal of breath because I have to think about how the movement of motor or servo can match the breaths which have different duration and quantity, will it act continuously during the long breath or act every slow as the duration of breath which will lead to extremely different emotion for another user. And can it have some distinctive movement when user A blows into specific number of breaths. All of this actually is part of our concept, but in reality, I tried it, and it couldn't match the breath duration so well, so I leave it and focus more on figuring out the multi-layer signals due to the time we have. Simultaneously, I also imagine a funny experience when both user A and user B blow into the product simultaneously, the neoxpixel and motor can create a totally different performance to remind both side you are kissing at the same time now which is a breakthrough concept compared to the original simple experience.

0

Conceptual Design(s): 

In our new design, we continue to use the combination of a wind sensor and Neopixel. However, we replaced the linear Neopixel strip with a circular Neopixel ring containing 12 lights, creating a more rounded and comfortable product shape. We propose that our new Kiss Communicator can record the user’s number of breaths and the duration of each breath. Additionally, we aim to enable users to know the exact number of kisses being sent and received at any given time. These detailed improvements enrich the user’s understanding of the transmitted information and add a playful dimension to sharing love messages.

For example, User A can send a sequence of kisses—5, 2, 0, 1, 3, 1, 4—to User B, conveying the love message "Love you forever" ("1314"-"yi sheng yi shi (in Chinese)"). User A can also create unique breathing rhythms for User B to experience a more personalized and intimate connection. Furthermore, if the love message is successfully sent but the recipient is unavailable to receive it, the message will remain stored to ensure it isn’t missed. In such cases, User A can continue to send multiple kisses, which will accumulate on User B’s device until they are retrieved (up to a maximum limit).

Ideally, the device would support bidirectional love message transmission, allowing both users to share their affection seamlessly across distances.

0
Concept
supermao - https://youtu.be/NEiCYS677J0
0

Prototype

The following includes the circuit diagram, basic workflow, and BOM (Bill of Materials).

0

Product Workflow

Phase 1: Sending a Kiss (User A)

Start Recording:

User A presses the button, and the Neopixel begins flashing a yellow light to indicate "recording started."

Blowing to Record Kisses:

While holding the button, User A blows into the device. After each blow, one Neopixel light turns pink to show a kiss has been recorded. User A can continue blowing multiple times, with the Neopixel keeping track of the total number of kisses by lighting up the corresponding number of pink lights. The duration of each blow can be adjusted to customize the length of each kiss.

Sending Kisses:

Once User A finishes, releasing the button initiates the sending process. The Neopixel flashes green to confirm that the kisses have been successfully sent.

0
//Device 1
// This #include statement was automatically added by the Particle IDE.
#include <ArduinoJson.h>

// This #include statement was automatically added by the Particle IDE.
#include <neopixel.h>

#include "Particle.h"
#include <iostream>
#include <string>
#include <stdio.h>

#define PIXEL_PIN SPI
#define PIXEL_COUNT 12
#define PIXEL_TYPE WS2812
#define MAX_SIZE 100

//particle serial monitor

int windSensorPin = A0;
int windSpeed = 0; //range:1700-2400
int threshold = 2000;
unsigned long lastFade = 0;
int Interval = 3000;
unsigned long servolastFade = 0;
int servoInterval = 20;
int buttonPin = D1;

//color
int redValue = 255;
int greenValue = 255; 
int blueValue = 255;
int color = 255;

//breathe
int breathecount_double = 0;
int breathecount = 0;
bool buttonsignal = true;
bool kissreceive = false;
bool currentwindbool = false;
bool firstsignalonce = true;
int largerthanthresholdcount = 0;
int mapbreathcount = 0;
int mapdelaytime = 0;
int j=0;

// int breathlength_max = ;
int delaytime = 2;
int windreadinterval = 50;
int breathelengthlist[MAX_SIZE]; 
int list_size = 0;
int listmax = 12;
//servo
// int servoPin = A5;
// int servoPosition = 0;
// Servo servo;

Adafruit_NeoPixel strip(PIXEL_COUNT, PIXEL_PIN, PIXEL_TYPE);
uint16_t i;
uint32_t c = strip.Color(0, 0, 0);
int k = 0;

void setup() {
    Serial.begin( 9600 );
    strip.begin();
    strip.show(); 
    Particle.variable("speed",&windSpeed, INT);
    pinMode( buttonPin , INPUT_PULLUP);
    // servo.attach(servoPin);
    Particle.subscribe("kissReceivesuccess", handlekisssuccess);
}

void handlekisssuccess(const char *event, const char *data) {
    kissreceive = true;
}

void loop() {
    windSpeed = analogRead(windSensorPin); 
    int buttonState = digitalRead( buttonPin );
    
    while( buttonState == LOW ){
        windSpeed = analogRead(windSensorPin); 
        delay(windreadinterval);
        
        if(firstsignalonce == true){
            Firststagesignal();
        }
        
        while(windSpeed>threshold){
            delayvariation();
            currentwindbool = true;
            firstsignalonce = false;
            buttonsignal = false;
        }
        mapbreathcount = map(largerthanthresholdcount,0,100,1,10);
        // Serial.println(largerthanthresholdcount);
        // Serial.println(mapbreathcount);
        
        if (currentwindbool==true) {
            if(windSpeed<threshold){
                // breathecount_double++;
                delaytime = (windreadinterval*mapbreathcount*0.1)/5*2;
                mapdelaytime = map(delaytime,0,30,1,12);
                Serial.println(mapdelaytime);
                append();
                showList();
                
                currentwindbool = false;
                delaytime = 2;
            }
            // Serial.println(breathecount_double);
        }
        buttonState = digitalRead( buttonPin );
        largerthanthresholdcount = 0;
        // Serial.println(list_size);
        //blink one more light when count+1
        breathtimeslightrecord();
    }
    // breathecount = breathecount_double*0.5;
    // Serial.println(delaytime);
    // Serial.println(breathecount); 
    
    if(buttonsignal == false){
        j=0;
        while(j < list_size){
            // unsigned long now = millis();
            Secondstagebreathe();
            // servomotion();
            // lastFade = now;
            j++;
        }
        // for(k=0;k<breathecount;k++){
        //     Secondstagebreathe();
        // }
        delay(500);
        for(i=0;i<3;i++){
            Sendsuccessfully();
            delay(50);
        }
        buttonsignal = true;
        firstsignalonce = true;
        //send
        Serial.println(list_size);
        
        char breathe[10];
        itoa(list_size, breathe, 10);
        Particle.publish("kissPublish",breathe);
        // Serial.println(breathecount);
            
        StaticJsonDocument<256> doc;
        JsonArray array = doc.createNestedArray("breathecountlist");
        for (i = 0; i < list_size; i++) {
            array.add(breathelengthlist[i]);
        }
        char jsonBuffer[256];
        serializeJson(doc, jsonBuffer);
        Particle.publish("delayPublish",jsonBuffer,PRIVATE);
        
        breathecount = 0;
    }
    breathecount = 0;
    delaytime = 5;
    clearList();
    
    if(kissreceive == true){
        i=0;
        while(i<3){
            kissreceivesuccess();
            i++;
        }
        kissreceive = false;
    }
}

void Firststagesignal(){
//     for(i=0; i< strip.numPixels(); i++) {
//         c = strip.Color(255, 255, 0);
//         strip.setPixelColor(i, c );
// 		strip.show();
		
//     }
//     for(i=0; i< strip.numPixels(); i++) {
//         c = strip.Color(0, 0, 0);
//         strip.setPixelColor(i, c );
// 		strip.show();
// // 		delay( 5 );
//     }
//     delay( 100 );
    for (int color = 0; color < 255; color++) {
        for (i = 0; i < strip.numPixels(); i++) {
            c = strip.Color(color, color, 0);
            strip.setPixelColor(i, c);
        }
        strip.show();
        delay(0);
    }
                
    for (int color = 255; color > 0; color--) {
        for (i = 0; i < strip.numPixels(); i++) {
            c = strip.Color(color, color, 0);
            strip.setPixelColor(i, c);
        }
        strip.show();
        delay(0);
    }
}

void kissreceivesuccess(){
    for (int color = 0; color < 255; color++) {
        for (i = 0; i < strip.numPixels(); i++) {
            c = strip.Color(0, 0, color);
            strip.setPixelColor(i, c);
        }
        strip.show();
        delay(0);
    }
                
    for (int color = 255; color > 0; color--) {
        for (i = 0; i < strip.numPixels(); i++) {
            c = strip.Color(0, 0, color);
            strip.setPixelColor(i, c);
        }
        strip.show();
        delay(0);
    }
}

void breathtimeslightrecord(){
    for (i = 0; i < list_size; i++){
        c = strip.Color(225, 0, 255);
        strip.setPixelColor(i, c);
        strip.show();
    }
}

void Secondstagebreathe(){
    for (int color = 0; color < 255; color++) {
        for (i = 0; i < strip.numPixels(); i++) {
            c = strip.Color(color, color, color);
            strip.setPixelColor(i, c);
        }
        strip.show();
        delay(breathelengthlist[j]);
    }
    for (int color = 255; color > 0; color--) {
        for (i = 0; i < strip.numPixels(); i++) {
            c = strip.Color(color, color, color);
            strip.setPixelColor(i, c);
        }
        strip.show();
        delay(breathelengthlist[j]);
    }
}

// void delayvariation(){
//     windSpeed = analogRead(windSensorPin);
//     delay(windreadinterval);
//     if(windSpeed>threshold){
//         largerthanthresholdcount++;
//         // Serial.println(largerthanthresholdcount);
//     }
//     // Serial.println(largerthanthresholdcount);
//     // delaytime = windreadinterval*largerthanthresholdcount;
// }

void delayvariation(){
    windSpeed = analogRead(windSensorPin);
    delay(windreadinterval);
    largerthanthresholdcount++;
    // Serial.println(largerthanthresholdcount);
}

void append() {
    if (list_size < listmax) {
        breathelengthlist[list_size] = mapdelaytime;
        list_size++;
    } 
}

void showList(){
    if (list_size == 0) {
        Serial.println("nothing");
        return;
    }
    for (int i = 0; i < list_size; i++) {
        Serial.print(breathelengthlist[i]);
        Serial.print(" ");
    }
    Serial.println();
}

void clearList() {
    list_size = 0;
}

void Sendsuccessfully(){
    //blink green lights
    c = strip.Color(0, 0, 0);
    for(i=0; i< strip.numPixels(); i++) {
        c = strip.Color(0, greenValue, 0);
        strip.setPixelColor(i, c );
		strip.show();
		delay( 50 );
        }
    for(i=0; i< strip.numPixels(); i++) {
        c = strip.Color(0, 0, 0);
        strip.setPixelColor(i, c );
		strip.show();
		delay( 50 );
    }
}
Click to Expand
0

Phase 2: Receiving a Kiss (User B)

Receiving the Love Message:

When User B receives the love message, the device clearly displays the number of kisses sent by User A. This is indicated by the corresponding number of pink lights flashing on User B’s Neopixel.

Accepting the Kisses:

When User B presses the button to accept the kisses, the pink lights stop flashing. Simultaneously, User A’s Neopixel flashes blue once to indicate that User B has started receiving the kisses. User B’s device then plays back User A’s kisses, with each kiss lasting as long as User A’s breath duration. The longer User A blew into the wind sensor, the longer each kiss lasts for User B.

Confirmation of Completion:

Once all the kisses have been displayed, User A’s Neopixel flashes blue four times to indicate that User B has finished playing all the kisses.

0
//Device2
// This #include statement was automatically added by the Particle IDE.
#include <ArduinoJson.h>

// This #include statement was automatically added by the Particle IDE.
#include <neopixel.h>

#include "Particle.h"

#define PIXEL_PIN SPI
#define PIXEL_COUNT 12
#define PIXEL_TYPE WS2812
#define MAX_LIST_SIZE 100

//particle serial monitor

Adafruit_NeoPixel strip(PIXEL_COUNT, PIXEL_PIN, PIXEL_TYPE);
uint16_t i;
uint32_t c = strip.Color(0, 0, 0);
int buttonPin = D2;
unsigned long lastFade = 0;
int Interval = 3000;

//recieve
bool receive_or_not = false;
bool signall = true;
bool runonce = false;
int breathecountreceive = 0;
int delaytimereceive = 5;
int buttonsignal = true;
int buttonState = digitalRead( buttonPin );
bool listreceive = false;

//servo
int servoPin = A5;
int servoPosition = 0;
Servo servo;

int breathreceiveList[MAX_LIST_SIZE];
int listSize = 0; 
int listmax = 12;
int j=0;

void setup(){
    strip.begin();
    strip.show();
    Particle.subscribe("kissReceive", handlekiss);
    Particle.subscribe("delayReceive",handledelay);
    Serial.begin( 9600 );
    servo.attach(servoPin);
    pinMode( buttonPin , INPUT_PULLUP);
}

void handlekiss(const char *event, const char *data) {
    Serial.println(data);
    breathecountreceive = atoi(data);
    receive_or_not = true;
    signall = true;
    runonce=true;
}

void handledelay(const char *event, const char *data) {
    
    // Serial.println("Received delayPublish event:");
    Serial.println(data);  // Log the raw JSON string

    StaticJsonDocument<256> doc;
    DeserializationError error = deserializeJson(doc, data);

    if (error) {
        // Serial.print("JSON deserialization failed: ");
        // Serial.println(error.c_str());
        return;
    }

    // Extract the array from the JSON object
    JsonArray array = doc["breathecountlist"];
    if (!array.isNull()) {
        // Serial.println("Parsed values:");
        for (JsonVariant v : array) {
            int value = v.as<int>();  // Convert each element to an integer
            if (listSize < MAX_LIST_SIZE) {
                breathreceiveList[listSize] = v.as<int>();
                listSize++;
                // Serial.println(listSize);
            }
            Serial.println(value);   // Log the value
        }
    } else {
        Serial.println("breathecountlist is null!");
    }
    
}

void loop(){
    // Serial.println(breathecountreceive);
    // Serial.println(delaytimereceive);
    // Serial.println(receive_or_not);
    // Serial.println(buttonState);
    // Serial.println(runonce);
    // Serial.println(listreceive);
    
    
    buttonState = digitalRead( buttonPin );
    // int breathecountreceive1 = atoi(data);
    // if(breathecountreceive1!=breathecountreceive){
    //     receive_or_not = true;
    // }
    
    if(receive_or_not == true){
        if (signall == true){
            Receivepinksignal();
        }
        
        while(buttonState==LOW){
            signall = false;
            Particle.publish("kissSuccess");
            if(runonce==true){
                j=0;
                while(j < listSize){
                    // Serial.println(breathecountreceive);
                    // unsigned long now = millis();
                    Secondstagebreathe1();
                    // servomovement();
                    // lastFade = now;
                    j++;
                }
                runonce=false;
            }
            runonce=false;
            receive_or_not = false;
            buttonState = digitalRead( buttonPin);
            clearList();
            
            // endbreathe();
        }
        // Serial.println(listSize);
    }
    // clearList();
    // breathecountreceive = 0;
    // receive_or_not = false;
}

void Receivepinksignal(){
    for(i=0; i< listSize; i++) {
        c = strip.Color(225, 0, 255);
        strip.setPixelColor(i, c );
		strip.show();
		delay( 150 );
    }
    for(i=0; i< listSize; i++) {
        c = strip.Color(0, 0, 0);
        strip.setPixelColor(i, c );
		strip.show();
		delay( 150 );
    }
    
//     for(i=0; i< breathecountreceive; i++) {
//         c = strip.Color(225, 0, 255);
//         strip.setPixelColor(i, c );
// 		strip.show();
// 		delay( 20 );
//     }
//     for(i=0; i< breathecountreceive; i++) {
//         c = strip.Color(0, 0, 0);
//         strip.setPixelColor(i, c );
// 		strip.show();
// 		delay( 20 );
//     }
}

void Secondstagebreathe1(){

    for (int color = 0; color < 255; color++) {
        for (i = 0; i < strip.numPixels(); i++) {
            c = strip.Color(color, color, color);
            strip.setPixelColor(i, c);
        }
        strip.show();
        delay(breathreceiveList[j]);
    }
                
    for (int color = 255; color > 0; color--) {
        for (i = 0; i < strip.numPixels(); i++) {
            c = strip.Color(color, color, color);
            strip.setPixelColor(i, c);
        }
        strip.show();
        delay(breathreceiveList[j]);
    }
}

// void endbreathe(){
//     for(i=0; i< strip.numPixels(); i++) {
//         c = strip.Color(225, 0, 255);
//         strip.setPixelColor(i, c );
// 		strip.show();
// 		delay( 150 );
//     }
//     for(i=0; i< strip.numPixels(); i++) {
//         c = strip.Color(0, 0, 0);
//         strip.setPixelColor(i, c );
// 		strip.show();
// 		delay( 150 );
//     }
// }

void clearList() {
    listSize = 0;
}
Click to Expand
0

Code Challenge

Our biggest challenge in designing the code was managing the complexity and connectivity of multiple signals. For example:

Handling the Yellow Signal:

When the button is initially pressed to start recording, the Neopixel begins flashing yellow to indicate "recording started." However, this yellow signal needs to stop as soon as the user blows into the wind sensor for the first time and in subsequent breaths to avoid interfering with the user’s interaction. The yellow signal should only reappear when the user releases the button and presses it again to start a new recording session.

Recording Breath Duration:

Implementing the logic for recording the duration of each breath was relatively complex, as it involved storing the duration of every breath in a list and successfully transmitting this data to another device. To achieve this, I introduced a dynamic boolean variable (default: false) to control the start and end of the recording.

When the wind sensor detects a value greater than the predefined threshold for the first time, the boolean is set to true.

While the boolean is true, if the wind intensity falls below the threshold, we can calculate the duration of this breath. The duration is determined by multiplying the reading interval of the wind sensor by the number of readings taken during the breath. This calculated duration is then stored in a corresponding list. Once the breath is complete, the boolean is reset to false, ready for the next breath.

This method allowed us to precisely measure and store each breath duration while ensuring the system resets appropriately for subsequent recordings.

0
Kiss Communicator Video 2
supermao - https://youtu.be/ClC92KVoCqs
0

WORKING WITH AI COPILOTS

We didn't reply on AI copilots a lot. The only thing we ask AI for help is how to send a number and a list though webhook. And the blow code is what AI gives us. It really gives us a great help because one of our important concepts is to match different breath duration.

We think AI like GPT can really provide us with some basic information like this, but when it comes to some complex logic, it cannot work very well as us. Like when I asked AI to give an idea how to control the first stage signal light to make sure it will stop after holding the button and blowing. What AI first gave me was use many if functions and Booleans, but I think it is more convenient to use while() function. So, I wrote by myself. Maybe if I continued asking AI to update the code following what I imagine, it indeed can be better than the first time.

0
void handledelay(const char *event, const char *data) {
    
    // Serial.println("Received delayPublish event:");
    Serial.println(data);  // Log the raw JSON string

    StaticJsonDocument<256> doc;
    DeserializationError error = deserializeJson(doc, data);

    if (error) {
        // Serial.print("JSON deserialization failed: ");
        // Serial.println(error.c_str());
        return;
    }

    // Extract the array from the JSON object
    JsonArray array = doc["breathecountlist"];
    if (!array.isNull()) {
        // Serial.println("Parsed values:");
        for (JsonVariant v : array) {
            int value = v.as<int>();  // Convert each element to an integer
            if (listSize < MAX_LIST_SIZE) {
                breathreceiveList[listSize] = v.as<int>();
                listSize++;
                // Serial.println(listSize);
            }
            Serial.println(value);   // Log the value
        }
    } else {
        Serial.println("breathecountlist is null!");
    }
Click to Expand
0

REFLECTION AND CRITIQUE 

Through this process, we have clearly recognized the complexity of transmitting abstract information like a love message over distance. Emotions are intricate, and sometimes even face-to-face communication struggles to convey emotions and thoughts accurately. This presents a huge challenge for products of this nature. Additionally, each person expresses emotions and signals differently, which further complicates the design of a product meant to foster emotional connection.

In designing a product like this, we believe it is important to give users the freedom to customize their own unique love messages. This was an aspect we envisioned for this project, but unfortunately, we were not able to fully implement it. For instance, we tried to allow different users to create their own kiss rhythm by interpreting the number and duration of kisses.

Furthermore, we had planned to add more interactive features beyond the Neopixel, but due to time constraints, we were unable to complete and showcase them before the course ended. After receiving feedback from the professors, I strongly agree that kiss communication can expand into other forms, such as sound. One professor mentioned a sound that only humans can produce, which has yet to be defined with a specific meaning. This special sound concept inspired me to think about transmitting unique signals, as our love message system also needs to create differentiation between users to foster stronger emotional connections.

Moreover, the product form itself can be diversified, including items like necklaces, watches, masks, and more. We believe that in the future, love connection products will continue to improve in simulating real intimate experiences.

0
Kiss Communicator video 3
supermao - https://youtu.be/GzXqnqwyVzE
0
Kiss Communicator Video 4
supermao - https://youtu.be/_V7R3y4laqk
0

References: 

Buchenau, Marion, and Jane Fulton Suri. "Experience prototyping." Proceedings of the 3rd conference on Designing interactive systems: processes, practices, methods, and techniques. 2000.

https://tangible.media.mit.edu/project/bubble-talk/

https://www.designboom.com/technology/prana-b-reel-creative-light-breath-08-17-2015/

x
Share this Project


Focused on
About

Our project tends to simulate kisses to bridge the gap between physical and digital connection and strengthen long-Distance relationships