jianxiag@andrew.cmu.edu – Intro to Physical Computing: Student Work https://courses.ideate.cmu.edu/60-223/f2018/work Intro to Physical Computing: Student Work Sun, 16 Dec 2018 16:38:47 +0000 en-US hourly 1 https://wordpress.org/?v=4.9.25 Bingo Box by Team Jeffrey: Final Documentation https://courses.ideate.cmu.edu/60-223/f2018/work/bingo-box-by-team-jeffrey-final-documentation/ https://courses.ideate.cmu.edu/60-223/f2018/work/bingo-box-by-team-jeffrey-final-documentation/#respond Tue, 11 Dec 2018 23:27:28 +0000 https://courses.ideate.cmu.edu/60-223/f2018/work/?p=5154 overview

This project is a Bingo-style game for Jeffrey to play with his 6-year-old granddaughter, Stella. This game, partially disguised as trivia, would be used as a tool to spark conversations about interests and personal topics as a way to get both young kids and adult family members to know each other better. See here for documentation of our initial meeting with Jeffrey and see here for documentation of our prototype.

What we built

 

 

We built an electronic board-game, with very simple rules, which could be used for both a 73 year old and a 6 year old. The game-play is a mix between Trivial Pursuit, Bingo and Truth or Dare.

Before the game starts, each Player writes down a set of questions for the other player to answer. The use of this analog method is to create a constantly evolving game, which grows and complexifies as Stella does. Player 1 asks a question from their stack of cards, and player 2 picks a spot on the board and attempts to answer. If answered correctly, player one presses their green button which locks the location on the button pad (as seen in the image below). If the answer is wrong, however, the illuminated button disappears, and player 2 must try again in the next round. This format continue until one player obtains 5 lit-up buttons in a row.

For a long game play, penalty cards such as “Tell me a story in song” or “Invent a short dance routine”, may be given to the person who has answered wrong at the end of each turn. For a short game play, the penalty card may be given at the end of the game.

 

Player 1 reading a question card, while player 2 chooses a position on the button pad.

 

Detail shot of the LCD screen, giving the players indications: “Green asks, Pink answers.” You also get a sense of the box’s transparency, which was a customer request made by Jeffrey.

 

Detail shot of the button pad, correct and incorrect buttons, as well as the LCD screen. 

 

Detail shot of the speakers and box design on the back face.

 

Narrative Sketch

Stella gets dropped off by her single father at Jeffrey’s house, for their weekly Wednesday hangout. Stella would like to play a game, but Jeffrey isn’t sure how to successfully entertain a 6-year girl. Instead of having his wife help him out, he pulls out their customized board-game.

Stella likes to start, because she enjoys the pretty colors the button pad generates. As the game starts, a rainbow pattern illuminates the board, making Stella smile.

The first step of the game is for each player to write down a list of personalized questions on their assigned cards, which can be re-used or re-written as pleased. The aim of these questions is to generate conversation, and learn something from one another. Then, Stella starts by pressing a button of her chosen location on the button pad. Jeffrey asks her a question from his green deck of cards .

 

Player 1 chooses a location on the board.

Stella attempts to answer the question. If answered correctly, Jeffrey will select the green button, locking in the position on the buttonpad. If the answer is incorrect, Jeffrey presses the red button, and the button’s light goes out. Stella can then try again during the next round. In a long-play game, Stella would then have to perform a penalty found on the white stack of cards.

 

Player 2 reads the card from their stack, and prompts Player 1 to answer. If correct, Player 2 presses the “correct” button, and the button pad stays lit.

 

After Stella’s turn, Jeffrey can pick a position and attempt to answer a question. The game goes on until a player is able to line up 5 of their colored buttons on the pad. During a short play game, the loser must perform a penalty found on the white stack of cards, only at the end of the game.

 

When the game is over, the orange “reset” button can be pressed, a rainbow pattern appears and the board is cleared. 

 

How we got here

Initial Meeting and Brainstorm

On November 1st, 2018, our group – Chloé Desaulles, Jiatian Sun, Jianxiao Ge –  firstly met up with Jeffrey, who is an older man participating in the CMU Osher program, with the intention of building a useful device for him.

 

 

We had a meeting and interviewed Jeffrey in the university cafe. After the first interview, we started by brainstorming ideas to make Jeffrey’s life easier or more pleasurable. He did not seem to need anything fixed, so we tried to look into ways of enhancing good things in his life. It became clear very quickly that he kept gravitating towards his granddaughter Stella, and the time he and his wife get to spend with her. We additionally noticed that Jeffrey might not perfectly know how to interact with a six-year-old, often letting his wife entertain her.

Our main challenge was creating an interesting and fun game for such a wide age gap. Making a game intuitive and worth playing was a hard thinking exercise considering none of us had any game design experience. In addition, we needed to add physical computing elements into the game, which made it even harder.

Finally, we were inspired by the traditional game Gomoku & Bingo and decided to create a similar game with trivia elements, which match Jeffrey’s interests, and a simple and colorful button interface, with a changeable gameplay so the game can be adaptable as Stella grows up.

Preliminary sketches of our game board.

Prototype and the Second Meeting

After settling on the concept and gameplay, we started working on the layout and board design, option for big buttons with fun interactions, which would make the game more interesting to a 6-year-old. However, because large buttons took up too much space, we ended up with a medium sized button.

Working on the conceptualization and physical design/implementation of the game board.

We then started programming the colored buttons, LCD screen, and button pads separately and finally got together to merge our individual codes.

Jianxiao and Jiatian working on merging code sections.

The LCD screen welcoming Jeffrey and Stella to their custom game.

One of the biggest challenges we met during prototype was the core part of our game, the button pad. The RGB keypad we needed was out of stock at that time. We contacted the manufacturer Adafruit and received a reply that it would be two weeks before we could have the keypad available. Therefore, we had to order the LED version of the pad as an alternative in order to finish the prototype on time.

 

Setup of our first button pad trial with LED keypad.

 

Button Pad is able to light up according to the color of LED beneath it.

 

The completed first prototype.

We met Jeffrey again after the prototype presentation. Jeffrey seemed very excited by our prototype and believed he and Stella would have fun playing the game. He was also happy about our choices in tactility and color and even suggested we add sound (which is something we had been shying away from because we thought it might be irritating Jeffrey and his wife). What’s more, he gave us a lot of input when it came to the aesthetics of the board, suggesting we should make it out of clear acrylic so that Stella could see the colorful wiring on the inside. Some other fabrication advice he and Zach gave us included building a box to hold question and penalty cards. The crit was useful for our team and allowed us to move confidently forward with our design.

 

Jeffrey giving us feedback on our prototype.

 

Final Fabrication

After the second meeting, we adjusted the game based on the feedback. For example, combine the card box with the game board, add the game sound effect, and remove people’s name from the instruction on the LCD screen to involve other family members as well.

Chloé and Jiatian adjusting the game after the second meeting.

Another good news was that RGB Keypad, which was out of stock before, had been replenished. With the help of Zach, we got the keypad in time and immediately started to adjust the code as well as the soldering and testing.

Soldering four 4*4 RGB Keypads together.

At the same time, we started the design of the game box and material preparation. According to Jeffrey’s suggestion, we chose the white fog-faced acrylic as the main material to highlight the colorful buttons and create a light and transparent visual effect.

We sketched the game board by hand and determined the general layout. After actually measuring the size of each part, we drew it exactly one-to-one and then redrew it in our laptop using Autocad and Adobe Illustrator.

 

Rough hand sketch of game board design and dimensions.

 

Drawing it one-to-one after measuring the size of each part.

 

While Jiatian was in charge of the code adjustment and part testing,  Chloé and Jianxiao took the acrylic to the laser cut studio. The laser cutting process was not as smooth as expected. Because of the measuring error, the buttons and the LCD screen couldn’t be embedded smoothly. So we measured again, adjusted the size and then carried out a second laser cut, finally got the panel in line with the requirements and started the assembly.

 

During the assembly, we found that the card box was smaller than expected, so the card could only be erected. But we thought it would make it easier to pull out the cards and make the game go more smoothly, so we didn’t redo the box.

 

On December 4th, a month after the initial meeting, we had the final presentation and met Jeffrey again. We were delighted to see that he was very pleased with our final product. He played the game with us and was ready to give it to his granddaughter Stella as a Christmas present.

Jeffrey playing the game with us during the final desk crits.

Here is our team! (From left to right: Jiatian Sun, Jeffrey, Chloé Desaulles, Jianxiao Ge)

Conclusions and lessons learned

To our surprise, almost all of the oral feedbacks we received during the final crit were positive, whether it was about the experience of the game or the design of the box itself. (which, of course, could be because people were too shy to make suggestions in person). We were happy to see that older people seemed to be very interested in the game. Some of them even thought that it had commercial value. Since the question cards can be written by the players themselves, it gives the game more possibilities. It can be a bridge for family members to communicate and learn from each other, even if there is a huge age gap.

After the crit, we also see a lot of very good advice from the written feedback. For example, “It would be useful to add the name/instructions onto the game.” Because of the novel mechanics of the game, people may be confused when they start to play. Although we try to give each step a hint on the LCD screen, some instructions are not clear and accurate due to the size of the screen. At the same time, an extra game description may help people understand the game faster.

Here is another one: “It could be smaller/thinner – effects portability. Would be nice if users could choose which colors they are in the button pad – maybe they have a favorite color.” The final volume of the game box was indeed larger than we expected. In order to prevent the electronic components from being unable to fit, we reserved much space during the box design. But when we actually assembled it, we found that most of the space in the box was empty. If we can make full use of space, the game can be greatly improved in portability (although the portability may be not that important for a family game box). For the color part, we did take Jeffrey and Stella’s preferences into account and set up keyboard lighting based on their favorite colors. It might be a better idea to let players themselves choose the own lighting colors.

We didn’t have much experience working with older people before, but the process of working with Jeffrey was very pleasant. He was very talkative and provided us with many practical suggestions, which helped us adjust in different stages and finally completed a work that satisfied us all. Due to the time limit, our initial interview could only be held in the university cafe. Although we talked a lot, it seemed difficult for us to get Jeffrey’s demand quickly from his conversation, which made us struggle about where to start in the brainstorm. Next time we have the opportunity to cooperate with the elderly, maybe we will choose their home as the interview place. On the one hand, it can make them feel more comfortable and be willing to talk more, on the other hand, it is more convenient for us to observe the details of their life so that we can understand their needs more quickly.

All in all, this project is an interesting interdisciplinary cooperation experience. We had a lot of brainstorming in the early stage and repeatedly discussed and improved the design scheme according to Jeffrey and Zach’s feedback. In the post-fabrication stage, we divided the work according to everyone’s background and expertise, and finally achieved satisfactory results. If you have any more questions or suggestions, please feel free to contact us by email: jianxiag@andrew.cmu.edu, thanks!

Technical details

Schematic

Code

/* 
 *  This code makes reference to sample code of Adafruit seesaw liborary
 * whose source code can be viewed at https://github.com/adafruit/Adafruit_Seesaw
 * 
 * Game Logic part Author: Caroline Sun & Jianxiao Ge
 * This is a game is similar to Bingo. To create a enjoyable gaming experience, we add extra
 * features like playing sound sound to the game.
 */
 
#include "Adafruit_NeoTrellis.h"
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27, 16, 2);

#define DEBUG 1

// 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 
// Define a special note, 'R', to represent a rest
#define  R     0

#define Y_DIM 8 //number of rows of key
#define X_DIM 8 //number of columns of keys

//Define game states
#define QUESTION 0
#define ANSWER 1
#define NEXT 2

//Define Answer to Questions
#define YESJ 2
#define YESS 4
#define NOJ 3
#define NOS 5

//Define button pins
#define RESET 6
#define NEXTJ 5
#define NEXTS 6

//Define player states
#define STELLA 0
#define JEFFREY 1

//Define Board Value
#define MIDDLE 3
#define INVALID (-1)
#define BINGOSIZE 5

//Define speaker pins
#define SPEAKER 9

//Color value
int val = 125;

//Declare game States
int isStart;
int state;
int player;
int currSquare;
int scoreS;
int scoreJ;
int SIZE = 8 ;
int totalSize = SIZE * SIZE;
int board[64];

// Set overall Speaker configuration
long tempo = 10000;
int pause = 1000;
int rest_count = 100;


// MELODY and TIMING  =======================================
//  melody[] is an array of notes, accompanied by beats[], 
//  which sets each note's relative length (higher #, longer note) 

//This is the tune of correct answer
int successMelody[] = {  c,  e,  g,  C, C, C};
int successBeats[]  = { 8, 8, 8,  8, 8,8}; 
int SUCCESS_MAX_COUNT = sizeof(successMelody) / 2; 

//This is the tune of wrong answer
int failureMelody[] = {  g,  e,  d,  c, c};
int failureBeats[]  = { 8, 8,  8,  8, 8}; 
int FAILURE_MAX_COUNT = sizeof(failureMelody) / 2; 

//This is the tune of winning
int winMelody[] = {  e,  e,  e,  e, f, f, f, f,  g,g, g, g, g, g, g, g, f, f, f, f,  e,e, e, e, d, d, d, d};
int winBeats[]  = { 16, 16, 16, 16, 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16};
int WIN_MAX_COUNT = sizeof(winMelody) / 2; 

//This is the tune of reset
int resetMelody[] = {  c,  c,  c,  c, g, g, g, g,  c,  c,  c,  c, g, g, g, g};
int resetBeats[]  = { 16, 16, 16, 16, 16,16,16,16, 16, 16, 16, 16,16,16,16,16};
int RESET_MAX_COUNT = sizeof(resetMelody) / 2; 


// PLAY TONE FUNCTION  ==============================================
// Pulse the speaker to play a tone for a particular duration
void playTone(int tone_, int beat, int duration) {
  long elapsed_time = 0;
  if (tone_ > 0) { // if this isn't a Rest beat, while the tone has 
    //  played less long than 'duration', pulse speaker HIGH and LOW
    while (elapsed_time < duration) {

      digitalWrite(SPEAKER,HIGH);
      delayMicroseconds(tone_ / 2);

      // DOWN
      digitalWrite(SPEAKER, LOW);
      delayMicroseconds(tone_ / 2);

      // Keep track of how long we pulsed
      elapsed_time += (tone_);
    } 
  }
  else { // Rest beat; loop times delay
    for (int j = 0; j < rest_count; j++) { // See NOTE on rest_count
      delayMicroseconds(duration);  
    }                                
  }                                 
}

//Play Melody function
void playMelody(int* pitches, int* beats, int count){
  // Set up a counter to pull from melody[] and beats[]
  int tone_,beat,duration;
  for (int i=0; i<count; i++) {
    tone_ = pitches[i];
    beat = beats[i];

    duration = beat * tempo; // Set up timing

    playTone(tone_,beat,duration); 
    // A pause between notes...
    delayMicroseconds(pause);
  }
}

//create a matrix of trellis panels
Adafruit_NeoTrellis t_array[Y_DIM/4][X_DIM/4] = {
  
  { Adafruit_NeoTrellis(0x2E), Adafruit_NeoTrellis(0x2F) },

  { Adafruit_NeoTrellis(0x30), Adafruit_NeoTrellis(0x31) }
  
};


//pass this matrix to the multitrellis object
Adafruit_MultiTrellis trellis((Adafruit_NeoTrellis *)t_array, Y_DIM/4, X_DIM/4);


//Print Board Function for testing
void printBoard(){
  Serial.println("board");
  for(int i = 0;i < totalSize;i++){
    Serial.print(board[i]);
    Serial.print(" ");
  }
  Serial.println("");
}

//Helper function that cleans up a array
void cleanArr(int *arr, int n){
  for(int i = 0; i < n;i++){
    arr[i] = 0;
  }
}


//Write a two-line message to the LCD screen
void writemessage(String message1, String message2){
   lcd.backlight();
   lcd.setCursor(0, 0);
   lcd.print(message1);
   lcd.setCursor(0, 1);
   lcd.print(message2);
}

//Turn on the button first being pressed
int turnOnFirstPressed(int num){
  int val = 125;
  if(player == JEFFREY) val = 255;
  trellis.setPixelColor(num, Wheel(val));
  trellis.show();
  return num;
}


// Input a value 0 to 255 to get a color value.
// The colors are a transition r - g - b - back to r.
uint32_t Wheel(byte WheelPos) {
  if(WheelPos < 85) {
   return seesaw_NeoPixel::Color(WheelPos * 3, 255 - WheelPos * 3, 0);
  } else if(WheelPos < 170) {
   WheelPos -= 85;
   return seesaw_NeoPixel::Color(255 - WheelPos * 3, 0, WheelPos * 3);
  } else {
   WheelPos -= 170;
   return seesaw_NeoPixel::Color(0, WheelPos * 3, 255 - WheelPos * 3);
  }
  return 0;
}


//Check if one index is in bound of a board
bool inBound(int x, int y, int width, int height){
  return x>=0 && y >=0 && x < width && y <height; 
}

//define a callback for key presses
TrellisCallback blink(keyEvent evt){
  
  if(evt.bit.EDGE == SEESAW_KEYPAD_EDGE_RISING){
    //Wheel(map(evt.bit.NUM, 0, X_DIM*Y_DIM, 0, 255))
    Serial.print("blink! ");
    Serial.print(evt.bit.NUM);
    Serial.print(" ");
    Serial.print(val);
    Serial.println(" prit");
    trellis.setPixelColor(evt.bit.NUM, Wheel(val));
    val = val>125? 125: 225;
    
  }//on rising
  else if(evt.bit.EDGE == SEESAW_KEYPAD_EDGE_FALLING)
    trellis.setPixelColor(evt.bit.NUM, 0); //off falling
    
  trellis.show();
  return 0;
}

//The function that checks if there exists winner on the board
bool checkWinner(){
  int rowCorrect[2];
  int colCorrect[2];
  int diagDownCorrect[2];
  int diagUpCorrect[2];
  int currCont[2] = {0,0};
  
  cleanArr(rowCorrect,2);
  cleanArr(colCorrect,2);
  cleanArr(diagUpCorrect,2);
  cleanArr(diagDownCorrect,2);
  cleanArr(currCont, 2);

  //Check if there is bingo in a row
  for(int i = 0; i < SIZE; i++){
    for(int j = 1; j < SIZE; j++){
      int currP = board[i * SIZE + j];
      if(board[i * SIZE + j] == board[i * SIZE + j -1] && board[i * SIZE + j]!=INVALID){
        currCont[currP] += 1;
        if(currCont[currP] > rowCorrect[currP]) rowCorrect[currP] = currCont[currP];
      }
      else{
        cleanArr(currCont, 2);
      }
    }
  }

  cleanArr(currCont, 2);
  //Check if there is Bingo in a column
  for(int j = 0; j < SIZE; j++){
    for(int i = 1; i < SIZE; i++){
      int currP = board[i * SIZE + j];
      if(board[i * SIZE + j] == board[(i-1) * SIZE + j] && board[i * SIZE + j]!=INVALID ){
        currCont[currP] += 1;
        if(currCont[currP] > colCorrect[currP]) colCorrect[currP] = currCont[currP];
      }
      else{
        cleanArr(currCont, 2);
      }
    }
  }


  //Check if there is bingo in a diagonal
  for(int xShift = -SIZE +1; xShift<SIZE;xShift++){
  cleanArr(currCont, 2);
  for(int diagX = 1; diagX<SIZE;diagX++){
    int xPos = diagX + xShift;
    int yPos = diagX;
    int prevX = xPos -1;
    int prevY = yPos -1;
    if(!inBound(xPos,yPos,SIZE,SIZE) || !inBound(prevX,prevY,SIZE,SIZE)){
      cleanArr(currCont,2);
      continue;
    }
    int currP = board[yPos * SIZE + xPos];
    if(currP == board[prevY * SIZE + prevX] && currP!=INVALID){
        currCont[currP] += 1;
        if(currCont[currP] > diagDownCorrect[currP]) diagDownCorrect[currP] = currCont[currP];
      }
      else{
        cleanArr(currCont, 2);
      }
  }
  }

  for(int xShift = 0; xShift<2 * SIZE -1;xShift++){
  cleanArr(currCont, 2);
  for(int diagX = 1; diagX<SIZE;diagX++){
    int xPos = -diagX + xShift;
    int yPos = diagX;
    int prevX = xPos +1;
    int prevY = yPos -1;
    if(!inBound(xPos,yPos,SIZE,SIZE) || !inBound(prevX,prevY,SIZE,SIZE)){
      cleanArr(currCont,2);
      continue;
    }
    int currP = board[yPos * SIZE + xPos];
    if(currP == board[prevY* SIZE + prevX] && currP!=INVALID){
        currCont[currP] += 1;
        if(currCont[currP] > diagUpCorrect[currP]) diagUpCorrect[currP] = currCont[currP];
      }
      else{
        cleanArr(currCont, 2);
      }
  }
  }

  int sScore = max(colCorrect[STELLA],max(rowCorrect[STELLA],max(diagDownCorrect[STELLA],diagUpCorrect[STELLA])));
  int jScore = max(colCorrect[JEFFREY],max(rowCorrect[JEFFREY],max(diagDownCorrect[JEFFREY],diagUpCorrect[JEFFREY])));
  
  //Display the winner of the game
  if(sScore >= BINGOSIZE-1 && jScore >=BINGOSIZE-1){
    Serial.println("Both of you wins!");
    writemessage("  Both of you  ","    Wins!   ");
    playMelody(winMelody,winBeats,WIN_MAX_COUNT);
    return true;
  }
  if(sScore >= BINGOSIZE-1){
    Serial.println("Pink wins!");
    writemessage("     Pink       ","     Wins!      ");
    playMelody(winMelody,winBeats,WIN_MAX_COUNT);
    return true;
  }
  if(jScore >= BINGOSIZE-1){
    Serial.println("Green wins!");
    writemessage("     Green    ","      Wins!      ");
    playMelody(winMelody,winBeats,WIN_MAX_COUNT);
    return true;
  }
  return false;
}

//The callBack function on the Trellis Buttons
//This function tells the Button to update the game state, 
//if it is pressed when someone is answering question
TrellisCallback gameControl(keyEvent evt){
  if(isStart){
      // go through every button
   if(state == QUESTION){
      if(evt.bit.EDGE == SEESAW_KEYPAD_EDGE_RISING){
        currSquare = turnOnFirstPressed(evt.bit.NUM);
        if(currSquare>=0){
          state = ANSWER;
        }
     }
   }
  }
}

//Setup the game
void setup() {
  Serial.begin(9600);
  //while(!Serial);

  if(!trellis.begin()){
    Serial.println("failed to begin trellis");
    while(1);
  }

  //Register all buttons
  pinMode(YESJ,INPUT_PULLUP);
  pinMode(YESS,INPUT_PULLUP);
  pinMode(NOJ,INPUT_PULLUP);
  pinMode(NOS,INPUT_PULLUP);
  pinMode(NEXTJ,INPUT_PULLUP);
  pinMode(NEXTS,INPUT_PULLUP);
  pinMode(RESET,INPUT_PULLUP);

  //Register speaker
  pinMode(SPEAKER,OUTPUT);

  //Initialize LCD
  lcd.init();                      // initialize the lcd
  // Print a message to the LCD.
  lcd.backlight();
  lcd.setCursor(0, 0);
  lcd.print("Pink & Green");
  lcd.setCursor(0, 1);
  lcd.print("Welcome to BINGO");

  //Clear board
  for(int i = 0; i< SIZE * SIZE;i++){
    board[i] = -1;
  }
  
  //Go through each button on the board and display different color
  for(int i=0; i<Y_DIM*X_DIM; i++){
      trellis.setPixelColor(i, Wheel(map(i, 0, X_DIM*Y_DIM, 0, 255))); 
      trellis.show();
      delay(20);
  }

  //Setup the trellis button pad
  for(int y=0; y<Y_DIM; y++){
    for(int x=0; x<X_DIM; x++){
      //activate rising and falling edges on all keys
      trellis.activateKey(x, y, SEESAW_KEYPAD_EDGE_RISING, true);
      trellis.activateKey(x, y, SEESAW_KEYPAD_EDGE_FALLING, true);
      trellis.registerCallback(x, y,gameControl);
      trellis.setPixelColor(x, y, 0x000000); //addressed with x,y
      trellis.show(); //show all LEDs
      delay(20);
    }
  }

  //Randomize a player to start the game
  float r = random()%2;
  if( r > 0){
    player = STELLA;
  }
  else{
    player = JEFFREY;
  }

  //Reset Game State
  isStart = 1;
  state = QUESTION;
  scoreS = 0;
  scoreJ = 0;
  currSquare = -1;

 //Display welcome message
  writemessage("Pink & Green","Welcome to BINGO");

}


void loop() {
  trellis.read();
  delay(20);

  //If Rest Button is pressed, reset the game
  if(digitalRead(RESET)<1){
    writemessage("     Reset!     ","                ");
    delay(500);
    playMelody(resetMelody,resetBeats,RESET_MAX_COUNT);
    setup();
    return;
  }
  //let the game continue only if the game has not ended yet
  if(isStart){
   //If it is question and question time
   if(state == QUESTION){
    
    //Display messages showing states
    if(player ==STELLA)
      writemessage("   Green asks     ", "  Pink answers    ");
    else
      writemessage("   Pink asks  ", "  Green answers   ");
   }
   //If it is revealing answer state,
   else if(state == ANSWER){
    if(player==STELLA){
      writemessage("   Is Pink's    ", " answer correct? ");
      //If Pink's answer is correct
      if(digitalRead(YESJ)<1){
        board[currSquare] = STELLA;
        Serial.print("Pink places at position: ");
        Serial.println(currSquare);
        printBoard();
        //play music
        playMelody(successMelody,successBeats,SUCCESS_MAX_COUNT);
        //Change game state to next round
        currSquare = -1;
        state = QUESTION;
        isStart = !checkWinner();
        player = !player;
      }
      //If Pink's answer is wrong
      else if(digitalRead(NOJ)<1){
        trellis.setPixelColor(currSquare, 0);
        trellis.show();
        //play music
        playMelody(failureMelody,failureBeats,FAILURE_MAX_COUNT);
        //change game state to next round
        currSquare = -1;
        state = QUESTION;
        isStart = !checkWinner();
        player = !player;
      }
    }
    else{
      writemessage(" Is Green's  ", "answer correct? ");
      //If Green's answer is correct
      if(digitalRead(YESS)<1){
        board[currSquare] = JEFFREY;
        Serial.print("Green places at position: ");
        Serial.println(currSquare);
        printBoard();
        playMelody(successMelody,successBeats,SUCCESS_MAX_COUNT);
        currSquare = -1;
        state = QUESTION;
        isStart = !checkWinner();
        player = !player;
      }
      //If Green's answer is wrong
      else if(digitalRead(NOS)<1){
        trellis.setPixelColor(currSquare, 0);
        trellis.show();
        playMelody(failureMelody,failureBeats,FAILURE_MAX_COUNT);
        currSquare = -1;
        state = QUESTION;
        isStart = !checkWinner();
        player = !player;
        
      }
    }
   }
  }
  
}

Chloé Desaulles

Jiatian Sun

Jianxiao Ge

]]>
https://courses.ideate.cmu.edu/60-223/f2018/work/bingo-box-by-team-jeffrey-final-documentation/feed/ 0
Team Jeffrey – Prototype Documentation https://courses.ideate.cmu.edu/60-223/f2018/work/team-jeffrey-prototype-documentation/ https://courses.ideate.cmu.edu/60-223/f2018/work/team-jeffrey-prototype-documentation/#respond Thu, 15 Nov 2018 18:05:36 +0000 https://courses.ideate.cmu.edu/60-223/f2018/work/?p=5009

 

Introduction

This post is a detailed explanation of our team’s process towards a functioning behave’s-like prototype. After our first conversation with our older collaborator Jeffrey, we decided to make a bingo-style game for him to play with his 6 year old granddaughter, Stella. This game, partially disguised as trivia, would be used as a tool to spark conversations about interests and personal topics as a way to get to know each other.

 

 

Part one of Prototype Demo

Part two of Prototype Demo

Product

This game is essentially a mix of the core ideas behind trivial pursuit, bingo and truth or dare. Each user comes up with a set of questions which are personally relevant to them at the beginning of each game, and then switch stacks. This allows for it to be analog and generative, a constant change of questions and induced conversation between each game. Player A picks a card from player B’s stack, reads the question and attempts to answer it. Example questions could be: Who is my favorite artist? What are the lyrics to the star spangled banner? How did I meet my wife? What do I want to be when I grow up? What is Taylor Swift’s new hit song?

 

If answered correctly, player A gets to choose a box, which will illuminate to show the person’s color. Player A may then press the “Correct” button, to forward the turn to player B.

 

If answered incorrectly however, player A must pick a penalty card. Penalty cards require you to complete a certain task, such as inventing a funny song on the spot about a given sentence, or telling a story as a response to a prompt. When the story is completed, player A presses “Incorrect” and forwards the turn to player B. No buttons light up.

The buttons serve as a function to alternate turns and keep count of score.

The first player to reach five boxes in a row wins the game.

 

Process

 

We started by brainstorming ideas to make Jeffrey’s life easier or more pleasurable. He did not seem to need anything fixed, so we started looking into ways of enhancing good things in his life. It became clear very quickly that he kept gravitating towards his granddaughter, and the time he and his wife get to spend with her. We additionally noticed that Jeffrey might not perfectly know how to interact with a six year old, often letting his wife entertain her. We therefore decided to create a game with trivia elements, which match Jeffrey’s interests, and a simple and colorful button interface, with a changeable game play so the game can be adaptable as Stella grows up.  

 

Preliminary sketches of our game board

 

We started by creating a game plan, which would be fitting for both a 6 and 73 year old.  This ended up being on of our biggest challenges, as none of our group members had any game design experience.

 

Working on conceptualization and physical design/implementation of the game board.

 

After settling on a concept and game play, we started working on layout and board design, option for big buttons with fun interactions, which would make a ‘trivia’ game more interesting to a 6 year old.

Setup of  our first button pad trial. We are currently waiting for the RGB version of the pads, which should arrive in the mail in a week or two.

 

We then started programming the colored buttons, LCD screen and button pads separately and finally got together to merge our individual codes.

Button Pad are able to light up according to the color of LED beneath it.

The LCD screen welcoming Jeffrey and Stella to their custom game.

 

J and Ca working on merging code sections.

 

Last Tuesday, we were lucky to be able to meet with Jeffrey again to discuss our idea and game implementation. We received a lot of design feedback. He seemed very excited overall at the idea of being able to play this new custom game with Stella.

 

Jeffrey, Ca and C discussing game intricacies and board aesthetics.

 

 

Discussion

As mentioned prior, because our team is so diverse and complementary in skill set, our main challenge was creating an interesting and fun game for such a wise age gap. Making a game intuitive and worth playing was a hard thinking exercise considering none of us had any game design experience.  We ended up settling on a malleable and analog game, which would allow Jefferey and Stella to change the questions by hand as Stella grows older. We additionally tried to make the  interface loose so Stella can invent her own rules if she one day wishes.

 

November 13th critique:

Jeffrey seemed very excited by our prototype and believed he and Stella would have fun playing the game. He gave us a lot of input when it came to the aesthetics of the board, suggesting we should make it out of clear acrylic so that Stella could see the colorful wiring on the inside. Some other fabrication advices he gave us include building a box to hold question and penalty cards.

The crit was useful for our team and allowed us to move confidently forward with our design. Jeffrey in particular seemed happy about our choices in tactility and color, and even suggested we add sound (which is something we had been shying away from because we thought it might be irritating Jeffrey and his wife).

Over the next few weeks, we will be starting fabrication while waiting for the RGB button pads. We will working on making a clean and durable product, which will hopefully bring joy to Jeffrey and Stella for a long time.

]]>
https://courses.ideate.cmu.edu/60-223/f2018/work/team-jeffrey-prototype-documentation/feed/ 0
Final Project – Initial Meeting with Jeffrey https://courses.ideate.cmu.edu/60-223/f2018/work/initial-meeting-with-jeffrey/ https://courses.ideate.cmu.edu/60-223/f2018/work/initial-meeting-with-jeffrey/#respond Tue, 06 Nov 2018 16:24:29 +0000 https://courses.ideate.cmu.edu/60-223/f2018/work/?p=4866 A brief introduction

On November 1st, our group (David, Caroline and Chloé) met up with Jeffrey, an older man participating in the Osher program next to our physical computing lab, with the intention of building him a useful device. In order to do so, it was necessary for our group to gain a better understanding of who he was as a person: what he enjoyed doing, what tasks he had issues completing, what the course of his day looked like. We bought him a coffee and sat down to talk about his life.

our meeting agenda
  • Introduction to our past projects
  • Talk about assistive device research, how we started by doing it for ourselves
  • Show each individual project and talk about our technical abilities
  • Ask Jeffrey to tell us about himself, including his habits and self-identified personality traits.
  • Ask Jeffrey to share an experience he has really enjoyed recently.
  • Ask Jeffrey to tell us about his hobbies/what he enjoys doing in his free time.
  • Ask Jeffrey when was the last time he felt frustrated with something in his home and at work.
Meeting summary and major takeaways

As we started the conversation with questions mainly in two directions: Jeffrey’s interest and problems he faces, the discussion initially carried out with emphasis on Jeffrey’s personal interest, daily activities and the arrangement of his house. However, we later discovered that when talking about his own life, he talked a lot about his granddaughter Stella, for example, his granddaughter’s favorite shows, his granddaughters’ hobbies, his granddaughters’ achievements in primary school and so on. Thus, with more questions focusing on interactions between his granddaughter and him, we discovered that he and his wife moved to Pittsburgh actually to help their son taking care of their granddaughter.

However, Jeffrey also acknowledged that he rarely participates in Stella’s favorite activity in spare time: cooking, since this always create a huge mess in the kitchen. Therefore, we thought it would be meaningful to create an implement that can let Jeffrey and Stella share some meaningful time together and enhance their understanding for each other. This eventually became the main focus of the talk, yet before coming down to this thought, we have drifted through a lot of other options, for example, making a comparatively intelligent suitcase for Jeffrey, since he suggested that he needs a “self-driving” suitcase to carry his lecture materials around, which is obviously too hard to be finished in such a short duration of time.

Also, he proposed that if we are trying to make anything related to his hobby of playing tennis, it could be something that improves his forehand. These options generally turned out to be too hard for us as the course project and there was not much we can improve for these options.

Therefore, even though we discussed broadly with Jeffrey from different aspects of his life, eventually we narrow down to his interaction with his granddaughter.

Meeting with Jeffrey in the cafe.

our thoughts after holding the meeting and discussing as a team

Jeffrey is very approachable, so the whole meeting followed our agenda and went very smoothly. Although he is a retired man, he has a really busy schedule and teaches several courses at the same time. We tried to start brainstorm from the problems he met during the daily life but found that he seemed to keep everything in order while what he suggested us to build is too hard, making us a little overwhelmed at one point. Finally,  we decided to start our design with his interaction with his granddaughter.

Ideation sketching during group meeting

The first interview was done in a university cafe because time was tight. The whole team thought the discussion was going well, but we would like to visit him at his house next time so that we could observe the details of his life more carefully, and also communicate with his wife and granddaughter, which would be very helpful for us to know Jeffrey better.

 

Chloé Desaulles

Jiatian Sun

Jianxiao Ge

 

]]>
https://courses.ideate.cmu.edu/60-223/f2018/work/initial-meeting-with-jeffrey/feed/ 0
Digital Stylist https://courses.ideate.cmu.edu/60-223/f2018/work/digital-stylist/ https://courses.ideate.cmu.edu/60-223/f2018/work/digital-stylist/#respond Mon, 29 Oct 2018 12:51:46 +0000 https://courses.ideate.cmu.edu/60-223/f2018/work/?p=4750 Overview

It’s not a special day, facing the closet, you hesitate about what to wear to go to class today. Don’t want to waste time choosing clothes every day? Digital Stylist will help you make the decision!

The appearance of Digital Stylist when the power is off.

The appearance of Digital Stylist when the power is on.

The front of the Digital Stylist.

Top view of the Digital Stylist.

The detail of the LED stripe.

The detail of the buttons and the LCD screen.

The randomly generated numbers represent different combinations of clothing (T: T-shirt, J: Jacket, C: Winter Coat, S: Shorts, P: Pants), and you can switch between different seasons (Spring/Fall, Summer, Winter). The lights will also tell the color you will wear today. With the help of Digital Stylist. you don’t have to make the difficult decision every morning!

Process images and review

Some other ideation sketchings.

Some other ideation sketchings.

My initial concept was to make a machine to help me pick what I wear every day. Since 90% of my clothes are black, I didn’t think of the color combination in the beginning. But after discussing with Professor Zach and TA Runchang, I thought it would be interesting to visualize the results. So I decided to add the LED strip to my project.

The biggest problem I encountered during the production was the coding of the LED strip. I chose a library called “FastLED” at first, which was a little complicated for me. Although I tried my best to understand it and the code was actually running perfectly, the LED strip didn’t work at all. So after meeting, I took Zach’s advice and switched to another simpler library called “PololuLedStrip”. Although the final assembly was a bit rough because the coding part took me so much time, it worked very well.

The prototype of the Digital Stylist.

I tried to add the LED strip to the prototype.

I replaced the small buttons with bigger and colorful buttons since it would be easier for people to press.

Due to time constraints, I made the box manually.

The main panel was assembled.

The main structure of the box was completed.

The internal structure of the Digital Stylist.

It was born twenty minutes before the presentation!

Discussion

I did learn a lot from the project critique. The advice gave me a lot of inspiration that I hadn’t thought of before.

For example, “I appreciate the design for its idea of choosing clothes! I guess it would be even better if it actually has the algorithm to memorize your past choices so that you can change the probability of generating the random number according to your past choices.” Because at that time, the project requirement was to make an assistive device for my own use. For a guy who only has dark color clothes and doesn’t care much about dress collocation, I didn’t think much of this perspective. My original intention of the Digital Stylist is for lazy people who don’t care about dressing (like me). But it’s a really interesting idea to generate numbers according to the past choice or fixed number combination instead of generating some random numbers, which would make the dressing choice much more reasonable.

Here is another great feedback:” What do you do to categorize your clothes that are in your dirty laundry? Maybe a laundry basket by RFID could eliminate options from the database.” I did think of the laundry issue when I started writing the code. But because there are lots of color combinations for different random numbers, the code became more complex than I expected (more than 550 lines). So I left the laundry part on hold in order to finish the project on time. Although I’m not sure if the RFID is the best way, this feedback definitely gave me a new approach to solve this problem.

I’m quite satisfied with the final result of this project. Although the appearance seems a bit rough, it works pretty well. The buttons are satisfying and the UI screen is fluent, which showed my idea clearly. What’s more, I am very happy that this concept has struck a chord with many people.

In the meantime, I think the code part and the LED strip did not work very well as I expected. Due to problems using “FastLED” library, I didn’t have time to optimize my final code. My final solution was listing every possible number combinations to change the LED colors, which was a time-consuming work. I’m pretty sure that there will be a better and easier way to optimize the code. Originally, I was planning to take Zach’s advice to put the LED strip as a person’s shape so that people can have a more intuitionistic feeling to the color collocation of the dress. At last, I didn’t make it because I spent most of my time in coding.

However, I also learned a lot from working on my own. The same electronic component can always have many different libraries to make it work. It can save much time if you choose the easiest one to learn and understand. On the other hand, there is an old saying goes that “the onlooker sees most of the game”. In the conceptual stage, we should not just bury our head in the hard work, but discuss with teachers and classmates, so as to get more comprehensive thinking. And feedback after a completed project is often valuable.

If I have the chance to build another iteration of the Digital Stylist, I want it to be “smarter” so that it can remember the preferred dressing choice and eliminate the laundry clothes from the database. At the same time, I hope to further reduce the size of the device to provide more space for a better shaped LED strip.

Technical information

Digital Stylist Schematic

/*
  Project 2: Digital Stylist
  Author: Jianxiao Ge (jianxiag)
  Date: October 10th,2018
  Brief explanation:
  It's not a special day, facing the closet, you hesitate about what to wear to go to class today.
  Don't want to waste time choosing clothes every day? Digital Stylist will help you make the decision!
  The randomly generated numbers represent different combinations of clothing, and you can switch between different seasons. The light will also tell the color you will wear today.
  With the help of Digital Stylist. you don't have to make the difficult decision every morning!
*/

// Assume I have
// 10 T-shirts: 1-5 are orange (250,110,0), 6-10 are cyan (0,250,130)
// 10 Jackets: 11-15 are blue(0,0,255), 16-20 are purple(170,0,255)
// 5 Winter Coats: 21-25 are grey (160,160,160)
// 5 Shorts: 1-2 are yellow(230,230,0), 3-5 are Light red(255,0,70)
// 5 Pants: 6-7 are Light blue(0,180,255), 8-10 are white(255,255,255)

// Set up the LCD
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27, 16, 2);

// Set up the LED Strip
#include <PololuLedStrip.h>
// Create an ledStrip object and specify the pin it will use.
PololuLedStrip<6> ledStrip;
// Create a buffer for holding the colors (3 bytes per color).
#define LED_COUNT 30
rgb_color colors[LED_COUNT];

int tshirtDressNumber;
int jacketDressNumber;
int winterCoatDressNumber;
int shortsDressNumber;
int pantsDressNumber;
const int SPRINGFALLBUTTON = 2;
const int SUMMERBUTTON = 3;
const int WINTERBUTTON = 4;
const int RESETBUTTON = 5;
int springFallButtonValue = 0;
int summerButtonValue = 0;
int winterButtonValue = 0;
int resetButtonValue = 0;

void setup() {
  pinMode(SPRINGFALLBUTTON, INPUT_PULLUP);
  pinMode(SUMMERBUTTON, INPUT_PULLUP);
  pinMode(WINTERBUTTON, INPUT_PULLUP);
  pinMode(RESETBUTTON, INPUT_PULLUP);

  lcd.init();                      // initialize the lcd
  // Print a message to the LCD.
  lcd.backlight();
  lcd.setCursor(0, 0);
  lcd.print("Digital  Stylist");
  lcd.setCursor(5, 1);
  lcd.print("Howdy!");

  byte time = millis() >> 2;      // initialize the LED strip
  for (uint16_t i = 0; i < LED_COUNT; i++)
  { byte x = time - 8 * i;
    colors[i] = rgb_color(x, 255 - x, x);
  }
  // Write the colors to the LED strip.
  ledStrip.write(colors, LED_COUNT);
  delay(10);
}

// Make the LED strip shine as rainbow
rgb_color hsvToRgb(uint16_t h, uint8_t s, uint8_t v)
{
  uint8_t f = (h % 60) * 255 / 60;
  uint8_t p = (255 - s) * (uint16_t)v / 255;
  uint8_t q = (255 - f * (uint16_t)s / 255) * (uint16_t)v / 255;
  uint8_t t = (255 - (255 - f) * (uint16_t)s / 255) * (uint16_t)v / 255;
  uint8_t r = 0, g = 0, b = 0;
  switch ((h / 60) % 6) {
    case 0: r = v; g = t; b = p; break;
    case 1: r = q; g = v; b = p; break;
    case 2: r = p; g = v; b = t; break;
    case 3: r = p; g = q; b = v; break;
    case 4: r = t; g = p; b = v; break;
    case 5: r = v; g = p; b = q; break;
  }
  return rgb_color(r, g, b);
}

void loop() {

  springFallButtonValue = digitalRead(SPRINGFALLBUTTON);
  summerButtonValue = digitalRead(SUMMERBUTTON);
  winterButtonValue = digitalRead(WINTERBUTTON);
  resetButtonValue = digitalRead(RESETBUTTON);

  // Spring Mode
  if (digitalRead(SPRINGFALLBUTTON) == LOW) {
    tshirtDressNumber = random (1, 10);
    jacketDressNumber = random (10, 20);
    pantsDressNumber = random (6, 10);
    lcd.backlight();
    lcd.setCursor(0, 0);
    lcd.print("   Season:S/F   ");
    lcd.setCursor(0, 1);
    lcd.print("  T:");
    lcd.setCursor(4, 1);
    lcd.print(tshirtDressNumber);
    lcd.setCursor(5, 1);
    lcd.print(",J:");
    lcd.setCursor(8, 1);
    lcd.print(jacketDressNumber);
    lcd.setCursor(10, 1);
    lcd.print(",P:");
    lcd.setCursor(13, 1);
    lcd.print(pantsDressNumber);
    lcd.setCursor(14, 1);
    lcd.print("  ");

    //LED displays randomly selected clothing colors
    if ((tshirtDressNumber <= 5) && (jacketDressNumber <= 15) && (pantsDressNumber <= 7)) {
      for (uint16_t i = 0; i < 10; i++)
      {
        colors[i] = rgb_color(250, 110, 0);
      }
      for (uint16_t i = 10; i < 20; i++)
      {
        colors[i] = rgb_color(0, 0, 255);
      }
      for (uint16_t i = 20; i < 30; i++)
      {
        colors[i] = rgb_color(0, 155, 155);
      }
      // Write the colors to the LED strip.
      ledStrip.write(colors, LED_COUNT);
      delay(100);
    }

    if ((tshirtDressNumber <= 5) && (jacketDressNumber <= 15) && (pantsDressNumber > 7)) {
      for (uint16_t i = 0; i < 10; i++)
      {
        colors[i] = rgb_color(250, 110, 0);
      }
      for (uint16_t i = 10; i < 20; i++)
      {
        colors[i] = rgb_color(0, 0, 255);
      }
      for (uint16_t i = 20; i < 30; i++)
      {
        colors[i] = rgb_color(255, 255, 255);
      }
      // Write the colors to the LED strip.
      ledStrip.write(colors, LED_COUNT);
      delay(100);
    }

    if ((tshirtDressNumber <= 5) && (jacketDressNumber > 15) && (pantsDressNumber <= 7)) {
      for (uint16_t i = 0; i < 10; i++)
      {
        colors[i] = rgb_color(250, 110, 0);
      }
      for (uint16_t i = 10; i < 20; i++)
      {
        colors[i] = rgb_color(170, 0, 255);
      }
      for (uint16_t i = 20; i < 30; i++)
      {
        colors[i] = rgb_color(0, 155, 155);
      }
      // Write the colors to the LED strip.
      ledStrip.write(colors, LED_COUNT);
      delay(100);
    }

    if ((tshirtDressNumber <= 5) && (jacketDressNumber > 15) && (pantsDressNumber > 7)) {
      for (uint16_t i = 0; i < 10; i++)
      {
        colors[i] = rgb_color(250, 110, 0);
      }
      for (uint16_t i = 10; i < 20; i++)
      {
        colors[i] = rgb_color(170, 0, 255);
      }
      for (uint16_t i = 20; i < 30; i++)
      {
        colors[i] = rgb_color(255, 255, 255);
      }
      // Write the colors to the LED strip.
      ledStrip.write(colors, LED_COUNT);
      delay(100);
    }

    if ((tshirtDressNumber > 5) && (jacketDressNumber <= 15) && (pantsDressNumber <= 7)) {
      for (uint16_t i = 0; i < 10; i++)
      {
        colors[i] = rgb_color(0, 250, 130);
      }
      for (uint16_t i = 10; i < 20; i++)
      {
        colors[i] = rgb_color(0, 0, 255);
      }
      for (uint16_t i = 20; i < 30; i++)
      {
        colors[i] = rgb_color(0, 155, 155);
      }
      // Write the colors to the LED strip.
      ledStrip.write(colors, LED_COUNT);
      delay(100);
    }

    if ((tshirtDressNumber > 5) && (jacketDressNumber <= 15) && (pantsDressNumber > 7)) {
      for (uint16_t i = 0; i < 10; i++)
      {
        colors[i] = rgb_color(0, 250, 130);
      }
      for (uint16_t i = 10; i < 20; i++)
      {
        colors[i] = rgb_color(0, 0, 255);
      }
      for (uint16_t i = 20; i < 30; i++)
      {
        colors[i] = rgb_color(255, 255, 255);
      }
      // Write the colors to the LED strip.
      ledStrip.write(colors, LED_COUNT);
      delay(100);
    }

    if ((tshirtDressNumber > 5) && (jacketDressNumber > 15) && (pantsDressNumber <= 7)) {
      for (uint16_t i = 0; i < 10; i++)
      {
        colors[i] = rgb_color(0, 250, 130);
      }
      for (uint16_t i = 10; i < 20; i++)
      {
        colors[i] = rgb_color(170, 0, 255);
      }
      for (uint16_t i = 20; i < 30; i++)
      {
        colors[i] = rgb_color(0, 155, 155);
      }
      // Write the colors to the LED strip.
      ledStrip.write(colors, LED_COUNT);
      delay(100);
    }

    if ((tshirtDressNumber > 5) && (jacketDressNumber > 15) && (pantsDressNumber > 7)) {
      for (uint16_t i = 0; i < 10; i++)
      {
        colors[i] = rgb_color(0, 250, 130);
      }
      for (uint16_t i = 10; i < 20; i++)
      {
        colors[i] = rgb_color(170, 0, 255);
      }
      for (uint16_t i = 20; i < 30; i++)
      {
        colors[i] = rgb_color(255, 255, 255);
      }
      // Write the colors to the LED strip.
      ledStrip.write(colors, LED_COUNT);
      delay(100);
    }
  }

  // Summer Mode = T-shirt + Shorts
  if (digitalRead(SUMMERBUTTON) == LOW) {
    tshirtDressNumber = random (1, 10);
    shortsDressNumber = random (1, 6);
    lcd.backlight();
    lcd.setCursor(0, 0);
    lcd.print(" Season:Summer  ");
    lcd.setCursor(0, 1);
    lcd.print("T-shirt:");
    lcd.setCursor(7, 1);
    lcd.print(tshirtDressNumber);
    lcd.setCursor(8, 1);
    lcd.print(",shorts:");
    lcd.setCursor(15, 1);
    lcd.print(shortsDressNumber);

    //LED displays randomly selected clothing colors
    if ((tshirtDressNumber <= 5) && (shortsDressNumber <= 2)) {
      for (uint16_t i = 0; i < 15; i++)
      {
        colors[i] = rgb_color(250, 110, 0);
      }
      for (uint16_t i = 15; i < 30; i++)
      {
        colors[i] = rgb_color(230, 230, 0);
      }
      // Write the colors to the LED strip.
      ledStrip.write(colors, LED_COUNT);
      delay(100);
    }

    if ((tshirtDressNumber <= 5) && (shortsDressNumber > 2)) {
      for (uint16_t i = 0; i < 15; i++)
      {
        colors[i] = rgb_color(250, 110, 0);
      }
      for (uint16_t i = 15; i < 30; i++)
      {
        colors[i] = rgb_color(255, 0, 70);
      }
      // Write the colors to the LED strip.
      ledStrip.write(colors, LED_COUNT);
      delay(100);
    }

    if ((tshirtDressNumber > 5) && (shortsDressNumber <= 2)) {
      for (uint16_t i = 0; i < 15; i++)
      {
        colors[i] = rgb_color(0, 250, 130);
      }
      for (uint16_t i = 15; i < 30; i++)
      {
        colors[i] = rgb_color(230, 230, 0);
      }
      // Write the colors to the LED strip.
      ledStrip.write(colors, LED_COUNT);
      delay(100);
    }

    if ((tshirtDressNumber > 5) && (shortsDressNumber > 2)) {
      for (uint16_t i = 0; i < 15; i++)
      {
        colors[i] = rgb_color(0, 250, 130);
      }
      for (uint16_t i = 15; i < 30; i++)
      {
        colors[i] = rgb_color(255, 0, 70);
      }
      // Write the colors to the LED strip.
      ledStrip.write(colors, LED_COUNT);
      delay(100);
    }
  }

  // Winter Mode = T-shirt + Jacket + Winter Coat + Pants
  if (digitalRead(WINTERBUTTON) == LOW) {
    tshirtDressNumber = random (1, 10);
    jacketDressNumber = random (10, 20);
    winterCoatDressNumber = random (20, 26);
    pantsDressNumber = random (6, 10);
    lcd.backlight();
    lcd.setCursor(0, 0);
    lcd.print(" Season:Winter  ");
    lcd.setCursor(0, 1);
    lcd.print("T:");
    lcd.setCursor(2, 1);
    lcd.print(tshirtDressNumber);
    lcd.setCursor(3, 1);
    lcd.print(",J:");
    lcd.setCursor(6, 1);
    lcd.print(jacketDressNumber);
    lcd.setCursor(8, 1);
    lcd.print(",C:");
    lcd.setCursor(11, 1);
    lcd.print(winterCoatDressNumber);
    lcd.setCursor(13, 1);
    lcd.print(",P");
    lcd.setCursor(15, 1);
    lcd.print(pantsDressNumber);

    //LED displays randomly selected clothing colors
    if ((tshirtDressNumber <= 5) && (jacketDressNumber <= 15) && (pantsDressNumber <= 7)) {
      for (uint16_t i = 0; i < 7; i++)
      {
        colors[i] = rgb_color(250, 110, 0);
      }
      for (uint16_t i = 7; i < 15; i++)
      {
        colors[i] = rgb_color(0, 0, 255);
      }
      for (uint16_t i = 15; i < 22; i++)
      {
        colors[i] = rgb_color(160, 160, 160);
      }
      for (uint16_t i = 22; i < 30; i++)
      {
        colors[i] = rgb_color(0, 155, 155);
      }
      // Write the colors to the LED strip.
      ledStrip.write(colors, LED_COUNT);
      delay(100);
    }

    if ((tshirtDressNumber <= 5) && (jacketDressNumber <= 15) && (pantsDressNumber > 7)) {
      for (uint16_t i = 0; i < 7; i++)
      {
        colors[i] = rgb_color(250, 110, 0);
      }
      for (uint16_t i = 7; i < 15; i++)
      {
        colors[i] = rgb_color(0, 0, 255);
      }
      for (uint16_t i = 15; i < 22; i++)
      {
        colors[i] = rgb_color(160, 160, 160);
      }
      for (uint16_t i = 22; i < 30; i++)
      {
        colors[i] = rgb_color(255, 255, 255);
      }
      // Write the colors to the LED strip.
      ledStrip.write(colors, LED_COUNT);
      delay(100);
    }

    if ((tshirtDressNumber <= 5) && (jacketDressNumber > 15) && (pantsDressNumber <= 7)) {
      for (uint16_t i = 0; i < 7; i++)
      {
        colors[i] = rgb_color(250, 110, 0);
      }
      for (uint16_t i = 7; i < 15; i++)
      {
        colors[i] = rgb_color(170, 0, 255);
      }
      for (uint16_t i = 15; i < 22; i++)
      {
        colors[i] = rgb_color(160, 160, 160);
      }
      for (uint16_t i = 22; i < 30; i++)
      {
        colors[i] = rgb_color(0, 155, 155);
      }
      // Write the colors to the LED strip.
      ledStrip.write(colors, LED_COUNT);
      delay(100);
    }

    if ((tshirtDressNumber <= 5) && (jacketDressNumber > 15) && (pantsDressNumber > 7)) {
      for (uint16_t i = 0; i < 7; i++)
      {
        colors[i] = rgb_color(250, 110, 0);
      }
      for (uint16_t i = 7; i < 15; i++)
      {
        colors[i] = rgb_color(170, 0, 255);
      }
      for (uint16_t i = 15; i < 22; i++)
      {
        colors[i] = rgb_color(160, 160, 160);
      }
      for (uint16_t i = 22; i < 30; i++)
      {
        colors[i] = rgb_color(255, 255, 255);
      }
      // Write the colors to the LED strip.
      ledStrip.write(colors, LED_COUNT);
      delay(100);
    }

    if ((tshirtDressNumber > 5) && (jacketDressNumber <= 15) && (pantsDressNumber <= 7)) {
      for (uint16_t i = 0; i < 7; i++)
      {
        colors[i] = rgb_color(0, 250, 130);
      }
      for (uint16_t i = 7; i < 15; i++)
      {
        colors[i] = rgb_color(0, 0, 255);
      }
      for (uint16_t i = 15; i < 22; i++)
      {
        colors[i] = rgb_color(160, 160, 160);
      }
      for (uint16_t i = 22; i < 30; i++)
      {
        colors[i] = rgb_color(0, 155, 155);
      }
      // Write the colors to the LED strip.
      ledStrip.write(colors, LED_COUNT);
      delay(100);
    }

    if ((tshirtDressNumber > 5) && (jacketDressNumber <= 15) && (pantsDressNumber > 7)) {
      for (uint16_t i = 0; i < 7; i++)
      {
        colors[i] = rgb_color(0, 250, 130);
      }
      for (uint16_t i = 7; i < 15; i++)
      {
        colors[i] = rgb_color(0, 0, 255);
      }
      for (uint16_t i = 15; i < 22; i++)
      {
        colors[i] = rgb_color(160, 160, 160);
      }
      for (uint16_t i = 22; i < 30; i++)
      {
        colors[i] = rgb_color(255, 255, 255);
      }
      // Write the colors to the LED strip.
      ledStrip.write(colors, LED_COUNT);
      delay(100);
    }

    if ((tshirtDressNumber > 5) && (jacketDressNumber > 15) && (pantsDressNumber <= 7)) {
      for (uint16_t i = 0; i < 7; i++)
      {
        colors[i] = rgb_color(0, 250, 130);
      }
      for (uint16_t i = 7; i < 15; i++)
      {
        colors[i] = rgb_color(170, 0, 255);
      }
      for (uint16_t i = 15; i < 22; i++)
      {
        colors[i] = rgb_color(160, 160, 160);
      }
      for (uint16_t i = 22; i < 30; i++)
      {
        colors[i] = rgb_color(0, 155, 155);
      }
      // Write the colors to the LED strip.
      ledStrip.write(colors, LED_COUNT);
      delay(100);
    }

    if ((tshirtDressNumber > 5) && (jacketDressNumber > 15) && (pantsDressNumber > 7)) {
      for (uint16_t i = 0; i < 7; i++)
      {
        colors[i] = rgb_color(0, 250, 130);
      }
      for (uint16_t i = 7; i < 15; i++)
      {
        colors[i] = rgb_color(170, 0, 255);
      }
      for (uint16_t i = 15; i < 22; i++)
      {
        colors[i] = rgb_color(160, 160, 160);
      }
      for (uint16_t i = 22; i < 30; i++)
      {
        colors[i] = rgb_color(255, 255, 255);
      }
      // Write the colors to the LED strip.
      ledStrip.write(colors, LED_COUNT);
      delay(100);
    }
  }

  // Reset Button, there will be a little surprise if you keep pressing reset button!
  if (digitalRead(RESETBUTTON) == LOW) {
    lcd.backlight();
    lcd.setCursor(0, 0);
    lcd.print("Digital  Stylist");
    lcd.setCursor(0, 1);
    lcd.print("Have a nice day!");

    // Update the colors.
    uint16_t time = millis() >> 2;
    for (uint16_t i = 0; i < LED_COUNT; i++)
    {
      byte x = (time >> 2) - (i << 3);
      colors[i] = hsvToRgb((uint32_t)x * 359 / 256, 255, 255);
    }

    // Write the colors to the LED strip.
    ledStrip.write(colors, LED_COUNT);
    delay(5);
  }
}

 

Jianxiao Ge

]]>
https://courses.ideate.cmu.edu/60-223/f2018/work/digital-stylist/feed/ 0
Surprise: Moving Courier Box ! https://courses.ideate.cmu.edu/60-223/f2018/work/surprise-moving-courier-box/ https://courses.ideate.cmu.edu/60-223/f2018/work/surprise-moving-courier-box/#respond Mon, 24 Sep 2018 07:02:39 +0000 https://courses.ideate.cmu.edu/60-223/f2018/work/?p=3921

The appearance of moving courier box.

The appearance of moving courier box.

The internal structure of moving courier box.

The internal structure of moving courier box.

The top view of moving courier box.

Bottom of moving courier box.

The driving motors and wheels of the moving courier box.

Surprise: Moving Courier Box !
Narrative Description

The project was inspired by an Arduino project found online where a prank was done to people who were trying to get tissue from a tissue box. The tissue box would sense a hand is getting close and the box would move away from the approaching hand, not letting the person get the tissue. The aim of this project was similar, which was producing an object that would sense a human approaching and run away.

The object was intentionally chosen as an object that would be an everyday-item that people would expect to see at any place instead of an item specially made for this project. This induces the surprise of the project by making an object that people would not expect to move. Original ideas included chairs or tables in the lab, but because of physical constraints, the object was chosen to be a courier box.

progress images

An ultrasonic ranger was used in our first attempt. However, it didn’t work well.

After several attempts, the IR sensor was selected since its good performance. The code and circuit parts were basically completed.

The power output of the motors was not enough to support the movement of the large box. Also, the bottom cardboard was bent due to the weight of the battery and other stuff. Therefore, we decided to change the size of the box.

Having learned the lesson for the first time, we made the underside support structure out of wood to make sure the motors and wheels could work properly.

Discussion

Our initial concept was that once a hand approaches above the box in an attempt to open it, the box would sense the hand and run away from it in a random direction with random speed, using the random numbers that were generated by the Arduino.

Instead of the ultrasonic ranger that was introduced in class, an IR sensor has been used due to the instability of the data that the ultrasonic ranger gave and the smaller size of the IR sensor.

After the completion of the code and circuit parts, we tried to put it in a large cardboard box, but the power output of the motor was not enough to support the movement of the box. What’s more, the bottom cardboard was bent due to the weight of the battery and other stuff. Therefore, we had to choose a smaller courier box to achieve our initial goal.

The IR sensor’s unstable data and the physical installation of the electronics into the box resulted in a wobbly irregular movement of the box. It was not behaving perfectly as originally planned, but the mere appearance of a package box with a label moving around on a table still had a surprising element of an ordinary item moving as intended.

Schematic

Schematic of the moving courier box

Code
//pin numbers

const int MOTORR1 = 6; //first pin for motor on the right

const int MOTORR2 = 5; //second pin for motor on the right

const int MOTORL1 = 10; //first pin for motor on the left

const int MOTORL2 = 11; //second pin for motor on the left

const int IRSENSOR = A0;

const int SWITCHPIN = 2;

const int DRIVERPIN = 13;

int direc;

int speedR;

int speedL;




void setup() {

  //Initialize pins

  pinMode(MOTORR1, OUTPUT);

  pinMode(MOTORR2, OUTPUT);

  pinMode(MOTORL1, OUTPUT);

  pinMode(MOTORL2, OUTPUT);

  pinMode(IRSENSOR, INPUT); //sensing distance

  pinMode(SWITCHPIN, INPUT); //decides whether to turn the driver on or off

  pinMode(DRIVERPIN, OUTPUT); //on/off will be delivered to driver

  //box will start not moving

  digitalWrite(MOTORR1, LOW);

  digitalWrite(MOTORR2, LOW);

  digitalWrite(MOTORL1, LOW);

  digitalWrite(MOTORL2, LOW);

  randomSeed(analogRead(0)); //don't plug anything into 0

  //Serial.begin(9600);

}




void loop() {

  //box will check if anything is too close

  int distance = analogRead(IRSENSOR);

  //Serial.println(distance);

  direc = random(2);

  speedR = random(255);

  speedL = random(255);

  int switchVal = digitalRead(SWITCHPIN);

  //driver turned on

  if (switchVal == HIGH) {

    digitalWrite(DRIVERPIN, HIGH);

    //maximum distance below is 50 for now, but it can be adjusted by wiggling the potentiometer

    //IR sensor will give low number when nothing is nearby and high number when something is close

    if (distance > 50 ) { //people are too close, so it wants to run away 🙁

      if (direc == 0) { //box moves forward

        //speed for motors will be different from each other

        //speed for each motor will be random so that the box will move at a random direction

        analogWrite(MOTORR1, speedR);

        analogWrite(MOTORR2, 0);

        analogWrite(MOTORL1, speedL);

        analogWrite(MOTORL2, 0);

        //Serial.println("forward,"+ String(speedR));

        delay(3000);

      }

      if (direc == 1) { //box moves backward

        analogWrite(MOTORR1, 0);

        analogWrite(MOTORR2, speedR);

        analogWrite(MOTORL1, 0);

        analogWrite(MOTORL2, speedL);

        //Serial.println("backward");

        delay(3000);

      }

    }

    else {

      digitalWrite(MOTORR1, LOW);

      digitalWrite(MOTORR2, LOW);

      digitalWrite(MOTORL1, LOW);

      digitalWrite(MOTORL2, LOW);

    }

  }

  else {

    digitalWrite(DRIVERPIN, LOW);

    //Serial.println("driver off");

  }

}

 

Author

]]>
https://courses.ideate.cmu.edu/60-223/f2018/work/surprise-moving-courier-box/feed/ 0
Click Canvas https://courses.ideate.cmu.edu/60-223/f2018/work/click-canvas/ https://courses.ideate.cmu.edu/60-223/f2018/work/click-canvas/#respond Thu, 30 Aug 2018 03:08:10 +0000 https://courses.ideate.cmu.edu/60-223/f2018/work/?p=3836 Project title: Click Canvas, an Interactive Wall

Project creator: Natthakit Kim Kang

Short description: Click Canvas is an interactive wall for anyone to show their creativity. It’s an interactive 8-bit retro display serving people at the Yelo house gallery in Bangkok.

Response: I like it because it provides an easy but interesting way for people to create and show their ideas in a short time. It has the potential to become a landmark if it’s larger and put in a city square. If I have a chance to improve this, I’d probably set another small monitor besides the installation so that people (especially kids and old people) can also draw the patterns in the smaller screen to control the light.

]]>
https://courses.ideate.cmu.edu/60-223/f2018/work/click-canvas/feed/ 0