How We Built a Smart Tea Vending Machine with Arduino

Oct 3, 2016
6 min read

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.