Match Maker – Chaoyue Yuan – Professor Rudi
Context and Significance
My midterm project is similar to the previous group project in a way that helps to emphasize my understanding of interaction. Both projects require the audience to create an input that will produce an output. The last group project we focused on how our project would react to humans’ motions. For this project we created matchmaking box that requires input from two users to produce either a matched or an unmatched output. Our project is unique because it is like a blind dating game condense into a small box, allowing people to express their true feelings anonymously, limiting biased answers. Compared to an actual reality show blind dating game, our project is more accessible everyone. Our target market is people that want to find a partner or confess their love for someone.
Conception and Design
We originally had the idea to sit two users across from each other with a board separating them. On top of that board would’ve been the LCD screen and on the table itself would be the yes or no buttons. However re-came to the conclusion that this would allow a lot of wires to show. We also didn’t have two screens, one for each user to see. So we came up with a new idea to create a box design with the screen in the center of a heart frame and the push buttons on the two sides of the box.
For this project we built the box and cut the heart from cardboard. We also used black paint for the box and red and pink paint for the heart. We also used cotton, illuminated by LEDs, to create a very cute and sweet look. We were just going to make the whole box black and cover the top with cardboard but we use cotton instead because that would create more loving and happy atmosphere.
Fabrication and Production
For this project, my role was to make the box, the heart frame, and decorate the entire project. After refining our idea many times I began boating the cardboard box in the heart frame and then painting them. I also created a little pieces that made sure all of the wires and hot glue were covered. I also left a bunch of cotton to create a very fluffy cloud like look. My partner did the coding and wires. I think this worked well because we both did tasks that we were better at.
For this project majority of the problems came from the code and the wiring. For the code, we had problems with the delay in displaying each question. we also had problems with the wiring because the wires are frizzy and hard to plug-in. I also caused a lot of trouble when the wires fall out because we used every slot on the board except for two. We found a solution by binding the wires of each session together with tape.
During the user testing session, we were given advice to better indicate where that users are supposed to stand. we positioned two chairs in front of the box to have both users facing the screen. We were also told that output at the finale is not significant enough. We then separated the LED code to light up one section of the heart after two questions were answered the same. So at the end there would be a bigger finale where the whole heart would light up.
Conclusion
And conclusion our project aligns with my definition of interaction. My project uses the users input to create different types of output. For example if the two users got more than four questions and the last question the same then the heart would light up red and the screen will display that they match. However, if the users enter more than half of the questions differently and the last question differently the heart would turn blue, and the LCD would display they did not match.
If I had more time to improve my project I would definitely add happy music for successful matches and sad music for unsuccessful matches.
Code
// for tft LCD display #define BLACK 0x0000 #define RED 0xF800 //for color #define WHITE 0xFFFF // 这行代码有什么用,用来维护世界和平 (fill color必须用hex!) 或者其实刚刚才发现 pixels.Color(r, g, b, w)可以直接转换 #include <Adafruit_GFX.h> #include <MCUFRIEND_kbv.h> //for lcd display MCUFRIEND_kbv tft; // for NeoPixel #include <Adafruit_NeoPixel.h> #ifdef __AVR__ #include <avr/power.h> // Required for 16 MHz Adafruit Trinket #endif #define NUMPIXELS 140 // Popular NeoPixel ring size #define PIN 19 // On Trinket or Gemma, suggest changing this to 1 // When setting up the NeoPixel library, we tell it how many pixels, // and which pin to use to send signals. Note that for older NeoPixel // strips you might need to change the third parameter -- see the // strandtest example for more information on possible values. Adafruit_NeoPixel pixels(NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800); // claim button pins #define yes1 13 #define yes2 10 #define no1 11 #define no2 12 // claim variables to store buttons' state int yes_1=0; int yes_2=0; int no_1=0; int no_2=0; int increment = 0; // to pass results to the telepathy int telepathy = 0; // to count the number of winning int questionStage = 1; // to enter different stages on the game unsigned long time = 0; // is not used in the code, so far bool runChecker = false; // so that the line will only run once in the loop bool runCheckerFinal = false; // for the final stage neopixel to only display once bool gameInitiator = false; // to start the game // for lcd Display char* display[] = {"0", "Do you likeeating out?", //claim string set "Are you an adventurous person?", "Do you likegoing to the gym?", "Would you prefer an outgoing partner?", "Can your partnerhave a best friend of the opposite sex?", "Is it important that you see youpartner every day?", "Do you like the personin front of you?" }; void setup(){ Serial.begin(9600); pinMode(2, INPUT); pinMode(3, INPUT); pinMode(4, INPUT); pinMode(5, INPUT); //claim pinmode for buttons // These lines are specifically to support the Adafruit Trinket 5V 16 MHz. // Any other board, you can remove this part (but no harm leaving it): #if defined(__AVR_ATtiny85__) && (F_CPU == 16000000) clock_prescale_set(clock_div_1); #endif // END of Trinket-specific code. pixels.begin(); // INITIALIZE NeoPixel strip object (REQUIRED) 你猜因为漏了这行代码我查了多久? 一个多小时你妈的 10.22 uint16_t ID = tft.readID(); tft.begin(ID); tft.fillScreen(BLACK); tft.setRotation(1); //initialize the LCD screen } void telepathyValue(int inc, int runStateReporter=0){ // to check and record how many questions they have answered correctly, call led to visualize // use default runStateReporter to different normal neopixel light effect, from the stateupdate at the end of each round telepathy += inc; // wonder if this switch case statement could work, much neater, instead of if statment switch (telepathy) { case 0: ledStripe(0, 1); increment = 0; break; case 1: ledStripe(0, 3); increment = 0; break; case 2: ledStripe(0, 5); increment = 0; break; case 3: ledStripe(0, 7); increment = 0; break; case 4: ledStripe(0, 12); increment = 0; break; case 5: ledStripe(0, 14); increment = 0; break; case 6: ledStripe(0, 17); increment = 0; break; case 7: ledStripeHuge(); increment = 0; break; } if (runStateReporter==1) { stateUpdate(); } } void loop(){ initiator(); if (gameInitiator == true) { // to start the game buttonReadSaver(); // to read the buttons and lock them up until the end of one round question(questionStage); // to forward the stage of the game while also avoid the annoying function of loop that runs the former one over and over again // buttonChecker(); // check if the buttons work or not telepathyValue(increment); // with the second input as default, making the circling LED effect, without LCD display on } else { beforeStart(); } } void lcdDisplay(char b[], int position=0, int fontSize=8){ // passes string for LCD display tft.fillScreen(BLACK); // to clear the screen after each question is answered? tft.setCursor(0,position); tft.setTextColor(WHITE); tft.print(b); } void fuckyou(){ // restore the variables to original state to get ready for the next round yes_1=0; yes_2=0; no_1=0; //directly assign value to the variables without using "int", so that changes the variable as global instead of definning a new local variable no_2=0; runChecker=false; questionStage ++; tft.fillScreen(BLACK); // to clear the screen after each question is answered? // delay(1000); // may not be necessary because the auto delay going on } void buttonReadSaver (){ // read out the buttons and lock them once they reach HIGH if (yes_1==1) { yes_1==1; } else { yes_1 = digitalRead(yes1); delay(1); } if (yes_2==1) { yes_2==1; } else { yes_2 = digitalRead(yes2); delay(1); } if (no_1==1) { no_1==1; } else { no_1 = digitalRead(no1); delay(1); } if (no_2==1) { no_2==1; } else { no_2 = digitalRead(no2); delay(1); } } void question(int question){ if (runChecker == false) { if (questionStage==5 || questionStage==6) { tft.setTextSize(5); // set font size in case any outside change } else { tft.setTextSize(7); } lcdDisplay(display[questionStage]); // Serial.println(display[question]); // for preliminary check runChecker = true; } if (questionStage==7) { // the final question is peculiar that doesn't follow the matchup convention used in previous quesitons if (yes_1==1 && yes_2 == 1) { // lcdDisplay("You Match!!!"); // Serial.println("great job"); // for preliminary check fuckyou(); increment = 1; telepathyValue(increment, 1); lcdDisplaySucc(); gameInitiator = false; // reset game to resting stage } if (yes_1==1 && no_2 ==1 || no_1==1 && yes_2 ==1 || no_1==1 && no_2 ==1) { // lcdDisplay("Uh-Oh No Match"); // Serial.println("try harder"); fuckyou(); increment = 0; telepathyValue(increment, 1); lcdDisplayFail(); gameInitiator = false; // reset game to resting stage } } else { if (yes_1==1 && yes_2 == 1 || no_1==1 && no_2 ==1) { // lcdDisplay("You Match!!!"); // Serial.println("great job"); // for preliminary check fuckyou(); increment = 1; telepathyValue(increment); } if (yes_1==1 && no_2 ==1 || no_1==1 && yes_2 ==1) { // lcdDisplay("Uh-Oh No Match"); // Serial.println("try harder"); fuckyou(); increment = 0; telepathyValue(increment); // to check and record how many questions they have answered correctly, call led and lcd to visualize } } } void buttonChecker(){ // check if the buttons work or not // Serial.print(yes_1); // Serial.print(yes_2); // Serial.print(no_1); // Serial.print(no_2); Serial.println(increment); delay(1); } void ledStripe (int initialPixel, int pixelNum) { // light up the pixel with both filling colors and also visualize different stages pixels.fill(WHITE, 45, 90); // decide what the filling color would be pixels.show(); // Serial.println("led works"); // to check if the function has been called correctly, so that we know that if the LED doesn't work, it must have to do with something else time = millis(); while (millis()-time<=50) { // Pause before next pass through loop pixels.fill(0, 6, 39); // don't fucking 你妈的用 pixel.clear()!! 你妈的全局清空,fill还有什么用? pixels.show(); } for (int i = initialPixel+6; i <= initialPixel+pixelNum+6; i++) { // For each pixel... time = millis(); while (millis()-time<=50) { // Pause before next pass through loop pixels.setPixelColor(i, pixels.Color(255, 0, 0)); pixels.setBrightness(128); pixels.setPixelColor(45 - i, pixels.Color(0, 0, 255)); pixels.setBrightness(128); pixels.show(); // Send the updated pixel colors to the hardware. // pixels.setPixelColor(i, pixels.Color(0, 0, 0)); // pixels.setPixelColor(45 - i, pixels.Color(0, 0, 0)); // pixels.show(); // add a piece of code here can make do running effect } } } void ledStripeHuge () { // for the final stage of the game while (runCheckerFinal == false) { runCheckerFinal = true; while (millis()-time<=50) { // Pause before next pass through loop pixels.fill(0, 6, 39); // don't fucking 你妈的用 pixel.clear()!! 你妈的全局清空,fill还有什么用? pixels.show(); } for (int i = 6; i <= 23; i++) { // For each pixel... time = millis(); while (millis()-time<=50) { pixels.setPixelColor(i, pixels.Color(255, 0, 0, 128)); pixels.setPixelColor(45 - i, pixels.Color(0, 0, 255, 128)); pixels.show(); // Send the updated pixel colors to the hardware. pixels.setPixelColor(i, pixels.Color(0, 0, 0)); pixels.setPixelColor(45 - i, pixels.Color(0, 0, 0)); pixels.show(); // add a piece of code here can make do running effect } } while (millis()-time<=500) { // fill out the pixels on the heart with nothing, namely doing a partial clear pixels.fill(0, 6, 39); pixels.show(); } for (int i = 23; i >= 6; i--) { time = millis(); while (millis()-time<=15) { // Pause before next pass through loop pixels.setPixelColor(i, pixels.Color(255, 0, 0, 150)); pixels.setPixelColor(45 - i, pixels.Color(255, 0, 0, 150)); pixels.show(); // Send the updated pixel colors to the hardware. } } } // the following code is to do the breathing effect for the leds for (int i = 0; i <= 255; i++) { time = millis(); while (millis()-time<=50) { // Pause before next pass through loop pixels.fill(pixels.Color(255, 0, 0), 6, 39); pixels.setBrightness(i); pixels.show(); } } for (int i = 255; i <= 0; i--) { time = millis(); while (millis()-time<=50) { // Pause before next pass through loop pixels.fill(pixels.Color(255, 0, 0), 6, 39); pixels.setBrightness(i); pixels.show(); } } } // 带着个指针的void还有函数老是报错, invalid use of void expression. // void strGenartor (int telepathy, int question) { // // for lcd string output // char str1[80]; // char str2[80]; // sprintf(str1, "You Have Matched %i", telepathy+1); // sprintf(str2, "Remaining Questions %i", 7-question); // char c[] = {str1, str2}; // return(c); // } // 但感觉好像直接这样写不就行了 void stateUpdate () { tft.fillScreen(BLACK); // to clear the screen tft.setCursor(0, 0); tft.setTextSize(8); // if (questionCorrectness==0) { // tft.print("UNMATCH"); // questionCorrectness = 2; // } // if (questionCorrectness==1) { // tft.print( "MATCH"); // questionCorrectness = 2; // } // delay(2000); tft.setTextColor(WHITE); tft.print("YOU HAVE MATCHED "); char b[10]; // create a local variable to convert an int into char for display sprintf(b, "%d", telepathy); tft.print(b); delay(2000); // so that the text could stay on the screen for a while } void lcdDisplayFail () { tft.fillScreen(BLACK); // to clear the screen tft.setCursor(0, 0); tft.setTextSize(10); tft.print("YOU DID NOT MATCH >_<"); time = millis(); while (millis()-time<=50) { // Pause before next pass through loop pixels.fill(0, 6, 39); pixels.show(); } for (int i = 0+6; i <= 23; i++) { // For each pixel... time = millis(); while (millis()-time<=50) { // Pause before next pass through loop pixels.setPixelColor(i, pixels.Color(0, 76, 153)); pixels.setBrightness(128); pixels.setPixelColor(45 - i, pixels.Color(0, 76, 153)); pixels.setBrightness(128); pixels.show(); // Send the updated pixel colors to the hardware. } } delay(10000); } void lcdDisplaySucc () { tft.fillScreen(BLACK); // to clear the screen tft.setCursor(0, 0); tft.setTextSize(10); tft.print("YOU ARE PERFECT MATCH (p>w<q)"); delay(10000); } void beforeStart () { increment = 0; telepathy = 0; pixels.fill(BLACK); pixels.rainbow(0, 1, 255, 128, true); pixels.show(); char array1[] = "TEST YOUR TELEPATHY "; char array2[] = "TWO PLAYERS "; char array3[] = "PLEASE SIT DOWN"; char array4[] = "PRESS ANY BUTTON TO BEGIN"; tft.setCursor(0,0); tft.setTextSize(8); for (int positionCounter1=0; positionCounter1<32; positionCounter1++) { // to get running word effect for LCD tft.print(array1[positionCounter1]); delay(50); } delay(1000); tft.fillScreen(BLACK); tft.setCursor(0,0); for (int positionCounter1=0; positionCounter1<18; positionCounter1++) { // to get running word effect for LCD tft.print(array2[positionCounter1]); delay(50); } delay(1000); tft.fillScreen(BLACK); tft.setCursor(0,0); for (int positionCounter1=0; positionCounter1<15; positionCounter1++) { // to get running word effect for LCD tft.print(array3[positionCounter1]); delay(50); } delay(1000); tft.fillScreen(BLACK); tft.fillScreen(BLACK); tft.setCursor(0,0); for (int positionCounter1=0; positionCounter1<25; positionCounter1++) { // to get running word effect for LCD tft.print(array4[positionCounter1]); delay(50); } delay(1000); tft.fillScreen(BLACK); } void initiator () { buttonReadSaver(); if (yes_1==1 || yes_2==1 || no_1==1 || no_2==1) { gameInitiator = true; delay(1000); yes_1=0; yes_2=0; no_1=0; //directly assign value to the variables without using "int", so that changes the variable as global instead of definning a new local variable no_2=0; } } void trainSession () { tft.setCursor(0, 0); tft.setTextColor(WHITE); tft.setTextSize(8); tft.print("Here's To Show You How To Play!"); tft.fillScreen(BLACK); tft.setTextSize(10); unsigned long time = millis(); while (millis-time<=1000) { tft.print("<--- NO --->"); tft.println("<--- YES --->"); } tft.fillScreen(BLACK); tft.print("LET US TRY"); }