/*
Project: Vending Machine
Description: Presents a menu, counts coins / bills,
dispenses stuff, gives change
Creation date: 11/26/23
Author: AnonEngineering
Stormblade630 - Discord | Arduino | coding-help | Vending Machine
mechButton: https://github.com/leftCoast/LC_baseTools/tree/master
License: https://en.wikipedia.org/wiki/Beerware
*/
#include <Servo.h>
#include <mechButton.h>
#include <LiquidCrystal.h>
const int DISPENSE_TIME = 3000;
const int DISPLAY_TIME = 2000;
const int NUM_OF_PRODUCTS = 6;
const int SEL_BTN_PIN = 6;
const int ENT_BTN_PIN = 5;
const int RET_BTN_PIN = 4;
const int BILL_PIN = 3;
const int COIN_PIN = 2;
const int DATA_PIN = A0;
const int LATCH_PIN = A1;
const int CLOCK_PIN = A2;
const int SERVO_PIN = A3;
const int RS = 12, EN = 11, D4 = 10, D5 = 9, D6 = 8, D7 = 7;
struct productInfo {
char name[17];
int price;
};
productInfo product[NUM_OF_PRODUCTS] = {
{ "Snickers", 225 },
{ "M & M\'s", 200 },
{ "Reeses\'s", 250 },
{ "Gummy Bears", 150 },
{ "Twizzlers", 175 },
{ "Jaw Breakers", 100 },
};
bool canSelect = true;
int credit = 0;
int currentProduct = 0;
Servo servo;
LiquidCrystal lcd (RS, EN, D4, D5, D6, D7);
mechButton entBtn(ENT_BTN_PIN);
mechButton retBtn(RET_BTN_PIN);
mechButton selBtn(SEL_BTN_PIN);
mechButton billSense(BILL_PIN);
mechButton coinSense(COIN_PIN);
char* makePrice(int value) {
static char msgPrice[17];
int dollarVal = value / 100;
int centVal = value % 100;
snprintf(msgPrice, 17, "$%d.%02d ", dollarVal, centVal);
char* msgPointer = msgPrice;
return msgPointer;
}
void billCallback(void) {
if (billSense.trueFalse()) {
// pull in the bill
setMotors(0xC0);
credit = credit + 100;
showCredit();
char* creditVal = makePrice(credit);
Serial.println("Bill inserted");
Serial.print("Credit: ");
Serial.println(creditVal);
dispenseProduct();
} else {
// stop bill pull
setMotors(0x00);
}
}
void coinCallback(void) {
if (!coinSense.trueFalse()) {
credit = credit + 25;
showCredit();
char* creditVal = makePrice(credit);
Serial.println("Coin inserted");
Serial.print("Credit: ");
Serial.println(creditVal);
dispenseProduct();
}
}
void dispenseProduct() {
if (credit >= product[currentProduct].price) {
int motor = 1 << currentProduct;
setMotors(motor);
delay(DISPENSE_TIME);
setMotors(0x00);
canSelect = true;
credit = credit - product[currentProduct].price;
char* creditVal = makePrice(credit);
lcd.clear();
lcd.print("Credit: ");
lcd.print(creditVal);
Serial.print("Credit: ");
Serial.println(creditVal);
delay(DISPLAY_TIME);
lcd.clear();
lcd.setCursor(5, 0);
lcd.print("Enjoy!");
delay(DISPLAY_TIME);
showSelect();
}
}
void entCallback(void) {
if (!entBtn.trueFalse()) {
//Serial.println("Enter pressed");
canSelect = false;
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Insert bills");
lcd.setCursor(0, 1);
lcd.print("or coins: ");
lcd.setCursor(10, 1);
char* printVal = makePrice(product[currentProduct].price - credit);
lcd.print(printVal);
}
}
void retCallback(void) {
if (!retBtn.trueFalse()) {
//Serial.println("Return pressed");
canSelect = true;
while (credit > 0) {
lcd.setCursor(0, 0);
lcd.print("Collect coins ");
for (int angle = 180; angle > 90; angle--) {
servo.write(angle);
delay(10);
}
credit = credit - 25;
lcd.setCursor(0, 1);
lcd.print("Credit: ");
char* creditVal = makePrice(credit);
lcd.print(creditVal);
for (int angle = 90; angle < 180; angle++) {
servo.write(angle);
delay(10);
}
//credit = credit - 25;
}
showCredit();
delay(DISPLAY_TIME);
showSelect();
}
}
void selCallback(void) {
static int productNumber = 0;
if (!selBtn.trueFalse() && canSelect) {
//Serial.println("Select pressed");
lcd.clear();
lcd.setCursor(0, 0);
lcd.print(product[productNumber].name);
lcd.setCursor(0, 1);
char* printValue = makePrice(product[productNumber].price);
lcd.print(printValue);
Serial.print(product[productNumber].name);
Serial.print(" \t");
Serial.println(printValue);
currentProduct = productNumber;
productNumber++;
if (productNumber >= NUM_OF_PRODUCTS) productNumber = 0;
}
}
void setMotors(int motors) {
digitalWrite(LATCH_PIN, LOW);
shiftOut(DATA_PIN, CLOCK_PIN, LSBFIRST, motors);
digitalWrite(LATCH_PIN, HIGH);
}
void showCredit() {
lcd.clear();
char* priceVal = makePrice(product[currentProduct].price);
lcd.print("Price: ");
lcd.print(priceVal);
lcd.setCursor(0, 1);
char* creditVal = makePrice(credit);
lcd.print("Credit: ");
lcd.print(creditVal);
}
void showSelect() {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Choose a treat!");
lcd.setCursor(2, 1);
lcd.print("Press Select");
}
void setup() {
Serial.begin(115200);
lcd.begin(16, 2);
servo.write(180); // minimize glitch?
servo.attach(SERVO_PIN);
//servo.write(180);
pinMode(SEL_BTN_PIN, INPUT_PULLUP);
pinMode(ENT_BTN_PIN, INPUT_PULLUP);
pinMode(RET_BTN_PIN, INPUT_PULLUP);
//pinMode(BILL_PIN, INPUT);
//pinMode(COIN_PIN, INPUT);
pinMode(DATA_PIN, OUTPUT);
pinMode(LATCH_PIN, OUTPUT);
pinMode(CLOCK_PIN, OUTPUT);
selBtn.setCallback(selCallback);
entBtn.setCallback(entCallback);
retBtn.setCallback(retCallback);
billSense.setCallback(billCallback);
coinSense.setCallback(coinCallback);
setMotors(0x00);
// splash screen
lcd.setCursor(2, 0);
lcd.print("Vend-o-Matic");
lcd.setCursor(5, 1);
lcd.print("V1.00");
delay(DISPLAY_TIME);
showSelect();
}
void loop() {
idle();
}
Select
Enter
Return
Bills
Coins
Machine accepts $1.00
bills and quarters only!
Coin Return
Bill grabbers
Product motors