/////////////////////////////////////////////////////////////
// HELMET AUTOMATIC FOG WASHER //
// 09-28-24 //
// auth: Oliver Feronel -> Queen
//
// //
// 10-06-24 //
// initial coding for pins and interrupt //
// 10-070-24
// fix timer issue to:
// get indevidual time per wash type
// turn of and false boolean
// add "2" ultrasonic for tank check
// coin slot light active if value > 0
// 10-17-24
// add air pump control at A2
/////////////////////////////////////////////////////////////
// ACRONAMES
// wtl = water tank level
// ftl = foam tank level
// wfcl = water foam current level label
//
#include <Arduino.h>
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27, 20, 4);
/////////////////////////////////////////
// PIN DEFINITION AND VARIABLES //
// SSR PIN BUTTON BUZZER SLOT //
/////////////////////////////////////////
#define COIN_SLOT 2 // Arduino Uno int pin
#define COIN_SLOT_LIGHT A7 // SSR control foam wash
#define AIR_PUMP_SSR A0 // SSR control air wash
#define SOLENOID_SSR A1 // SSR air pupm control
#define WATER_PUMP_SSR A2 // SSR control water wash
#define WATER_TANK_GATE_VALVE_SSR A3 // SSR control air pump
#define SHAMPO_TANK_CHECK_VALVE A6 /// SSR control solenoid
#define BUZZER 9 // Buzzer
#define AIR_BUTTON 8 // Button
#define WATER_BUTTON 7 // Button
#define FOAM_BUTTON 6 // Button
// ultrasonic pin
#define TRIG_PIN_1 10
#define ECHO_PIN_1 11
#define TRIG_PIN_2 12
#define ECHO_PIN_2 13
#define DEBUG // Comment this line to disable debug messages ->
// will save some memory if deactivated
// Debounce variables for button
unsigned long lastDebounceTime1 = 0;
unsigned long lastDebounceTime2 = 0;
unsigned long lastDebounceTime3 = 0;
const unsigned long debounceDelay = 10;
// Timer start times for each wash
unsigned long previousMillis_timer = 0;
unsigned long countdownTime_timer = 0;
long wtl = 100;
long ftl = 100;
// Button states of debounce
int lastFoamButtonState = HIGH;
int foamButtonState = HIGH;
int lastWaterButtonState = HIGH;
int waterButtonState = HIGH;
int lastAirButtonState = HIGH;
int airButtonState = HIGH;
int price_of_air = 5;
int price_of_water = 8;
int price_of_foam = 10;
int tank_threshold = 30; // unit in percent
int ON = 255;
int OFF = 0;
/////////////////////////////////////////
// WASH TIME IN SEC //
// VARIABLES //
/////////////////////////////////////////
int FOAM_WASH_TIME = 60;
int AIR_WASH_TIME = 30;
int WATER_WASH_TIME = 120;
// Last displayed values
String lastRow1 = " PYSONETO05 ";
String lastRow2 = "3 IN 1 WASHING";
String lastRow3 = "INSERT COIN NOW !!!";
String lastRow4 = "T1=" + String(wtl) + "%" + " T2=" + String(ftl) + "%"; // Corrected String concatenation
String wftl = String(wtl) + " " + String(ftl);
volatile int credit = 0; // this will use to interrupt
volatile bool is_credited = false;
bool FOAM_WASH = false;
bool AIR_WASH = false;
bool WATER_WASH = false;
bool timerRunning = false;
bool water_tank_ok = true;
bool foam_tank_ok = false;
void setup()
{
Serial.begin(9600);
// Initialize the LCD
lcd.init();
lcd.backlight();
updateLCD(); // Initial display update
// Coin slot Pin and Light
pinMode(COIN_SLOT, INPUT_PULLUP);
pinMode(COIN_SLOT_LIGHT, OUTPUT);
// Setup button pins as input
pinMode(WATER_TANK_GATE_VALVE_SSR, OUTPUT);
pinMode(AIR_PUMP_SSR, OUTPUT);
pinMode(WATER_PUMP_SSR, OUTPUT);
pinMode(SOLENOID_SSR, OUTPUT);
pinMode(SHAMPO_TANK_CHECK_VALVE, OUTPUT);
// Set the trigger and echo pins as OUTPUT and INPUT respectively
pinMode(TRIG_PIN_1, OUTPUT);
pinMode(ECHO_PIN_1, INPUT);
pinMode(TRIG_PIN_2, OUTPUT);
pinMode(ECHO_PIN_2, INPUT);
pinMode(FOAM_BUTTON, INPUT_PULLUP);
pinMode(WATER_BUTTON, INPUT_PULLUP);
pinMode(AIR_BUTTON, INPUT_PULLUP);
for (int i = 0; i < 5; i++)
{
wftl = check_ultrasonic();
lcd_display("WELCOME PYSONET05", "3 & 1 Washer", "INSERT COIN NOW", wftl);
tone(BUZZER, 2000);
delay(500);
lcd_display("WELCOME PYSONET05", "AIR / WASH / FOAM", "", wftl);
noTone(BUZZER);
delay(500);
}
// Setup interrupt functionW
attachInterrupt(digitalPinToInterrupt(COIN_SLOT), add_coin, FALLING); // Coin slot interrupt setup
}
void loop()
{
wftl = check_ultrasonic();
if (water_tank_ok && foam_tank_ok)
{
///////////////////////////////////////////////////////////
// Credits function //
///////////////////////////////////////////////////////////
if (credit)
{
if (!FOAM_WASH && !AIR_WASH && !WATER_WASH)
{
lcd_display(lastRow1, lastRow2, "Credit : " + String(credit) + " PHP", wftl);
if (credit >= price_of_air || credit >= price_of_water || credit >= price_of_foam)
{
debounceButton(FOAM_BUTTON, lastFoamButtonState, foamButtonState, lastDebounceTime1, FOAM_WASH);
debounceButton(WATER_BUTTON, lastWaterButtonState, waterButtonState, lastDebounceTime2, WATER_WASH);
debounceButton(AIR_BUTTON, lastAirButtonState, airButtonState, lastDebounceTime3, AIR_WASH);
}
}
}
else
{
FOAM_WASH = false;
WATER_WASH = false;
AIR_WASH = false;
timerRunning = false; // Stop timer if no credit
// lcd_display("WELCOME PYSONET05", "AIR / WASH / FOAM", "INSERT COIN NOW", wftl);
}
if (AIR_WASH && credit >= price_of_air)
{
if (!timerRunning)
{
countdownTime_timer = AIR_WASH_TIME; // Set countdown timer for air wash
timerRunning = true; // Start the timer
}
relay_status(ON, ON, OFF, OFF, OFF, ON);
updateTimer_timer("AIR_WASH", price_of_air);
/////////////////////////////////////////////////////////////////////////////////////
}
else if (WATER_WASH && credit >= price_of_water)
{
if (!timerRunning)
{
countdownTime_timer = WATER_WASH_TIME; // Set countdown timer for water wash
timerRunning = true; // Start the timer
}
relay_status(OFF, OFF, ON, OFF, OFF, ON);
updateTimer_timer("WATER_WASH", price_of_water);
/////////////////////////////////////////////////////////////////////////////////////
}
else if (FOAM_WASH && credit >= price_of_foam)
{
if (!timerRunning)
{
countdownTime_timer = FOAM_WASH_TIME; // Set countdown timer for foam wash
timerRunning = true; // Start the timer
}
relay_status(ON, ON, OFF, OFF, OFF, ON);
updateTimer_timer("FOAM_WASH", price_of_foam);
/////////////////////////////////////////////////////////////////////////////////////
}
else
{
// If no wash is active, ensure all controls are o
relay_status(OFF , OFF, OFF, OFF, OFF, OFF);
if (!credit)
{
lcd_display(lastRow1, "DONT INSERT COIN", "INSERT COIN", wftl);
}
}
}
else
{
lcd_display(lastRow1, wftl, "DONT INSERT COIN", "TANK LEVEL CRITICAL.");
}
if (is_credited)
{
tone(BUZZER, 1000);
delay(10);
is_credited = false;
}
else
{
noTone(BUZZER);
}
Serial.println(is_credited);
}
void relay_status(int r1, int r2, int r3, int r4, int r5, int r6)
{
//rx represent relay NO.
analogWrite(AIR_PUMP_SSR, r1);
analogWrite(SOLENOID_SSR, r2);
analogWrite(WATER_PUMP_SSR, r3);
analogWrite(WATER_TANK_GATE_VALVE_SSR, r4);
analogWrite(SHAMPO_TANK_CHECK_VALVE, r5);
analogWrite(COIN_SLOT_LIGHT, r6);
}
String check_ultrasonic()
{
///////////////////////////////////////////////////////////
// Tanks Check //
///////////////////////////////////////////////////////////
// Measure distance for Sensor 1
long wtl = measureDistance(TRIG_PIN_1, ECHO_PIN_1);
// Measure distance for Sensor 2
long ftl = measureDistance(TRIG_PIN_2, ECHO_PIN_2);
water_tank_ok = (wtl > tank_threshold) ? true : false;
foam_tank_ok = (ftl > tank_threshold) ? true : false;
// Print the distances
print("Tank Fill @ :", 0);
print(wtl, 0);
print(" %,", 1);
print("Tank Fill @ : ", 0);
print(ftl, 0);
print(" %", 1);
return "WTR : " + String(wtl) + "% FWM : " + String(ftl) + "%";
}
// Function to update the LCD display
void lcd_display(String row_1, String row_2, String row_3, String row_4)
{
// Only update rows if new value is different from the last displayed value
if (row_1 != lastRow1)
{
lastRow1 = row_1;
lcd_clear_row(0); // Clear row 1
}
if (row_2 != lastRow2)
{
lastRow2 = row_2;
lcd_clear_row(1); // Clear row 2
}
if (row_3 != lastRow3)
{
lastRow3 = row_3;
lcd_clear_row(2); // Clear row 3
}
if (row_4 != lastRow4)
{
lastRow4 = row_4;
lcd_clear_row(3); // Clear row 4
}
// Update the LCD display
updateLCD();
}
// Function to update the LCD content after clearing
void updateLCD()
{
lcd.setCursor(0, 0);
lcd.print(lastRow1);
lcd.setCursor(0, 1);
lcd.print(lastRow2);
lcd.setCursor(0, 2);
lcd.print(lastRow3);
lcd.setCursor(0, 3);
lcd.print(lastRow4);
}
// Function to clear a specific row
void lcd_clear_row(int row)
{
String spacing = "";
for (int i = 0; i < 20; i++)
{
spacing += " ";
}
lcd.setCursor(0, row);
lcd.print(spacing); // Clear row with spaces
}
// Debounce button function
void debounceButton(int buttonPin, int &lastButtonState, int &buttonState,
unsigned long &lastDebounceTime, bool &what_to_wash)
{
/* Debounce function */
int reading = digitalRead(buttonPin);
if (reading != lastButtonState)
{
lastDebounceTime = millis();
}
if ((millis() - lastDebounceTime) > debounceDelay)
{
if (reading != buttonState)
{
buttonState = reading;
if (buttonState == LOW)
{
what_to_wash = true;
print(what_to_wash, 1);
}
}
}
lastButtonState = reading;
}
// Function to update the timer
void updateTimer_timer(String selected_option, int &price)
{
unsigned long currentMillis = millis();
// Check if it's time to update the timer
if (timerRunning && (currentMillis - previousMillis_timer >= 1000))
{ // Update every second
previousMillis_timer = currentMillis; // Save the last update time
countdownTime_timer--; // Decrease countdown time by 1 second
// Print the current countdown time to the serial monitor
printCountdownTime_timer(selected_option);
if (countdownTime_timer <= 10)
{
tone(BUZZER, 2000);
delay(50);
noTone(BUZZER);
}
// If countdown reaches zero, stop the timer
if (countdownTime_timer == 0)
{
print("Time's up!", 1); // Print to serial monitor
timerRunning = false; // Stop the washing process
FOAM_WASH = false; // Reset wash flags
AIR_WASH = false;
WATER_WASH = false;
credit -= price;
relay_status(OFF , OFF, OFF, OFF, OFF, OFF);
}
}
}
// Function to print the current countdown time
void printCountdownTime_timer(String selected_option)
{
String countdownString = "Time: " + String(countdownTime_timer / 60) + ":" + (countdownTime_timer % 60 < 10 ? "0" : "") + String(countdownTime_timer % 60);
print(countdownString, 1); // Print the entire countdown string
lcd_display(lastRow1, lastRow2, "Function: " + selected_option + " PHP", countdownString);
}
template <typename T>
void print(T print_value, int next_line)
{
#ifdef DEBUG
if (next_line)
{
Serial.println(print_value);
}
else
{
Serial.print(print_value);
}
#endif
}
// Function to measure distance using an ultrasonic sensor
long measureDistance(int trigPin, int echoPin)
{
// Send a 10 microsecond pulse to trigger
digitalWrite(trigPin, LOW);
delayMicroseconds(2);
digitalWrite(trigPin, HIGH);
delayMicroseconds(10);
digitalWrite(trigPin, LOW);
// Measure the time for the echo to return
long duration = pulseIn(echoPin, HIGH);
// Calculate the distance in cm (duration / 2) * (speed of sound in cm/us)
long distance = (duration * 0.0343) / 2; // Convert duration to distance in cm
return calculateTankFillPercentage(distance, 400); // Assuming tankHeight is 400 cm
}
// Function to calculate the tank fill percentage
float calculateTankFillPercentage(long distance, long tankHeight)
{
if (tankHeight <= 0)
{
return 0; // Avoid division by zero
}
// Calculate fill percentage
float fillPercentage = (1 - (float)distance / tankHeight) * 100;
// Constrain the fill percentage between 0% and 100%
return constrain(fillPercentage, 0, 100); // Use constrain for proper range
}
void add_coin()
{
// Interrupt handler to increment credit
credit++;
is_credited = true;
}