Automated Tea Brewer
2016-06-01 | By Joshua Bishop
License: General Public License
Overview
At its simplest, tea brewing is very straightforward. Take water, add heat, add tea, done. However, depending on the type of tea, the water needs to be at a certain temperature and it needs to steep a specific amount of time to achieve an ideal cup. Get either of these factors off and the tea is weak or too strong or bitter. So we decided to automate the process as much as possible while still giving the option of using real tea leaves instead of vacuum packed cups or packets.
Approach
For our project, the tea brewer will sit on the side of the cup of tea. The idea is to have a mesh tea leaf holder that is then placed into and pulled out of the water at the appropriate temperature after steeping for the appropriate time. This mesh tea leaf holder will be moved by a small servo motor. A small temperature gauge will be a part of the cup holder that will be low enough to constantly be in the water. When hot water is directly added, the tea brewer will wait until it has cooled to the appropriate temperature before putting in the tea. If the cup is being directly heated, the tea brewer will put the tea in once the water has reached the appropriate temperature. In this scenario, the user will be required to turn off the heat source so that water does not get too hot. After the tea has steeped long enough, the motor will pull the leaves out of the water.
To simplify the controls, the entire device will be programmed with an app via BLE, or Bluetooth Smart. Variables for different types of tea will be programmed in and can be selected in the app. Another feature in the app will be a single button on the device itself which can be set as a “favorite” button to brew the most commonly brewed tea without the app.
Challenges and Design Considerations
Any interaction with the physical world can be a challenge as there are always variables that cannot be controlled. While a servo motor is extremely easy to control, there was a lot of discussion on whether there should be any sort of feedback to make sure there was no harm or damage in the operation of the motor. After much debate, it was decided that there would be no feedback at this point. If the prototype showed that additional measures were needed, we would implement them at that time. However, due to the small size of the servo motor, the only damage we feared was it overloading itself, so we anticipate putting a current sensor to cut-out the motor if it is drawing excessive or damaging levels of power.
Another challenge was integrating the BLE, as we’d only used first generation Bluetooth up until this point. We found that the modules available made interfacing with the BLE module much easier than we were anticipating, while also having FCC pre-certification. Despite our concerns, this turned into practically a non-issue.
While we have quite a bit of design experience with firmware creation for embedded systems, the app design was a bit of a challenge. We were able to make a functional, if utilitarian interface, but we would almost certainly want an expert app designer to make a sleek, easy-to-use and attractive interface.
As we have limited mechanical experience as well, we were worried about balancing issues as well as making sure the hardware was firmly attached to the side of the cup. This required a bit of trial and error, but the original hardware we planned on using was used with only minor modifications.
End Result
In the end, the tea brewer works exactly as intended. While not a particularly elegant piece of hardware, it is fully functional and brews tea exactly according to recommended specifications. All in all, it worked as a perfect proof-of-concept, showing that the brewing of tea could be exact, flexible in time and temperature, while avoiding proprietary sources of tea leaves.
Moving Forward
There isn’t much we’d change from the top-level, though we’d likely want to make many minor changes to improve reliability and aesthetics, reduce costs and perhaps tweak the app interface for ease of use. We’re currently in discussions to see if there is a sufficient market for this device as it is, if we’ll need to make any design changes due to user requirements versus technical reasons, if there are any potential legal issues and costs associated with scaling this design up to full production.
Code – main.c:
#include "msp430.h"
#include "DS18B20Lib\DS18B20.h"
//============================Pins definition===================================
//---------------------------------PORT1----------------------------------------
#define BT_RX BIT1
#define BT_TX BIT2
//---------------------------------PORT2----------------------------------------
#define DEFAULT BIT2
#define BT_KEY BIT3
#define LED_G BIT4
#define LED_Y BIT5
#define SERVO BIT6
//-----------------------Servo motor definitions--------------------------------
#define SMCLK_CLOCK 1000000
#define PWM_FREQUENCY 50//In Hertz, ideally 50Hz.
#define SERVO_STEPS 180//Maximum amount of steps in degrees (180 is common)
#define SERVO_MIN 1300//The minimum duty cycle for this servo
#define SERVO_MAX 1800//The maximum duty cycle
//=======================Variables definition===================================
DS18B20 sensor(1,3);
signed int temp;//temperature
unsigned int voltage;//battery voltage
unsigned char presence;//temperature sensor presence flag
unsigned int half_sec;//half of the seconds counter
unsigned char led_pwm;//implementation pwm for LEDs
signed int blue,red;//Brightness of blue and red LEDs
char state; //State of the device:
//'d'-waiting for data input
//'w'-waiting for pulling down the tea contaner
//'e'-error, water is absent or has too low temperature
//'b'-brewing of the tea
//'f'-finishing the process and pulling up the tea container
unsigned int init_temp;//temperature to begin the brewing
unsigned int min, sec;//Brewing time (minutes and seconds)
char tx_buf[8] = {0,0,0,'.',0,0,'\r','\n'};//data to be send fo the phone "t__._s"
char rx_buf[20];//data to be received from phone:
//i _ _ - initial temperature for brewing start
//m _ _ - brewing time (minutes)
//s _ _ - brewing time (seconds)
char rec_bytes;//number of bytes received;
unsigned int PWM_Period = SMCLK_CLOCK / PWM_FREQUENCY;//PWM Period for the servo motor
unsigned int *Flash_ptrD;//Pointer to the information segment D of the flash memory
//==========================General purpose functions===========================
//----------------------------System initialization-----------------------------
void init (void)
{
//---------------Watchdog timer--------------------
WDTCTL = WDTPW WDTHOLD;//Stop watchdog timer
//-----------------Clock system--------------------
BCSCTL1 = CALBC1_8MHZ;//Set MCLK frequency as 8 MHz
DCOCTL = CALDCO_8MHZ;//Set MCLK frequency as 8 MHz
BCSCTL2 |= DIVS_3;//Set SMCLK frequency as 1 MHz
//-----------------I/O pins------------------------
//------------------PORT1--------------------------
P1SEL = BT_RX BT_RX;//Pins BT_RX and BT_TX are
P1SEL2 = BT_TX BT_RX;//controlled by UCSI module
P1REN = 0;//No internal resistor on the PORT1
//------------------PORT2--------------------------
P2SEL = SERVO;//Connect the P2.4 pin to the output of the TA0CCR1 block
P2SEL2 = 0;//Connect the P2.4 pin to the output of the TA0CCR1 block
P2DIR = LED_G LED_Y SERVO BT_KEY;//Set the specified pins as outputs
P2REN = DEFAULT;//Set the internal resistor on pins WAKEUP
P2OUT = DEFAULT;//Pull-up resistor on pin WAKEUP and pull_down on pin BT_KEY
P2IES = DEFAULT;//Falling edge interrupt at WAKEUP pin
P2IE = DEFAULT;//Interrupt enable from WAKEUP pin
//---------------USCI module for UART mode---------
UCA0CTL1 |= UCSSEL1;//Set DCO as clocking source for USCI module
UCA0BR0 = 104;//Set baudrate of 9600 kbaud
UCA0BR1 = 0;//Set baudrate of 9600 kbaud
UCA0MCTL = UCBRS_1;//Set baudrate of 9600 kbaud
UCA0CTL1 &= ~UCSWRST;//USCI module start
IE2 = UCA0RXIE;//Interrupt enable on receive data
//-------------Timer 1-----------------------------
TA1CTL = TASSEL_2 ID_3 MC_1 TAIE;//Timer 0 settings:
//SMCLK for timer clocking
//Divider is 8, so frequency is 125 kHz
//Direct mode (counts from 0 to TACCR0)
TA1CCR0 = 62500;//Timer 0 period is 62500/125000 = 0.5 s
//------------Timer 0 (PWM for servo motor)--------
TA0CCTL1 = OUTMOD_7;//TACCR1 reset/set
TA0CTL = TASSEL_2 MC_1;//SMCLK, upmode
TA0CCR0 = PWM_Period-1;//PWM Period
TA0CCR1 = SERVO_MIN;//TACCR1 PWM Duty Cycle
//----------Variables initialization---------------
rec_bytes = 0;
state = 'd';
//----------Configure DS18B20 sensor---------------
sensor.SetResolution(9); //Set DS18B20 resolution as 9 bits (0.5 centigrades)
sensor.ConversionStart();
//-----------------Set operating mode--------------
__bis_SR_register(LPM1_bits GIE);//Low-power mode 4 with interrupts
}
//-----------------------Delay function-----------------------------------------
void delay(unsigned int n)
{
volatile unsigned int i;
for (i = 0; i < n; i );
}
//=======================Interrupt subroutines==================================
//---------------------USCI data receive interrupt------------------------------
#pragma vector=USCIAB0RX_VECTOR
__interrupt void RX_ISR (void)
{
rx_buf[rec_bytes] = UCA0RXBUF;//Save received byte in the buffer
switch (rx_buf[rec_bytes])//check the first received byte
{
case 'i'://if got 'i'
state = 'd';
P2SEL = SERVO;//Connect the P2.4 pin to the output of the TA0CCR1 block
TA0CCR1 = SERVO_MIN;//TACCR1 PWM Duty Cycle
init_temp = (rx_buf[rec_bytes-2]-0x30)*10 (rx_buf[rec_bytes-1]-0x30);//save initial temperature
break;
case 'm'://if got 'm'
min = (rx_buf[rec_bytes-2]-0x30)*10 (rx_buf[rec_bytes-1]-0x30);//save minutes of brewing
break;
case 's'://if got 's'
sec = (rx_buf[rec_bytes-2]-0x30)*10 (rx_buf[rec_bytes-1]-0x30);//save seconds of brewing
sec = min*60;//add the minutes realculated to seconds
Flash_ptrD = (unsigned int *) 0x1000; //Set pointer to the start address of the segment D
FCTL1 = FWKEY ERASE; //Set ERASE bit
FCTL3 = FWKEY; //Clear LOCK bit
*Flash_ptrD = 0; //Dummy write to erase the segment
FCTL1 = FWKEY WRT; //Set WRT bit
*Flash_ptrD = init_temp; //Write temperarture to the flash memory
*Flash_ptrD = sec; //Write seconds to the flash memory
FCTL1 = FWKEY; //Clear WRT bit
FCTL3 = FWKEY LOCK; //Set LOCK bit
rec_bytes = 0;//clear buffer counter
if (temp >= init_temp*9) //If water is hot enough then wait until it get colder
{
state = 'w';
}
else //If water is 10% colder than is required then set "error" state
{
state = 'e';
}
break;
}
rec_bytes ;
}
//---------------------PORT2 pin change interrupt-------------------------------
#pragma vector=PORT2_VECTOR
__interrupt void PORT2_ISR (void)
{
if (~P2IN & DEFAULT)//if button is pressed
{
delay(2000);//Debounce delay
if (~P2IN & DEFAULT)//if button is still pressed
{
while (~P2IN & DEFAULT);//Waiting until button is pressed
delay (2000);//debounce delay
if (P2IN & DEFAULT)//if button is released
{
if ((state == 'd') || (state == 'f')) //If data still has not been entered of brewing has been completed
{
Flash_ptrD = (unsigned int *) 0x1000; //Set pointer to the start address of the segment D
if (*Flash_ptrD != 0xFF) //If data is valid
{
init_temp = *Flash_ptrD ; //Restore temperature form flash memory
sec = *Flash_ptrD; //Restore seconds form flash memory
if (temp >= init_temp*9) //If water is hot enough then wait until it get colder
{
state = 'w';
}
else //If water is 10% colder than is required then set "error" state
{
state = 'e';
}
}
}
}
}
}
P2IFG &= ~DEFAULT;//reset the interrupt flag
}
//-------------------------Timer 1 overflow interrupt---------------------------
#pragma vector=TIMER1_A1_VECTOR
__interrupt void TIMER1_OWF_ISR (void)
{
unsigned char i;
half_sec ;//increase a counter
switch (half_sec % 4)//repeat each activity one time in two seconds
{
case 0:
if (temp != -990)//if sensor presents
{
tx_buf[0]='t';//represents temperature OK
tx_buf[1]=temp/100 0x30;//tens of the temperature
tx_buf[2]=(temp/10) 0x30;//ones of the temperature
tx_buf[4]=temp 0x30;//tenth part of the temperature
}
else//otherwise
{
tx_buf[0]='e';//represents sensor error
}
tx_buf[5]=state;//Device state
for (i = 0; i < 8; i )//Loop to transmit the data
{
while (~IFG2&UCA0TXIFG);//Wait until transmit buffer is empty
UCA0TXBUF = tx_buf[i];//Send the next data
}
break;
case 1:
sensor.ConversionStart();//send "convert temperature" command
break;
case 3:
temp = sensor.GetData10();//read the temperature value
if (temp>999)//to avoid error (temperature cannot be more than 100 degreees)
temp = 999;
break;
}
switch (state)
{
case 'd':
P2OUT &= ~LED_Y;
P2OUT &= ~LED_G;
P2SEL &= ~SERVO;//Disconnect the P2.4 pin from the output of the TA0CCR1 block
break;
case 'w':
P2OUT ^= LED_Y;
P2SEL &= ~SERVO;//Disconnect the P2.4 pin from the output of the TA0CCR1 block
if (temp < init_temp*10) //If water is good then start brewing
{
half_sec = 0;
state = 'b';
}
break;
case 'e':
P2OUT ^= LED_Y;
P2OUT ^= LED_G;
P2SEL &= ~SERVO;//Disconnect the P2.4 pin from the output of the TA0CCR1 block
if (temp >= init_temp*10) //If water is hot enough then wait until it get colder
{
state = 'w';
}
break;
case 'b':
P2OUT |= LED_Y;
P2OUT &= ~LED_G;
P2SEL |= SERVO;//Disconnect the P2.4 pin from the output of the TA0CCR1 block
if (TA0CCR1 < SERVO_MAX)//Pull the tea container down into the cup
TA0CCR1 =20;
else //If tea container is in the cup
{
P2SEL &= ~SERVO;//disconnect the P2.4 pin from the output of the TA0CCR1 block
P2OUT |= SERVO;
}
if (half_sec > 2*sec) //If brewing time is over then finish brewing
{
state = 'f';
}
break;
case 'f':
P2SEL |= SERVO;//Disconnect the P2.4 pin from the output of the TA0CCR1 block
if (TA0CCR1 > SERVO_MIN)//Get up tea container down into the cup
TA0CCR1-=20;
else //If tea container is out of the cup
{
P2SEL &= ~SERVO;//disconnect the P2.4 pin from the output of the TA0CCR1 block
P2OUT |= SERVO;
P2OUT &= ~LED_Y;
P2OUT |= LED_G;
}
break;
}
TA1CTL &= ~TAIFG;//reset the interrupt flag;
}
//=======================Main function==========================================
void main( void )
{
init();
while(1);
}
Have questions or comments? Continue the conversation on TechForum, DigiKey's online community and technical resource.
Visit TechForum