Harmony in Chaos - Traditional Innovative Tea Ceremony
Planned and Executed: Nathan Ynson and Korn Kijwattanapong
Course: Interaction Lab (INTM-SHU 101) at New York University, Shanghai
Credits : Andy Garcia, Angy He, FAB Lab, and IMA Fellows: For guidance and technical support.
Course Instructor/ Professor: Viola (Leqi) He
The concept of Tea Harmony in Chaos emerged from the idea of blending the serenity of traditional tea ceremonies with modern technology to create an interactive and mindful experience. Drawing inspiration from Apple’s Think Different ethos and the principles of minimalism, we aimed to design a system that is simple yet powerful, hiding the complexity of sensors and electronics while preserving an authentic, easy-to-understand experience.
Our design decisions were heavily influenced by how users would interact with the project. The use of ultrasonic, tilt, and touch sensors facilitated a guided, step-by-step tea preparation ritual. The project encourages users to engage both visually and physically with the tea-making process. During user testing, we encountered challenges with the tilt sensor being overly sensitive. This led us to fine-tune its responsiveness, ensuring a seamless interaction that enhanced, rather than disrupted, the user’s focus. Feedback also revealed that the LED strip, integrated to provide rhythm and visual cues, added a layer of fun and meditation, affirming its role as a vital component in the experience.
We focused on making the design look simple and beautiful while keeping it functional. We chose light-colored wood for the mainboard because it feels natural, warm, and cozy. To make the design clean, we laser-cut the wood for accuracy and hid all the sensors and wires underneath so they wouldn’t distract from the experience.
We used three main sensors: an ultrasonic sensor to detect distance, a tilt sensor to sense movement, and a touch sensor for interaction. These were chosen because they are simple to use and worked well for guiding the tea-making process. We considered using a gyroscope instead of a tilt sensor, but it was too complex and expensive for this project.
Working with my partner, Nathan, was both fun and challenging. The tilt sensor was tricky because it was too sensitive at first. Fixing it took time, patience, and testing, but it was a great learning experience.
Upper
Front
The goal of Tea Harmony in Chaos was to make tea-making a calm and interactive experience using technology. We achieved this goal by creating a step-by-step ritual that used sensors, lights, and interaction to help users find moments of peace. People responded well to the mix of visuals, rhythm, and feedback, showing that the project was both engaging and relaxing.
If we had more time, I would improve the tilt sensor further and add options like sound or custom lights to make the experience even more personal. This project taught me how important it is to balance technology and simplicity. I also learned that solving technical problems, like fixing sensors, requires patience and repeated testing.
Overall, this project showed me how combining traditional tea ceremonies with modern technology can create something meaningful, helping people find calm and connection in their busy lives.
Returning IxLAB Equipments
Putting the card boards in the recycle bin
Disconnect the circuit from the Breadboard and Arduino. Put them back into the Arduino toolkit.
Arduino:
#include <NewPing.h>
#include <Adafruit_NeoPixel.h>
// Ultrasonic Sensor Configuration
#define SONAR_NUM 3 // Number of ultrasonic sensors
#define MAX_DISTANCE 200 // Maximum distance (in cm)
// Neopixel Configuration
#define NUM_LEDS 60 // Number of LEDs per strip
#define PIN_STRIP1 8 // Pin for Neopixel Strip 1
#define PIN_STRIP2 9 // Pin for Neopixel Strip 2
NewPing sonar[SONAR_NUM] = {
NewPing(3, 2, MAX_DISTANCE),
NewPing(5, 4, MAX_DISTANCE),
NewPing(7, 6, MAX_DISTANCE)
};
// Pins for piezo sensors and tilt switch
const int piezoPins[] = {A2, A4, A5};
const int tiltSwitchPin = 11;
int tilt = 1;
// Initialize Neopixel Strips
Adafruit_NeoPixel strip1(NUM_LEDS, PIN_STRIP1, NEO_GRB + NEO_KHZ800);
Adafruit_NeoPixel strip2(NUM_LEDS, PIN_STRIP2, NEO_GRB + NEO_KHZ800);
// Variables for flashing effect
unsigned long flashStartTime = 0;
unsigned long lastFlashTime = 0;
bool isRedOn = false;
bool tiltActivated = false; // To track when tilt switch is activated
void setup() {
Serial.begin(115200);
// Initialize tilt switch and piezo sensors
pinMode(tiltSwitchPin, INPUT);
for (int i = 0; i < 3; i++) {
pinMode(piezoPins[i], INPUT);
}
// Initialize Neopixel strips
strip1.begin();
strip2.begin();
strip1.show(); // Turn off all LEDs initially
strip2.show();
}
void loop() {
int tiltState = digitalRead(tiltSwitchPin);
if (tiltState == HIGH) {
tilt = 1;
}
// Read piezo sensors and ultrasonic sensors
int piezo1 = analogRead(piezoPins[0]);
int piezo2 = analogRead(piezoPins[1]);
int piezo3 = analogRead(piezoPins[2]);
int tilt = digitalRead(tiltSwitchPin);
int ucc1 = sonar[0].ping_cm();
int ucc2 = sonar[1].ping_cm();
int ucc3 = sonar[2].ping_cm();
// Send sensor data over Serial
Serial.print(piezo1); Serial.print(",");
Serial.print(piezo2); Serial.print(",");
Serial.print(piezo3); Serial.print(",");
Serial.print(tilt); Serial.print(",");
Serial.print(ucc1); Serial.print(",");
Serial.print(ucc2); Serial.print(",");
Serial.println(ucc3); // End with newline
// Check if the tilt switch is activated
if (tiltState == LOW && !tiltActivated) { // If tilt switch is pressed and not yet activated
tiltActivated = true;
executeColorSequence();
}
// Flash red for 15 seconds before switching to orange
if (millis() - flashStartTime < 15000 && !tiltActivated) {
flashRed();
} else {
// After 15 seconds, revert to calm orange unless tilt is activated
if (!tiltActivated) {
setStripToOrange(strip1);
setStripToOrange(strip2);
}
}
// After the color sequence, revert to calm orange if tilt was activated
if (tiltActivated) {
setStripToOrange(strip1);
setStripToOrange(strip2);
tiltActivated = false; // Reset the flag after completing the sequence
}
delay(20);
}
// Function to set an entire Neopixel strip to white light
void setStripToWhite(Adafruit_NeoPixel &strip) {
for (int i = 0; i < NUM_LEDS; i++) {
strip.setPixelColor(i, strip.Color(255, 255, 255)); // Full white
}
strip.show();
}
// Function to set an entire Neopixel strip to orange light
void setStripToOrange(Adafruit_NeoPixel &strip) {
for (int i = 0; i < NUM_LEDS; i++) {
strip.setPixelColor(i, strip.Color(255, 100, 0)); // Orange color
}
strip.show();
}
// Function to set an entire Neopixel strip to red light
void setStripToRed(Adafruit_NeoPixel &strip) {
for (int i = 0; i < NUM_LEDS; i++) {
strip.setPixelColor(i, strip.Color(255, 0, 0)); // Full red
}
strip.show();
}
// Function to set an entire Neopixel strip to yellow light
void setStripToYellow(Adafruit_NeoPixel &strip) {
for (int i = 0; i < NUM_LEDS; i++) {
strip.setPixelColor(i, strip.Color(255, 255, 0)); // Yellow color
}
strip.show();
}
// Function to set an entire Neopixel strip to green light
void setStripToGreen(Adafruit_NeoPixel &strip) {
for (int i = 0; i < NUM_LEDS; i++) {
strip.setPixelColor(i, strip.Color(0, 255, 0)); // Green color
}
strip.show();
}
// Function to handle the flashing red effect for 15 seconds
void flashRed() {
unsigned long currentTime = millis();
// Flash every 500 milliseconds (on for 250ms, off for 250ms)
if (currentTime - lastFlashTime >= 500) {
lastFlashTime = currentTime;
if (isRedOn) {
// Turn off red light
setStripToBlack(strip1);
setStripToBlack(strip2);
isRedOn = false;
} else {
// Turn on red light
setStripToRed(strip1);
setStripToRed(strip2);
isRedOn = true;
}
}
}
// Function to set an entire Neopixel strip to black (off)
void setStripToBlack(Adafruit_NeoPixel &strip) {
for (int i = 0; i < NUM_LEDS; i++) {
strip.setPixelColor(i, strip.Color(0, 0, 0)); // Turn off all LEDs
}
strip.show();
}
// Function to execute the color sequence (Red, Yellow, Green)
void executeColorSequence() {
// Set Red for 2 seconds
setStripToRed(strip1);
setStripToRed(strip2);
delay(2000);
// Set Yellow for 2 seconds
setStripToYellow(strip1);
setStripToYellow(strip2);
delay(2000);
// Set Green for 2 seconds
setStripToGreen(strip1);
setStripToGreen(strip2);
delay(2000);
// After 6 seconds, revert to calm orange
setStripToOrange(strip1);
setStripToOrange(strip2);
Processing:
import processing.serial.*;
Serial serialPort;
// Image declarations
PImage photo, mountainImage, finalImage, teacupImage, teaLeavesImage, glassImage, uploadedImage, cup2Image, glassfillImage;
int NUM_OF_VALUES_FROM_ARDUINO = 7;
/* This array stores values from Arduino */
int arduino_values[] = new int[NUM_OF_VALUES_FROM_ARDUINO];
float prevX, prevY;
boolean firstRun = true;
// cups
boolean cup1 = false;
boolean cup2 = false;
boolean cup3 = false;
boolean glass1 = false;
boolean cup11 = false;
boolean cup12 = false;
boolean cup13 = false;
void setup() {
size(1280, 720);
background(0);
// Load and resize images
photo = loadImage("file.png");
mountainImage = loadImage("mountain.png");
finalImage = loadImage("wood.jpg");
teacupImage = loadImage("chinese tea cup no background.png");
teaLeavesImage = loadImage("tealeaves.png");
glassImage = loadImage("glass.png");
uploadedImage = loadImage("cupnobackground.png");
cup2Image = loadImage("cup filled.png");
glassfillImage = loadImage("glassfilled.png");
photo.resize(width, height);
mountainImage.resize(width, height);
finalImage.resize(width, height);
teacupImage.resize(300, 300);
teaLeavesImage.resize(150, 150);
glassImage.resize(200, 200);
uploadedImage.resize(300, 300); // Resize as necessary for display
printArray(Serial.list());
serialPort = new Serial(this, "COM3", 115200);
}
void draw() {
background(0); // Clear screen each frame
imageMode(CENTER);
image(finalImage, width / 2, height / 2);
image(teacupImage, 800, 500, 500, 500);
image(glassImage, 400, 500, 300, 300);
textSize(60);
text("Welcome to the Tea Room", 330, 360);
// Get sensor values
getSerialData();
float val1 = arduino_values[0]; // Piezo sensor 1
float val2 = arduino_values[1]; // Piezo sensor 2
float val3 = arduino_values[2]; // Piezo sensor 3
float val4 = arduino_values[3]; // Tilt switch
float val5 = arduino_values[4]; // Ultrasonic sensor 1
float val6 = arduino_values[5]; // Ultrasonic sensor 2
float val7 = arduino_values[6]; // Ultrasonic sensor 3
// Debugging sensor values
println("Sensor Values: " + join(str(arduino_values), ", "));
// Update flags based on conditions
if (val1 > 30) { // Piezo sensor threshold
cup1 = true;
}
if (val2 > 30) { // Piezo sensor threshold
cup3 = true;
}
if (val3 > 40) { // Piezo sensor threshold
cup2 = true;
}
if (val4 == 1) { // Tilt switch activation
glass1 = true;
}
if (val5 >= 30 && val5 <= 40) { // Ultrasonic sensor 1 threshold
cup13 = true;
}
if (val6 >= 30 && val6 <= 40) { // Ultrasonic sensor 2 threshold
cup12 = true;
}
if (val7 >= 30 && val7 <= 40 ){ // Ultrasonic sensor 3 threshold
cup11 = true;
}
// Draw images based on flags
if (cup1) {
image(uploadedImage, width / 3 - 100, height / 4);
}
if (cup2) {
image(uploadedImage, width / 2, height / 4);
}
if (cup3) {
image(uploadedImage, 2 * width / 3 + 100, height / 4);
}
if (glass1) {
image(glassfillImage, 400, 480, 380, 380);
}
if (cup11) {
image(cup2Image, 325, 150, 400, 400);
}
if (cup12) {
image(cup2Image, 650, 150, 400, 400);
}
if (cup13) {
image(cup2Image, 975, 150, 400, 400);
}
}
void getSerialData() {
while (serialPort.available() > 0) {
String in = serialPort.readStringUntil(10); // Read until newline
if (in != null) {
println("Raw Data: " + in);
String[] serialInArray = split(trim(in), ",");
if (serialInArray.length == NUM_OF_VALUES_FROM_ARDUINO) {
try {
for (int i = 0; i < serialInArray.length; i++) {
arduino_values[i] = int(serialInArray[i]);
}
println("Parsed Values: " + join(str(arduino_values), ", "));
} catch (NumberFormatException e) {
println("Error: Unable to parse data: " + in);
}
} else {
println("Invalid Data: " + in + " (Expected " + NUM_OF_VALUES_FROM_ARDUINO + " values)");
}
}
}
}