hello everyone I just want to ask for help with my project which is a 3 in 1 carwash vending machine. I am using an Arduino Uno, I2C 20x4, 3 buttons, and 3 relays for Water, Soap, and Blower, along with a coin slot. My problem with the code is that it shows a credit balance: when I press the buttons, it doesn't give time to each relay. I've been troubleshooting this for almost 4 weeks and it still doesn't work. I'm just a beginner in coding, and I hope someone can help me for my personal use. below is the code i am using.
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
// LCD setup
LiquidCrystal_I2C lcd(0x27, 20, 4);
// Pin assignments
const int coinSlotPin = 2; // Pin for the coin slot sensor
const int waterButton = 3;
const int soapButton = 4;
const int blowerButton = 5;
const int relayWater = 6;
const int relaySoap = 7;
const int relayBlower = 8;
const int buzzer = 9; // Buzzer pin
// Default times
const int DEFAULT_WATER_TIME = 60;
const int DEFAULT_SOAP_TIME = 45;
const int DEFAULT_BLOWER_TIME = 75;
// Pause limit
const int MAX_PAUSE_COUNT = 3;
// Timing constants
const unsigned long BLINK_INTERVAL = 450; // Blink interval in milliseconds
const unsigned long BUZZER_CHECK_INTERVAL = 1000; // Buzzer check interval in milliseconds
const unsigned long COIN_DEBOUNCE_TIME = 300; // Coin slot debounce time in milliseconds
// Buzzer intervals (in seconds)
const int BUZZER_INTERVALS[] = {10, 6, 2};
const int BUZZER_INTERVAL_COUNT = sizeof(BUZZER_INTERVALS) / sizeof(BUZZER_INTERVALS[0]);
// System state
int balance = 0;
int waterTime = 0, soapTime = 0, blowerTime = 0;
bool waterActive = false, soapActive = false, blowerActive = false;
bool waterPaused = false, soapPaused = false, blowerPaused = false;
int waterPauseCount = 0, soapPauseCount = 0, blowerPauseCount = 0;
unsigned long lastCoinTime = 0, lastUpdateTime = 0, lastButtonBlinkTime = 0;
unsigned long lastBuzzerTime = 0, lastInsertCoinBlinkTime = 0; // Separate variable for --Insert Coin!-- blinking
bool showInsertCoin = true, showPressButton = true;
bool screenInitialized = false;
// Separate blinking states for paused timers
bool waterBlinkState = false, soapBlinkState = false, blowerBlinkState = false;
unsigned long waterLastBlinkTime = 0, soapLastBlinkTime = 0, blowerLastBlinkTime = 0;
// Coin counting variables
volatile int coinsInserted = 0; // Volatile because it's modified in an interrupt
const int COIN_VALUE = 1; // Value of each coin in pesos
// Display update flags
bool balanceChanged = true; // Flag to track balance changes
bool timeChanged = true; // Flag to track timer changes
// Button debouncing variables
unsigned long lastDebounceTimeWater = 0; // Separate debounce timestamps for each button
unsigned long lastDebounceTimeSoap = 0;
unsigned long lastDebounceTimeBlower = 0;
const unsigned long DEBOUNCE_DELAY = 50; // Debounce delay in milliseconds
// Last button states for debouncing
int lastWaterButtonState = HIGH;
int lastSoapButtonState = HIGH;
int lastBlowerButtonState = HIGH;
void setup() {
// Initialize pins
pinMode(coinSlotPin, INPUT_PULLUP);
pinMode(waterButton, INPUT_PULLUP);
pinMode(soapButton, INPUT_PULLUP);
pinMode(blowerButton, INPUT_PULLUP);
pinMode(relayWater, OUTPUT);
pinMode(relaySoap, OUTPUT);
pinMode(relayBlower, OUTPUT);
pinMode(buzzer, OUTPUT);
// Initialize relays to OFF state (HIGH for low-trigger relays)
digitalWrite(relayWater, HIGH);
digitalWrite(relaySoap, HIGH);
digitalWrite(relayBlower, HIGH);
// Initialize LCD
lcd.init();
lcd.backlight();
displayInitialScreen();
// Attach interrupt for coin slot
attachInterrupt(digitalPinToInterrupt(coinSlotPin), Add, FALLING);
}
void loop() {
unsigned long currentMillis = millis();
// Handle initial screen and blinking "Insert Coin" message
if (balance == 0 && waterTime == 0 && soapTime == 0 && blowerTime == 0) {
if (!screenInitialized) {
displayInitialScreen();
screenInitialized = true;
}
// Independent blinking for --Insert Coin!--
if (currentMillis - lastInsertCoinBlinkTime > BLINK_INTERVAL) {
lastInsertCoinBlinkTime = currentMillis;
showInsertCoin = !showInsertCoin;
blinkInsertCoin();
}
} else {
screenInitialized = false;
}
// Handle coin insertion
if (coinsInserted > 0) {
balance += coinsInserted * COIN_VALUE; // Add the value of inserted coins to the balance
coinsInserted = 0; // Reset the coin counter
balanceChanged = true; // Set flag to update balance display
displayActiveScreen(); // Update the display
}
// Check buzzer timing
checkBuzzerTiming();
// Update timers every second
if (currentMillis - lastUpdateTime >= 1000) {
lastUpdateTime = currentMillis;
updateTime();
}
// Handle button presses and blinking "Press a Button" message
if (balance >= 5 || waterTime > 0 || soapTime > 0 || blowerTime > 0) {
if (currentMillis - lastButtonBlinkTime > BLINK_INTERVAL) {
lastButtonBlinkTime = currentMillis;
showPressButton = !showPressButton;
blinkPressButton();
}
// Call handleButton with separate debounce timestamps and last button states
handleButton(waterButton, relayWater, waterTime, waterActive, waterPaused, waterPauseCount, DEFAULT_WATER_TIME, lastDebounceTimeWater, lastWaterButtonState);
handleButton(soapButton, relaySoap, soapTime, soapActive, soapPaused, soapPauseCount, DEFAULT_SOAP_TIME, lastDebounceTimeSoap, lastSoapButtonState);
handleButton(blowerButton, relayBlower, blowerTime, blowerActive, blowerPaused, blowerPauseCount, DEFAULT_BLOWER_TIME, lastDebounceTimeBlower, lastBlowerButtonState);
}
// Handle blinking for paused timers
handlePausedBlinking();
// Ensure --Insert Coin!-- is displayed when balance is 0
if (balance == 0) {
if (currentMillis - lastInsertCoinBlinkTime > BLINK_INTERVAL) {
lastInsertCoinBlinkTime = currentMillis;
showInsertCoin = !showInsertCoin;
blinkInsertCoin();
}
}
}
// Interrupt Service Routine (ISR) for coin insertion
void Add() {
coinsInserted++; // Increment the coin counter
}
// Check and sound buzzer at specific intervals
void checkBuzzerTiming() {
unsigned long currentMillis = millis();
if (currentMillis - lastBuzzerTime >= BUZZER_CHECK_INTERVAL) {
lastBuzzerTime = currentMillis;
if (waterActive && !waterPaused) checkAndSoundBuzzer(waterTime);
if (soapActive && !soapPaused) checkAndSoundBuzzer(soapTime);
if (blowerActive && !blowerPaused) checkAndSoundBuzzer(blowerTime);
}
}
void checkAndSoundBuzzer(int timeLeft) {
static int lastBuzzerInterval = -1; // Keep track of the last interval the buzzer was sounded
for (int i = 0; i < BUZZER_INTERVAL_COUNT; i++) {
if (timeLeft == BUZZER_INTERVALS[i] && timeLeft != lastBuzzerInterval) {
soundBuzzer();
lastBuzzerInterval = timeLeft; // Update the last buzzer interval
break; // Exit after sounding the buzzer
}
}
// Reset the last buzzer interval if the timeLeft is not in the intervals
if (timeLeft != BUZZER_INTERVALS[0] && timeLeft != BUZZER_INTERVALS[1] && timeLeft != BUZZER_INTERVALS[2]) {
lastBuzzerInterval = -1;
}
}
void soundBuzzer() {
tone(buzzer, 2000, 500); // 2000 Hz for 500ms
}
void updateTime() {
bool displayNeedsUpdate = false;
if (waterActive && !waterPaused && waterTime > 0) {
waterTime--;
displayNeedsUpdate = true;
}
if (soapActive && !soapPaused && soapTime > 0) {
soapTime--;
displayNeedsUpdate = true;
}
if (blowerActive && !blowerPaused && blowerTime > 0) {
blowerTime--;
displayNeedsUpdate = true;
}
if (waterTime == 0 && waterActive) {
waterActive = false;
digitalWrite(relayWater, HIGH);
}
if (soapTime == 0 && soapActive) {
soapActive = false;
digitalWrite(relaySoap, HIGH);
}
if (blowerTime == 0 && blowerActive) {
blowerActive = false;
digitalWrite(relayBlower, HIGH);
}
if (balance == 0 && waterTime == 0 && soapTime == 0 && blowerTime == 0) {
displayInitialScreen();
} else if (displayNeedsUpdate) {
timeChanged = true; // Set flag to update time display
updateTimeDisplay();
}
}
void handleButton(int button, int relay, int &time, bool &active, bool &paused, int &pauseCount, int defaultTime, unsigned long &lastDebounceTime, int &lastButtonState) {
int buttonState = digitalRead(button); // Read the current button state
unsigned long currentMillis = millis();
// Check if the button state has changed (due to noise or pressing)
if (buttonState != lastButtonState) {
lastDebounceTime = currentMillis; // Reset the debounce timer
}
// If the button state has been stable for longer than the debounce delay, consider it a valid press
if ((currentMillis - lastDebounceTime) > DEBOUNCE_DELAY) {
// If the button state has changed, handle the press
if (buttonState == LOW && lastButtonState == HIGH) {
// Button is pressed and debounced
if (!active && balance >= 5) {
// Activate the function if not already active
balance -= 5;
time += defaultTime;
active = true;
paused = false;
pauseCount = 0;
digitalWrite(relay, LOW); // Turn on the relay
balanceChanged = true; // Set flag to update balance display
timeChanged = true; // Set flag to update time display
Serial.println("Button pressed - Relay activated"); // Debugging
} else if (active) {
// Handle pausing/resuming
if (!paused && pauseCount < MAX_PAUSE_COUNT) {
paused = true;
pauseCount++;
digitalWrite(relay, HIGH); // Turn off the relay (pause)
Serial.println("Paused"); // Debugging
} else if (paused) {
paused = false;
digitalWrite(relay, LOW); // Turn on the relay (resume)
Serial.println("Resumed"); // Debugging
}
timeChanged = true; // Set flag to update time display
}
}
}
// Save the current button state for the next loop iteration
lastButtonState = buttonState;
}
void displayInitialScreen() {
lcd.setCursor(3, 0);
lcd.print(" 3in1 CARWASH");
lcd.setCursor(0, 2);
lcd.print("WATER SOAP BLOWER");
lcd.setCursor(0, 3);
lcd.print("00:00 00:00 00:00");
lcd.setCursor(0, 1);
lcd.print(" --Insert Coin!--");
}
void blinkInsertCoin() {
lcd.setCursor(0, 1);
if (showInsertCoin) {
lcd.print(" --Insert Coin!-- ");
} else {
lcd.print(" ");
}
}
void displayActiveScreen() {
lcd.setCursor(3, 0);
if (balance > 0) {
lcd.print("PRESS A BUTTON");
} else {
lcd.print(" 3in1 CARWASH ");
}
lcd.setCursor(2, 1);
if (balance > 0) {
lcd.print(" Balance: ");
lcd.print(balance);
} else {
lcd.print("--Insert Coin!--");
}
lcd.setCursor(0, 2);
lcd.print("WATER SOAP BLOWER");
updateTimeDisplay();
blinkPressButton();
}
void blinkPressButton() {
lcd.setCursor(3, 0);
if (balance > 0) {
lcd.print(showPressButton ? "PRESS A BUTTON" : " ");
} else {
lcd.print(" 3in1 CARWASH ");
}
}
void updateTimeDisplay() {
if (balanceChanged) {
lcd.setCursor(2, 1);
lcd.print(" "); // Clear the entire line
lcd.setCursor(2, 1); // Reset cursor position
if (balance > 0) {
lcd.print(" Balance: ");
lcd.print(balance);
} else {
lcd.print("--Insert Coin!--");
}
balanceChanged = false;
}
if (timeChanged) {
lcd.setCursor(0, 3);
displayTime(waterTime, waterPaused, 0, waterBlinkState, waterLastBlinkTime);
lcd.setCursor(7, 3);
displayTime(soapTime, soapPaused, 7, soapBlinkState, soapLastBlinkTime);
lcd.setCursor(14, 3);
displayTime(blowerTime, blowerPaused, 14, blowerBlinkState, blowerLastBlinkTime);
timeChanged = false;
}
}
void displayTime(int seconds, bool paused, int column, bool &blinkState, unsigned long &lastBlinkTime) {
if (paused) { // Only apply blinking logic if the timer is paused
if (millis() - lastBlinkTime > BLINK_INTERVAL) {
lastBlinkTime = millis();
blinkState = !blinkState;
}
if (blinkState) {
lcd.setCursor(column, 3);
lcd.print(" ");
} else {
lcd.setCursor(column, 3);
lcd.print(formatTime(seconds));
}
} else { // If not paused, display the time normally
lcd.setCursor(column, 3);
lcd.print(formatTime(seconds));
}
}
String formatTime(int seconds) {
int mins = seconds / 60;
int secs = seconds % 60;
char buf[6];
sprintf(buf, "%02d:%02d", mins, secs);
return String(buf);
}
void handlePausedBlinking() {
if (waterPaused) {
if (millis() - waterLastBlinkTime > BLINK_INTERVAL) {
waterLastBlinkTime = millis();
waterBlinkState = !waterBlinkState;
lcd.setCursor(0, 3);
displayTime(waterTime, waterPaused, 0, waterBlinkState, waterLastBlinkTime);
}
}
if (soapPaused) {
if (millis() - soapLastBlinkTime > BLINK_INTERVAL) {
soapLastBlinkTime = millis();
soapBlinkState = !soapBlinkState;
lcd.setCursor(7, 3);
displayTime(soapTime, soapPaused, 7, soapBlinkState, soapLastBlinkTime);
}
}
if (blowerPaused) {
if (millis() - blowerLastBlinkTime > BLINK_INTERVAL) {
blowerLastBlinkTime = millis();
blowerBlinkState = !blowerBlinkState;
lcd.setCursor(14, 3);
displayTime(blowerTime, blowerPaused, 14, blowerBlinkState, blowerLastBlinkTime);
}
}
}