Interactive Tea Room, Nathan Ynson, Viola
(Interaction Example and Picture of the Build)
(Early draft idea for the project)
Our project aims to recreate the traditional tea-serving experience with an interactive twist. The setup features LED lights underneath a board, which signal different stages of tea preparation. When the lights flash red, it indicates that the tea is still boiling and not ready to pour. Once the lights turn to a calm orange, it's time to pour the tea. The system includes multiple sensors—one tilt switch, three piezo sensors, and three ultrasonic sensors. Each sensor triggers a specific action in the tea-serving experience, and the digital representation of the board on the screen changes accordingly. My partner, who has extensive knowledge of traditional tea practices, pointed out that these traditions are becoming increasingly obsolete. With this in mind, we wanted to preserve these traditions through an engaging, modern interaction. During user testing, we introduced several key enhancements. One was incorporating the LED lights to guide users through the different stages of tea preparation. We also used laser cutting and acrylic boards to improve the aesthetics of the tea table and create a cleaner, more refined interaction.
Initially, our project was overly ambitious and lacked a clear plan. We had intended to incorporate more sensors and stages, but this led us to attempt coding beyond our skill level, ultimately resulting in messy code and the need to use AI, which only added to the confusion. However, we eventually refined our approach, creating a more concrete plan and simplifying our code for better clarity and functionality. We decided to build the tea-serving table with two boards: one on top and one underneath. The space between the boards would house our wires and LED strips, which we included to enhance the atmosphere and effect of the project. The first sensor we used was a tilt sensor, which triggers the tea pouring action. Next, we added a knock sensor, setting the threshold value to 30 to prevent accidental triggers. This means users need to knock on the cup with a bit more force to activate the sensor. Finally, we incorporated ultrasonic sensors, which were challenging to configure. I used a new library called NewPing to smooth out data collection. Although more sensors could have been used, we chose these three because we were already familiar with them from previous projects. On the processing side, we used booleans and loaded images. When a sensor's trigger value was met, the corresponding image would be displayed on the screen.
All in all, I believe this project was a success. We were able to achieve our goal of creating a traditional yet modern interactive tea experience. The idea was initially brought to life by my partner’s passion and vision. Over time, I truly immersed myself in the world of tea and learned several valuable lessons. First, I learned the importance of being clear with my goals and having a solid plan in place before allowing myself to become distracted by other ideas. Second, I realized that having a clear plan helps prevent the temptation to take shortcuts, and that it’s better to stick to what works and put in the necessary effort. Finally, I discovered that, much like making tea, patience is crucial. By staying calm and patient, I was able to address problems and setbacks more effectively.
Seeing our project come together and presenting it at the IMA show was a deeply rewarding experience. We were thrilled to see the older generation appreciate our project for its deep roots in culture and tradition, while also watching kids and teens enjoy the interactive elements of the tea experience.
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)");
}
}
}
}
}