Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Select an option

  • Save Ali-Shaikh/3b38a410079c8b1a5cd616ebf3ee0b98 to your computer and use it in GitHub Desktop.

Select an option

Save Ali-Shaikh/3b38a410079c8b1a5cd616ebf3ee0b98 to your computer and use it in GitHub Desktop.
ESP32 Traffic Light with Ultrasonic Sensor - Kids Electronics Project
#include <WiFi.h>
#include <WebServer.h>
#include <ArduinoOTA.h>
// ================= Find full instructions and more projects like this at https://alishaikh.me/ ===========
// ========== WIFI CONFIGURATION - CHANGE THESE ==========
const char* ssid = ""; // Change to your WiFi name
const char* password = ""; // Change to your WiFi password
// Static IP Configuration
IPAddress local_IP(192, 168, 0, 115); // Change to your desired IP
IPAddress gateway(192, 168, 0, 1); // Usually your router IP
IPAddress subnet(255, 255, 255, 0); // Subnet mask
IPAddress primaryDNS(192, 168, 0, 1); // Optional
// =======================================================
// OTA Configuration
const char* ota_hostname = "TrafficLight"; // Name that appears in Arduino IDE
// To generate your own: echo -n "yourpassword" | sha256sum
const char* ota_password_hash = "";
// =======================================================
// Pin definitions - adjust these based on your actual wiring
const int RED_LED = 25;
const int YELLOW_LED = 26;
const int GREEN_LED = 27;
const int BTN_RED = 33;
const int BTN_YELLOW = 32;
const int BTN_GREEN = 35;
// ========== PROXIMITY SENSOR PINS ==========
const int TRIG_PIN = 18; // Ultrasonic sensor trigger
const int ECHO_PIN = 19; // Ultrasonic sensor echo
// ================================================
// ========== TIMING CONFIGURATION - ADJUST THESE ==========
// Traffic Light Mode Timings (in milliseconds)
const unsigned long TRAFFIC_RED_TIME = 4000; // How long red stays on
const unsigned long TRAFFIC_YELLOW_TIME = 2000; // How long yellow stays on
const unsigned long TRAFFIC_GREEN_TIME = 4000; // How long green stays on
// Fast Blink Mode Timing (in milliseconds)
const unsigned long FAST_BLINK_TIME = 300; // How long each LED stays on in sequence
// ========== PEEK-A-BOO CONFIGURATION ==========
const int PEEK_DISTANCE_CM = 40; // How close to trigger (40cm = ~16 inches)
// ========== DISTANCE GAME CONFIGURATION ==========
const int FAR_DISTANCE_CM = 100; // Red turns on (far)
const int MID_DISTANCE_CM = 80; // Yellow turns on (medium)
const int CLOSE_DISTANCE_CM = 50; // Green turns on (close)
const int VERY_CLOSE_DISTANCE_CM = 30; // Start blinking (very close!)
// ====================================================
// ==========================================================
// LED states for toggle mode
bool redState = false;
bool yellowState = false;
bool greenState = false;
// Button state tracking
bool lastRedBtn = HIGH;
bool lastYellowBtn = HIGH;
bool lastGreenBtn = HIGH;
// Debounce timing
unsigned long lastDebounce[3] = {0, 0, 0};
const int debounceDelay = 50;
// Triple press detection
int greenPressCount = 0;
int yellowPressCount = 0;
int redPressCount = 0;
unsigned long lastGreenPress = 0;
unsigned long lastYellowPress = 0;
unsigned long lastRedPress = 0;
const unsigned long tripleClickWindow = 1000;
// Mode control
enum Mode { MANUAL, TRAFFIC_LIGHT, FAST_BLINK, PEEK_A_BOO, DISTANCE_GAME };
Mode currentMode = MANUAL;
// Traffic light timing
unsigned long lastTrafficChange = 0;
int trafficState = 0;
// Fast blink timing
unsigned long lastBlinkChange = 0;
int blinkState = 0;
// ========== PEEK-A-BOO STATE ==========
unsigned long lastPeekCheck = 0;
bool wasClose = false;
// ======================================
// ========== DISTANCE GAME STATE ==========
unsigned long lastDistanceCheck = 0;
unsigned long lastDistanceBlink = 0;
bool distanceBlinkState = false;
long lastStableDistance = 0;
int stableReadings = 0;
// ==========================================
// Web server
WebServer server(80);
void setup() {
Serial.begin(115200);
Serial.println("Traffic Light Controller Starting...");
pinMode(RED_LED, OUTPUT);
pinMode(YELLOW_LED, OUTPUT);
pinMode(GREEN_LED, OUTPUT);
pinMode(BTN_RED, INPUT_PULLUP);
pinMode(BTN_YELLOW, INPUT_PULLUP);
pinMode(BTN_GREEN, INPUT_PULLUP);
digitalWrite(RED_LED, LOW);
digitalWrite(YELLOW_LED, LOW);
digitalWrite(GREEN_LED, LOW);
// Initialize sensor pins
pinMode(TRIG_PIN, OUTPUT);
pinMode(ECHO_PIN, INPUT);
digitalWrite(TRIG_PIN, LOW);
Serial.println("Waiting for sensor to stabilize...");
delay(1000);
// Warm-up readings
Serial.println("Warming up sensor...");
for (int i = 0; i < 10; i++) {
digitalWrite(TRIG_PIN, LOW);
delayMicroseconds(5);
digitalWrite(TRIG_PIN, HIGH);
delayMicroseconds(10);
digitalWrite(TRIG_PIN, LOW);
pulseIn(ECHO_PIN, HIGH, 30000);
delay(150);
}
Serial.println("Sensor ready!");
// Configure static IP
if (!WiFi.config(local_IP, gateway, subnet, primaryDNS)) {
Serial.println("Static IP configuration failed!");
}
// Connect to WiFi
Serial.println("Connecting to WiFi...");
WiFi.begin(ssid, password);
int attempts = 0;
while (WiFi.status() != WL_CONNECTED && attempts < 20) {
delay(500);
Serial.print(".");
attempts++;
}
if (WiFi.status() == WL_CONNECTED) {
Serial.println("\n====================================");
Serial.println("WiFi Connected!");
Serial.print("Static IP Address: ");
Serial.println(WiFi.localIP());
Serial.println("====================================");
Serial.print("Open this in your phone's browser: http://");
Serial.println(WiFi.localIP());
Serial.println("====================================");
// Setup OTA
setupOTA();
} else {
Serial.println("\nWiFi Connection Failed!");
Serial.println("Controller will work with buttons only");
}
// Setup web server routes
server.on("/", handleRoot);
server.on("/toggle/red", handleToggleRed);
server.on("/toggle/yellow", handleToggleYellow);
server.on("/toggle/green", handleToggleGreen);
server.on("/mode/manual", handleModeManual);
server.on("/mode/traffic", handleModeTraffic);
server.on("/mode/blink", handleModeBlink);
server.on("/mode/peekaboo", handleModePeekaBoo);
server.on("/mode/distance", handleModeDistance);
server.on("/status", handleStatus);
server.begin();
Serial.println("Web server started");
Serial.println("\nReady! Press buttons to toggle LEDs.");
Serial.println("Press GREEN 3 times for traffic light mode");
Serial.println("Press YELLOW 3 times for fast blink mode");
Serial.println("Press RED 3 times to return to manual mode");
Serial.println("Use web interface for Peek-a-Boo and Distance Game!");
}
void setupOTA() {
// Set OTA hostname
ArduinoOTA.setHostname(ota_hostname);
// Set OTA password using SHA256 hash (secure method)
ArduinoOTA.setPasswordHash(ota_password_hash);
ArduinoOTA.onStart([]() {
String type;
if (ArduinoOTA.getCommand() == U_FLASH) {
type = "sketch";
} else {
type = "filesystem";
}
Serial.println("Start updating " + type);
digitalWrite(RED_LED, HIGH);
digitalWrite(YELLOW_LED, HIGH);
digitalWrite(GREEN_LED, HIGH);
});
ArduinoOTA.onEnd([]() {
Serial.println("\nEnd");
for (int i = 0; i < 5; i++) {
digitalWrite(RED_LED, LOW);
digitalWrite(YELLOW_LED, LOW);
digitalWrite(GREEN_LED, LOW);
delay(100);
digitalWrite(RED_LED, HIGH);
digitalWrite(YELLOW_LED, HIGH);
digitalWrite(GREEN_LED, HIGH);
delay(100);
}
});
ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) {
Serial.printf("Progress: %u%%\r", (progress / (total / 100)));
static unsigned long lastBlink = 0;
if (millis() - lastBlink > 200) {
digitalWrite(RED_LED, !digitalRead(RED_LED));
digitalWrite(YELLOW_LED, !digitalRead(YELLOW_LED));
digitalWrite(GREEN_LED, !digitalRead(GREEN_LED));
lastBlink = millis();
}
});
ArduinoOTA.onError([](ota_error_t error) {
Serial.printf("Error[%u]: ", error);
if (error == OTA_AUTH_ERROR) {
Serial.println("Auth Failed");
} else if (error == OTA_BEGIN_ERROR) {
Serial.println("Begin Failed");
} else if (error == OTA_CONNECT_ERROR) {
Serial.println("Connect Failed");
} else if (error == OTA_RECEIVE_ERROR) {
Serial.println("Receive Failed");
} else if (error == OTA_END_ERROR) {
Serial.println("End Failed");
}
for (int i = 0; i < 10; i++) {
digitalWrite(RED_LED, HIGH);
delay(100);
digitalWrite(RED_LED, LOW);
delay(100);
}
});
ArduinoOTA.begin();
Serial.println("====================================");
Serial.println("OTA Update Enabled!");
Serial.print("Hostname: ");
Serial.println(ota_hostname);
Serial.println("Using SHA256 secure authentication");
Serial.println("====================================");
}
void loop() {
unsigned long now = millis();
ArduinoOTA.handle();
server.handleClient();
if (currentMode == MANUAL) {
handleManualMode(now);
} else if (currentMode == TRAFFIC_LIGHT) {
handleTrafficLightMode(now);
} else if (currentMode == FAST_BLINK) {
handleFastBlinkMode(now);
} else if (currentMode == PEEK_A_BOO) {
handlePeekaBooMode(now);
} else if (currentMode == DISTANCE_GAME) {
handleDistanceGameMode(now);
}
delay(10);
}
// ========== PROXIMITY SENSOR FUNCTIONS ==========
long getDistance() {
digitalWrite(TRIG_PIN, LOW);
delayMicroseconds(5);
digitalWrite(TRIG_PIN, HIGH);
delayMicroseconds(10);
digitalWrite(TRIG_PIN, LOW);
long duration = pulseIn(ECHO_PIN, HIGH, 50000);
if (duration == 0 || duration > 25000) {
return 0;
}
long distance = duration * 0.034 / 2;
if (distance < 2 || distance > 400) {
return 0;
}
return distance;
}
// Get stable distance reading (average of 3 readings)
long getStableDistance() {
long total = 0;
int validReadings = 0;
Serial.println("--- Taking 3 sensor readings ---");
for (int i = 0; i < 3; i++) {
long dist = getDistance();
Serial.print(" Reading ");
Serial.print(i + 1);
Serial.print(": ");
Serial.print(dist);
Serial.println(" cm");
if (dist > 0 && dist < 400) { // Valid range
total += dist;
validReadings++;
}
delay(10);
}
if (validReadings > 0) {
long average = total / validReadings;
Serial.print(" Average: ");
Serial.print(average);
Serial.print(" cm (from ");
Serial.print(validReadings);
Serial.println(" valid readings)");
return average;
}
Serial.println(" No valid readings!");
return 0;
}
bool isBabyClose() {
long distance = getStableDistance();
return (distance > 0 && distance < PEEK_DISTANCE_CM);
}
// ========== PEEK-A-BOO MODE ==========
void handlePeekaBooMode(unsigned long now) {
// Allow red button triple-press to exit
bool currentRedBtn = digitalRead(BTN_RED);
if (currentRedBtn == LOW && lastRedBtn == HIGH && (now - lastDebounce[0]) > debounceDelay) {
if (now - lastRedPress < tripleClickWindow) {
redPressCount++;
if (redPressCount >= 3) {
currentMode = MANUAL;
Serial.println("=== RETURNING TO MANUAL MODE ===");
redPressCount = 0;
digitalWrite(RED_LED, LOW);
digitalWrite(YELLOW_LED, LOW);
digitalWrite(GREEN_LED, LOW);
wasClose = false;
}
} else {
redPressCount = 1;
}
lastRedPress = now;
lastDebounce[0] = now;
}
lastRedBtn = currentRedBtn;
// Check sensor every 200ms
if (now - lastPeekCheck > 200) {
bool isClose = isBabyClose();
// Get and print actual distance
long distance = getStableDistance();
Serial.print("Peek-a-Boo Distance: ");
Serial.print(distance);
Serial.print(" cm | Trigger threshold: ");
Serial.print(PEEK_DISTANCE_CM);
Serial.print(" cm | Status: ");
Serial.println(isClose ? "CLOSE" : "FAR");
if (isClose && !wasClose) {
// Baby just got close - PEEK-A-BOO!
Serial.println("*** PEEK-A-BOO! ***");
// Quick flash
for (int i = 0; i < 3; i++) {
digitalWrite(RED_LED, HIGH);
digitalWrite(YELLOW_LED, HIGH);
digitalWrite(GREEN_LED, HIGH);
delay(100);
digitalWrite(RED_LED, LOW);
digitalWrite(YELLOW_LED, LOW);
digitalWrite(GREEN_LED, LOW);
delay(100);
}
// Leave them all on
digitalWrite(RED_LED, HIGH);
digitalWrite(YELLOW_LED, HIGH);
digitalWrite(GREEN_LED, HIGH);
} else if (!isClose && wasClose) {
// Baby moved away
Serial.println("*** Baby moved away ***");
digitalWrite(RED_LED, LOW);
digitalWrite(YELLOW_LED, LOW);
digitalWrite(GREEN_LED, LOW);
} else if (isClose) {
// Baby still close - sparkle effect
Serial.println(" Sparkling...");
if ((now / 300) % 2 == 0) {
digitalWrite(RED_LED, HIGH);
digitalWrite(YELLOW_LED, HIGH);
digitalWrite(GREEN_LED, HIGH);
} else {
int whichLight = (now / 300) % 3;
digitalWrite(RED_LED, whichLight == 0);
digitalWrite(YELLOW_LED, whichLight == 1);
digitalWrite(GREEN_LED, whichLight == 2);
}
}
wasClose = isClose;
lastPeekCheck = now;
}
}
// ========== DISTANCE GAME MODE ==========
void handleDistanceGameMode(unsigned long now) {
// Allow red button triple-press to exit
bool currentRedBtn = digitalRead(BTN_RED);
if (currentRedBtn == LOW && lastRedBtn == HIGH && (now - lastDebounce[0]) > debounceDelay) {
if (now - lastRedPress < tripleClickWindow) {
redPressCount++;
if (redPressCount >= 3) {
currentMode = MANUAL;
Serial.println("=== RETURNING TO MANUAL MODE ===");
redPressCount = 0;
digitalWrite(RED_LED, LOW);
digitalWrite(YELLOW_LED, LOW);
digitalWrite(GREEN_LED, LOW);
}
} else {
redPressCount = 1;
}
lastRedPress = now;
lastDebounce[0] = now;
}
lastRedBtn = currentRedBtn;
// Check sensor every 200ms for stability
if (now - lastDistanceCheck > 200) {
long distance = getStableDistance();
// OUTPUT DISTANCE TO SERIAL
if (distance > 0) {
Serial.print("Distance: ");
Serial.print(distance);
Serial.println(" cm");
} else {
Serial.println("Distance: Out of range or no reading");
}
// Only update if distance changed significantly (more than 5cm)
if (abs(distance - lastStableDistance) > 5) {
lastStableDistance = distance;
stableReadings = 0;
Serial.println(">> Distance changed significantly");
} else {
stableReadings++;
}
// Use stable distance if we have consistent readings
if (stableReadings > 2) {
distance = lastStableDistance;
}
if (distance > 0 && distance < VERY_CLOSE_DISTANCE_CM) {
// VERY CLOSE! Blink all lights fast!
Serial.println(">>> VERY CLOSE - BLINKING!");
if (now - lastDistanceBlink > 150) {
distanceBlinkState = !distanceBlinkState;
digitalWrite(RED_LED, distanceBlinkState);
digitalWrite(YELLOW_LED, distanceBlinkState);
digitalWrite(GREEN_LED, distanceBlinkState);
lastDistanceBlink = now;
}
} else {
// Not very close - use progressive lighting
digitalWrite(RED_LED, LOW);
digitalWrite(YELLOW_LED, LOW);
digitalWrite(GREEN_LED, LOW);
if (distance > 0 && distance < FAR_DISTANCE_CM) {
// Within far range - turn on RED
digitalWrite(RED_LED, HIGH);
Serial.println(">> RED ON");
if (distance < MID_DISTANCE_CM) {
// Within medium range - also turn on YELLOW
digitalWrite(YELLOW_LED, HIGH);
Serial.println(">> RED + YELLOW ON");
if (distance < CLOSE_DISTANCE_CM) {
// Within close range - turn on all lights!
digitalWrite(GREEN_LED, HIGH);
Serial.println(">> ALL LIGHTS ON");
}
}
} else if (distance > 0) {
Serial.println(">> Too far - all lights OFF");
}
}
lastDistanceCheck = now;
}
}
// =====================================================
// ========== WEB SERVER HANDLERS ==========
void handleRoot() {
String html = "<!DOCTYPE html><html><head>";
html += "<meta charset='UTF-8'>";
html += "<meta name='viewport' content='width=device-width, initial-scale=1'>";
html += "<style>";
html += "body { font-family: Arial; text-align: center; margin: 10px; background: #f0f0f0; }";
html += ".container { max-width: 400px; margin: 0 auto; background: white; padding: 15px; border-radius: 10px; box-shadow: 0 2px 10px rgba(0,0,0,0.1); }";
html += "h1 { color: #333; margin: 10px 0; font-size: 24px; }";
html += "h2 { color: #333; margin: 15px 0 10px 0; font-size: 18px; }";
html += ".status { font-size: 16px; margin: 10px 0; padding: 10px; background: #e8f4f8; border-radius: 5px; }";
html += ".btn { width: 100%; padding: 18px; margin: 5px 0; font-size: 16px; border: none; border-radius: 5px; cursor: pointer; color: white; font-weight: bold; }";
html += ".btn-red { background: #dc3545; }";
html += ".btn-yellow { background: #ffc107; color: #333; }";
html += ".btn-green { background: #28a745; }";
html += ".btn-manual { background: #6c757d; }";
html += ".btn-traffic { background: #007bff; }";
html += ".btn-blink { background: #0fbcac; }";
html += ".btn-peekaboo { background: #f74da2; }";
html += ".btn-distance { background: #e46411; }";
html += ".btn:active { opacity: 0.7; }";
html += ".mode-section { margin-top: 15px; padding-top: 15px; border-top: 2px solid #ddd; }";
html += ".ota-info { font-size: 11px; color: #666; margin-top: 15px; padding: 8px; background: #f8f9fa; border-radius: 5px; }";
html += ".info { font-size: 11px; color: #666; margin: 5px 0; padding: 6px; background: #fff3cd; border-radius: 5px; line-height: 1.3; }";
html += "</style>";
html += "<script>";
html += "function updateStatus() {";
html += " fetch('/status').then(r => r.json()).then(data => {";
html += " document.getElementById('mode').textContent = data.mode;";
html += " document.getElementById('red').textContent = data.red;";
html += " document.getElementById('yellow').textContent = data.yellow;";
html += " document.getElementById('green').textContent = data.green;";
html += " });";
html += "}";
html += "function sendCommand(url) {";
html += " fetch(url).then(() => setTimeout(updateStatus, 100));";
html += "}";
html += "setInterval(updateStatus, 1000);";
html += "</script>";
html += "</head><body>";
html += "<div class='container'>";
html += "<h1>&#128678; Traffic Light 01</h1>";
html += "<div class='status'>";
html += "<strong>Mode:</strong> <span id='mode'>MANUAL</span><br>";
html += "Red: <span id='red'>OFF</span> | ";
html += "Yellow: <span id='yellow'>OFF</span> | ";
html += "Green: <span id='green'>OFF</span>";
html += "</div>";
html += "<h2>Toggle LEDs</h2>";
html += "<button class='btn btn-red' onclick='sendCommand(\"/toggle/red\")'>Red</button>";
html += "<button class='btn btn-yellow' onclick='sendCommand(\"/toggle/yellow\")'>Yellow</button>";
html += "<button class='btn btn-green' onclick='sendCommand(\"/toggle/green\")'>Green</button>";
html += "<div class='mode-section'>";
html += "<h2>Modes</h2>";
html += "<button class='btn btn-manual' onclick='sendCommand(\"/mode/manual\")'>&#9881; Manual</button>";
html += "<button class='btn btn-blink' onclick='sendCommand(\"/mode/blink\")'>&#9889; Fast Blink</button>";
html += "<button class='btn btn-peekaboo' onclick='sendCommand(\"/mode/peekaboo\")'>&#128118; Peek-a-Boo</button>";
html += "<div class='info'>Lights flash when baby peeks close (" + String(PEEK_DISTANCE_CM) + "cm)!</div>";
html += "<button class='btn btn-distance' onclick='sendCommand(\"/mode/distance\")'>&#127939; Distance Game</button>";
html += "<div class='info'>" + String(FAR_DISTANCE_CM) + "cm=Red | " + String(MID_DISTANCE_CM) + "cm=Red+Yellow | " + String(CLOSE_DISTANCE_CM) + "cm=All | " + String(VERY_CLOSE_DISTANCE_CM) + "cm=Blink!</div>";
html += "<button class='btn btn-traffic' onclick='sendCommand(\"/mode/traffic\")'>&#128678; Traffic Light</button>";
html += "</div>";
html += "<div class='ota-info'>";
html += "OTA: " + String(ota_hostname) + " | IP: " + WiFi.localIP().toString();
html += "</div>";
html += "</div>";
html += "<script>updateStatus();</script>";
html += "</body></html>";
server.send(200, "text/html", html);
}
void handleToggleRed() {
if (currentMode == MANUAL) {
redState = !redState;
digitalWrite(RED_LED, redState);
Serial.println(redState ? "Web: Red ON" : "Web: Red OFF");
}
server.send(200, "text/plain", "OK");
}
void handleToggleYellow() {
if (currentMode == MANUAL) {
yellowState = !yellowState;
digitalWrite(YELLOW_LED, yellowState);
Serial.println(yellowState ? "Web: Yellow ON" : "Web: Yellow OFF");
}
server.send(200, "text/plain", "OK");
}
void handleToggleGreen() {
if (currentMode == MANUAL) {
greenState = !greenState;
digitalWrite(GREEN_LED, greenState);
Serial.println(greenState ? "Web: Green ON" : "Web: Green OFF");
}
server.send(200, "text/plain", "OK");
}
void handleModeManual() {
currentMode = MANUAL;
digitalWrite(RED_LED, LOW);
digitalWrite(YELLOW_LED, LOW);
digitalWrite(GREEN_LED, LOW);
redState = false;
yellowState = false;
greenState = false;
wasClose = false;
Serial.println("Web: MANUAL MODE");
server.send(200, "text/plain", "OK");
}
void handleModeTraffic() {
currentMode = TRAFFIC_LIGHT;
trafficState = 0;
lastTrafficChange = millis();
wasClose = false;
Serial.println("Web: TRAFFIC LIGHT MODE");
server.send(200, "text/plain", "OK");
}
void handleModeBlink() {
currentMode = FAST_BLINK;
blinkState = 0;
lastBlinkChange = millis();
wasClose = false;
Serial.println("Web: FAST BLINK MODE");
server.send(200, "text/plain", "OK");
}
void handleModePeekaBoo() {
currentMode = PEEK_A_BOO;
wasClose = false;
digitalWrite(RED_LED, LOW);
digitalWrite(YELLOW_LED, LOW);
digitalWrite(GREEN_LED, LOW);
Serial.println("Web: PEEK-A-BOO MODE");
server.send(200, "text/plain", "OK");
}
void handleModeDistance() {
currentMode = DISTANCE_GAME;
lastStableDistance = 0;
stableReadings = 0;
digitalWrite(RED_LED, LOW);
digitalWrite(YELLOW_LED, LOW);
digitalWrite(GREEN_LED, LOW);
Serial.println("Web: DISTANCE GAME MODE");
server.send(200, "text/plain", "OK");
}
void handleStatus() {
String json = "{";
json += "\"mode\":\"";
if (currentMode == MANUAL) json += "MANUAL";
else if (currentMode == TRAFFIC_LIGHT) json += "TRAFFIC LIGHT";
else if (currentMode == FAST_BLINK) json += "FAST BLINK";
else if (currentMode == PEEK_A_BOO) json += "PEEK-A-BOO";
else json += "DISTANCE GAME";
json += "\",";
json += "\"red\":\"" + String(digitalRead(RED_LED) ? "ON" : "OFF") + "\",";
json += "\"yellow\":\"" + String(digitalRead(YELLOW_LED) ? "ON" : "OFF") + "\",";
json += "\"green\":\"" + String(digitalRead(GREEN_LED) ? "ON" : "OFF") + "\"";
json += "}";
server.send(200, "application/json", json);
}
// ========== BUTTON HANDLING ==========
void handleManualMode(unsigned long now) {
bool currentRedBtn = digitalRead(BTN_RED);
if (currentRedBtn == LOW && lastRedBtn == HIGH && (now - lastDebounce[0]) > debounceDelay) {
if (now - lastRedPress < tripleClickWindow) {
redPressCount++;
if (redPressCount >= 3) {
Serial.println("=== ALREADY IN MANUAL MODE ===");
redPressCount = 0;
}
} else {
redPressCount = 1;
}
lastRedPress = now;
redState = !redState;
digitalWrite(RED_LED, redState);
Serial.println(redState ? "Red ON" : "Red OFF");
lastDebounce[0] = now;
}
lastRedBtn = currentRedBtn;
bool currentYellowBtn = digitalRead(BTN_YELLOW);
if (currentYellowBtn == LOW && lastYellowBtn == HIGH && (now - lastDebounce[1]) > debounceDelay) {
if (now - lastYellowPress < tripleClickWindow) {
yellowPressCount++;
if (yellowPressCount >= 3) {
currentMode = FAST_BLINK;
Serial.println("=== FAST BLINK MODE ACTIVATED ===");
yellowPressCount = 0;
lastBlinkChange = now;
blinkState = 0;
digitalWrite(RED_LED, LOW);
digitalWrite(YELLOW_LED, LOW);
digitalWrite(GREEN_LED, LOW);
lastDebounce[1] = now;
lastYellowBtn = currentYellowBtn;
return;
}
} else {
yellowPressCount = 1;
}
lastYellowPress = now;
yellowState = !yellowState;
digitalWrite(YELLOW_LED, yellowState);
Serial.println(yellowState ? "Yellow ON" : "Yellow OFF");
lastDebounce[1] = now;
}
lastYellowBtn = currentYellowBtn;
bool currentGreenBtn = digitalRead(BTN_GREEN);
if (currentGreenBtn == LOW && lastGreenBtn == HIGH && (now - lastDebounce[2]) > debounceDelay) {
if (now - lastGreenPress < tripleClickWindow) {
greenPressCount++;
if (greenPressCount >= 3) {
currentMode = TRAFFIC_LIGHT;
Serial.println("=== TRAFFIC LIGHT MODE ACTIVATED ===");
greenPressCount = 0;
trafficState = 0;
lastTrafficChange = now;
digitalWrite(RED_LED, LOW);
digitalWrite(YELLOW_LED, LOW);
digitalWrite(GREEN_LED, LOW);
lastDebounce[2] = now;
lastGreenBtn = currentGreenBtn;
return;
}
} else {
greenPressCount = 1;
}
lastGreenPress = now;
greenState = !greenState;
digitalWrite(GREEN_LED, greenState);
Serial.println(greenState ? "Green ON" : "Green OFF");
lastDebounce[2] = now;
}
lastGreenBtn = currentGreenBtn;
}
void handleTrafficLightMode(unsigned long now) {
bool currentRedBtn = digitalRead(BTN_RED);
if (currentRedBtn == LOW && lastRedBtn == HIGH && (now - lastDebounce[0]) > debounceDelay) {
if (now - lastRedPress < tripleClickWindow) {
redPressCount++;
if (redPressCount >= 3) {
currentMode = MANUAL;
Serial.println("=== RETURNING TO MANUAL MODE ===");
redPressCount = 0;
digitalWrite(RED_LED, LOW);
digitalWrite(YELLOW_LED, LOW);
digitalWrite(GREEN_LED, LOW);
redState = false;
yellowState = false;
greenState = false;
}
} else {
redPressCount = 1;
}
lastRedPress = now;
lastDebounce[0] = now;
}
lastRedBtn = currentRedBtn;
switch (trafficState) {
case 0:
if (now - lastTrafficChange > TRAFFIC_RED_TIME) {
digitalWrite(RED_LED, LOW);
digitalWrite(YELLOW_LED, HIGH);
digitalWrite(GREEN_LED, LOW);
trafficState = 1;
lastTrafficChange = now;
Serial.println("Traffic: YELLOW");
} else if (now - lastTrafficChange == 0 || digitalRead(RED_LED) == LOW) {
digitalWrite(RED_LED, HIGH);
digitalWrite(YELLOW_LED, LOW);
digitalWrite(GREEN_LED, LOW);
}
break;
case 1:
if (now - lastTrafficChange > TRAFFIC_YELLOW_TIME) {
digitalWrite(RED_LED, LOW);
digitalWrite(YELLOW_LED, LOW);
digitalWrite(GREEN_LED, HIGH);
trafficState = 2;
lastTrafficChange = now;
Serial.println("Traffic: GREEN");
}
break;
case 2:
if (now - lastTrafficChange > TRAFFIC_GREEN_TIME) {
digitalWrite(RED_LED, HIGH);
digitalWrite(YELLOW_LED, LOW);
digitalWrite(GREEN_LED, LOW);
trafficState = 0;
lastTrafficChange = now;
Serial.println("Traffic: RED");
}
break;
}
}
void handleFastBlinkMode(unsigned long now) {
bool currentRedBtn = digitalRead(BTN_RED);
if (currentRedBtn == LOW && lastRedBtn == HIGH && (now - lastDebounce[0]) > debounceDelay) {
if (now - lastRedPress < tripleClickWindow) {
redPressCount++;
if (redPressCount >= 3) {
currentMode = MANUAL;
Serial.println("=== RETURNING TO MANUAL MODE ===");
redPressCount = 0;
digitalWrite(RED_LED, LOW);
digitalWrite(YELLOW_LED, LOW);
digitalWrite(GREEN_LED, LOW);
redState = false;
yellowState = false;
greenState = false;
}
} else {
redPressCount = 1;
}
lastRedPress = now;
lastDebounce[0] = now;
}
lastRedBtn = currentRedBtn;
if (now - lastBlinkChange > FAST_BLINK_TIME) {
digitalWrite(RED_LED, LOW);
digitalWrite(YELLOW_LED, LOW);
digitalWrite(GREEN_LED, LOW);
switch (blinkState) {
case 0:
digitalWrite(RED_LED, HIGH);
blinkState = 1;
Serial.println("Blink: RED");
break;
case 1:
digitalWrite(YELLOW_LED, HIGH);
blinkState = 2;
Serial.println("Blink: YELLOW");
break;
case 2:
digitalWrite(GREEN_LED, HIGH);
blinkState = 0;
Serial.println("Blink: GREEN");
break;
}
lastBlinkChange = now;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment