Tea Timer
Author: Yiming Jiao
Front view of the Tea Timer, without power on, and all the components are positing at off position.
This project helps me to keep the time to make a cup of tea, and detecting the temperature of the tea. I will be able to set a time between 1 second to 99 minute 59 second. After time set, it will start to counting down, while I can still change the time when it counting down. Once the time count down to 0, the filter will be lift accompany with a beeping. Then it will stay untill the tea temperature cooling below 70 celcies degree. Once it hit 70 celcies degree, it will beeping every 5 second to alarm me to drink the tea. If I press any bottom after the fillter lifted, it will be drop the fillter back to its beginning position.
This photo showing the wire connection of this tea timer. It include a DC motor, a speaker, a LCD Screen, 4 bottom, a waterproof thermometer, an arduino, a breadboard, and jump lines.
Another backview photo of tea timer, showing a clear vision of motor, LCD screen, and bottoms.
A front view, showing how it attached with tea cup and filter.
This drawing shows how does the model built. Constructed in rhino to get to the fabrication.
In this video, I show operation of Tea Timer, press left or right botton to switch the digit, press "+" or "-" botton to increase or decrease 1 from current digit. After time set for 10 seconds, it will start to count down. When time reached 0, filter will be lifted with beeping. Then press a button, the fillter will be droped to its original position.
In this video, I show operation of Tea Timer in a front view.
In this video, I show operation of Tea Timer in a side view.
Process
This photo shows how does the electronic circuit work in a process. It was started using potentialmeter to set the time. However using potentialmeter as a control is not as smooth as I expect, and it is also hard to change after the count down stage.
It was a early stage model of the mechanic system. It originally proposed lifted by lead screw and a set of gears. But due to the complex and hard to work on those material, finally switch to a string pull up system.
This photo shows how does the motor sticked on the plywood. Before this arrangement, I experiment some different layout of the motor, and as a result find the most effective layout.
After assembly, everthing a hidden behind the sheel, but due to the pin lenght, the back broad is not able to installed.
The whole system is using an idea of state. The machine should be a a state, and move to another state once it fullfill some prerequest to move.
Discussion
"I really like your idea of having the filter be picked up by what im assuming is a motor up above, it seems very smooth when picking it up but you maybe should have had the filter go higher so there is more room for you to grab/place the tea cup."
Yes, in my design the filter shouold go higher to leave more space to pick the cup. However, there are some problem of the motor control, it don't move the same distance every time. I'm proposing that is because the friction on guide rails are different everytime.
"Commercial product of this could maybe use a lead screw but we dont have that"
Yes, in my original concept, it should lift up by some lead screws since they are more accurate. But it is also much hard to design and make such gear system work than the string one.
"Wonderful craftsmanship and thought out design for the chassy. Very intuitive on how it raisese the tea cup, however, I feel ilke the timer buttons could be implemented better, maybe with just 2? Also a begin timer button would help with clarity."
It could be more concise while use 2 buttons to control the time. The reason I use 4 is because it is a more symmetrical design, I'm assuming people might natrally seeking for a symmetrical control on a system.
The outcomes achieved have met most of my initial objectives, yet two persistent issues remain. Firstly, the filter fails to consistently lift to the same position every time, necessitating motor position adjustments every 3-5 cycles. Additionally, there are sporadic inconsistencies with the buttons, causing erratic behavior in testing, almost as if they're being activated by an unseen force.
This project has been a gratifying endeavor for me. I find contentment in successfully constructing the electronic circuit, scripting, crafting the fabrication, and amalgamating all the components. The process of advancing through this project has been immensely fulfilling, which is more important than that of the final result for me.
Undoubtedly, there exists substantial potential for future development in this project. Enhancements to the mechanical system, such as transitioning to a lead screw and gear mechanism, could significantly improve its reliability. The current string-based system, reliant on tension between the ropes, lacks the precision and consistency offered by a gear-based system.
Technical information
/***************************************************************
* Project name: Tea Timer
* Project author: Yiming Jiao
* Project discription:
* - This project is worked for CMU 60-223 Physical Computing project2
* - This machine is aiming to help me make tea.
* 1. When it start, I will be able to set a time by using 4 bottons.
* 2. After time seted, it will counting down.
* 3. when time up, the motor will rotate to rise the filter, then a beeping will happen.
* 4. When filter is rised, if temperature of tea gets cooling, beeping will continuing untill any botton pressed.
* 5. Then the motor will down the fillter and back to the beginning.
* Project Pin map:
* A0: Botton0
* A1: Botton1
* A2: Botton2
* A3: Botton3
* 4: Thermometer
* 8: Buzzer
* 9: H-bridge Enable
* 10: H-bridge IN1
* 11: H-bridge IN2
*
* Reference:
* H-bridge and DC motor: https://learn.adafruit.com/adafruit-arduino-lesson-15-dc-motor-reversing/arduino-code
****************************************************************/
#include <Key.h>
#include <Keypad.h>
#include <OneWire.h>
#include <DallasTemperature.h>
#include <Servo.h>
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
using namespace std;
LiquidCrystal_I2C screen(0x27, 16, 2);
const int BOTTOMPIN0 = A0;
const int BOTTOMPIN1 = A1;
const int BOTTOMPIN2 = A2;
const int BOTTOMPIN3 = A3;
const int THERMOPIN = 4;
const int MOTOR0 = 9;
const int MOTOR1 = 10;
const int MOTOR2 = 11;
const int THERMPDETECTTIME = 10000; // thermometer will detect every 10 second
const int PRESSDETECT = 100;
const int MOTORTIME = 1200;
const int TIMESETTING = 10000;
const int TIMESTATE1 = 10000;
int speakerPin = 8; // define the speaker pin
int frequency = 1000; // set the frequency in Hertz
int duration = 500; // set the duration in milliseconds
double temperature;
int setTime[4] = { 0, 0, 0, 0 };
int timePosition = 3;
bool isPress0 = false;
bool isPress1 = false;
bool isPress2 = false;
bool isPress3 = false;
long timeSetting;
bool timeSetted = false;
int timeReduce;
bool teaTime = false;
bool isLift = false;
bool isDown = false;
int state = 0;
unsigned long stateTime;
OneWire oneWire(THERMOPIN);
DallasTemperature sensors(&oneWire);
void setup(void) {
sensors.begin();
screen.init();
screen.backlight();
screen.home();
screen.print("Welcome to TEATIMER!");
delay(1000);
screen.clear();
pinMode(BOTTOMPIN0, INPUT);
pinMode(BOTTOMPIN1, INPUT);
pinMode(BOTTOMPIN2, INPUT);
pinMode(BOTTOMPIN3, INPUT);
pinMode(MOTOR1, OUTPUT);
pinMode(MOTOR2, OUTPUT);
pinMode(speakerPin, OUTPUT);
}
// For state need to read botton input
bool readBotton(void) {
isPress0 = digitalRead(BOTTOMPIN0);
isPress1 = digitalRead(BOTTOMPIN1);
isPress2 = digitalRead(BOTTOMPIN2);
isPress3 = digitalRead(BOTTOMPIN3);
return ((isPress0 + isPress1 + isPress2 + isPress3) > 0);
}
// For state1, chance time based on press
// I finally decide to keep the setTime array of four ints.
// The reason is if I use second represent time, it will also change its left digit when a carry or borrow happen.
// But I don't want a such influence happen, because it not fit what I expect on interacting with it.
void settingTime(void) {
if (isPress0) {
setTime[timePosition] += 1;
if (timePosition == 2) {
setTime[timePosition] = setTime[timePosition] % 6;
} else {
setTime[timePosition] = setTime[timePosition] % 10;
}
}
if (isPress1) {
if (timePosition == 2) {
setTime[timePosition] += 5;
setTime[timePosition] = setTime[timePosition] % 6;
} else {
setTime[timePosition] += 9;
setTime[timePosition] = setTime[timePosition] % 10;
}
}
if (isPress2) {
timePosition += 1;
timePosition = timePosition % 4;
}
if (isPress3) {
timePosition += 3;
timePosition = timePosition % 4;
}
}
// include all screen display codes
void screenDisplay(void) {
screen.setCursor(0, 0);
screen.print("Current Temp: ");
screen.setCursor(14, 0);
screen.print(temperature);
screen.setCursor(0, 1);
screen.print("Set Time: ");
screen.setCursor(10, 1);
screen.print(" ");
screen.setCursor(10, 1);
screen.print(setTime[0]);
screen.setCursor(11, 1);
screen.print(setTime[1]);
screen.setCursor(12, 1);
screen.print(":");
screen.setCursor(13, 1);
screen.print(setTime[2]);
screen.setCursor(14, 1);
screen.print(setTime[3]);
screen.setCursor(0, 2);
screen.print(" ");
screen.setCursor(0, 2);
switch(state){
case 0:
case 1:
screen.print("Set the time");
break;
case 2:
screen.print("Tea is making");
break;
default:
screen.print("Tea is ready");
break;
}
if (timePosition < 2) {
screen.setCursor(10 + timePosition, 1);
} else {
screen.setCursor(11 + timePosition, 1);
}
screen.cursor();
}
// For state3, counting down the time
void timeReducing(void) {
if (millis() - timeReduce > 1000) {
setTime[3] -= 1;
if (setTime[3] == -1) {
setTime[3] = 9;
setTime[2] -= 1;
if (setTime[2] == -1) {
setTime[2] = 5;
setTime[1] -= 1;
if (setTime[1] == -1) {
setTime[1] = 9;
setTime[0] -= 1;
}
}
}
timeReduce = millis();
}
if (setTime[0] == 0 && setTime[1] == 0 && setTime[2] == 0 && setTime[3] == 0) {
analogWrite(MOTOR2, 255);
digitalWrite(MOTOR1, HIGH);
digitalWrite(MOTOR0, LOW);
stateTime = millis();
state = 3;
}
}
// state0 - start state, waiting for user to set time
void state0(void) {
if(readBotton()) {
settingTime();
stateTime = millis();
state = 1;
}
}
// State1 - user is setting the time, recording time after last press. If time is long enough, move to next state.
void state1(void) {
if(readBotton()) {
settingTime();
stateTime = millis();
} else {
if (millis() - stateTime >= TIMESTATE1) {
stateTime = millis();
state = 2;
}
}
}
// state2 - couting down the time. If any press happened, move back. If time up, active the motor and move next.
void state2(void) {
if(readBotton()) {
settingTime();
stateTime = millis();
state = 1;
} else {
timeReducing();
}
}
// state3 - counting the motor move time. If reached MOTORTIME, stop motor and move next.
void state3(void) {
if (millis() - stateTime >= MOTORTIME) {
analogWrite(MOTOR2, 255);
digitalWrite(MOTOR1, LOW);
digitalWrite(MOTOR0, LOW);
tone(speakerPin, frequency, duration);
stateTime = millis();
state = 4;
}
}
// state4 - detecting temperature of tea. If any press happen, act motor down, and move to state 6. If temperature lower than 70C, move to state5.
void state4(void) {
if(readBotton()) {
stateTime = millis();
analogWrite(MOTOR2, 255);
digitalWrite(MOTOR0, HIGH);
digitalWrite(MOTOR1, LOW);
state = 6;
} else {
if (temperature <= 70) {
tone(speakerPin, frequency, duration);
stateTime = millis();
state = 5;
}
}
}
// state5 - alarming the user. If any press happen, act motor down, and move to state 6.
void state5(void) {
if(readBotton()) {
stateTime = millis();
analogWrite(MOTOR2, 255);
digitalWrite(MOTOR0, HIGH);
digitalWrite(MOTOR1, LOW);
state = 6;
} else {
if (millis() - stateTime >= TIMESTATE1) {
tone(speakerPin, frequency, duration);
stateTime = millis();
}
}
}
// state6 - counting the motor move time. If reached MOTORTIME - 210, stop motor and move to start.
void state6(void) {
if (millis() - stateTime >= MOTORTIME - 210) {
analogWrite(MOTOR2, 255);
digitalWrite(MOTOR1, LOW);
digitalWrite(MOTOR0, LOW);
stateTime = millis();
state = 0;
}
}
//Identify wich state is now, and move to the state.
void stateIdentify(void) {
switch (state) {
case 0:
state0();
break;
case 1:
state1();
break;
case 2:
state2();
break;
case 3:
state3();
break;
case 4:
state4();
break;
case 5:
state5();
break;
case 6:
state6();
break;
}
}
void loop(void) {
sensors.requestTemperatures();
temperature = sensors.getTempCByIndex(0);
stateIdentify();
screenDisplay();
}