// Libraries
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <EEPROM.h>
#include <Servo.h> // Include Servo library
#include <RTClib.h>
// Constants
#define DEBOUNCE_DELAY 30
#define CountDown 20000
// Pins
#define coinSlot 18
#define SIGNAL_PIN 19
#define button1 24
#define button2 28
#define button3 32
#define button4 36
#define button5 40
#define button6 44
#define sukliButtonPin 48 //sukli
#define changePricesButton 52 //admin mode
#define buzzer A8 //buzzer
#define RELAY_PIN A10 //relay for coin hopper disable
#define RELAY_PIN1 A9 //relay for coin slot disable
//#define StockSensor1 A6
//#define StockSensor2 A7
//#define StockSensor3 A8
//#define StockSensor4 A9
//#define StockSensor5 A10
//#define StockSensor6 A11
#define VerifierPin1 A0
#define VerifierPin2 A1
#define VerifierPin3 A2
#define VerifierPin4 A3
#define VerifierPin5 A4
#define VerifierPin6 A5
// Define pin numbers
const int Sound1 = 39; //1. ANG ITEM NA ITO AY MERON PANG STOCK PERO KULANG ANG PERA MO
const int Sound2 = 41; //2. ANG ITEM NA ITO AY OUT OF STOCK
const int Sound3 = 43; //3. Cash Register (Kaching) - Sound Effect (HD)
const int Sound4 = 45; //4. Pindutin ang button sa ibaba para makuha ang itong sukli. maraming salamat.
// Servo setup
Servo servos[6]; // Create an array of Servo objects
const int servoPins[] = {2, 3, 4, 5, 6, 7}; // Define servo control pins
const int servoAngles[] = {0, 35, 75}; // Start, mid, end angles for servos
const int ServoShake = 3;
// LCD setup
LiquidCrystal_I2C lcd(0x27, 20, 4); // Set the LCD address to 0x27 for a 20 chars and 4 line display
char lcdBuffer[21];
char prefix[] = " Balance: PHP";
char ZZ[] = ".00";
// Global variables
volatile int pulse = 0;
int prices[] = {1, 2, 3, 4, 5, 6};
int previousPulse = 0;
bool initialized = false;
int phase = 1;
const int buttons[6] = {button1, button2, button3, button4, button5, button6}; // Array of button pins
//const int StockSensors[6] = {StockSensor1, StockSensor2, StockSensor3, StockSensor4, StockSensor5, StockSensor6}; // Array of StockSensor IR pins
const int VerifierPins[6] = {VerifierPin1, VerifierPin2, VerifierPin3, VerifierPin4, VerifierPin5, VerifierPin6}; // Array of VerifierSensor IR pins
// Variable to store the button state
int sukliButtonState = 0;
int lastsukliButtonState = HIGH;
int delayISR = 30;
// Variable to store the number of coins dispensed
volatile int coinsDispensed = 0;
int coinsToBeDispensed = pulse; // Number of coins to be dispensed
// Variable to indicate if dispensing is in progress
bool dispensingInProgress = false;
// Variable to indicate if the system is ready to count coins
bool systemReady = false;
// Variable to indicate if the buttons are enabled
bool buttonsEnabled = true; // Flag to enable/disable button inputs
// Timing variables for blinking
unsigned long previousMillis = 0;
bool isBlinking = true;
#define BLINK_INTERVAL 400 // Interval for blinking in milliseconds
volatile unsigned long lastPulseTime = 0;
bool InStock = true;
int buttonsdisabletime = 1000;
RTC_DS3231 rtc;
int stocks[6] = {0, 0, 0, 0, 0, 0}; // Example stock values, you can initialize them as needed
volatile bool recentHopperPulseFlag = false; // Flag to indicate a recent coin pulse (volatile for ISR use)
unsigned long lastHopperPulseTime = 0; // Time when we last acknowledged a pulse
bool buzzFlag = false; // New flag for controlling the buzzer
//************Button*****************//
int P1=32; // Button SET MENU'
int P2=28; // Button +
int P3=24; // Button -
//************Variables**************//
int hourupg;
int minupg;
int yearupg;
int monthupg;
int dayupg;
int menu =0;
int btndelay = 40;
unsigned long previousMillisClock = 0;
int timeDisplayDuration = 25; // 25 seconds
int status = 0; // 0 for time display
bool displayDateTimeflag = true;
// Function prototypes
void setup();
void loop();
void initializeHardware();
void initializeButtons();
//void initializeStockSensors();
void initializeVerifierPins();
void initializeServos();
void initializeLCD();
void initializePins();
void insertcoinpage();
void printCredits();
void dispenseVariant(int index);
void transitionToPhase1();
void transitionToPhase2();
void activateServo(int index);
void operateServo(int servoPin, int index);
void moveToAngle(int servo, int angle);
void Buzz();
void BuzzLow();
void changePrices();
void savePricesToEEPROM();
void loadPricesFromEEPROM();
void coinISR();
void Sukli();
void activateRelay();
void deactivateRelay();
void Hoppercount();
void GetSukli();
void displayTimeoutMessage();
void displayCountdown(int start);
void activateCoinslot();
void deactivateCoinslot();
void updateInsertCoinPage();
void checkStock(int index);
void setupSoundPins(); // Call the function to initialize sound pins
void playSound(int pin);
void setupRTC();
void processRTC();
String getDateTime();
void sendSalesData(int variant, int price);
void changeStocks();
bool checkCurrentStock(int variantIndex);
void displayAdminMenu();
void setup() {
deactivateRelay();
Serial.begin(9600); // Initialize Serial communication
Serial2.begin(9600); // Initialize Serial2 for sales data (TX2 on Pin 16, RX2 on Pin 17)
initializeHardware();
loadPricesFromEEPROM();
insertcoinpage();
initializePins();
attachInterrupt(digitalPinToInterrupt(SIGNAL_PIN), Hoppercount, FALLING);
attachInterrupt(digitalPinToInterrupt(coinSlot), coinISR, FALLING); // Attach interrupt to coinSlot pin
setupSoundPins(); // Call the function to initialize sound pins
setupRTC();// Call the function to setup RTC
pulse = 0;// Clear any false pulse count
previousPulse = 0;
initialized = true;// Set initialization flag
pinMode(P1,INPUT_PULLUP);
pinMode(P2,INPUT_PULLUP);
pinMode(P3,INPUT_PULLUP);
int menu=0;
displayDateTimeLcd();
}
void loop() {
unsigned long currentMillis = millis();
if (phase == 1 && initialized) {
printCredits();
for (int i = 0; i < sizeof(buttons) / sizeof(buttons[0]); i++) {
dispenseVariant(i);
}
if (digitalRead(changePricesButton) == LOW) {
Buzz();
transitionToPhase2();
}
Sukli();
processRTC();
}
if (phase == 2) {
displayAdminMenu(); // Display the menu once at the start of Phase 2
while (phase == 2) { // Stay in this loop until an action is taken
// Check if button1 is pressed to change prices
if (digitalRead(button1) == LOW) {
changePrices();
savePricesToEEPROM();
transitionToPhase1(); // Return to Phase 1 after saving prices
break; // Exit the while loop
}
// Check if button2 is pressed to change stocks
if (digitalRead(button2) == LOW) {
changeStocks();
transitionToPhase1(); // Return to Phase 1 after changing stocks
break; // Exit the while loop
}
if (digitalRead(button3) == LOW) {
delay(1000);
changeDateTime();
transitionToPhase1(); // Return to Phase 1 after changing stocks
break;
}
if (digitalRead(button4) == LOW) {
delay(1000);
DisplaySetDateTimeFlag();
transitionToPhase1(); // Return to Phase 1 after changing stocks
break;
}
if (digitalRead(changePricesButton) == LOW) {
lcd.clear();
lcd.setCursor(0,0);
lcd.print("Cancelled...");
lcd.setCursor(0,1);
lcd.print("Please wait...");
delay(500);
transitionToPhase1();
break;
}
delay(100); // Add a small delay to prevent excessive CPU usage
}
}
updateInsertCoinPage();
// Re-enable buttons after 2 seconds if they were disabled
static unsigned long lastButtonDisableTime = 0;
if (!buttonsEnabled) {
if (currentMillis - lastButtonDisableTime >= buttonsdisabletime) {
buttonsEnabled = true; // Re-enable buttons
lastButtonDisableTime = currentMillis; // Reset the time
}
}
if (buzzFlag) {
Buzz(); // Activate the buzzer
buzzFlag = false; // Reset the flag after buzzing
}
}
void initializeHardware() {
initializeButtons();
//initializeStockSensors();
initializeVerifierPins();
initializeServos();
initializeLCD();
pinMode(coinSlot, INPUT_PULLUP);
pinMode(changePricesButton, INPUT_PULLUP);
}
void initializeButtons() {
for (int i = 0; i < sizeof(buttons) / sizeof(buttons[0]); i++) {
pinMode(buttons[i], INPUT_PULLUP);
}
}
//void initializeStockSensors() {
// for (int i = 0; i < sizeof(StockSensors) / sizeof(StockSensors[0]); i++) {
// pinMode(StockSensors[i], INPUT_PULLUP);
//}
//}
void initializeVerifierPins() {
for (int i = 0; i < sizeof(VerifierPins) / sizeof(VerifierPins[0]); i++) {
pinMode(VerifierPins[i], INPUT_PULLUP);
}
}
void initializeServos() {
for (int i = 0; i < sizeof(servoPins) / sizeof(servoPins[0]); i++) {
servos[i].attach(servoPins[i]); // Attach the servo control pins
moveToAngle(i, servoAngles[0]); // Move all servos to start angle
delay(500);
servos[i].detach(); // Detach the servo control pins
}
delay(3000);
}
void initializeLCD() {
lcd.init();
lcd.backlight();
lcd.clear();
}
void insertcoinpage() {
lcd.setCursor(0, 2);
lcd.print(" ");
delay(200);
lcd.setCursor(0, 2);
lcd.print(" Insert Coins");
delay(100);
printCredits();
}
void updateInsertCoinPage() {
unsigned long currentMillis = millis();
if (currentMillis - previousMillis >= BLINK_INTERVAL) {
previousMillis = currentMillis;
// Toggle the blinking state
isBlinking = !isBlinking;
lcd.setCursor(0, 2);
if (isBlinking) {
lcd.print(" Insert Coins ");
} else {
lcd.print(" ");
}
}
}
void printCredits() {
if (pulse > previousPulse) {
//Buzz();
previousPulse = pulse;
}
sprintf(lcdBuffer, "%s %d%s", prefix, pulse, ZZ);
lcd.setCursor(0, 3);
lcd.print(lcdBuffer);
// Serial print statement
Serial.print("Credits: ");
Serial.println(pulse);
}
void GetSukli() {
lcd.clear();
delay(200);
lcd.setCursor(0, 0);
lcd.print("PLEASE GET YOUR");
lcd.setCursor(0, 1);
lcd.print("CHANGE...");
delay(200);
}
void dispenseVariant(int index) {
if (buttonsEnabled && digitalRead(buttons[index]) == LOW) {
//checkStock(index);
if (pulse >= prices[index] && checkCurrentStock(index)) {
deactivateCoinslot();
detachInterrupt(digitalPinToInterrupt(coinSlot));
//Buzz();
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Dispensing Variant ");
//lcd.setCursor(0, 1);
//lcd.print(" ");
lcd.print(index + 1);
for (int i = 0; i < 2; i++) {
Buzz();
delay(200);
}
activateServo(index);
/*
pulse -= prices[index];
previousPulse = pulse;
// Print the price of the selected variant
//sprintf(lcdBuffer, "Price: PHP %d%s", prices[index], ZZ);
lcd.setCursor(0, 2);
lcd.print(lcdBuffer);
//Print the Change of the transaction
sprintf(lcdBuffer, "Change: PHP %d%s", pulse, ZZ);
lcd.setCursor(0, 3);
lcd.print(lcdBuffer);
*/
delay(50);
lcd.clear();
insertcoinpage();
printCredits();
delay(50);
for (int i = 0; i < 2; i++) {
Buzz();
delay(50);
// Capture pulse count before re-attaching interrupt
int finalPulse = pulse;
int postAttachPulse = finalPulse;
delay(500);
activateCoinslot();
attachInterrupt(digitalPinToInterrupt(coinSlot), coinISR, FALLING);
// Re-capture pulse count immediately after re-attaching interrupt
delay(200);
pulse = postAttachPulse;
}
} else if (pulse < prices[index] && checkCurrentStock(index)) {
// Play Sound 1
playSound(Sound1); //1. ANG ITEM NA ITO AY MERON PANG STOCK PERO KULANG ANG PERA MO
lcd.clear();
// Print "IN STOCK:" and the current stock count with "PCS"
lcd.setCursor(0, 0);
lcd.print(" Variant ");
lcd.print(index + 1);
lcd.setCursor(0, 2);
lcd.print(" IN STOCK: ");
lcd.print(stocks[index]); // Display the number of stocks available
lcd.print(" PCS"); // Add "PCS" as the unit
// Print the price of the selected variant
sprintf(lcdBuffer, " PRICE: PHP %d%s", prices[index], ZZ);
lcd.setCursor(0, 3);
lcd.print(lcdBuffer);
delay(3000);
lcd.clear();
// Print "Insufficient Balance" message
lcd.setCursor(0, 0);
lcd.print(" Insufficient");
lcd.setCursor(0, 1);
lcd.print(" Balance ");
// Buzz feedback for insufficient balance
for (int i = 0; i < 2; i++) {
BuzzLow();
delay(80);
}
delay(500);
lcd.clear();
insertcoinpage();
printCredits();
delay(100);
}
}
}
void transitionToPhase1() {
lcd.clear();
phase = 1;
insertcoinpage();
}
void transitionToPhase2() {
phase = 2;
lcd.clear();
loadPricesFromEEPROM(); // Load prices from EEPROM
}
void activateServo(int index) {
servos[index].attach(servoPins[index]); // Attach the servo control pins
operateServo(servoPins[index], index);
servos[index].detach();
}
void operateServo(int servoPin, int index) {
bool DoneDispensing = false;
int DispensedYosi = 0;
int attemptCounter = 0; // Initialize attempt counter
const int maxAttempts = 3; // Maximum number of attempts
unsigned long startMillis; // Variable to store the start time of the wait period
const unsigned long timeout = 2000; // Timeout period in milliseconds (2 seconds)
while (!DoneDispensing && attemptCounter < maxAttempts) {
attemptCounter++; // Increment attempt counter
// Move servo back and forth to shake the item loose
for (int i = 0; i < ServoShake; i++) {
moveToAngle(index, servoAngles[1]); // Move to mid angle
delay(200); // Hold for 0.2 seconds
moveToAngle(index, servoAngles[0]); // Move to start angle
delay(200);
}
moveToAngle(index, servoAngles[2]); // Move to end angle
//delay(200);
startMillis = millis(); // Get the current time
while (millis() - startMillis < timeout) { // Wait for the timeout period
if (digitalRead(VerifierPins[index]) == LOW) {
DispensedYosi = 1;
delay(500);
break; // Exit the loop if the item is detected
}
//delay(1); // Short delay to avoid excessive CPU usage
}
moveToAngle(index, servoAngles[0]); // Move back to start angle
delay(400);
if (DispensedYosi == 1) {
DoneDispensing = true;
pulse -= prices[index];
previousPulse = pulse;
stocks[index] --;
// Play Sound3
playSound(Sound3); //3. Cash Register (Kaching) - Sound Effect (HD)
sendSalesData(index + 1, prices[index]); //(variant, price)
// Print the price of the selected variant
sprintf(lcdBuffer, "Price: PHP %d%s", prices[index], ZZ);
lcd.setCursor(0, 2);
lcd.print(lcdBuffer);
// Print the change of the transaction
sprintf(lcdBuffer, "Change: PHP %d%s", pulse, ZZ);
lcd.setCursor(0, 3);
lcd.print(lcdBuffer);
delay(1000);
playSound(Sound4); //Pindutin ang button sa ibaba para makuha ang itong sukli. maraming salamat.
break;
}
if (attemptCounter >= maxAttempts && DispensedYosi == 0) {
Serial.println("Variant maybe out of stock or DISPENSER ERROR");
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("DISPENSER ERROR");
lcd.setCursor(0, 3);
lcd.print("Please try again.");
stocks[index] = 0;
// Play Sound 2
playSound(Sound2); //2. ANG ITEM NA ITO AY OUT OF STOCK
delay(1000);
break;
}
}
// Final cleanup
moveToAngle(index, servoAngles[0]); // Move back to start angle
delay(1500); // Prevent button bouncing
}
void moveToAngle(int servo, int angle) {
if (servo < 0 || servo >= sizeof(servos)/sizeof(servos[0])) {
Serial.print("Invalid servo index: ");
Serial.println(servo);
return;
}
servos[servo].write(angle);
Serial.print("Servo ");
Serial.print(servo);
Serial.print(" moved to ");
Serial.print(angle);
Serial.println(" degrees");
}
void Buzz() {
//delay(30);
tone(buzzer, 3000); // Send 3 kHz tone to pin 13 (buzzer)
delay(30);
noTone(buzzer);
}
void BuzzLow() {
tone(buzzer, 250); // Send 250 Hz tone to pin 13 (buzzer)
delay(30);
noTone(buzzer); // Stop the tone
}
void changePrices() {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Entering Change");
lcd.setCursor(0, 1);
lcd.print("Price Mode...");
for (int i = 0; i < 2; i++) {
Buzz();
delay(80);
}
delay(1000);
for (int i = 0; i < sizeof(prices) / sizeof(prices[0]); i++) {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print(" Variant ");
lcd.print(i + 1);
lcd.print(" Price: ");
lcd.setCursor(0, 2);
lcd.print(" PHP ");
lcd.print(prices[i]);
lcd.print(".00");
Buzz();
while (true) {
if (digitalRead(button1) == LOW) {
Buzz();
if(prices[i] > 0){
prices[i] -= 1;
}
delay(70); // Debounce delay
lcd.clear();
lcd.setCursor(0, 0);
lcd.print(" Variant ");
lcd.print(i + 1);
lcd.print(" Price: ");
lcd.setCursor(0, 2);
lcd.print(" PHP ");
lcd.print(prices[i]);
lcd.print(".00");
}
if (digitalRead(button2) == LOW) {
Buzz();
prices[i] += 1;
delay(70); // Debounce delay
lcd.clear();
lcd.setCursor(0, 0);
lcd.print(" Variant ");
lcd.print(i + 1);
lcd.print(" Price: ");
lcd.setCursor(0, 2);
lcd.print(" PHP ");
lcd.print(prices[i]);
lcd.print(".00");
}
if (digitalRead(button3) == LOW) {
Buzz();
delay(100);
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Saving price...");
delay(100);
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Prices saved");
delay(100);
for (int i = 0; i < 2; i++) {
Buzz();
delay(80);
}
break;
}
}
}
}
void savePricesToEEPROM() {
for (int i = 0; i < sizeof(prices) / sizeof(prices[0]); i++) {
EEPROM.write(i, prices[i]);
delay(100);
}
}
void loadPricesFromEEPROM() {
for (int i = 0; i < sizeof(prices) / sizeof(prices[0]); i++) {
prices[i] = EEPROM.read(i);
}
}
void coinISR() {
static unsigned long lastInterruptTime = 0;
unsigned long interruptTime = millis();
if (interruptTime - lastInterruptTime > DEBOUNCE_DELAY) {
pulse++;
lastInterruptTime = interruptTime;
// Disable buttons when coin is detected
buttonsEnabled = false;
buzzFlag = true; // Set the buzz flag instead of calling Buzz() directly
}
}
void initializePins() {
// Initialize the button pin as an input
pinMode(sukliButtonPin, INPUT_PULLUP);
// Initialize the relay pin as an output
pinMode(RELAY_PIN, OUTPUT);
// Initialize the relay pin as an output
pinMode(RELAY_PIN1, OUTPUT);
// Initialize the signal pin as an input with pull-UP resistor
pinMode(SIGNAL_PIN, INPUT_PULLUP);
// Set the relay pin to HIGH (relay off)
digitalWrite(RELAY_PIN, LOW);
// Set the relay pin to HIGH (relay ofF)
digitalWrite(RELAY_PIN1, HIGH);
Serial.println("Pins initialized.");
}
void Sukli() {
// Read the state of the button
sukliButtonState = digitalRead(sukliButtonPin);
// Check if the button is pressed and dispensing is not already in progress
if (buttonsEnabled && sukliButtonState == LOW && lastsukliButtonState == HIGH && !dispensingInProgress && pulse > 0) {
detachInterrupt(digitalPinToInterrupt(coinSlot));
deactivateCoinslot();
dispensingInProgress = true;
coinsDispensed = 0; // Reset the coin counter
systemReady = false; // Reset system ready flag
int initialPulse = pulse; // Track the initial pulse count
coinsToBeDispensed = pulse; // Set the number of coins to be dispensed based on pulse
activateRelay();
// Add a small delay to let the relay stabilize
delay(100);
systemReady = true;
lastHopperPulseTime = millis(); // Initialize last hopper pulse time
// Attach the interrupt to count pulses
attachInterrupt(digitalPinToInterrupt(SIGNAL_PIN), Hoppercount, FALLING);
unsigned long startTime = millis(); // Track the start time
GetSukli();
// Loop until dispensing is complete or timeout
while (dispensingInProgress) {
// Check if dispensing is in progress and update the coins dispensed
//GetSukli();
// Check if the required coins have been dispensed
if (coinsDispensed >= coinsToBeDispensed) {
deactivateRelay();
dispensingInProgress = false; // Reset the dispensing flag
systemReady = false; // Reset system ready flag
break;
}
// If recent pulse detected, acknowledge and reset flag
if (recentHopperPulseFlag) {
lastHopperPulseTime = millis(); // Update the last hopper pulse time
recentHopperPulseFlag = false; // Reset flag for the next pulse
}
// Check if the timeout duration has passed with no pulses
if (!recentHopperPulseFlag && (millis() - lastHopperPulseTime > 3000)) {
// Stop hopper if timeout has passed
deactivateRelay();
delay(2000);
detachInterrupt(digitalPinToInterrupt(SIGNAL_PIN));
// Capture pulse count before re-attaching interrupt
int finalcoinsDispensed = coinsDispensed;
int postAttachcoinsDispensed = finalcoinsDispensed;
delay(500);
attachInterrupt(digitalPinToInterrupt(SIGNAL_PIN), Hoppercount, FALLING);
// Re-capture pulse count immediately after re-attaching interrupt
delay(200);
coinsDispensed = postAttachcoinsDispensed;
Serial.println("Timeout reached. Hopper may be empty.");
displayTimeoutMessage();
dispensingInProgress = false; // Reset the dispensing flag
systemReady = false; // Reset system ready flag
displayCountdown(3);
break;
}
/*
// Check if it's time to pause for every 10 coins dispensed
if (coinsDispensed > 0 && coinsDispensed % 10 == 0) {
detachInterrupt(digitalPinToInterrupt(SIGNAL_PIN));
delay(100);
deactivateRelay();
// Capture pulse count before re-attaching interrupt
int finalcoinsDispensed = coinsDispensed;
int postAttachcoinsDispensed = finalcoinsDispensed;
delay(1500);
attachInterrupt(digitalPinToInterrupt(SIGNAL_PIN), Hoppercount, FALLING);
delay(100);
activateRelay();
// Re-capture pulse count immediately after re-attaching interrupt
delay(200);
coinsDispensed = postAttachcoinsDispensed;
// Skip past the 10-coin boundary to prevent continuous toggling
while (coinsDispensed % 10 == 0) {
delay(100); // Short delay to avoid busy-waiting
}
lastPulseTime = millis(); // Reset the last pulse time after pausing
}
*/
/*
// Check for the 5-second timeout since the last pulse
if (millis() - lastPulseTime >= 3000) {
detachInterrupt(digitalPinToInterrupt(SIGNAL_PIN));
delay(100);
deactivateRelay();
// Capture pulse count before re-attaching interrupt
int finalcoinsDispensed = coinsDispensed;
int postAttachcoinsDispensed = finalcoinsDispensed;
delay(500);
attachInterrupt(digitalPinToInterrupt(SIGNAL_PIN), Hoppercount, FALLING);
// Re-capture pulse count immediately after re-attaching interrupt
delay(200);
coinsDispensed = postAttachcoinsDispensed;
Serial.println("Timeout reached. Hopper may be empty.");
displayTimeoutMessage();
dispensingInProgress = false; // Reset the dispensing flag
systemReady = false; // Reset system ready flag
displayCountdown(3);
break;
}
*/
}
if (coinsDispensed >= coinsToBeDispensed) {
lcd.clear();
lcd.setCursor(0, 0); // Set cursor to the beginning of the first line
lcd.print("Coins Dispensed: ");
lcd.print(coinsDispensed);
delay(1500);
}
// Calculate the final pulse count after dispensing
pulse = initialPulse - coinsDispensed;
if (pulse < 0) {
pulse = 0;
}
coinsToBeDispensed = 0;
coinsDispensed = 0;
delay(1000);
lcd.clear();
insertcoinpage();
printCredits();
Serial.println("Dispensing complete. Relay OFF.");
Serial.print("credits: ");
Serial.println(pulse);
delay(500);
// Capture pulse count before re-attaching interrupt
int finalPulse = pulse;
int postAttachPulse = finalPulse;
delay(500);
activateCoinslot();
attachInterrupt(digitalPinToInterrupt(coinSlot), coinISR, FALLING);
// Re-capture pulse count immediately after re-attaching interrupt
delay(200);
pulse = postAttachPulse;
}
// Update the last button state
lastsukliButtonState = sukliButtonState;
}
void activateRelay() {
// Set the relay pin to LOW (relay on)
digitalWrite(RELAY_PIN, HIGH);
Serial.println("Relay ON.");
// Add a small delay to let the relay stabilize
delay(100);
// Set system ready flag after delay
systemReady = true;
}
void deactivateRelay() {
// Set the relay pin to HIGH (relay off)
digitalWrite(RELAY_PIN, LOW);
Serial.println("Relay OFF.");
}
void activateCoinslot() {
// Set the relay pin to LOW (relay on)
digitalWrite(RELAY_PIN1, HIGH);
Serial.println("Coinslot ON.");
// Add a small delay to let the relay stabilize
delay(100);
// Set system ready flag after delay
systemReady = true;
}
void deactivateCoinslot() {
// Set the relay pin to HIGH (relay off)
digitalWrite(RELAY_PIN1, LOW);
Serial.println("Coinslot OFF.");
}
// Function to display timeout message on the LCD
void displayTimeoutMessage() {
lcd.clear();
lcd.setCursor(0, 0); // Set cursor to the beginning of the first line
lcd.print("Coins Dispensed: ");
lcd.print(coinsDispensed);
delay(3000);
lcd.setCursor(0, 0); // Set cursor to the beginning of the first line
lcd.print("Coin dispenser maybe");
lcd.setCursor(0, 1); // Set cursor to the beginning of the second line
lcd.print("empty...");
lcd.setCursor(0, 3); // Set cursor to the beginning of the second line
lcd.print("Please try again.");
//delay(1000);
}
/*void checkStock(int index) {
if (digitalRead(StockSensors[index]) == HIGH){
InStock = false;
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("STATUS: OUT OF STOCK");
// Play Sound 2
playSound(Sound2); //2. ANG ITEM NA ITO AY OUT OF STOCK
for (int i = 0; i < 2; i++) {
BuzzLow();
delay(80);
}
delay(2000);
lcd.clear();
}
else if (digitalRead(StockSensors[index]) == LOW){
InStock = true;
}
}
*/
// Function to display a countdown timer on the LCD
void displayCountdown(int start) {
lcd.setCursor(0, 3); // Set cursor to the beginning of the first line
lcd.print(" ");
for (int i = start; i >= 0; i--) {
lcd.setCursor(0, 3); // Set cursor to the beginning of the first line
lcd.print("Please try again. ");
lcd.print(i); // Print the current number in the countdown
delay(1000); // Wait for 1 second before updating the display
}
}
void Hoppercount() {
static unsigned long lastInterruptTime = 0;
unsigned long interruptTime = millis();
//lastPulseTime = millis();
// Debounce delay (adjust as needed)
if (interruptTime - lastInterruptTime > delayISR) {
// Check if the system is ready to count coins
if (systemReady) {
coinsDispensed++;
Serial.print("Coins dispensed: ");
Serial.println(coinsDispensed);
recentHopperPulseFlag = true; // Set flag indicating a recent pulse
} else {
Serial.println("System not ready. Ignoring false trigger.");
}
if (coinsDispensed >= coinsToBeDispensed) {
deactivateRelay();
dispensingInProgress = false; // Reset the dispensing flag
systemReady = false; // Reset system ready flag
//break;
}
}
lastInterruptTime = interruptTime;
}
// Function to initialize sound pins as OUTPUT and set them to HIGH (inactive)
void setupSoundPins() {
pinMode(Sound1, OUTPUT);
pinMode(Sound2, OUTPUT);
pinMode(Sound3, OUTPUT);
pinMode(Sound4, OUTPUT);
// Initialize pins to HIGH (inactive state)
digitalWrite(Sound1, HIGH);
digitalWrite(Sound2, HIGH);
digitalWrite(Sound3, HIGH);
digitalWrite(Sound4, HIGH);
}
// Function to play a sound by turning a specific pin HIGH for 500ms, then LOW
void playSound(int pin) {
digitalWrite(pin, LOW); // Turn on (active)
delay(500); // Wait for 500ms
digitalWrite(pin, HIGH); // Turn off (inactive)
}
void setupRTC() {
if (!rtc.begin()) {
Serial.println("Couldn't find RTC");
//while (1); // Halt the program
}
if (rtc.lostPower()) {
Serial.println("RTC lost power, setting the time!");
// Add 15 seconds to the compile time when setting the RTC
DateTime compiledTime = DateTime(F(__DATE__), F(__TIME__));
compiledTime = compiledTime + TimeSpan(0, 0, 0, 15); // Adds 15 seconds
rtc.adjust(compiledTime);
}
Serial.println("RTC initialized successfully");
}
void processRTC() {
Serial.println(getDateTime());
displayDateTimeLcd();
}
String getDateTime() {
DateTime now = rtc.now();
char buffer[30];
sprintf(buffer, "%02d/%02d/%d, %02d:%02d:%02d %s",
now.month(), now.day(), now.year(),
now.hour() > 12 ? now.hour() - 12 : now.hour(),
now.minute(),
now.second(),
now.hour() >= 12 ? "PM" : "AM");
return String(buffer);
}
void sendSalesData(int variant, int price) {
String currentTime = getDateTime();
String salesData = currentTime + "," + String(variant) + "," + String("1") + "," + String(price);
Serial.println(salesData); // Serial.write() can also be used if transmitting raw bytes
Serial2.println(salesData); // Serial.write() can also be used if transmitting raw bytes
}
void changeStocks() {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Entering Change");
lcd.setCursor(0, 1);
lcd.print("Stock Mode...");
for (int i = 0; i < 2; i++) {
Buzz();
delay(80);
}
delay(1000);
// Loop to manually adjust stock levels for each variant
for (int i = 0; i < sizeof(stocks) / sizeof(stocks[0]); i++) {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print(" Variant ");
lcd.print(i + 1);
lcd.print(" Stock: ");
lcd.setCursor(0, 2);
lcd.print(" ");
lcd.print(stocks[i]);
lcd.print(" PCS");
Buzz();
while (true) {
// Subtract stock when button1 is pressed
if (digitalRead(button1) == LOW) {
Buzz();
if (stocks[i] > 0) {
stocks[i] -= 1;
}
delay(70); // Debounce delay
lcd.clear();
lcd.setCursor(0, 0);
lcd.print(" Variant ");
lcd.print(i + 1);
lcd.print(" Stock: ");
lcd.setCursor(0, 2);
lcd.print(" ");
lcd.print(stocks[i]);
lcd.print(" PCS");
}
// Add stock when button2 is pressed
if (digitalRead(button2) == LOW) {
Buzz();
stocks[i] += 1;
delay(70); // Debounce delay
lcd.clear();
lcd.setCursor(0, 0);
lcd.print(" Variant ");
lcd.print(i + 1);
lcd.print(" Stock: ");
lcd.setCursor(0, 2);
lcd.print(" ");
lcd.print(stocks[i]);
lcd.print(" PCS");
}
// Save and proceed to the next variant when button3 is pressed
if (digitalRead(button3) == LOW) {
Buzz();
delay(250);
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Saving stock...");
delay(250);
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Stock saved");
delay(250);
for (int j = 0; j < 2; j++) {
Buzz();
delay(80);
}
break; // Exit the loop to proceed to the next variant
}
}
}
}
bool checkCurrentStock(int variantIndex) {
// Check the stock for the specified variant index using the stocks array
if (stocks[variantIndex] > 0) {
// Stock is available
return true;
} else {
// No stock available
lcd.clear();
lcd.setCursor(0, 0);
lcd.print(" Variant ");
lcd.print(variantIndex + 1);
lcd.setCursor(0, 2);
lcd.print("STATUS: OUT OF STOCK");
// Print the price of the selected variant
sprintf(lcdBuffer, " PRICE: PHP %d%s", prices[variantIndex], ZZ);
lcd.setCursor(0, 3);
lcd.print(lcdBuffer);
// Play Sound 2
playSound(Sound2); // Out of stock sound
for (int i = 0; i < 2; i++) {
BuzzLow();
delay(80);
}
delay(2000);
lcd.clear();
return false; // Stock is not available
}
}
void displayAdminMenu() {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("1. Change Prices");
lcd.setCursor(0, 1);
lcd.print("2. Change Stocks");
lcd.setCursor(0, 2);
lcd.print("3. Change Date/Time");
lcd.setCursor(0, 3);
lcd.print("4. Display Date/Time");
delay(1200);
}
void DisplayDateTime ()
{
unsigned long currentMillis = millis();
if (status == 0) {
// Displaying time
if (currentMillis - previousMillisClock >= (timeDisplayDuration*1000)) {
previousMillisClock = currentMillis;
lcd.clear();
}
// We show the current date and time
DateTime now = rtc.now();
lcd.setCursor(0, 1);
lcd.print("Hour: ");
if (now.hour()<=9)
{
lcd.print("0");
}
lcd.print(now.hour(), DEC);
hourupg=now.hour();
lcd.print(":");
if (now.minute()<=9)
{
lcd.print("0");
}
lcd.print(now.minute(), DEC);
minupg=now.minute();
lcd.print(":");
if (now.second()<=9)
{
lcd.print("0");
}
lcd.print(now.second(), DEC);
lcd.setCursor(0, 0);
lcd.print("Date: ");
if (now.month()<=9)
{
lcd.print("0");
}
lcd.print(now.month(), DEC);
monthupg=now.month();
lcd.print("/");
if (now.day()<=9)
{
lcd.print("0");
}
lcd.print(now.day(), DEC);
dayupg=now.day();
lcd.print("/");
lcd.print(now.year(), DEC);
yearupg=now.year();
}
}
void DisplaySetHour()
{
// time setting
lcd.clear();
DateTime now = rtc.now();
if(digitalRead(P2) == LOW)
{
if(hourupg==23)
{
hourupg=0;
}
else
{
hourupg=hourupg+1;
}
}
if(digitalRead(P3) == LOW)
{
if(hourupg==0)
{
hourupg=23;
}
else
{
hourupg=hourupg-1;
}
}
lcd.setCursor(0,0);
lcd.print("Set hour:");
lcd.setCursor(0,1);
lcd.print(hourupg,DEC);
delay(btndelay);
}
void DisplaySetMinute()
{
// Setting the minutes
lcd.clear();
if(digitalRead(P2) == LOW)
{
if (minupg==59)
{
minupg=0;
}
else
{
minupg=minupg+1;
}
}
if(digitalRead(P3) == LOW)
{
if (minupg==0)
{
minupg=59;
}
else
{
minupg=minupg-1;
}
}
lcd.setCursor(0,0);
lcd.print("Set Minutes:");
lcd.setCursor(0,1);
lcd.print(minupg,DEC);
delay(btndelay);
}
void DisplaySetYear()
{
// setting the year
lcd.clear();
if(digitalRead(P2) == LOW)
{
yearupg=yearupg+1;
}
if(digitalRead(P3) == LOW)
{
if(yearupg==0){
yearupg=0;
}
else{
yearupg=yearupg-1;
}
}
lcd.setCursor(0,0);
lcd.print("Set Year:");
lcd.setCursor(0,1);
lcd.print(yearupg,DEC);
delay(btndelay);
}
void DisplaySetMonth()
{
// Setting the month
if (monthupg==0)
{
monthupg=1;
}
lcd.clear();
if(digitalRead(P2) == LOW)
{
if (monthupg==12)
{
monthupg=1;
}
else
{
monthupg=monthupg+1;
}
}
if(digitalRead(P3) == LOW)
{
if (monthupg==1 || monthupg==0)
{
monthupg=12;
}
else
{
monthupg=monthupg-1;
}
}
lcd.setCursor(0,0);
lcd.print("Set Month:");
lcd.setCursor(0,1);
lcd.print(monthupg,DEC);
delay(btndelay);
}
void DisplaySetDay()
{
// Setting the day
if (dayupg==0)
{
dayupg=1;
}
lcd.clear();
if(digitalRead(P2) == LOW)
{
if (dayupg==31)
{
dayupg=1;
}
else
{
dayupg=dayupg+1;
}
}
if(digitalRead(P3) == LOW)
{
if (dayupg==1 || dayupg==0)
{
dayupg=31;
}
else
{
dayupg=dayupg-1;
}
}
lcd.setCursor(0,0);
lcd.print("Set Day:");
lcd.setCursor(0,1);
lcd.print(dayupg,DEC);
delay(btndelay);
}
void StoreAgg()
{
// Variable saving
lcd.clear();
lcd.setCursor(0,0);
lcd.print("SAVING IN");
lcd.setCursor(0,1);
lcd.print("PROGRESS");
rtc.adjust(DateTime(yearupg,monthupg,dayupg,hourupg,minupg,0));
delay(btndelay);
}
void changeDateTime() {
menu = 1; // Initialize menu
unsigned long lastPressTime = 0; // For debouncing
const unsigned long debounceDelay = 200; // Adjust debounce delay as needed
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Entering Change");
lcd.setCursor(0, 1);
lcd.print("Date/Time Mode...");
delay(2000);
while (menu > 0) {
// Display the current menu state
switch (menu) {
case 1:
DisplaySetMonth(); // Set Month
break;
case 2:
DisplaySetDay(); // Set Day
break;
case 3:
DisplaySetYear(); // Set Year
break;
case 4:
DisplaySetHour(); // Set Hour
break;
case 5:
DisplaySetMinute(); // Set Minute
break;
case 6:
StoreAgg(); // Save changes
delay(500);
menu = 0; // Exit the loop
break;
default:
menu = 0; // Exit the loop for invalid menu states
break;
}
// Check if the SET button is pressed and debounce
if (digitalRead(P1) == LOW) {
unsigned long currentTime = millis();
if (currentTime - lastPressTime > debounceDelay) {
lastPressTime = currentTime; // Update last press time
menu++; // Increment menu
delay(1000);
}
}
delay(btndelay); // Add a delay to avoid rapid menu switching
}
}
// Function to display the current date and time in 12-hour format on LCD
void displayDateTimeLcd() {
if(displayDateTimeflag){
DateTime now = rtc.now();
// Convert time to 12-hour format
int hour = now.hour();
String period = "AM";
if (hour >= 12) {
period = "PM";
if (hour > 12) hour -= 12; // Convert to 12-hour format
} else if (hour == 0) {
hour = 12; // Handle midnight (00:00)
}
// Format the date and time as "MM/DD/YYYY H:MM AM/PM"
String dateTime = String(now.month()) + "/" +
String(now.day()) + "/" +
String(now.year()) + " " +
String(hour) + ":" +
(now.minute() < 10 ? "0" : "") + String(now.minute()) + " " +
period;
// Display the date and time on the first line
lcd.setCursor(0, 0); // Set cursor to the first row
lcd.print(" " + dateTime); // Print the formatted date and time
}
}
void DisplaySetDateTimeFlag()
{
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Entering Display");
lcd.setCursor(0, 1);
lcd.print("Date/Time Mode...");
delay(2000);
lcd.clear();
while (true) {
if (digitalRead(button1) == LOW) {
displayDateTimeflag = true;
}
if (digitalRead(button2) == LOW) {
displayDateTimeflag = false;
}
if (digitalRead(button3) == LOW) {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Saving Setting...");
delay(500);
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Setting saved");
delay(500);
break;
}
lcd.setCursor(0, 0);
lcd.print("Display Date/Time:");
lcd.setCursor(0, 1);
if(displayDateTimeflag)
{
lcd.print("ON ");
}
else
{
lcd.print("OFF");
}
}
}