#include <Wire.h>
#include <LiquidCrystal_I2C.h>
// ===== LCD =====
LiquidCrystal_I2C lcd(0x27, 16, 2);
// ===== Pins (adjust if needed) =====
// RGB LED (common cathode)
const int rgbRedPin = 14;
const int rgbGreenPin = 12;
const int rgbBluePin = 13;
const int pirPin = 33; // PIR motion sensor OUT
const int buttonPin = 19; // button to GND (INPUT_PULLUP)
const int buzzerPin = 32; // active/passive buzzer (tone works on ESP32)
// ===== States =====
enum State { IDLE, MOTION, REGISTERED };
State state = IDLE;
// ===== Timing =====
unsigned long tStateStart = 0;
// Variables for MOTION cycle steps
unsigned long tStep = 0;
int motionStep = 0;
// Button debounce
int lastButtonReading = HIGH;
int stableButtonState = HIGH;
unsigned long tLastDebounce = 0;
const unsigned long debounceMs = 30;
// ===== RGB helpers =====
void rgbWrite(bool r, bool g, bool b) {
digitalWrite(rgbRedPin, r ? HIGH : LOW);
digitalWrite(rgbGreenPin, g ? HIGH : LOW);
digitalWrite(rgbBluePin, b ? HIGH : LOW);
}
void rgbOff() { rgbWrite(false, false, false); }
void rgbBlue() { rgbWrite(false, false, true); }
void rgbGreen(){ rgbWrite(false, true, false); }
// ===== LCD helpers =====
void lcdShow(const char* line1, const char* line2) {
lcd.setCursor(0, 0);
lcd.print(" ");
lcd.setCursor(0, 1);
lcd.print(" ");
lcd.setCursor(0, 0);
lcd.print(line1);
lcd.setCursor(0, 1);
lcd.print(line2);
}
// ===== Friendly sound (mini jingle) =====
void friendlyBeep() {
tone(buzzerPin, 880);
delay(80);
tone(buzzerPin, 1300);
delay(100);
tone(buzzerPin, 0); // stop
}
// ===== Button read with debounce (returns true on press edge) =====
bool buttonPressedEdge() {
int reading = digitalRead(buttonPin);
if (reading != lastButtonReading) {
tLastDebounce = millis();
lastButtonReading = reading;
}
if ((millis() - tLastDebounce) > debounceMs) {
if (reading != stableButtonState) {
stableButtonState = reading;
// Using INPUT_PULLUP → pressed = LOW
if (stableButtonState == LOW) return true;
}
}
return false;
}
// ===== Change system state =====
void setState(State s) {
state = s;
tStateStart = millis();
if (state == IDLE) {
rgbBlue();
lcdShow("Welcome", "");
}
if (state == MOTION) {
// Initialize motion cycle
motionStep = 0;
tStep = millis();
}
if (state == REGISTERED) {
rgbGreen();
lcdShow("Thank you :)", "");
}
}
void setup() {
Serial.begin(115200);
pinMode(rgbRedPin, OUTPUT);
pinMode(rgbGreenPin, OUTPUT);
pinMode(rgbBluePin, OUTPUT);
pinMode(pirPin, INPUT_PULLDOWN);
pinMode(buttonPin, INPUT_PULLUP);
pinMode(buzzerPin, OUTPUT);
lcd.init();
lcd.backlight();
setState(IDLE);
}
void loop() {
const bool motion = (digitalRead(pirPin) == HIGH);
// If button is pressed in any state → register action
if (buttonPressedEdge()) {
setState(REGISTERED);
}
// Motion-based transitions
if (state == IDLE) {
if (motion) setState(MOTION);
}
else if (state == MOTION) {
if (!motion) setState(IDLE);
}
// ===== State logic =====
if (state == IDLE) {
// Maintain idle state
return;
}
if (state == REGISTERED) {
// Keep green LED for 1 second
if (millis() - tStateStart >= 1000) {
// After 2 seconds total → return to idle
if (millis() - tStateStart >= 2000) {
setState(IDLE);
}
}
return;
}
if (state == MOTION) {
// Repeat while motion is detected:
// - Blink LED 3 times
// - Play friendly sound
// - Alternate LCD messages
// - Wait 2s between repetitions
const unsigned long blinkOn = 150;
const unsigned long blinkOff = 150;
const unsigned long msgHold = 1000;
const unsigned long gapBetweenReps = 2000;
unsigned long now = millis();
switch (motionStep) {
case 0:
rgbBlue();
friendlyBeep();
tStep = now;
motionStep = 1;
break;
case 1:
if (now - tStep >= blinkOn) { rgbOff(); tStep = now; motionStep = 2; }
break;
case 2:
if (now - tStep >= blinkOff) { rgbBlue(); tStep = now; motionStep = 3; }
break;
case 3:
if (now - tStep >= blinkOn) { rgbOff(); tStep = now; motionStep = 4; }
break;
case 4:
if (now - tStep >= blinkOff) { rgbBlue(); tStep = now; motionStep = 5; }
break;
case 5:
if (now - tStep >= blinkOn) { rgbOff(); tStep = now; motionStep = 6; }
break;
case 6:
if (now - tStep >= blinkOff) {
rgbBlue();
lcdShow("Hello there!", "");
tStep = now;
motionStep = 7;
}
break;
case 7:
if (now - tStep >= msgHold) {
lcdShow("Register here", "");
tStep = now;
motionStep = 8;
}
break;
case 8:
if (now - tStep >= msgHold) {
tStep = now;
motionStep = 9;
}
break;
case 9:
if (now - tStep >= gapBetweenReps) {
if (digitalRead(pirPin) == HIGH) {
motionStep = 0;
} else {
setState(IDLE);
}
}
break;
}
}
}