Maker.io main logo

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. 

 

Copy Code
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);
}

Tea Brewer Photo 1

Tea Brewer Photo 2

Tea Brewer Photo 3

Tea Brewer Photo 4

Tea Brewer Photo 5

Tea Brewer Photo 6

Tea Brewer Photo 7

Tea Brewer Photo 8

Have questions or comments? Continue the conversation on TechForum, DigiKey's online community and technical resource.