The automatic drink maker has a front display and a dial to select the drink. Then the mixed drink comes out of the spout at the front.
A user puts 4 different drinks that are used in their desired menu of mixed drinks onto the sides of the automatic drink maker. Then the user uses the programmed drink selection system to scroll through the menu using the dial. On selection of the drink, the drink maker will dispense the correct amounts of every liquid into the user's cup under the spout.
Overall image of the drink maker showing the size and functionality of the tubes going into various drinks, with a user's cup in front.
A single 12 V barrel jack powers the whole system. The barrel jack is on the back side of the machine.
The above image shows the user interface. The arrow shows the drink that is selected. You rotate the dial next to the screen to change the selection. You then push the dial in to run the system.
The tube on the outside of the box is put into the liquid ingredient's bottle. The length of the tube is long enough to reach the bottom of the bottle so there is no missed liquid.
The dial selection is turned to select the drink recipe. Then the dial is pushed to select the drink. The select amounts of each ingredient are pumped into the user's cup out of the spout. While this is happening, the display reads Making Drink "name of drink" Please Wait.... Then it says Drink Completed after the drink is done being made.
I began my project by getting one pump to run. I had to do a lot of wiring for each motor. It included a MOSFET, 2 resistors, and a diode. At first, I made it so it would run based on a button push. After doing this, I repeated the circuit multiple times so I could run multiple pumps at once, all independently, for different amounts of time.
Because the breadboarding was very complicated and jumper wires could not be adjusted in length, I decided to solder all of my components. I chose to do two protoboards with a pair of pumps on each one. I also made a protoboard that was responsible for the power. I had one rail that was 12 V and another that was 5 V, using a voltage regulator.
I mounted my Arduino on the wall for clean access to upload new code into the Arduino. This is necessary because before to set the menu and ingredient amounts you must upload new code to the arduino to match up the pumps to the ingredients.
The LCD screen and the rotary encoder are wall mounted so the screen can be read from the outside and the dial is turned from the outside.
The wall-mounted pump allows one tube to go straight down to the ingredient bottle. The other tube goes back into the box and out of the front spout. I had to drill holes for the bolts and the tube.
To finish the box, I wood glued the box together and put a PVC elbow to act as a spout.
The actual manufacturing of the box and wall mounting everything took longer than I expected. I knew it would take a while, but the soldering and wall mounting took longer. I also designed my box in Fusion before seeing the material. I originally made it too big for the 1x2 ft pieces of wood, so had to make it smaller.
Many of the comments I received during my review expressed concern with the cleaning or hygiene of the drink maker. Residue of the different ingredients may get stuck in the tubes, so it is important to flush the tubes with hot, soapy water in between uses. I should add a cleaning cycle to the drink selection so that the user can quickly do this when they are done using the machine. Other comments said that I should make the housing look like a Solo Cup or make it clear to show the components inside. Although the clear idea would be cool, the only thing visible would be electrical components that don't move or do anything. There is no action happening on the inside of the casing. The solo cup or another more pleasing housing, rather than a big wood box, would have been a good touch to the project, but I was more focused on the functionality of it.
I am happy with how the project came out. This was a project I have been wanting to make for a while now, so it was nice to finally be able to make it. I did not make the wiring of the components as clean as I wanted it to be or the distribution spout, but that was because I ran out of time. I was really happy with the UI and the ease of use. I wish I could have done larger motors so the drinks could have been made faster, but with the tools I had, I was happy with the work.
My advice to my past self on this project would have been to give myself more time on the housing and the aesthetic of the project. Even though I knew this was my weak point, and because of this, I gave myself extra time, it still took longer than expected. I spent a lot of time getting all of the pumps to run at the same time, and I had concerns about power distribution. Looking back, it was really easy to copy the same circuit 4 times for each motor, and power distribution was no issue, so I wish I hadn't spent as much time on this. Finally, I wish I had ordered food-safe tubing and gone through the processes so I could have demonstrated it with actual drinks rather than water and food dye.
I plan on ordering the food-safe tubing myself and completing the project. I also think it would be fun in the future to make a full bar system that already has a set pump for every ingredient at the bar, so the menu can be much larger. Larger pumps so drinks can be made faster would also be fun. Many easy additions can be made to this project, but they mostly include better hardware and increasing the size of the device.
/*
Automatic Drink Maker - Leighton Tarke
Automatically dispenses drinks for a user based on user selection from
drink list.
pin mapping:
Arduino pin | role | description
___________________________________________________________________
6 input Encoder pin CLK
7 input Encoder pin DT
8 input Encoder pin SW
9 output Pump 1
5 output Pump 2
3 output Pump 3
10 output Pump 4
A4 output LCD screen SCL
A5 output LCD Screen SDA
Code released to the public domain by the author, 4/1/2026
Leighton Tarke, ltarke@andrew.cmu.edu
*/
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27, 20, 4);
// Encoder pins
const int clkPin = 6;
const int dtPin = 7;
const int swPin = 8;
// Pump pins
//1:12 4 oz
const int pump1Pin = 9;
const int pump2Pin = 5;
const int pump3Pin = 3;
const int pump4Pin = 10;
// Priming pumps
const unsigned long PRIME_TIME = 4000; // 4 seconds
// Recipe Structure
struct Recipe {
String name;
unsigned long pump1Time;
unsigned long pump2Time;
unsigned long pump3Time;
unsigned long pump4Time; // NEW
};
// Recipes
Recipe recipes[] = {
{"Well Drink", 36000, 72000, 0, 0},
{"Moscow Mule", 36000, 0, 9000, 72000},
{"Margarita", 36000, 18000, 9000, 0},
{"Everything", 36000, 36000, 36000, 36000},
{"Small mix", 5000, 10000, 15000, 20000}
};
const int recipeCount = sizeof(recipes)/sizeof(recipes[0]);
// Encoder
int lastCLK;
int menuIndex = 0;
int scrollOffset = 0;
bool makingDrink = false;
bool cycleRunning = false;
unsigned long pump1EndTime = 0;
unsigned long pump2EndTime = 0;
unsigned long pump3EndTime = 0;
unsigned long pump4EndTime = 0;
unsigned long cycleStartTime = 0;
// Pump timing and LCD
void startCycle(int recipeIndex) {
unsigned long now = millis();
cycleRunning = true;
makingDrink = true;
cycleStartTime = now;
pump1EndTime = now + recipes[recipeIndex].pump1Time;
pump2EndTime = now + recipes[recipeIndex].pump2Time;
pump3EndTime = now + recipes[recipeIndex].pump3Time;
pump4EndTime = now + recipes[recipeIndex].pump4Time;
lcd.clear();
lcd.setCursor(0,0);
lcd.print("Making Drink");
lcd.setCursor(0,1);
lcd.print(recipes[recipeIndex].name);
lcd.setCursor(0,2);
lcd.print("Please Wait...");
}
void drawMenu() {
lcd.clear();
lcd.setCursor(0,0);
lcd.print("Drink Selection");
for(int i = 0; i < 3; i++) {
int index = scrollOffset + i;
if(index >= recipeCount) break;
lcd.setCursor(0, i+1);
if(index == menuIndex)
lcd.print("> ");
else
lcd.print(" ");
lcd.print(recipes[index].name);
}
}
//LCD scrolling
void updateScroll() {
if(menuIndex < scrollOffset)
scrollOffset = menuIndex;
if(menuIndex >= scrollOffset + 3)
scrollOffset = menuIndex - 2;
}
//Reading dial
void readEncoder() {
int currentCLK = digitalRead(clkPin);
if(currentCLK != lastCLK && currentCLK == LOW) {
if(digitalRead(dtPin) != currentCLK)
menuIndex++;
else
menuIndex--;
menuIndex = constrain(menuIndex, 0, recipeCount-1);
updateScroll();
drawMenu();
}
lastCLK = currentCLK;
}
//Reading encoder button
void checkButton() {
if(digitalRead(swPin) == LOW && !cycleRunning) {
delay(200); // debounce
startCycle(menuIndex);
}
}
// Setup Pins
void setup() {
lcd.init();
lcd.backlight();
pinMode(clkPin, INPUT);
pinMode(dtPin, INPUT);
pinMode(swPin, INPUT_PULLUP);
pinMode(pump1Pin, OUTPUT);
pinMode(pump2Pin, OUTPUT);
pinMode(pump3Pin, OUTPUT);
pinMode(pump4Pin, OUTPUT);
digitalWrite(pump1Pin, LOW);
digitalWrite(pump2Pin, LOW);
digitalWrite(pump3Pin, LOW);
digitalWrite(pump4Pin, LOW);
lastCLK = digitalRead(clkPin);
Serial.begin(9600);
// Prime pumps on startup
lcd.clear();
lcd.setCursor(0,0);
lcd.print("Priming Pumps...");
digitalWrite(pump1Pin, HIGH);
digitalWrite(pump2Pin, HIGH);
digitalWrite(pump3Pin, HIGH);
digitalWrite(pump4Pin, HIGH);
delay(PRIME_TIME);
digitalWrite(pump1Pin, LOW);
digitalWrite(pump2Pin, LOW);
digitalWrite(pump3Pin, LOW);
digitalWrite(pump4Pin, LOW);
drawMenu();
}
// Run pumps
void loop() {
unsigned long now = millis();
if(!makingDrink) {
readEncoder();
checkButton();
}
if(cycleRunning) {
bool p1on = (now < pump1EndTime);
bool p2on = (now < pump2EndTime);
bool p3on = (now < pump3EndTime);
bool p4on = (now < pump4EndTime);
digitalWrite(pump1Pin, p1on ? HIGH : LOW);
digitalWrite(pump2Pin, p2on ? HIGH : LOW);
digitalWrite(pump3Pin, p3on ? HIGH : LOW);
digitalWrite(pump4Pin, p4on ? HIGH : LOW);
if(!p1on && !p2on && !p3on && !p4on) {
cycleRunning = false;
makingDrink = false;
lcd.clear();
lcd.setCursor(0,1);
lcd.print("Drink Complete!");
delay(2000);
drawMenu();
}
}
}