ECE4760 Final Project Report

"Automated Drink Mixer"

Justin Grzyb and Austin Gage

Abstract

The automated drink mixer takes orders from a push-button menu, and moves a regular 16-ounce glass under a series of inverted bottles while dispensing specified amounts of mixers to make perfect non-alcoholic beverages.

Introduction

            If you’ve ever been to a crowded bar or restaurant and waited a long time just to get served an ill-prepared drink by someone that you still have to tip afterwards, then you understand the potential benefits of having an automated drink mixer. With this device, a user simply has to place his or her glass on the specified starting location, select a drink from the push-button menu, and wait less than 30 seconds for the beverage to be dispensed and the glass to be returned to its initial position. Drinks are made efficiently and consistently, as each drink is prepared the same way every time. In choosing this project, we wanted to do something both fun and relevant, with clear functionality in a real-world application.

            For this project, we used a bidirectional DC motor and solenoid valves to turn an ordinary Lazy Susan into a rotating platform system that could move a glass under inverted bottles and dispense liquids in a controlled manner, in order to make mixed drinks. Movement and liquid flow were controlled by an Atmel Mega1984 microcontroller, which was programmed using C software written in AVR Studio4, and the position of the glass was tracked by an Infrared (IR) sensor and feeback control loop. Drink orders are placed via a push-button menu. Ultimately, we were able to successfully implement our design for a menu consisting of five different non-alcoholic drinks: cherry coke, coke & lime, coke-sprite, Arnold Palmer, and Shirley Temple.

Design

            The design for our automated drink mixer focuses on simplicity, as to minimize the number of moving parts needed in the system and reduce mechanical error. A rotating Lazy Susan is centered on a wooden platform with five supports made of PVC pipe spaced around it evenly at 60 degree angles, leaving one space open for the user to initially place the glass. Each support holds a mixer above the rotating platform, contained in an inverted plastic bottle with a solenoid valve attached at the end to control its flow into the glass. A 12V bidirectional DC motor is attached to a 2 inch rubber wheel which is placed against the edge of the 14 inch rotating platform. The motor operates at approximately 70RPM when 12V is applied, thus rotating the platform at a speed of about 10RPM as to not spill or knock over the glass. The software run from the microcontroller is dictated by an overarching state machine, whose transitions are controlled by the status of the push-button. Initially, the system sits in an idle state where it polls for an input from one of the buttons. Once an input is received, the glass is moved under mixers according to what drink needs to be made. In order to control the position of the glass, an IR emitter and phototransistor detect black strips of tape along the edge of the platform, and a comparator digitizes the signal, which is then used to trigger an interrupt to count the number of 60 degree spaces the glass has moved. The liquid from each mixer is dispensed by simply opening and closing the corresponding valve for the appropriate amount of time. Once all the ingredients have been added, the glass is returned to its starting position, and the state machine is reset to its initial idle state.

            The idea for the automated drink mixer was original; however, different aspects of the design were borrowed from a similar amateur project found on the internet, as well as previous Cornell University ECE4760 labs. The idea of having a rotating circular platform which moved in predetermined steps was based on an “Automated Bartender” video on IEEE TV, which used a slotted Geneva drive to move a glass. The idea for using the IR sensor to track position came from the tachometer in Lab 4 which measured the speed of a rotating fan blade, and the state machine used to debounce the menu input was similar to that used on the DTMF dialer in Lab 2. Although our project is interesting, after viewing other projects on the internet, it does not appear that any single aspect of our design is unique enough to warrant any copyright/patent considerations or concerns.

            Throughout the design process, there were two main issues that largly determined the outcome of our final product: control of the glass position, and control of the flow of mixers into the glass. Initially we tried to move the glass by simply enabling and disabling the DC motor for a specified amount of time via the microcontroller, but found that this was not always accurate enough to keep the dispensing liquid within the circumference of the glass, and that any position error that existed accumulated over time, making the system hard to run repeatedly without physically reseting the platform. By implementing an IR feedback loop in the hardware instead, we were able to monitor the position of the glass in real-time and always knew if it was in the right position for dispensing due to the comparator input. In contrast, the control of the valves used to dispense the mixers was plagued by hardware issues. Because these types of solenoid valves are usually used for larger-scale plumbing, they have minimum pressure requirements needed to allow flow which could not be meet with the amount of liquid in a 2-liter bottle. In order to fix this problem, we needed to physically adjust the solenoid gasket mechanisms inside the valves, but in doing so, we introduced small amount of leakage in some of the valves. Furthermore, our original design idea implemented precision mixing by first dispensing liquid from each bottle into a compartment of a predefined volume before dispensing it into the glass; however, this idea had to be abandonned entirely due to problems with pressure. In the end, we simply had to use a single valve for each mixer and rely on timed measurements to know how much liquid would be dispensed. Overall, it took time to find the right balance between reliability of software versus hardware.

Hardware

            Our automated drink mixer concept was implemented as a circular rotating structure that places the user’s glass underneath inverted plastic bottles containing different mixers. The entire structure sits on a base board which holds the Lazy Susan, IR sensor, DC motor, and PVC pipe in place. Elbow adapters angle the PVC pipe to hold the solenoid vavles over the rotating platform on which the glass sits. The nozzles of the inverted bottles are attached to the valves, which are opened and closed by the microcontroller. An upper keystone board is used to support the bottles to prevent them from leaning when the weight of the liquid is added. The user input is received via a push-button menu near the front of the base board. During the drink making process, the location of the user’s glass is controlled by a feedback loop by means of an IR emitter and receiver pointed at the edge of the rotating platform. Diagrams of the final structure can be seen below:

Details of the major subsystems of our physical structure are as follows:

During construction, our structure had to be modified from our original design concept.. Our original design did not have the motor directly in contact with the Lazy Susan. Instead, the two were separated, and a rubber belt was wrapped around the edge of the Lazy Susan and the motor. This turned out to be extremely troublesome because the belt had a tendency to slip up or down the motor wheel as the system ran for an extended amount of time. As a result, we decided it would be simpler to change the location of the motor and place it in direct contact with the platform.

           The circuitry for the automated drink mixer consisted of four separate parts: the push-button menu, the IR sensor and comparator, valve control, and the bidirectional motor drive. The details of the electronics of each of these four subsystems can be seen below:

Software

            The software for this program uses a state machine to control the order of functions called to move the glass to different positions and dispense mixers. The following is a list of all functions and interrupts and their descriptions:

When the main program starts, it initializes the hardware inputs and outputs, defines the INT0 interrupt, and initializes key state variables. It then enables the ISR and begins polling for a menu input. Once an input is received, the state machine debounces the input, and if it is valid, it fills the order. If not, it returns to its idle state. To fill an order, the software uses a combination of calls to movoto() and dispense(). Once the order is filled and the glass is back in the start position, the state machine waits to debounce the release of the push-button. Once the release is validated, the system again enters its initial idle state. The code for this project in its entirety can be found in Appendix A. A diagram of the full state machine can be seen here:

Results

            Overall, our design was successfully implemented, and the system was able to make all the drinks that we programmed into the software. Futhermore, the system is both simple and straightforward to use. The user just places a glass on the marked circle on the Lazy Susan, and presses the button corresponding to their desired beverage. The rest of the process consists of waiting and admiring the drink mixer as it prepares their tasty beverage. In testing, we found the following average times for the automated drink mixer to prepare the corresponding beverages:

Additionally, we found the mean number of successful runs between errors to be 20. Our three primary error modes were as follows:

Because the IR sensor and emitter are position close to the platform, we did not find interference to be an issue. Our design also included a certain degree of safety to prevent against catostrophic failures, like mixers being dispensed while the glass is not in position and liquid spilling onto any electronics. First, the IR feedback loop provided enough accuracy that the glass was never far enough out of position for the despensing liquid to "miss". In addition, if the motor did happen to slip, the IR sensor would never detect the next marker, so it is impossible for dispensing to occur prematurely. Second, bottle stoppers were used at the end of each valve to direct liquid flow in a small, well-aimed stream without splatter. Last, a delay period was placed after each call to dispense(), so that the glass never started to move during the switching time of the valve.

            The system is robust enough that reprogramming the software for new drink orders could be easily done, and the number of mixers could be easily expanded given a larger physical structure. One issue that our final design did not fully overcome is the fact that there is a small amount of leakage from the solenoid valves and bottle stoppers, especially when a carbonated ingredient is used. This was an issue throughout our entire process, but in the end we were able to minimize leakage by extensive re-assembly of the gaskets inside our valves.

Conclusions

            In terms of functional requirements, our final design met all of our desired characteristics. We were repeatedly able to mix all 5 drinks on our menu without having to manually adjust the system, and created a dynamic system, instead of just dispensing mixers into a static glass. As mentioned above, we do not feel that any part of our system is unique enough to warrant any intellectual property applications or concerns.; nor are there any legal concerns, given that all mixers used for this project were non-alcoholic.

            As far as ethical considerations are concerned, we believe that we abide by all 10 of the principles listed in the IEEE Code of Ethics. All of the contributing ideas that were borrowed from other's work have been acknowledged in this document, and can be found listed as references in Appendix D, and no part of this project was used for the purpose of making money. Furthermore, all of the results found in this document are known to be true to the best of our knowledge, and have not been forged. In terms of safety, it is possible that the automated drink mixer might be used for non-intended functions, such as making alcoholic cocktails. However, if the user is in possession of alcohol and desires to abuse our system with it, we must assume that they are of age, and an adult who is capable of making their own decisions. Finally, all known system shortcomings have been discussed thoroughly and honestly in this document.

            Given more time and resources, there are certainly more features that we would have liked to implement to improve the user experience. First and foremost, using higher quality valves that did not leak would be the major improvement, since this problem requires the most amount of maintenance to mitigate. Next, creating a touch screen display and interface to enhance the menu would make the final product much more appealing for use in the actual service industry. Last, the improvement and inclusion of more visually pleasing aesthetics for the system would make the design appear more complete. Flashing LED's and so on would make the experience of using this fun machine even more novel. If such a device were perfected and made to work consistently over long periods of use, we beleive that it could definitely serve both a functional and commercial purpose at any restaurant or bar.

Appendix A - Code

//**********************************************************
// ECE4760 Final Project - Automated Drink Mixer
//**********************************************************

#define F_CPU 16000000UL
#include <inttypes.h>
#include <avr/io.h>
#include <stdio.h>
#include <util/delay.h>
#include <avr/interrupt.h>
#include <math.h>

#define begin {
#define end }

//position definitions and variables
#define start 1
#define coke 2
#define grenadine 3
#define sprite 4
#define lemon 5
#define tea 6
#define restart 7

float position;
volatile char count;
volatile char last;

//state machine definitions and variables
#define NoPush 1
#define MaybePush 2
#define Pushed 3
#define MaybeNoPush 4

unsigned char PushState;
unsigned char order;
unsigned char maybe;

//**********************************************************
// external interrupt ISR
ISR (INT0_vect) {
        count++;
}

//**********************************************************
//dispense drink and refill compartment
void dispense(void)
begin
            if(position == 2)
            begin
                        PORTC = 0x01;
                        _delay_ms(2700);
                        PORTC = 0x00;
                        if(last) _delay_ms(2000);
            end
            else if(position == 3)
            begin
                        PORTC = 0x02;
                        _delay_ms(2000);
                        PORTC = 0x00;
                        if(last) _delay_ms(2000);
            end
            else if(position == 4)
            begin
                        PORTC = 0x10;
                        _delay_ms(2000);
                        PORTC = 0x00;
                        if(last) _delay_ms(2000);
            end
            else if(position == 5)
            begin
                        PORTC = 0x40;
                        _delay_ms(1000);
                        PORTC = 0x00;
                        if(last) _delay_ms(2000);
            end
            else if(position == 6)
            begin
                        PORTC = 0x80;
                        _delay_ms(3000);
                        PORTC = 0x00;
                        if(last) _delay_ms(2000);
            end     
end

//**********************************************************
//move glass to new position
void moveto(float num)
begin
            float diff;
            diff = position - num;
            position = num;
            count = 0;

            //if the difference is positive, rotate counterclockwise
            if(signbit(diff) == 0)
            begin
                        PORTD = 0x02;
                        while (count < diff);
                        PORTD = 0x00;
            end

            //otherwise, rotate clockwise
            else
            begin
                        diff = -1 * diff;
                        PORTD = 0x01;
                        while (count < diff);
                        PORTD = 0x00;
            end
end

//**********************************************************
//drink 1
void cherrycoke(void)
begin
            moveto(coke);
            last = 0;
            dispense();
            dispense();
            dispense();
            last = 1;
            dispense();
            moveto(grenadine);
            dispense();
            moveto(start);
end

//**********************************************************
//drink 2
void cokelime(void)
begin
            moveto(coke);
            last = 0;
            dispense();
            dispense();
            dispense();
            last = 1;
            dispense();
            moveto(lemon);
            dispense();
            moveto(restart);
            position = start;
end

//**********************************************************
//drink 3
void spoke(void)
begin
            moveto(coke);
            last = 0;
            dispense();
            last = 1;
            dispense();
            moveto(sprite);
            last = 0;
            dispense();
            last = 1;
            dispense();
            moveto(start);
end

//**********************************************************
//drink 4
void arnold_palmer(void)
begin
            moveto(lemon);
            last = 1;
            dispense();
            moveto(tea);
            last = 0;
            dispense();
            dispense();
            dispense();
            last = 1;
            dispense();
            moveto(restart);
            position = start;
end

//**********************************************************
//drink 5
void shirley_temple(void)
begin
            moveto(sprite);
            last = 0;
            dispense();
            dispense();
            dispense();
            last = 1;
            dispense();
            moveto(grenadine);
            dispense();
            moveto(start);
end

//**********************************************************
//get the appropriate drink order from PORTB
void getorder(void)
begin
            if (~PINB & (1<<PINB0)) order=1;
            else if (~PINB & (1<<PINB2)) order=2;
            else if (~PINB & (1<<PINB4)) order=3;
            else if (~PINB & (1<<PINB6)) order=4;
            else if (~PINB & (1<<PINB7)) order=5;
            else order=0;
end

//**********************************************************      
//update the state machine
void checkorder(void)
begin
            getorder();
            switch (PushState)
            begin
                        case NoPush:
                                    if(order != 0) {maybe=order; PushState=MaybePush;}
                                    break;
            case MaybePush:
            if (order == maybe)
            begin
                                                if(order == 1) cherrycoke();
                                                else if(order == 2) cokelime();
                                                else if(order == 3) spoke();
                                                else if(order == 4) arnold_palmer();
                                                else if(order == 5) shirley_temple();
                                                PushState=Pushed;  
            end
            else PushState=NoPush;
            break;
            case Pushed: 
            if (order != maybe) PushState=MaybeNoPush; 
            break;
            case MaybeNoPush:
            if (order == maybe) PushState=Pushed;
            else PushState=NoPush;
            break;
            end
end

//**********************************************************      

int main(void)
begin 
            DDRB=0x00; //Push-button menu inputs
            PORTB=0xFF;
            DDRC=0xFF; //Valve outputs
            PORTC=0x00;
            DDRD=0x03; //Motor control outptus and IR sensor input
            PORTD=0x00;

            //set up INT0
            EIMSK = 1<<INT0; // turn on int0
            EICRA = 3;       // rising edge

            //initialize position
            position = start;

            //initialize state machine
            PushState = NoPush;

            //enable interrupts
            sei();

            //poll for drink order every 30msec
            while(1)
            begin
                        checkorder();
                        _delay_ms(30);
            end
end 

Appendix B - Costs

Note: All wood used in this project was salvaged from different scrap piles. Also, our power supply was acquired from Professor Land as a surplus piece of lab equipment for no extra charge.

Appendix C - Division of Labor

Since we live together, almost all tasks were done jointly. The only tasks done independently were as follows:

Appendix D - References