Project “Climbing the Mountain in Spring”

made by Jiaqi and Jingyi, instructed by Gottfried Haider

A. Context and Significance
From the lectures, we’ve seen some sample projects from previous students. I found the key part is the interaction which means the mechanism we build should be controlled by some sensors or servos. We referred to our previous projects of Kinetic Wearable and Actuators & Mechanisms, where we used 180 degree Servos and Motors to make the whole mechanics move. So combining their functions together, we chose to borrow three 360 degree servos from ER to move our mechanisms.

B. Concept and Design
For the overall concept of our design, we drew inspiration from an unexpected effect in the “Climbing Spring Mountain” segment of the 2024 CCTV Spring Festival Gala in China. In the segment, the spotlight was occupied for an extended period by one of three singers, who wore distinctly different attire from the others, sparking a series of public discussions. To incorporate this intriguing concept into our midterm project, we designed and created a game where three players compete in speed to climb a mountain. The first player to reach the top must sing into a microphone to win the game; otherwise, they will be conveyed back down the mountain.
The design materials mainly include rollers for the conveyor belt part, 360-degree servos, the conveyor belt itself, cardboard for the mountain structure, buttons and a microphone for interaction, and background music. In selecting materials, we focused on the stability of operation and the sensitivity of sensors to ensure that users could experience a scenario of running and climbing mountains as realistically as possible. We designed the game so that each user has two buttons in front of them. By alternatingly pressing these buttons, they can control their characters to move forward on the conveyor belt, simulating the action of climbing a mountain. Finally, to enhance the gaming experience and fun, we used buzzers to play different pieces of music at the start and end of the game, immersing participants more deeply.

(from Internet)

C. Fabrication and Production
As for the important steps in our production process which are also steps we encountered most of the difficulties, they are building the conveyor belt part and coding for the conveyor belt part. To depict the mountain climbing scenario, we built a pyramid-shaped mountain out of cardboard and created three pathways on its sides for the conveyor belts to help the characters ascend. Initially, we used fitness resistance bands for the conveyor belt, but due to low friction between the plastic rollers and bands, which caused many interruptions in belt movement, we switched to elastic bands that provided better friction and fit. This is the part I mainly engaged in. In the User Testing Session, most users suggested that I should change the material of the belt to make the movements smoother. I spent several days trying out different materials in the labs and finally borrowed elastic bands from students from Interactive fashion and things finally worked out. And I also made the circuit to connect buttons, servos and the microphone together for the whole mountain climbing process. Finally, we collaborated on painting and decorating the Spring Mountain. 

 
For the coding for the conveyor belt, we were stuck on how to make the servo stop when they reach the top of the mountain and the possibility to transfer them down if they lose. I could only write the codes for the servos to move and drive the little man to climb to the mountaintop. In our user-testing, users were confused if the game started and when they should sing to the microphone. We figured this out by adding codes on buzzer to play songs when the game starts and ends. Jingyi came up with a fantastic idea. Since the starting point is roughly the same, we only need to calculate the number of times the button needs to be alternately pressed to make the character reach the mountaintop. By recording the number of times, the program can determine whether it has reached the mountaintop and activate state2: singing. Thanks to professors and LAs from Interaction labs, we were finally able to use “millis” and different “state” to do the parts of climbing down the mountain and singing.
Also, during our production, I found that the circuit wasn’t stable enough every time and we should find a way to be able to repair the circuits. So I came up with a way that we open a door on the mountain and put circuits inside the mountain. So that we could open the door and fix the wrong circuits.

D. Conclusions
During the final presentation, three players stand on three sides of a mountain, and as the start music ends, they press buttons alternately to race their characters up the mountain. The first to reach the top and sound the microphone, failing to meet a 2-second duration, results in their character descending. The game concludes with a different piece of music. (Both pieces of music are built on our own and inspired by the melody from “Climbing Spring Mountain”).

Videos of the project: 

Here is our code:

#include 
#include "pitches.h"
Servo myservo1;
Servo myservo2;
Servo myservo3;  // Create servo object to control a servo
int MicrophoneVal;
bool playerAButtonEnabled = true;
bool playerBButtonEnabled = true;
bool playerCButtonEnabled = true;
long state2startTime;
long xiashan1;
long xiashan2;
long xiashan3;

bool state2FirstRun = true;
int state1Winner = 0;

int state = 1;
int playerAbutton1pin = 2;
int playerAbutton2pin = 4;
int playerAbutton1state;
int playerAbutton2state;
int playerAbutton1prev;
int playerAbutton2prev;
int playerAlastButton = 0;
int playerAposition = 0;

int playerBbutton1pin = 6;
int playerBbutton2pin = 8;
int playerBbutton1state;
int playerBbutton2state;
int playerBbutton1prev;
int playerBbutton2prev;
int playerBlastButton = 0;
int playerBposition = 0;

int playerCbutton1pin = 10;
int playerCbutton2pin = 12;
int playerCbutton1state;
int playerCbutton2state;
int playerCbutton1prev;
int playerCbutton2prev;
int playerClastButton = 0;
int playerCposition = 0;

unsigned long total;
unsigned int count;
const unsigned long INTERVAL = 5000;
unsigned long whenStartedA = 0;
unsigned long whenStartedB = 0;
unsigned long whenStartedC = 0;
unsigned long Singing = 0;

void setup() {
  Serial.begin(9600);
  myservo1.attach(7);
  myservo2.attach(9);
  myservo3.attach(11);
  Serial.println(F("******************* 上春山 *******************"));  //开始
  delay(3000);
  //麦克风在3pin
  pinMode(A0, INPUT);
  int melody[] = {
  NOTE_E5, NOTE_CS5, NOTE_B4, NOTE_CS5, NOTE_E5, 0, NOTE_CS5, NOTE_B4, NOTE_GS4, NOTE_B4, NOTE_CS5, NOTE_GS4, NOTE_B4, NOTE_CS5, NOTE_B4, NOTE_GS4, NOTE_FS4, 0, NOTE_E4, NOTE_CS4,  NOTE_E4, 0,  NOTE_E4,  NOTE_FS4,  NOTE_E4,  NOTE_E4,  NOTE_E4
};

// note durations: 4 = quarter note, 8 = eighth note, etc.:
int noteDurations[] = {
  8, 16, 8, 8, 4, 8, 16, 16,8, 8, 8, 8, 2,4, 8, 8, 8, 8, 8, 8, 4, 8, 16, 16, 8, 8, 4
};
  for (int thisNote = 0; thisNote < 27; thisNote++) {

    // to calculate the note duration, take one second divided by the note type.
    //e.g. quarter note = 1000 / 4, eighth note = 1000/8, etc.
    int noteDuration = 1000 / noteDurations[thisNote];
    tone(3, melody[thisNote], noteDuration);

    // to distinguish the notes, set a minimum time between them.
    // the note's duration + 30% seems to work well:
    int pauseBetweenNotes = noteDuration * 1.30;
    delay(pauseBetweenNotes);
    // stop the tone playing:
    noTone(3);
  }
  state = 1; 
}




void loop() {
  if (state == 1) {
    state1();
  } else if (state == 2) {
    state2();
  } else if (state == 3) {
    state3();
  }
}

  void state1() {
    // ascent state
    //player A
    playerAbutton1state = digitalRead(playerAbutton1pin);
    playerAbutton2state = digitalRead(playerAbutton2pin);

    if (playerAbutton1state == HIGH && playerAbutton1prev == LOW && playerAlastButton == 0 && playerAButtonEnabled == true) {
      whenStartedA = millis();
      playerAButtonEnabled = false;
      // go up
      playerAposition = playerAposition + 1;
      playerAlastButton = 1;
    } else if (playerAbutton2state == HIGH && playerAbutton2prev == LOW && playerAlastButton == 1 && playerAButtonEnabled == true) {
      whenStartedA = millis();
      playerAButtonEnabled = false;
      playerAposition = playerAposition + 1;
      playerAlastButton = 0;
    }

    if (millis() <= whenStartedA + 200) {
      myservo1.write(135);
    } else if (millis() <= whenStartedA + 300) {
      myservo1.write(90);
    } else {
      playerAButtonEnabled = true;
    }

    Serial.print("player 敬亭: ");
    Serial.println(playerAposition);
    //player B 魏晨
    playerBbutton1state = digitalRead(playerBbutton1pin);
    playerBbutton2state = digitalRead(playerBbutton2pin);

    if (playerBbutton1state == HIGH && playerBbutton1prev == LOW && playerBlastButton == 0 && playerBButtonEnabled == true) {
      whenStartedB = millis();
      playerBButtonEnabled = false;
      // go up
      playerBposition = playerBposition + 1;
      playerBlastButton = 1;
    } else if (playerBbutton2state == HIGH && playerBbutton2prev == LOW && playerBlastButton == 1 && playerBButtonEnabled == true) {
      whenStartedB = millis();
      playerBButtonEnabled = false;
      playerBposition = playerBposition + 1;
      playerBlastButton = 0;
    }

    if (millis() <= whenStartedB + 200) {
      myservo2.write(135);
    } else if (millis() <= whenStartedB + 300) {
      myservo2.write(90);
    } else {
      playerBButtonEnabled = true;
    }
    Serial.print("player 魏晨: ");
    Serial.println(playerBposition);

    //player C
    playerCbutton1state = digitalRead(playerCbutton1pin);
    playerCbutton2state = digitalRead(playerCbutton2pin);

    if (playerCbutton1state == HIGH && playerCbutton1prev == LOW && playerClastButton == 0 && playerCButtonEnabled == true) {
      whenStartedC = millis();
      playerCButtonEnabled = false;
      // go up
      playerCposition = playerCposition + 1;
      playerClastButton = 1;
    } else if (playerCbutton2state == HIGH && playerCbutton2prev == LOW && playerClastButton == 1 && playerCButtonEnabled == true) {
      whenStartedC = millis();
      playerCButtonEnabled = false;
      playerCposition = playerCposition + 1;
      playerClastButton = 0;
    }

    if (millis() <= whenStartedC + 200) {
      myservo3.write(135);
    } else if (millis() <= whenStartedC + 300) { myservo3.write(90); } else { playerCButtonEnabled = true; } Serial.print("player 大勋: "); Serial.println(playerCposition); if (playerAposition >= 25) {
      myservo1.write(90);
      myservo2.write(90);
      myservo3.write(90);
      Serial.println("End of Climbing!!!");
      state = 2;
      state1Winner = 1;
      delay(4000);
      //Serial.println("Listening to microphone");
    } else if (playerBposition >= 23) {
      myservo1.write(90);
      myservo2.write(90);
      myservo3.write(90);
      Serial.println("End of Climbing!!!");
      state = 2;
      state1Winner = 2;
      delay(4000);
    } else if (playerCposition >= 23) {
      myservo1.write(90);
      myservo2.write(90);
      myservo3.write(90);
      Serial.println("End of Climbing!!!");
      state = 2;
      state1Winner = 3;
      delay(4000);
    }

    playerAbutton1prev = playerAbutton1state;
    playerAbutton2prev = playerAbutton2state;
    playerBbutton1prev = playerBbutton1state;
    playerBbutton2prev = playerBbutton2state;
    playerCbutton1prev = playerCbutton1state;
    playerCbutton2prev = playerCbutton2state;
  }

  // singing state
  void state2() {
    if (state2FirstRun == true) {
      state2startTime = millis();
      state2FirstRun = false;
    }
    int MicrophoneVal = analogRead(A0);
    Serial.println(MicrophoneVal);
    delay(10);

    if (millis() - state2startTime < 2000 && MicrophoneVal < 56) {
      if (state1Winner == 1) {
        myservo1.write(0);  // 敬亭下山
        delay(4000);
        myservo1.write(90);
        Serial.println("敬亭 lost.");
        state = 3;
      } else if (state1Winner == 2) {
        myservo2.write(0);  // 魏晨下山
        delay(4000);
        myservo2.write(90);
        Serial.println("魏晨 lost.");
        state = 3;
      } else if (state1Winner == 3) {
        myservo3.write(0);  // 大勋下山
        delay(4000);
        myservo3.write(90);
        Serial.println("大勋 lost.");
        state = 3;
      }
    } else {
      Serial.println("You win!!!");
      delay(2000);
      state = 3;
    }
  }

  void state3() {

    Serial.println("End of Game.");
    int melody[] = {
      NOTE_CS5, NOTE_B4, NOTE_CS5, NOTE_E5, 0, NOTE_GS4, NOTE_FS4, NOTE_GS4, NOTE_B4, 0, NOTE_CS5, NOTE_B4, NOTE_GS4, NOTE_FS4, NOTE_E4, NOTE_CS4, NOTE_CS5, NOTE_B4, NOTE_FS4, NOTE_GS4, 0, NOTE_CS5, NOTE_B4, NOTE_CS5, NOTE_E5, 0, NOTE_GS4, NOTE_FS4, NOTE_GS4, NOTE_B4, 0, NOTE_CS5, NOTE_B4, NOTE_GS4, NOTE_FS4, NOTE_E4, 0, NOTE_CS4, NOTE_E4, 0, NOTE_E5, NOTE_FS5, NOTE_E5, NOTE_E5, NOTE_E5
    };

    int noteDurations[] = {
      4, 8, 8, 4, 4, 4, 8, 8, 4, 4, 4, 8, 8, 4, 8, 8, 4, 8, 8, 4, 4, 4, 8, 8, 4, 4, 4, 8, 8, 4, 4, 4, 8, 8, 8, 8, 8, 8, 4, 8, 16, 16, 8, 8, 4

    };

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

      // to calculate the note duration, take one second divided by the note type.
      //e.g. quarter note = 1000 / 4, eighth note = 1000/8, etc.
      int noteDuration = 1000 / noteDurations[thisNote];
      tone(3, melody[thisNote], noteDuration);

      // to distinguish the notes, set a minimum time between them.
      // the note's duration + 30% seems to work well:
      int pauseBetweenNotes = noteDuration * 1.30;
      delay(pauseBetweenNotes);
      // stop the tone playing:
      noTone(3);
    }
    while (true);
  } 

Our project’s foundation is centered on “Interaction,” aiming to adapt the original “Climbing Spring Mountain” program into an interactive game. This adaptation encompasses alternating button presses to simulate climbing, singing into a microphone to echo the original show’s essence, and setting background music, all while retaining the core concepts of climbing and singing but transforming the actual activities into interactive components.
Our professor suggested enhancing interactivity by replacing the button-pressing mechanism with foot pedals and incorporating an LED indicator to provide clear feedback when someone reaches the mountain’s summit. This modification aims to make the interaction more engaging and intuitive.
Personally, the current game design, where the first player to summit descends if they fail to sing, inadvertently lowers the engagement level for the remaining players. A potential improvement could involve allowing the other two players to continue their ascent after the first descends, maintaining engagement until one of the three successfully sings atop the mountain. This adjustment would enhance the overall interactive experience and participation.

E. Disassembly
Recycling components!!!

F. Appendix
“白敬亭风波影响,幕后人员被连累:没了奖金丢了饭碗!” [Affected by the Bai Jingting incident! Behind the scenes personnel are implicated: losing bonuses and losing jobs], 

https://www.sohu.com/a/759025916_121680812

Leave a Reply

Your email address will not be published. Required fields are marked *