Made by Xuer, Melissa
Instructor: Viola
Our project is intended to provide the users with an experience of blurred boundaries between fantasy and reality through interacting with a dragon(or part of it.). To achieve this goal, we’ve incorporated origami, Arduino, Processing and also some techniques into the project. We want the user to get curious and try to touch the dragon, triggering the dragon to react, or interact with the dragon face on the Screen. The dragon is supposed to be scary and intimidating to people, so I designed three modes for the dragon. They are ‘Rest Mode,’ ‘Triggered Mode’ and ‘Anger Mode.’ Rest Mode exists to indicate the living state of the dragon, which is why we include the fading white LEDs to indicate the ‘breathing.’ ‘Triggered Mode’ is for indication of valid interaction, including motion of the dragon's body or a change in facial expression in the dragon’s face. When the interaction lasts for too long, the dragon gets annoyed, switching to ‘Anger Mode,’ triggering the red LED, heater and intense motion and fire on the screen. We used origami to build the dragon’s body. I personally have worked with origami before in high school. When we were learning about servos, my first impression was that it could only provide simple motion, combined with origami, something that provides a sophisticated structure that can create complex motion with single force. During User Testing, people gave us advice on the decoration, which we made modifications later. I think they are effective, as the focus becomes more clear with decorations being simplified. But Melissa and I had conflicts in what does ‘simple’ look like, so eventually there were compromises.
For fabrication, we decided at the very beginning that the project will be sheltered in a box, we decided to open a hole in a regular box to both show and hide stuff. We chose the ultrasonic sensor to sense people approaching the dragon. For motion, we chose servo, for ‘fire’ effect, we did heater. To indicate mood and state of the dragon, we used LED lights, for it is natural to connect red light with anger or warning, and the breathing rhythm can also be intuitively linked with the fading effect of white LEDs. We used Origami to make the actual body of the dragon, since some origami structures look very like dragon scales. We tried a bunch of Origami structures including waterbomb, but we ended up using the simple Miura Origami. The waterbomb structure was too complicated and the paper we have couldn’t support it. Also, Miura origami looks pretty good already.
I encountered three major obstacles during the production process with my side of the building. The first one is about Origami. I tried to use wooden plates and lasercut the structure straight-forwardly, but it didn’t work out. I figured that the material has to be tenacious, bendable in a certain way, especially for the creases, all of the valley folds and mountain folds. After doing some research and experimenting with different materials, I decided to use plastic paper, for it is not easy to produce a fold-line on it, but once a fold-line exists, it’s not easy to fade, making the structure difficult to collapse. What I did was confirm the fold-lines bit by bit with my nails multiple times. At first, we tried to laser cut a wooden plate to make the ‘scales’, but after we tried, gluing all of them would be too heavy for the plastic paper and eventually it’ll break. So we selectively glued some of them. The second obstacle was with the code. For coding, I took care of the Arduino part and also PtoA communication part, while my teammate Melissa took care of the animation in Processing. I did more of them after she made it clear that coding was difficult for her. Building the overall structure of the code was difficult, brainstorming it itself took a long time. I was at first trying to trigger Anger Mode inside Triggered mode, but it didn’t work out. After meeting with Viola, she recommended that we execute if-statement twice, the second time of which will decide if the programme will go into Anger Mode or not. After pinning down the big structure, P to A communication was left. We didn’t talk about how the example code works in class, so I had to ask Kelvin to explain every detail of it before feeling safe to use it. Eventually it worked. The third biggest obstacle was building the circuit. My teammate wasn’t so confident in wiring either so I took care of it. I was a bit too rushed so the wiring was very messy, and after moving it, the sensor stopped working. But after that, my teammate tried to tackle it with me. We tried to sort out everything and made a box in the end to shelter all the wiring.
Some minor things we did for decoration include wrapping the wooden scales with metal tapes to make them shiny, using cotton as amplifiers of the red lights, and painting the inside of the box black to make lights more visible.
I’m not too satisfied with the final results, but it’s the best I can do for now. Our goal is to make the audience have a moment of blurred reality, but the actual effect was quite limited. However, our audience did have some joy of surprises while interacting with the project. What we did well was that the audience did get curious about it and decided to interact with it and explore the reactions of the dragon, but the ‘reaction’ we were able to provide was a bit limited. My definition of interaction would focus on the immersive experience of something. For me, if the interaction is meaningful and the emotions or feelings provided by the interaction were strong enough, it’ll make a difference in people's thinking. My project aligns with the definition in that it is devoted to providing a unique experience. If I had more time, I would modify the motion of the dragon to be more sophisticated, and try to think of another way to position all the delay() in the code, to make the interaction more coherent. I learnt that to work with a partner, it's best not to assume the best out of your partner, and avoid expecting them to have the same ability as you. Also when working with someone, it’s better to take into consideration whether your aesthetics match or not, especially when you’re dealing with an art project. From the accomplishments, I would say that I learnt that things aren't as scary as they seem sometimes, I just need to get started.
Going into trash can ⬇️
Code from Arduino:
#define NUM_OF_VALUES_FROM_PROCESSING 1 /* CHANGE THIS ACCORDING TO YOUR PROJECT */
/* This array stores values from Processing */
int processing_values[NUM_OF_VALUES_FROM_PROCESSING];
int Servo_output = 6;
int Servo_input = 7;
long distance;
#include <Servo.h>
Servo myservo;
int relay_1 = 5;
int LED = 3;
int RED_LED = 10;
int brightness = 0; // how bright the LED is
int fadeAmount = 5; // how many points to fade the LED by
int mode = 0;
int trigger_time;
bool triggered = false;
void setup() {
Serial.begin(9600);
pinMode(relay_1, OUTPUT);
pinMode(Servo_output, OUTPUT);
pinMode(Servo_input, INPUT);
myservo.attach(9);
pinMode(LED,OUTPUT);
pinMode(RED_LED,OUTPUT);
}
void getSerialData() {
static int tempValue = 0; // the "static" makes the local variable retain its value between calls of this function
static int tempSign = 1;
static int valueIndex = 0;
while (Serial.available()) {
char c = Serial.read();
if (c >= '0' && c <= '9') {
// received a digit:
// multiply the current value by 10, and add the character (converted to a number) as the last digit
tempValue = tempValue * 10 + (c - '0');
} else if (c == '-') {
// received a minus sign:
// make a note to multiply the final value by -1
tempSign = -1;
} else if (c == ',' || c == '\n') {
// received a comma, or the newline character at the end of the line:
// update the processing_values array with the temporary value
if (valueIndex < NUM_OF_VALUES_FROM_PROCESSING) { // should always be the case, but double-check
processing_values[valueIndex] = tempValue * tempSign;
}
// get ready for the new data by resetting the temporary value and sign
tempValue = 0;
tempSign = 1;
if (c == ',') {
// move to dealing with the next entry in the processing_values array
valueIndex = valueIndex + 1;
} else {
// except when we reach the end of the line
// go back to the first entry in this case
valueIndex = 0;
}
}
}
}
void RestMode(){
Serial.print("resting distance: ");
// Serial.print("distance: ");
Serial.println(distance);
// triggered = false;
//breathing
for (int fadeValue = 0; fadeValue <= 255; fadeValue += 5) {
// sets the value (range from 0 to 255):
analogWrite(LED, fadeValue);
// wait for 30 milliseconds to see the dimming effect
delay(30);
}
// fade out from max to min in increments of 5 points:
for (int fadeValue = 255; fadeValue >= 0; fadeValue -= 5) {
// sets the value (range from 0 to 255):
analogWrite(LED, fadeValue);
// wait for 30 milliseconds to see the dimming effect
delay(30);
}
}
void TriggerMode(){
Serial.println("triggered");
Serial.print("distance: ");
Serial.println(distance);
//trigger servo
digitalWrite(Servo_output, LOW);
delayMicroseconds(2);
digitalWrite(Servo_output, HIGH);
delayMicroseconds(10);
digitalWrite(Servo_output, LOW);
// pulseIn waits for signal to go from HIGH to LOW,
// timeout according to max range of sensor
long duration = pulseIn(Servo_input, HIGH, 17400);
// sound travels roughly 29cm per microsecond so we divide by 29,
// then by 2 since we recorded sound both going forth and back
distance = duration / 29 / 2;
Serial.println(distance);
myservo.write(60);
delay(300);
myservo.write(120);
delay(300);
}
void AngerMode(){
Serial.println("angry");
triggered = false;
//trigger red light
digitalWrite(RED_LED, HIGH);
delay(2000);
digitalWrite(RED_LED, LOW);
//trigger servo
myservo.write(80);
delay(200);
myservo.write(140);
delay(200);
myservo.write(90);
delay(200);
myservo.write(140);
delay(200);
//trigger relay
digitalWrite(relay_1, HIGH);
Serial.println("All relays ON");
delay(15000);
// turning off
digitalWrite(relay_1, LOW);
Serial.println("All relays OFF");
}
void loop() {
getSerialData();
float val = processing_values[0];
// measuring distance
// additional 2 microsecond delay to ensure pulse clarity
digitalWrite(Servo_output, LOW);
delayMicroseconds(2);
digitalWrite(Servo_output, HIGH);
delayMicroseconds(10);
digitalWrite(Servo_output, LOW);
long duration = pulseIn(Servo_input, HIGH, 17400);
// sound travels roughly 29cm per microsecond so we divide by 29,
// then by 2 since we recorded sound both going forth and back
distance = duration / 29 / 2;
if(distance < 10 && distance > 0 && triggered == false){
TriggerMode();
triggered = true;
//take current time
trigger_time = millis();
Serial.print("trigger_time: ");
Serial.println(trigger_time);
}
if ((millis()-trigger_time) >= 5000 && triggered == true) {
if(distance < 10 && distance > 0){
AngerMode();
} else {
RestMode();
triggered = false;
}
}
if (triggered == false) {
if (distance == 0 || distance > 10) {
RestMode();
}
}
if (processing_values[0] == 1) {
TriggerMode();
}
if (processing_values[0] == 2) {
AngerMode();
}
}
Code from Processing:
int hoverTime = 0; // 鼠标停留时间
boolean isHovering = false;
import processing.serial.*;
Serial serialPort;
int NUM_OF_VALUES_FROM_PROCESSING = 2; /* CHANGE THIS ACCORDING TO YOUR PROJECT */
/* This array stores values you might want to send to Arduino */
int processing_values[] = new int[NUM_OF_VALUES_FROM_PROCESSING];
void setup() {
size(600, 600);
serialPort = new Serial(this, "/dev/cu.usbmodem11101", 9600);
}
void draw() {
background(255);
// 检测鼠标是否在龙脸范围
if (mouseX > 150 && mouseX < 450 && mouseY > 100 && mouseY < 350) {
isHovering = true;
processing_values[0] = 1;
processing_values[1] = 1;
} else {
isHovering = false;
hoverTime = 0; // 重置计时
}
if (isHovering) {
hoverTime++;
}
// 动态参数
float eyebrowRotation = isHovering && hoverTime < 180 ? PI / 6 : 0; // 新眉毛旋转角度
float eyeWidth = isHovering ? 45 : 33; // 眼睛宽度动态变化
float eyeHeight = isHovering ? 40 : 55; // 眼睛高度动态变化
float eyeTilt = isHovering ? -10 : 0; // 眼睛上扬
// 外轮廓固定
noFill();
stroke(243, 247, 130);
strokeWeight(1); // 外轮廓线宽
fill(244,221,95);
bezier(395, 140, 319, 90, 224, 125, 213, 141); // 左耳朵
bezier(177, 157, -36, 90, 76, 271, 155, 207); // 右耳朵
bezier(431, 160, 464, 89, 705, 188, 446, 206);
bezier(452, 205, 467, 221, 469, 307, 461, 328);
bezier(158, 204, 127, 253, 138, 317, 133, 328);
bezier(461, 327, 594, 422, 29, 411, 132, 328);
bezier(466,334,529,39,75,36,133,328); //哦哦啊啊诶诶啊啊哦哦得个得个得个得个
noFill();
bezier(361, 305, 481, 392, 468, 241, 524, 343);
bezier(79, 324, 60, 212, 257, 379, 258, 307);
fill(200,150,21);
bezier(387, 136, 394, -134, 453, 81, 430, 169);
bezier(171, 162, 169, -123, 244, 50, 214, 136);
// 新眉毛
pushMatrix();
stroke(0);
translate(252, 190); // 左眉毛基点
rotate(eyebrowRotation);
strokeWeight(3);
line(-20, -10, 20, 10); // 左眉毛线条
popMatrix();
pushMatrix();
translate(353, 190); // 右眉毛基点
rotate(-eyebrowRotation);
line(-20, 10, 20, -10); // 右眉毛线条
popMatrix();
// 胡须动态(轻微弯曲)
noFill();
strokeWeight(2); // 加粗胡须
bezier(361, 305, 481, 392, 468, 241, 524, 343);
bezier(79, 324, 60, 212, 257, 379, 258, 307);
// 调整眼睛形状
fill(55);
stroke(0);
ellipse(252, 263 + eyeTilt, eyeWidth, eyeHeight); // 左眼
ellipse(353, 264 + eyeTilt, eyeWidth, eyeHeight); // 右眼
// 鼻子固定
fill(132, 102, 32);
ellipse(303, 290, 31, 8);
// 判断是否需要喷火
if (hoverTime > 120) {
drawFire();// 喷火效果
processing_values[0] = 2;
sendSerialData();
} else {
// 深红色嘴巴动态
fill(202, 29, 29);
bezier(273, 311, 268, 353, 347, 418, 343, 310);
}
sendSerialData();
}
// 喷火函数
void drawFire() {
noStroke();
for (int i = 0; i < 5; i++) {
fill(random(200, 255), random(50, 150), 0, 150);
ellipse(303 + random(-15, 15), 330 + random(-15, 15), random(20, 40), random(30, 50));
}
}
void sendSerialData() {
String data = "";
for (int i=0; i<processing_values.length; i++) {
data += processing_values[i];
// if i is less than the index number of the last element in the values array
if (i < processing_values.length-1) {
data += ","; // add splitter character "," between each values element
}
// if it is the last element in the values array
else {
data += "\n"; // add the end of data character linefeed "\n"
}
}
// write to Arduino
serialPort.write(data);
print("To Arduino: " + data); // this prints to the console the values going to arduino
}
Web pages used:
https://cuttle.xyz/@forresto/Origami-simulator-tips-W4lDXuB5m0xh
https://www.youtube.com/watch?v=Yr_MjzAKhe8