/*
* Gesture Control with PAJ7620U2 + OLED + Buzzer + Relay + Web Server
* ARD:icon II IoT - ESP32 Dev Module
* Λειτουργίες: WiFi Access Point ή Station
*/
#include <Wire.h>
#include <DFRobot_PAJ7620U2.h>
#include <Adafruit_SSD1306.h>
#include <Adafruit_GFX.h>
#include <WiFi.h>
#include <WebServer.h>
// ========== ΡΥΘΜΙΣΕΙΣ ΛΕΙΤΟΥΡΓΙΑΣ WiFi ==========
#define WIFI_MODE_AP 0 // 0 = Access Point (δημιουργεί δικό του δίκτυο)
#define WIFI_MODE_STA 1 // 1 = Station (συνδέεται σε υπάρχον router)
int wifiMode = WIFI_MODE_AP; // ΑΛΛΑΞΕ ΕΔΩ για εναλλαγή
// Αν χρησιμοποιείς Station (wifiMode = 1), συμπλήρωσε τα δικά σου:
const char* ssid = "Redmi"; // Όνομα δικτύου
const char* password = "12345678";
// ========== OLED ==========
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_RESET -1
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
// ========== ΑΙΣΘΗΤΗΡΑΣ PAJ7620U2 ==========
DFRobot_PAJ7620U2 paj;
// ========== ΕΞΟΔΟΙ ==========
#define BUZZER_PIN 5
#define RELAY_PIN 16
// ========== ΜΕΤΑΒΛΗΤΕΣ ==========
String lastGesture = "None";
bool relayState = LOW;
unsigned long lastGestureTime = 0;
const unsigned long debounceTime = 300; // ms μεταξύ χειρονομιών
WebServer server(80);
// Για feedback buzzer
void beep(int duration, int frequency = 2000) {
tone(BUZZER_PIN, frequency);
delay(duration);
noTone(BUZZER_PIN);
}
// ========== ΜΕΤΑΤΡΟΠΗ ΧΕΙΡΟΝΟΜΙΑΣ ΣΕ STRING ==========
String gestureToString(DFRobot_PAJ7620U2::eGesture_t gesture) {
switch(gesture) {
case paj.eGestureUp: return "UP";
case paj.eGestureDown: return "DOWN";
case paj.eGestureLeft: return "LEFT";
case paj.eGestureRight: return "RIGHT";
case paj.eGestureForward: return "FORWARD";
case paj.eGestureBackward: return "BACKWARD";
case paj.eGestureClockwise: return "CLOCKWISE";
case paj.eGestureAntiClockwise: return "COUNTER_CW";
default: return "None";
}
}
// ========== ΕΝΕΡΓΕΙΕΣ ΑΝΑ ΧΕΙΡΟΝΟΜΙΑ ==========
void handleGesture(DFRobot_PAJ7620U2::eGesture_t gesture) {
String gestureName = gestureToString(gesture);
lastGesture = gestureName;
// Ηχητική επιβεβαίωση (σύντομο μπιπ)
beep(80, 2500);
// Λογική ελέγχου ρελέ (παράδειγμα)
if (gesture == paj.eGestureUp) {
digitalWrite(RELAY_PIN, HIGH);
relayState = HIGH;
Serial.println("→ Relay ON (UP)");
beep(150, 2000); // μακρύτερο μπιπ για επιβεβαίωση
}
else if (gesture == paj.eGestureDown) {
digitalWrite(RELAY_PIN, LOW);
relayState = LOW;
Serial.println("→ Relay OFF (DOWN)");
beep(150, 2000);
}
// Προαιρετικά: άλλες χειρονομίες μπορούν να κάνουν άλλες ενέργειες
else if (gesture == paj.eGestureLeft) {
// π.χ. αναβοσβήνει η οθόνη
Serial.println("← Left gesture - no relay action");
}
else if (gesture == paj.eGestureRight) {
Serial.println("→ Right gesture - no relay action");
}
// Ενημέρωση οθόνης
updateDisplay();
}
// ========== ΕΝΗΜΕΡΩΣΗ OLED ==========
void updateDisplay() {
display.clearDisplay();
display.setTextSize(1);
display.setCursor(0, 0);
display.println("=== GESTURE CTRL ===");
display.setTextSize(2);
display.setCursor(0, 18);
display.print("Last:");
display.println(lastGesture);
display.setTextSize(1);
display.setCursor(0, 42);
display.print("Relay: ");
display.println(relayState ? "ON " : "OFF");
// Εμφάνιση IP ή λειτουργίας
display.setCursor(0, 52);
if(wifiMode == WIFI_MODE_AP) {
display.print("AP: " + WiFi.softAPIP().toString());
} else {
display.print(WiFi.localIP());
}
display.display();
}
// ========== HTML ΙΣΤΟΣΕΛΙΔΑΣ ==========
String getHTML() {
String html = R"rawliteral(
<!DOCTYPE html>
<html>
<head>
<meta charset='UTF-8'>
<meta name='viewport' content='width=device-width, initial-scale=1'>
<title>Gesture Control ARD:icon II</title>
<style>
body { font-family: Arial; text-align: center; background: #1a1a2e; color: white; margin: 0; padding: 20px; }
.container { max-width: 600px; margin: 0 auto; background: #16213e; border-radius: 30px; padding: 20px; box-shadow: 0 0 20px rgba(0,0,0,0.5); }
h1 { color: #e94560; }
.gesture-box { background: #0f3460; border-radius: 20px; padding: 20px; margin: 20px 0; }
.gesture { font-size: 40px; font-weight: bold; color: #e94560; }
.relay-state { font-size: 24px; margin: 10px; }
button { background: #e94560; border: none; padding: 12px 25px; font-size: 18px; border-radius: 50px; cursor: pointer; margin: 5px; transition: 0.2s; }
button:hover { opacity: 0.8; transform: scale(1.02); }
.on { background: #2ecc71; }
.off { background: #e74c3c; }
.footer { margin-top: 20px; font-size: 12px; opacity: 0.7; }
</style>
<script>
function fetchData() {
fetch('/data')
.then(response => response.json())
.then(data => {
document.getElementById('lastGesture').innerText = data.gesture;
document.getElementById('relayState').innerText = data.relay ? "ON 🔥" : "OFF ❄️";
document.getElementById('relayState').style.color = data.relay ? "#2ecc71" : "#e74c3c";
})
.catch(err => console.log('Error:', err));
}
function setRelay(state) {
fetch('/relay?state=' + state)
.then(() => fetchData());
}
setInterval(fetchData, 1000);
window.onload = fetchData;
</script>
</head>
<body>
<div class='container'>
<h1>🎮 Gesture Control</h1>
<div class='gesture-box'>
<div>Τελευταία χειρονομία:</div>
<div class='gesture' id='lastGesture'>---</div>
</div>
<div class='relay-state'>
Ρελέ: <span id='relayState'>---</span>
</div>
<div>
<button class='on' onclick='setRelay(1)'>🔛 ON</button>
<button class='off' onclick='setRelay(0)'>🔴 OFF</button>
</div>
<div class='footer'>
Χρησιμοποιήστε χειρονομίες UP/DOWN ή τα κουμπιά
</div>
</div>
</body>
</html>
)rawliteral";
return html;
}
// ========== WEB SERVER ROUTES ==========
void setupWebServer() {
server.on("/", []() {
server.send(200, "text/html", getHTML());
});
server.on("/data", []() {
String json = "{\"gesture\":\"" + lastGesture + "\",\"relay\":" + String(relayState) + "}";
server.send(200, "application/json", json);
});
server.on("/relay", []() {
if (server.hasArg("state")) {
int state = server.arg("state").toInt();
digitalWrite(RELAY_PIN, state);
relayState = state;
lastGesture = (state ? "Web ON" : "Web OFF");
updateDisplay();
beep(100);
server.send(200, "text/plain", "OK");
} else {
server.send(400, "text/plain", "Missing param");
}
});
server.begin();
Serial.println("Web server started");
}
// ========== SETUP ==========
void setup() {
Serial.begin(115200);
Serial.println("Gesture System Starting...");
// Ξεκινάμε I2C με χαμηλή ταχύτητα
Wire.begin(21, 22);
Wire.setClock(100000); // 100 kHz
// OLED αρχικοποίηση
if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
Serial.println("OLED failed!");
while (true);
}
display.clearDisplay();
display.setTextSize(1);
display.setTextColor(SSD1306_WHITE);
display.setCursor(0,0);
display.println("OLED Init OK");
display.display();
delay(1000);
// Buzzer, Relay
pinMode(BUZZER_PIN, OUTPUT);
pinMode(RELAY_PIN, OUTPUT);
digitalWrite(RELAY_PIN, LOW);
relayState = LOW;
// PAJ7620U2 με επαναφορές
delay(500);
int retry = 0;
while (paj.begin() != 0 && retry < 5) {
Serial.println("PAJ7620U2 not found! Retrying...");
display.clearDisplay();
display.println("PAJ Error");
display.println("Retrying...");
display.display();
delay(2000);
retry++;
}
if (retry == 5) {
Serial.println("PAJ7620U2 failed permanently");
display.println("PAJ Failed");
display.display();
while (true);
}
Serial.println("PAJ7620U2 OK");
paj.setGestureHighRate(true);
display.clearDisplay();
display.println("PAJ OK");
display.display();
delay(500);
// WiFi setup (όπως πριν)
if (wifiMode == WIFI_MODE_AP) {
WiFi.softAP("ARDicon_Gesture", "12345678");
Serial.print("AP IP: ");
Serial.println(WiFi.softAPIP());
display.clearDisplay();
display.println("AP Mode");
display.println(WiFi.softAPIP());
display.display();
} else {
WiFi.begin(ssid, password);
display.clearDisplay();
display.println("Connecting WiFi");
display.display();
int attempts = 0;
while (WiFi.status() != WL_CONNECTED && attempts < 40) {
delay(500);
Serial.print(".");
attempts++;
}
if (WiFi.status() == WL_CONNECTED) {
Serial.println("\nConnected IP: " + WiFi.localIP().toString());
display.clearDisplay();
display.println("Connected");
display.println(WiFi.localIP());
display.display();
} else {
Serial.println("\nWiFi Failed");
display.println("WiFi Error");
display.display();
while (true);
}
}
setupWebServer();
delay(1000);
updateDisplay();
beep(200, 2500);
Serial.println("Ready.");
}
// ========== LOOP ==========
void loop() {
server.handleClient();
// Ανίχνευση χειρονομίας
DFRobot_PAJ7620U2::eGesture_t gesture = paj.getGesture();
if (gesture != paj.eGestureNone && (millis() - lastGestureTime > debounceTime)) {
lastGestureTime = millis();
String g = gestureToString(gesture);
Serial.print("Gesture: ");
Serial.println(g);
handleGesture(gesture);
}
delay(30); // μικρή καθυστέρηση για σταθερότητα
}