/*
Smart Security Light (Event Detection) - ESP32 on Wokwi
Sensors:
- LDR (analog, voltage divider) -> ADC1_CH6 (GPIO34)
- PIR Motion Sensor (digital) -> GPIO27
Actuators:
- Buzzer (digital) -> GPIO25
- LED (digital) -> GPIO26
Event:
If DARK (LDR_ADC < LDR_THRESHOLD) AND MOTION (PIR == HIGH):
-> Buzzer ON for 5 seconds (non-blocking using millis)
-> LED ON while event active
Serial Monitor:
Prints raw ADC, mapped light %, PIR state, and event status.
*/
// ---- Pin Map ----
const int PIN_LDR = 34; // ADC1_CH6 (input only)
const int PIN_PIR = 27; // digital input
const int PIN_BUZZ = 25; // digital output
const int PIN_LED = 26; // digital output
// ---- Thresholds & Timings ----
int LDR_THRESHOLD = 1500; // Tune in setup() using calibration prints (0..4095). Lower = darker with typical divider.
float LIGHT_PERCENT_CEIL = 90.0; // For mapping ADC to an approximate "light %" (cosmetic)
unsigned long BUZZ_MS = 5000; // buzzer duration on event (ms)
unsigned long DEBOUNCE_MS= 200; // small debounce for PIR changes
unsigned long COOLDOWN_MS= 3000; // cooldown after a buzz to avoid spamming
// ---- Internal State ----
bool eventActive = false;
unsigned long eventStartMs = 0;
unsigned long lastPirChangeMs = 0;
unsigned long lastBuzzEndMs = 0;
void setup() {
Serial.begin(115200);
delay(100);
Serial.println(F("=== Smart Security Light (ESP32 + LDR + PIR) ==="));
Serial.println(F("Tips: Put hand over LDR to simulate darkness in Wokwi; toggle PIR by moving the green 'motion' ring."));
pinMode(PIN_PIR, INPUT);
pinMode(PIN_BUZZ, OUTPUT);
pinMode(PIN_LED, OUTPUT);
digitalWrite(PIN_BUZZ, LOW);
digitalWrite(PIN_LED, LOW);
// Optional: quick auto-scan to suggest threshold (simple baseline sampling at start)
int baseline = 0;
const int samples = 20;
for (int i=0; i<samples; i++) {
baseline += analogRead(PIN_LDR);
delay(10);
}
baseline /= samples;
// Suggest a threshold: 75% of baseline for "dark" if starting under normal light in Wokwi
int suggested = (int)(baseline * 0.75);
Serial.print(F("[Calib] Baseline ADC=")); Serial.print(baseline);
Serial.print(F(", suggested LDR_THRESHOLD≈")); Serial.println(suggested);
Serial.println(F("You can change LDR_THRESHOLD in code if needed."));
}
void loop() {
// Read sensors
int ldrRaw = analogRead(PIN_LDR); // 0..4095
bool pirRaw = digitalRead(PIN_PIR) == HIGH;
// Map LDR to pseudo 'light %' (cosmetic only)
float lightPct = (ldrRaw / 4095.0) * 100.0;
if (lightPct > LIGHT_PERCENT_CEIL) lightPct = LIGHT_PERCENT_CEIL; // clamp cosmetic ceiling
// Simple PIR debounce (ignore rapid toggles)
unsigned long now = millis();
static bool pirStable = false;
static bool pirPrev = false;
if (pirRaw != pirPrev) {
lastPirChangeMs = now;
pirPrev = pirRaw;
}
if (now - lastPirChangeMs > DEBOUNCE_MS) {
pirStable = pirRaw;
}
// Event Detection: DARK AND MOTION AND not in cooldown
bool isDark = (ldrRaw < LDR_THRESHOLD);
bool inCooldown = (now - lastBuzzEndMs < COOLDOWN_MS);
if (!eventActive && isDark && pirStable && !inCooldown) {
eventActive = true;
eventStartMs = now;
digitalWrite(PIN_BUZZ, HIGH);
digitalWrite(PIN_LED, HIGH);
Serial.println(F("[EVENT] DARK+MOTION detected → BUZZER ON, LED ON"));
}
// End event after BUZZ_MS
if (eventActive && (now - eventStartMs >= BUZZ_MS)) {
eventActive = false;
digitalWrite(PIN_BUZZ, LOW);
digitalWrite(PIN_LED, LOW);
lastBuzzEndMs = now;
Serial.println(F("[EVENT] Completed → BUZZER OFF, LED OFF (cooldown)"));
}
// Telemetry
static unsigned long lastPrint = 0;
if (now - lastPrint > 500) {
lastPrint = now;
Serial.print("LDR_ADC="); Serial.print(ldrRaw);
Serial.print(" (~"); Serial.print((int)lightPct); Serial.print("%)");
Serial.print(" | PIR="); Serial.print(pirStable ? "HIGH" : "LOW");
Serial.print(" | isDark="); Serial.print(isDark ? "1" : "0");
Serial.print(" | event="); Serial.print(eventActive ? "ON " : "OFF");
Serial.print(" | cooldown="); Serial.println(inCooldown ? "YES" : "NO");
}
}