Electronics

Make an automated tea steeper- with Arduino

What we will be making

I recently got an opportunity to present my first workshop and thought that it could be interesting to share the result. It’s an introduction to Arduino for beginners during which we will be making a automated tea steeper. I chose this project because it provides a good way to introduce basics concepts of embedded programming, from lighting up an LED to dealing with an analog input, controlling a servo motor and keeping track of time, plus it was already very well documented (see the sources).

Basics soldering skills are required to complete the project, although jumper wires and a small breadboard can be used instead of the perfboard.

Table of contents

Components list

  • 1x 4.5mm thick foam board
  • 1x Arduino Uno
  • 1x 9g servo motor
  • 1x Piezo buzzer
  • 1x 9v battery holder
  • 1x 9v battery
  • 1x potentiometer
  • 1x pushbutton
  • 1x On/Off button
  • 2x 270ohms resistors
  • 1x 330ohms resistor
  • 1x 10kohms resistor
  • 1x green 5mm LED
  • 1x red 5mm LED
  • 1x male header (1×3)

Features

The servo is used to lift the tea bag in or out of the cup. A potentiometer allows to set the time of stirring. The pushbutton controls the beginning of the stirring cycle .When it stops the buzzer emits a beep. Each led is protected by a resistor. The whole circuit is powered by a 9v battery through the Vin pin (a power jack can be used as well).

Breadboard circuit

Start by testing your circuit on a breadboard in order to be sure that every component is working as it should be.

Here’s how the circuit works :

  • Potentiometer(RV1): it is a resistor of variable value. Depending on the position of the axis, the resistance fluctuates between 0 and infinity, thus 0->5v is fed to the analog pin A5 of the Arduino. The pin will read a value between 0 and 1023 that we will programatically rebase on a [0 – 10] range corresponding to our ‘stirring spectrum’.
  • Servo motor: used to lift the tea bag up and down. Servos are controlled by sending them a pulse of variable width. The control wire is used to send this pulse. The parameters for this pulse are that it has a minimum pulse, a maximum pulse, and a repetition rate.
    The angle of the shaft is determined by the duration of a pulse that is applied to the control wire. This is why the servo must be connected to a pin that allows Pulse Width Modulation (PWM).
  • Piezo buzzer: indicates end of cycle. You’re invited to mess with the code and add a nicer melody. Must be connected to a digital pin.
  • Pushbutton: a pull-up design is used so that the input pin is always in a clear logical condition (ie: never floating). When the circuit is opened (button unpressed), D6 is low impedance and current flows from 5v to the input pin which thus reads HIGH.
    Inversely when the circuit is closed (button pressed), the current flows directly to the ground and d6 reads LOW.

The code

You can find the code over here. Let’s go through it together.

We start by including the only library we will be using: Servo and by defining every relevant variable. You can adjust the angles of the servo with STIR_UP, STIR_HIGH and STIR_LOW. The stirring speed (how quick the shaft will go up and down) is defined by STIR_SMOOTH. The rest is pretty straightforward.

#include  

Servo myservo;  // create servo object to control a servo 

const int SERVO = 3;
// variables to store our basic servo positions : up, stirring high, stirring low
const int STIR_UP = 160;    // when the tea is lifted out of the cup
const int STIR_HIGH = 130; // high position for stirring
const int STIR_LOW = 90; // low position for stirring
// Delay between each servo position adjustment during stirring
// A lower value is associated with a quicker stirring
int STIR_SMOOTH = 50;
const int STATUS_LED = 5; // Used to indicate that stirring is in progress
const int POWER_LED = 4; // Always on when the Arduino is powered
const int BUTTON = 6; // Starts stirring when pressed
const int TIME_POT = A5; // Potentiometer pin
const int BUZZER = 8; // Used to indicate end of stirring. Needs to be a PWM pin.

In setup() we set every pin to the relevant mode and start the servo in the right position (which is shaft lifted).

void setup() 
{ 
        // Set up LED pins
        pinMode(STATUS_LED, OUTPUT);
        pinMode(POWER_LED, OUTPUT);
  
        digitalWrite(POWER_LED, HIGH); // Turn on power led
        digitalWrite(STATUS_LED, LOW); // Turn off status led
  
        myservo.attach(SERVO);  // attaches the servo on pin 3 to the servo object
        myservo.write(STIR_UP); // put servo to up position, ready to receive tea bag
  
        pinMode(BUZZER, OUTPUT); // Set up buzzer pin 
        digitalWrite(BUZZER, LOW); 
} 

Then we need to write several functions that will handle every aspect of the stirring procedure. Let’s start by handling the inputs.

The first input to deal with is the pushbutton. This function will check if the start button is being pressed and return the result as a boolean value:

bool isButtonPressed() {
/*
When the button is pressed, the TIMER_BUTTON pin is connected to ground and thus reads LOW.
Alternatively, when the button is not pressed the pin is connected to 5v through the resistor and reads HIGH
*/
        return (digitalRead(BUTTON) ? false : true);
}

Then we need to get the stirring time value from the potentiometer, which will be returned in milliseconds (thus the ‘long’ because an ‘int’ would max out at 32,767ms):

long getStirringTime() {
/*
Get analog value from potentiometer (range 0-1023), re-base between 0 and 10 and multiply by 60*1000 to get the time in milliseconds.
*/
        float time = mapFloat(analogRead(TIME_POT), 0, 1023, 0, 10);
        Serial.println(time); 
        long millisTime = (long)(time*60*1000);
        return millisTime;
}

You may have noticed that we call a mapFloat function. Why not use the already included map ? Because it only returns integers when we want a floating value, thus the following custom implementation:

float mapFloat(float x, float in_min, float in_max, float out_min, float out_max) {
/*
Helper function used to re-map an number from one range to another. We don't use the native 'map' because it only returns integers. 
*/
        return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}

So what next ? We need a way to move the servo from one position to another but we can’t simply use servo.write(position) because the servo would ‘jump’ between the two position when what we want is a smooth transition. So we’ll adjust the position by moving through all intermediate positions between our start and end angle.

void moveServo(int startPosition, int stopPosition) {
        int pos;
        
        if (startPosition > stopPosition) {
                for(pos = startPosition; pos >= stopPosition; pos -= 1)     // goes from STIR_HIGH to UP since it's the last position atteigned in stir()
                {
                        myservo.write(pos);              // tell servo to go to position in variable 'pos'
                        delay(STIR_SMOOTH);                       // waits x ms for the servo to reach the position
                }               
        } else {
                for(pos = startPosition; pos <= stopPosition; pos += 1)     // goes from STIR_HIGH to UP since it's the last position atteigned in stir()
                {
                        myservo.write(pos);              // tell servo to go to position in variable 'pos'
                        delay(STIR_SMOOTH);                       // waits x ms for the servo to reach the position
                }               
        }
}

Then we can add a function used to launch a stirring cycle where the shaft first goes down and then up again.

void stir() {
/*
We tell the servo to go from the STIR_HIGH to the STIR_LOW position, 
with a delay between each adjustment of position so that the movement is smooth.
*/ 
        moveServo(STIR_HIGH, STIR_LOW);
        moveServo(STIR_LOW, STIR_HIGH);
}

Isn't something missing ? We need a way to trigger the buzzer to to indicate the end of the stirring process.

void beepBuzzer() {
/*
To indicate the end of stirring, we play a G    7(sol) twice for 1 second with a 2 second intermission.
*/
        tone(BUZZER, 3136, 1000);
        delay(2000);
        tone(BUZZER, 3136, 1000);
}

Finally we can incorporate everything in a master function that will handle the complete stirring cycle given a duration. It will first move the shaft into stirring position and lights up the status LED to indicate that a cycle is in progress. It keeps on calling stir() until the duration is elapsed. It then moves the shaft back into high position, beeps the buzzer and lights down the LED.

void startStirring(long duration) {
/*
Moves servo from 'high' to stirring position and begin process. We keep track of the time 
that each stirring cycle took and subtract it to the duration passed as argument. 
When duration<0, we move the servo to 'high' position agin so that the tea bag is taken out from the cup. 
Two beeps are played by the buzzer and the status led is shut to indicate end of stirring. 
*/
        unsigned long referenceTime;
        
        moveServo(STIR_UP, STIR_HIGH);
        digitalWrite(STATUS_LED, HIGH);
        
        while(duration>0) {
                referenceTime = millis();
                stir();
                duration-= millis() - referenceTime;
        }
        
        moveServo(STIR_HIGH, STIR_UP);
        beepBuzzer();
        
        digitalWrite(STATUS_LED, LOW);
}

Now to integrate everything in the main loop: first get the stirring time, then check if the start button is pressed. If it is, start the stirring cycle. Easy!

void loop() 
{ 
        // Get stirring time from potentiometer value
        long duration = getStirringTime();

        if (isButtonPressed()) {
                startStirring(duration);
        }
} 

You can now upload your code (don't forget to include the Servo library).

Frame assembly

Start by cutting out the different parts of the structure. You could also use an acrylic board if you have access to a CNC router or a laser cutter (lucky you if you do!).

Then assemble everything and screw in the components.

Perfboard circuit

Heat up your soldering iron and gather all the remaining components.

You'll probably want to start by soldering the passive elements and the wires on the perfboard. Then solder the wires to the buttons, the led and the potentiometer already embedded to the frame.

The result

Plug in the arduino, the servo and enjoy your creation! Try and play with the potentiometer and the Serial console in order to match the angles with the corresponding time.

Sources

  • Instructables of similar projects:

  • Miscelleanous

    • Tone library documentation
    • Arduino switch tutorial
    • Arduino potentiometer tutorial
    • Servo library reference
  • The Eagle file and Fritzing diagram of the circuit as well as the Sketchup file of the frame are available here

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.