#include <Arduino.h>
#include <LiquidCrystal_I2C.h> // Include the LiquidCrystal_I2C library
#include <WiFi.h>
#include <HTTPClient.h>
#include <WebServer.h>
#include <uri/UriBraces.h>
// Define LCD parameters
#define I2C_ADDR 0x27 // Check your LCD's I2C address. Common ones are 0x27 or 0x3F.
#define LCD_COLUMNS 20
#define LCD_ROWS 4
// Create LCD object
LiquidCrystal_I2C lcd(I2C_ADDR, LCD_COLUMNS, LCD_ROWS);
// Define GPIO pins
const int cylinderPin1 = 34;
const int cylinderPin2 = 35;
const int cylinderPin3 = 32;
const int buttonPin = 26;
const int ledPin1 = 14;
const int ledPin2 = 13;
const int ledPin3 = 12;
int reading = 0;
int buttonValue = 0;
int cylinderNorm1 ;
int cylinderNorm2 ;
int cylinderNorm3 ;
int cylinderunlocked;
const int THRESHOLD = 10;
int UPPER_THRESHOLD ;
int LOWER_THRESHOLD ;
//--- board ID --
int boardID = 1;
// Variables for potentiometer values and lap triggers
int cylinderValue1;
int cylinderValue2;
int cylinderValue3;
// -- mapping --
int mappedCylinderValue1;
int mappedCylinderValue2;
int mappedCylinderValue3;
bool cylinderTriggered1 = false;
bool cylinderTriggered2 = false;
bool cylinderTriggered3 = false;
// Button state variables
int buttonState;
int previousButtonState = HIGH;
const int debounceDelay = 1;
unsigned long lastDebounceTime = 0;
// Stopwatch variables
unsigned long startTime = 0;
int runCount = 0;
bool stopwatchRunning = false;
unsigned long cylinderTime1 = 0;
unsigned long cylinderTime2 = 0;
unsigned long cylinderTime3 = 0;
const unsigned long stopwatchLimit = 60000; // 60 seconds in milliseconds
int cylinderunlocked1 = 0;
int cylinderunlocked2 = 0;
int cylinderunlocked3 = 0;
// Function to check if all pots are past 50%
bool allCylindersTriggered() {
return (cylinderTriggered1 == true && cylinderTriggered2 == true && cylinderTriggered3 == true);
}
// Function to check if all LEDs are off
bool allLedsOff() {
return (digitalRead(ledPin1) == LOW && digitalRead(ledPin2) == LOW && digitalRead(ledPin3) == LOW);
}
// WiFi credentials
const char* ssid = "Hardware Setup";
const char* password = "Lock6121";
//const int WIFI_CHANNEL = 6;
void setup() {
Serial.begin(115200);
pinMode(cylinderPin1, INPUT);
pinMode(cylinderPin2, INPUT);
pinMode(cylinderPin3, INPUT);
pinMode(buttonPin, INPUT_PULLUP);
pinMode(ledPin1, OUTPUT);
pinMode(ledPin2, OUTPUT);
pinMode(ledPin3, OUTPUT);
// Read initial button state
previousButtonState = digitalRead(buttonPin);
delay(100);
// Initialize the LCD
lcd.init();
lcd.backlight(); // Turn on the backlight
lcd.clear(); // Clear the LCD display
// Print initial message on LCD (optional)
lcd.setCursor(0, 0);
lcd.print("Loc Doc ");
lcd.print("ID:");
lcd.print(boardID);
// Print initial message on LCD
lcd.setCursor(0, 1);
lcd.print("Connecting to WiFi");
// Connect to WiFi (with detailed feedback on LCD)
WiFi.mode(WIFI_STA); // Set WiFi mode to station (client)
WiFi.begin(ssid, password);
int wifiRetryCount = 0;
const int MAX_WIFI_RETRIES = 1;
while (WiFi.status() != WL_CONNECTED && wifiRetryCount < MAX_WIFI_RETRIES) {
lcd.setCursor(0, 1);
lcd.print("Connecting...");
for (int i = 0; i < 4 && WiFi.status() != WL_CONNECTED; i++) {
lcd.print(".");
delay(500);
}
if (WiFi.status() != WL_CONNECTED) {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Connection failed");
lcd.setCursor(0, 1);
lcd.print("Retrying...");
delay(2000);
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Connecting to WiFi");
}
wifiRetryCount++;
}
if (WiFi.status() == WL_CONNECTED) {
// WiFi connected!
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Loc Doc ");
lcd.print("ID:");
lcd.print(boardID);
lcd.setCursor(0, 1);
lcd.print("WiFi connected!");
lcd.setCursor(0, 2);
lcd.print("URL:");
lcd.print(WiFi.localIP());
delay(2000); // Show message for a few seconds
lcd.clear();
} else {
// WiFi connection failed after multiple attempts
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("WiFi connection");
lcd.setCursor(0, 1);
lcd.print("failed!");
delay(2000);
}
const int cylinderBaseValue1 = analogRead(cylinderPin1);
cylinderNorm1 = cylinderBaseValue1 / 40.95;
const int cylinderBaseValue2 = analogRead(cylinderPin2);
cylinderNorm2 = cylinderBaseValue2 / 40.95;
const int cylinderBaseValue3 = analogRead(cylinderPin3);
cylinderNorm3 = cylinderBaseValue3 / 40.95;
//lcd.setCursor(0, 3);
//lcd.print(cylinderValue1 / 40.95);
}
void loop() {
reading = digitalRead(buttonPin);
//buttonValue = digitalRead(buttonPin);
Serial.print(reading);
// Read potentiometer values
cylinderValue1 = analogRead(cylinderPin1);
cylinderValue2 = analogRead(cylinderPin2);
cylinderValue3 = analogRead(cylinderPin3);
mappedCylinderValue1 = map(cylinderValue1, 0, 4095, 0, 100);
mappedCylinderValue2 = map(cylinderValue2, 0, 4095, 0, 100);
mappedCylinderValue3 = map(cylinderValue3, 0, 4095, 0, 100);
// --- LED Light Check (before button logic) ---
cylinderTriggered1 = checkLEDLight (mappedCylinderValue1, cylinderTriggered1, cylinderTime1, cylinderunlocked1, ledPin1, cylinderNorm1);
cylinderTriggered2 = checkLEDLight (mappedCylinderValue2, cylinderTriggered2, cylinderTime2, cylinderunlocked2, ledPin2, cylinderNorm2);
cylinderTriggered3 = checkLEDLight (mappedCylinderValue3, cylinderTriggered3, cylinderTime3, cylinderunlocked3, ledPin3, cylinderNorm3);
// --- Button logic with debouncing and LED check ---
// int reading = digitalRead(buttonPin);
if (reading != previousButtonState && (millis() - lastDebounceTime) > debounceDelay) {
if (reading == 0) { // Button pressed
if (stopwatchRunning) {
stopwatchRunning = false;
Serial.println("Stopwatch Stopped");
} else if (allLedsOff()) {
lcd.clear();
// Start the LED sequence
digitalWrite(ledPin1, HIGH); // Red LED on
lcd.setCursor(0, 0);
lcd.print(" WATCH THE LIGHTS");
delay(1000);
digitalWrite(ledPin2, HIGH); // Yellow LED on
delay(1000);
digitalWrite(ledPin3, HIGH); // Green LED on
delay(1000);
digitalWrite(ledPin1, LOW); // All LEDs off
digitalWrite(ledPin2, LOW);
digitalWrite(ledPin3, LOW);
stopwatchRunning = true;
startTime = millis();
cylinderTime1 = 0;
cylinderTime2 = 0;
cylinderTime3 = 0;
lcd.clear(); // Clear any previous messages
} else {
lcd.setCursor(0, 0);
lcd.print(" Reset cylinders! ");
delay(2000);
//break;
}
}
}
lastDebounceTime = millis();
previousButtonState = reading;
// --- Stopwatch and Cylinder Logic ---
if (stopwatchRunning) {
checkCylinderTrigger(mappedCylinderValue1, cylinderTriggered1, cylinderTime1, cylinderunlocked1, ledPin1, cylinderNorm1);
checkCylinderTrigger(mappedCylinderValue2, cylinderTriggered2, cylinderTime2, cylinderunlocked2, ledPin2, cylinderNorm2);
checkCylinderTrigger(mappedCylinderValue3, cylinderTriggered3, cylinderTime3, cylinderunlocked3, ledPin3, cylinderNorm3);
displayLapTimesOnLCD();
unsigned long currentTime = millis() - startTime;
if (currentTime >= stopwatchLimit || allCylindersTriggered()) {
stopwatchRunning = false;
if (currentTime >= stopwatchLimit) {
//lcd.setCursor(0, 0);
//lcd.print("Times Up!");
} else {
//lcd.setCursor(0, 0);
//lcd.print("Finished!");
}
} else {
displayTimeOnLCD(currentTime);
}
} else {
// --- Reset cylinders when stopwatch is not running ---
cylinderTriggered1 = checkLEDLight(mappedCylinderValue1, cylinderTriggered1, cylinderTime1, cylinderunlocked1, ledPin1, cylinderNorm1);
cylinderTriggered2 = checkLEDLight(mappedCylinderValue2, cylinderTriggered2, cylinderTime2, cylinderunlocked2, ledPin2, cylinderNorm2);
cylinderTriggered3 = checkLEDLight(mappedCylinderValue3, cylinderTriggered3, cylinderTime3, cylinderunlocked3, ledPin3, cylinderNorm3);
// --- Display "Ready" or "Finished!" ---
lcd.setCursor(0, 0);
if (allLedsOff()) {
lcd.print(" Ready ");
} else {
lcd.print("DONE! ");
}
}
}
// Function to format and display time on LCD
void displayTimeOnLCD(unsigned long time) {
unsigned long milliseconds = time % 1000;
unsigned long seconds = (time / 1000) % 60;
unsigned long minutes = (time / (1000 * 60)) % 60;
// Divide milliseconds by 10 to get 2 digits
int formattedMillis = milliseconds / 10;
lcd.setCursor(0, 0);
lcd.print("Timer:");
lcd.printf("%02d:%02d:%02d", minutes, seconds, formattedMillis);
}
void displayLapTimesOnLCD() {
lcd.setCursor(0, 1); // Set cursor to the beginning of the second line
lcd.print("C1 ");
displayLapTime(cylinderTime1);
lcd.setCursor(0, 2); // Set cursor to the beginning of the third line
lcd.print("C2 ");
displayLapTime(cylinderTime2);
lcd.setCursor(0, 3); // Set cursor to the beginning of the fourth line
lcd.print("C3 ");
displayLapTime(cylinderTime3);
}
void displayLapTime(unsigned long time) {
unsigned long milliseconds = time % 1000;
unsigned long seconds = (time / 1000) % 60;
unsigned long minutes = (time / (1000 * 60)) % 60;
// Divide milliseconds by 10 to get 2 digits
int formattedMillis = milliseconds / 10;
lcd.printf("%02d:%02d:%02d", minutes, seconds, formattedMillis);
}
void checkCylinderTrigger(int mappedCylinderValue, bool& cylinderTriggered, unsigned long& cylinderTime, int& cylinderUnlocked, int ledPin, int cylinderNorm) {
const int UPPER_THRESHOLD = cylinderNorm + THRESHOLD;
const int LOWER_THRESHOLD = cylinderNorm - THRESHOLD;
if ((mappedCylinderValue > UPPER_THRESHOLD || mappedCylinderValue < LOWER_THRESHOLD) && !cylinderTriggered) {
cylinderTime = millis() - startTime;
cylinderTriggered = true;
digitalWrite(ledPin, HIGH);
//... (other actions)
} else if (mappedCylinderValue >= LOWER_THRESHOLD && mappedCylinderValue <= UPPER_THRESHOLD) {
cylinderTriggered = false;
digitalWrite(ledPin, LOW);
//... (other actions)
}
}
bool checkLEDLight(int mappedCylinderValue, bool& cylinderTriggered, unsigned long& cylinderTime, int& cylinderUnlocked, int ledPin, int cylinderNorm) {
const int UPPER_THRESHOLD = cylinderNorm + THRESHOLD;
const int LOWER_THRESHOLD = cylinderNorm - THRESHOLD;
if (mappedCylinderValue1 > LOWER_THRESHOLD && mappedCylinderValue < UPPER_THRESHOLD) {
cylinderTriggered = false;
digitalWrite(ledPin, LOW);
cylinderunlocked = 0;
return false;
} else {
return cylinderTriggered;
}
}