#include <EEPROM.h>
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <SoftwareSerial.h>
LiquidCrystal_I2C lcd(0x27, 20, 4); // Create an Instance for LCD
// Bluetooth Serial Communication
SoftwareSerial BTSerial(10, 11); // RX, TX
const byte dumpCycles = 10; // Define number of allowed dump cycles
// Pin definitions
const byte sensorpin = 2;
const byte up_relay1pin = 8; // Up Solenoid Relay 1
const byte down_relay2pin = 12; // Down Solenoid Relay 2
const byte dump_relay3pin = 17; // Dump Solenoid Relay 3
const byte buzzerpin = 9;
const byte button1 = 6; // Mode selection Button
const byte button2 = 5; // Raises the cylinder (activates the Up relay on Pin 8).
const byte button3 = 4; // Lowers the cylinder (activates the Down relay on Pin 12).
const byte button4 = 3; // Activates the Dump relay (Pin 17) and Down relay (Pin 12) for 1 second, then both go low.
const byte eepromAddress = 0; // EEPROM address to start writing from, you can change this depending on your needs
bool prev4State = LOW; // Variable to hold button 4 previous state
// Other variable declarations
int cycleCounter = 0;
int totalCycleCounter = 0;
const byte maxCycles = 20;
int selectedMenu = 0;
unsigned long debounceDelay = 250;
// Variables to hold last button press time for dobounce delay
unsigned long lastDebounceTime1 = 0;
unsigned long lastDebounceTime4 = 0;
enum MenuState {
MENU_FAST_AUTO,
MENU_HALF_DROP,
MENU_MANUAL,
MENU_UNDEFINED
};
enum SubState {
SUB_STATE_1,
SUB_STATE_2,
NO_STATE
};
SubState currentSubState = SUB_STATE_1;
SubState lastSentSubState = NO_STATE; // To track the last sent substate
MenuState currentMenuState = MENU_MANUAL;
// Variables for button states and initially set them LOW
bool button1State, button2State, button3State, button4State = LOW;
byte menuNumber = 1; // This menu number will be send to Application
void setup() {
Serial.begin(9600);
BTSerial.begin(9600); // Initialize Bluetooth Serial
lcd.init();
lcd.setCursor(0, 0);
lcd.backlight();
lcd.display();
// Set pin Modes of Button as iNPUT with PULL-UP
pinMode(button1, INPUT_PULLUP); // Set button pin as input with Pull-UP
pinMode(button2, INPUT_PULLUP); // Set button pin as input with Pull-UP
pinMode(button3, INPUT_PULLUP); // Set button pin as input with Pull-UP
pinMode(button4, INPUT_PULLUP); // Set button pin as input with Pull-UP
pinMode(sensorpin, INPUT_PULLUP);
// Set Pin modes of Solenoid as output
pinMode(down_relay2pin, OUTPUT);
pinMode(dump_relay3pin, OUTPUT);
pinMode(up_relay1pin, OUTPUT);
pinMode(buzzerpin, OUTPUT);
resetCounters();
readTotalCycleCounter(); // Read total Cycle counter from eeprom
displayMessage(0, 3, "Total:", HIGH); // Print this message on dispaly and clear it as well
displayMessage(8, 0, "Manual ", LOW); // Display current menu on screen adn dont' clear
sendDataToPhone(); // send Data to phone if connected
}
void resetCounters() {
cycleCounter = 0;
totalCycleCounter = 0;
digitalWrite(down_relay2pin, LOW);
digitalWrite(dump_relay3pin, LOW);
digitalWrite(up_relay1pin, LOW);
}
void saveTotalCycleCounter() {
EEPROM.update(eepromAddress, totalCycleCounter); // If the value change only then update it to eeprom
}
void readTotalCycleCounter() {
EEPROM.get(eepromAddress, totalCycleCounter);
}
void loop() {
unsigned long currentTime = millis();
// Read physical button states and bluetooth Commands
checkBluetoothCommands(); // Check for bluetooth
readButtonStates();
if (button1State == HIGH) { // Check for button 1 press change menu state
button1State = LOW; // Reset button 1 state flag
selectedMenu = (selectedMenu + 1) % 2;
// Menu state logic
switch (selectedMenu) {
case 1:
currentMenuState = MENU_MANUAL;
displayMessage(8, 0, "Manual ", LOW); // Display current menu on screen adn dont' clear (Add white space after name if we get raw data previously)
menuNumber = 2; // Set menu number to 2 to be send to application
sendDataToPhone();
break;
case 2:
currentMenuState = MENU_FAST_AUTO;
break;
// ... [Add more cases for additional menus]
}
lastSentSubState = SUB_STATE_2; // change last sub state
}
// Menu selection logic
if (currentMenuState == MENU_FAST_AUTO && currentSubState == SUB_STATE_2 && button4State == HIGH && cycleCounter < dumpCycles) {
// if (cycleCounter >= dumpCycles) { // Check if cycle counter is greater then 10 reset it and change back to substate 1
// cycleCounter = 0; // Reset the counter
// // currentSubState = SUB_STATE_1; // Switch to Sub Menu 1
// // Optionally, reset other relevant states or counters as needed
// // This function resets cycleCounter and totalCycleCounter, adjust as necessary
// }
// ... [Add Fast Auto state functionality] // Raise the cylinder if sensorpin is LOW and dump_relay3pin (dump) is LOW
if (digitalRead(sensorpin) == HIGH) { // if the sensor pin is not triggered, the Up relay (Pin 8) is activated
digitalWrite(up_relay1pin, HIGH); // Activate solenoid1 to raise
} else {
digitalWrite(up_relay1pin, LOW); // Deactivate solenoid1
}
// If the sensor pin is triggered, the Dump relay (Pin 17) and Down relay (Pin 12) are activated for 1 second, and the system returns to Substate 1.
if (digitalRead(sensorpin) == LOW) {
digitalWrite(dump_relay3pin, HIGH); // Activate solenoid2 dumping cylinder
digitalWrite(down_relay2pin, HIGH);
cycleCounter++;
totalCycleCounter++;
saveTotalCycleCounter();
sendDataToPhone(); // Send updated data to phone
customDelay(1000);
sendDataToPhone(); // Send updated data to phone
digitalWrite(dump_relay3pin, LOW);
digitalWrite(down_relay2pin, LOW); // Deactivate solenoid2
}
}
// Menu functionality
switch (currentMenuState) {
case MENU_MANUAL:
currentSubState = SUB_STATE_1; // Change substate back to substate 1
if (button2State == HIGH && digitalRead(sensorpin) == HIGH && digitalRead(dump_relay3pin) == LOW) { // Button press raises cylinder
digitalWrite(up_relay1pin, HIGH);
} else if (button2State == LOW) { // Button released Stop raising cylinder
digitalWrite(up_relay1pin, LOW);
}
if (button3State == HIGH) { // Lower cylinder with button press
digitalWrite(down_relay2pin, HIGH);
} else {
digitalWrite(down_relay2pin, LOW);
}
if (button4State == HIGH) { // button press dumps cylinder Activates the Dump relay (Pin 17) and Down relay (Pin 12) for 1 second
digitalWrite(dump_relay3pin, HIGH); // Activate Dump Relay
digitalWrite(down_relay2pin, HIGH); // Activate Down Relay
// Mark and count cylinder dump
cycleCounter++; // Increase Cycle Count
totalCycleCounter++;
saveTotalCycleCounter(); // Save the counter to eeprom
sendDataToPhone(); // Send updated data to phone
customDelay(1000);
sendDataToPhone(); // Send updated data to phone
button4State = LOW; // reset button 4 state
digitalWrite(dump_relay3pin, LOW); // Turn off Dump relay
digitalWrite(down_relay2pin, LOW); // Turn off Down Relay
}
break;
case MENU_FAST_AUTO: // Switch-case logic for MENU_FAST_AUTO
if (button4State == HIGH) { // Toggle the substate on button 4 press
if (currentSubState == SUB_STATE_1) {
cycleCounter = 0; // Reset the counter
currentSubState = SUB_STATE_2;
prev4State = HIGH; // set button 4 previous state as high on substate 2
menuNumber = 4; // Set menu number to 4 to be send to application
sendDataToPhone();
} else if (prev4State == LOW) { // Change state only when button 4 is released
currentSubState = SUB_STATE_1;
button4State = LOW; // Reset button 4 state
}
}
switch (currentSubState) { // Check current sub state and take action
case SUB_STATE_1:
if (lastSentSubState != SUB_STATE_1) { // If last substate is not substate 1 update it and turn All relay LOW
displayMessage(8, 0, "AUTO STANDBY", LOW); // Display current menu on screen adn dont' clear
lastSentSubState = SUB_STATE_1; // Set last substate as substate 1
// Execute AUTO STANDBY specif
digitalWrite(dump_relay3pin, LOW); // Activate solenoid2 to dump
digitalWrite(down_relay2pin, LOW);
digitalWrite(up_relay1pin, LOW);
menuNumber = 3; // Set menu number to 3 to be send to application
sendDataToPhone();
// ... [Your specific logic for this sub-state]
}
break;
case SUB_STATE_2: // Cases for substate 2
lastSentSubState = SUB_STATE_2; // change last sub state
displayMessage(8, 0, "AUTO CYCLING", LOW); // Display current menu on screen adn dont' clear
break; // Clear the LCD display at set intervals
}
}
}
void sendDataToPhone() {
BTSerial.print(menuNumber); // Get current menu number and update it on display
BTSerial.print(";");
BTSerial.print(totalCycleCounter);
BTSerial.print(";");
BTSerial.print(cycleCounter);
BTSerial.println(";"); // Update the last sent menu state to MENU_IDLE
// Update cycle and total cycle counters on LCD
displayMessage(0, 2, cycleCounter);
displayMessage(6, 3, totalCycleCounter);
}
void customDelay(unsigned long waitTime) {
unsigned long cTime = millis();
while ((millis() - cTime) < waitTime) {
checkBluetoothCommands(); // Check for bluetooth commands
readButtonStates(); // Read button states
}
}
void readButtonStates() {
if ((digitalRead(button1) == LOW) && ((millis() - lastDebounceTime1) > debounceDelay)) { // Check if button 1 pressed
button1State = HIGH; // Set button 1 state as HIGH
lastDebounceTime1 = millis(); // Note current time when button is pressed
}
if (digitalRead(button2) == LOW) // Check if button 2 pressed
button2State = HIGH; // Set button 2 state as HIGH
else if (digitalRead(button2) == HIGH) // Check if button 2 Released
button2State = LOW; // Reset button 2 state back to low
if (digitalRead(button3) == LOW) // Check if button 3 pressed
button3State = HIGH; // Set button 3 state as HIGH
else if (digitalRead(button3) == HIGH) // Check if button 3 Released
button3State = LOW; // Reset Button 3 state back to low
if ((digitalRead(button4) == LOW) && ((millis() - lastDebounceTime4) > debounceDelay)) { // Check if button 1 pressed
button4State = HIGH; // Set button 4 state as HIGH
lastDebounceTime4 = millis(); // Note current time when button is pressed
} else if (digitalRead(button4) == HIGH) { // Check if button 4 is released and we are in substate 2
if (currentSubState == SUB_STATE_2) {
button4State = LOW; // Reset button 4 state
cycleCounter = 0;
prev4State = LOW;
currentSubState = SUB_STATE_1; // Get back to substate 1
}
}
}
void checkBluetoothCommands() {
while (BTSerial.available()) {
char btChar = BTSerial.read();
// Process single-character commands for Bluetooth control
// Process button state commands
switch (btChar) {
case 'A': button1State = HIGH; break;
case 'C': button2State = LOW; break;
case 'D': button2State = HIGH; break;
case 'E': button3State = LOW; break;
case 'F': button3State = HIGH; break;
case 'G': button4State = LOW; break;
case 'H': button4State = HIGH; break;
}
}
}
void displayMessage(int x, int y, const String& msg, bool clearScreen) {
// This function will receive cursor x, y position and message to display and a flag to clear previous display or not
if (clearScreen) { // If clear screen flag is high clear lcd
lcd.clear();
delay(500); // Wait for 500 seconds
}
lcd.setCursor(x, y); // Set cursor at x, y position
lcd.print(msg); // Print msg on lcd
}
void displayMessage(int x, int y, int value) { // This function will receive cursor x, y position and integer value to display
lcd.setCursor(x, y); // Set cursor at x, y position
lcd.print(value); // Print msg on lcd
lcd.print(" "); // Remove any extra value
}