How We Built a Smart Tea Vending Machine
This project began with a simple question: how can we offer affordable, easy-access tea at any hour? The answer was an RFID-enabled vending machine that detects cup placement, deducts user balance, and automatically dispenses tea.
Design Strategy
- Hardware: Arduino Mega 2560 controlled servo, MFRC522 RFID module, ultrasonic sensors, and a 16x2 LCD display.
- Authentication: RFID tags validate users and track balances stored in EEPROM-like arrays.
- Cup & Ingredient Sensing: Ultrasonic sensors identify cup presence and measure ingredient availability in real-time.
Control Logic
The user presents a card. Upon successful authentication, they choose a drink using the keypad. The system confirms available resources before activating the servo to dispense tea. It also provides cancellation options and updates user balance.
#include
#include
#include
#include
#include
#define RST_PIN 5
#define SS_PIN 53
byte idList[2][5]={ // Demo Server data base
{0x95,0x73,0x05,0xAB,200},
{}
};
int idNum;
int bItems;
double Aprice=20;
Servo servo; // create servo object to control a servo
int intPos= 80;
MFRC522 mrfc522(SS_PIN, RST_PIN);
LiquidCrystal lcd(7,8,9,10,11,12);
char type;
const byte ROWS = 4;
const byte COLS = 4;
//define the cymbols on the buttons of the keypads
char hexaKeys[ROWS][COLS] = {
{'A','B','C','D'},
{'3','6','9','#'},
{'2','5','8','0'},
{'1','4','7','*'}
};
byte rowPins[ROWS] = {42,40,38,36}; //connect to the row pinouts of the keypad
byte colPins[COLS] = {28,30,32,34}; //connect to the column pinouts of the keypad
Keypad customKeypad = Keypad( makeKeymap(hexaKeys), rowPins, colPins, ROWS, COLS);
const int trigPin1 = 2;
const int echoPin1 = 3;
const int trigPin2 = 4;
const int echoPin2= 6;
void lcdDef(){ // default Screen
lcd.clear();
lcd.noBlink();
lcd.setCursor(0,1);
lcd.print("Place your card");
lcd.setCursor(0,0);
lcd.print("A.T.Vending Mchn");
}
void setup() {
Serial.begin(9600); // Initialize serial communications with the PC
while (!Serial); // Do nothing if no serial port is opened (added for Arduinos based on ATMEGA32U4)
servo.attach(13);
servo.write(intPos);
SPI.begin(); // Init SPI bus
mrfc522.PCD_Init(); // Init MFRC522
// Show details of PCD - MFRC522 Card Reader details
lcd.begin(16, 2);
lcdDef();
// put your setup code here, to run once:
}
void loop() { // main code that needs to run repeatly
//bItems=0;
availability('O');
//lcd.autoscroll();
//lcd.print(" ");
delay(700);
if ( ! mrfc522.PICC_IsNewCardPresent()) {
return;
}
if ( ! mrfc522.PICC_ReadCardSerial()) {
return;
}
if (readId()){
delay(1000);
lcdDef();
return;}
int x=0;
while (x<300){
type=customKeypad.getKey();
lcd.noBlink();
if(type=='A'|| type=='B'){
if (selection(type)){
break;}
else {
lcdWrt("Not Available","");
delay(3000);
lcdDef();
return;}
}
x=x+1;
delay(100);
}
if (x==300){
lcdDef();
return;
}
lcdWrt("Enter Count ","T.Avb- Ac.Bal-"); //tAvlbl() and balance(idNum)
delay(1000);
int c=count();
Serial.print("in loop");
Serial.println(c);
if (!c){
lcdDef();
return;}
Serial.println(c);
deliverTea(type,c);
Serial.println(bItems);
idList[idNum][4]=idList[idNum][4]- bItems*Aprice;
Serial.print("balance : ");
Serial.print( idList[idNum][4]);
}
void lcdWrt(String text1,String text2){ //Write to the Lcd Screen
lcd.clear();
lcd.setCursor(0,0);
lcd.print(text1);
lcd.setCursor(0,1);
lcd.print(text2);
}
boolean readId(){ //read RFID card
lcd.noAutoscroll();
lcd.clear();
lcd.setCursor(0,0);
lcd.print("ID");
for (byte i = 0; i < mrfc522.uid.size; i++) {
lcd.print(mrfc522.uid.uidByte[i] < 0x10 ? " 0" : " ");
lcd.print(mrfc522.uid.uidByte[i], DEC);
}
if(checkId()){
return false;
}
lcd.setCursor(0,1);
lcd.print("Select drink ");
lcd.blink();
}
boolean checkId(){ // Check is RFID card is authenticated?
for(int i=0;i<2;i++){
if (mrfc522.uid.uidByte[0] != idList[i][0] ||
mrfc522.uid.uidByte[1] != idList[i][1] ||
mrfc522.uid.uidByte[2] != idList[i][2] ||
mrfc522.uid.uidByte[3] != idList[i][3] ) {
}
else {
idNum = i;
return false;
}
}
lcdWrt("Invalid card","Try again");
return true;
}
boolean selection(char x){ // select the drink
if(x=='A' & availability(x)){
return true;}
else {
return false;}
}
boolean availability(char y){ //check availablity of ingredients results in true or false
if(y=='O'){
if(ultrasonic(2,3)>13){
gsmModuleAct();
return false;}}
else if(y=='A'){
Serial.println(ultrasonic(2,3));
if(ultrasonic(2,3)>15){
return false;}
else{
return true;} }
else if(y=='B'){
return false;}
}
long ultrasonic(int trigPin,int echoPin){ //run ultrasonic and get distance in cm
long cm;
pinMode(trigPin, OUTPUT);
digitalWrite(trigPin, LOW);
delayMicroseconds(2);
digitalWrite(trigPin, HIGH);
delayMicroseconds(10);
digitalWrite(trigPin, LOW);
pinMode(echoPin, INPUT);
cm = pulseIn(echoPin, HIGH) / 29 / 2;
delay(100);
return cm;
}
void gsmModuleAct(){ // Gsm module msg sent code had to paste here
Serial.println(" low ingrediant level, SMS sent to the operator . .");}
int count(){ // successfully check for get counts and return count
char c;
int cNew;
for(int y=0;y<3;y++){
int x=0;
while(x<300){
c = customKeypad.getKey();
if (c){
break;}
x=x+1;
delay(100);}
if (c=='1'){ cNew= 1;}
else if (c=='2'){cNew= 2;}
else if (c=='3'){cNew= 3;}
else if (c=='4'){cNew= 4;}
else if (c=='5'){cNew= 5;}
else if (c=='6'){cNew= 6;}
else if (c=='7'){cNew= 7;}
Serial.println(cNew);
if (cNew<(tAvlbl()+1) & !(balance()<(cNew*Aprice))){
lcdWrt("Please wait . . .","");
delay(1000);
return cNew;
}
else if(cNew>tAvlbl()){
lcdWrt("Not that much" , "Material");
delay(3000);}
else if (balance()8 || ultrasonic(4,6)<0){
delay(20);
t=t+20;
if (t==5000 || 'C'==customKeypad.getKey()){
bItems=x;
lcdDef();
Serial.println(bItems);
return;}
}
lcdWrt("Press C if you", " want to cancel");
onServo();
delay(3000);
}
}
lcdWrt("Thank You..","Have a nice day");
delay(3000);
lcdDef();
delay(3000);
bItems=count;
return;
}
void onServo(){ // working servo as valve
servo.write(intPos-60);
Serial.println("");
delay(1000);
for(int x=0;x<10;x++){
servo.write(intPos-60);
if (ultrasonic(4,6)>12 || ultrasonic(4,6)<3){
servo.write(intPos);
delay(100);
lcdWrt("Please place","your cup correct");
delay(20);
int t=0;
while(ultrasonic(4,6)>8 || ultrasonic(4,6)<0){
t=t+10;
delay(10);
if (t==500){
return;}
}
}
lcdWrt("Ac.Bal "," Be hold");
delay(100);
}
servo.write(intPos);
}
int balance(){ // check balance of the accessed RFID tag
return idList[idNum][4];
}
int tAvlbl(){ // Available tea return in its count
int tAv=16-ultrasonic(2,3);
return tAv;
}
Challenges & Wins
- Initial issues with GSM integration for low-stock alerts were resolved in a later prototype.
- Ultrasonic sensors were an effective, low-cost way to monitor the system state.
It’s rewarding to see this vending machine prototype operate reliably in real-world conditions. Our work opens the door for even more accessible automated services.