// ESP32 Hareket Alarm Sistemi (Motion Alarm System)
// TR: PIR sensor + siren buzzer + 2 LED (alarm/ready) + OLED durum + opsiyonel Telegram bildirimi
// EN: PIR sensor + siren buzzer + 2 LEDs (alarm/ready) + OLED status + optional Telegram notification
// TR: Hareket alginca kirmizi LED + siren + OLED "ALARM!" + Telegram (etkinse). Sakin durumda yesil LED "GUVENLI".
// EN: On motion: red LED + siren + OLED "ALARM!" + Telegram (if enabled). When calm: green LED "SAFE".
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
// ============ TELEGRAM (OPSIYONEL / OPTIONAL) ============
// TR: Wokwi'de test icin ENABLE_TELEGRAM false birak. Gercek donanimda true yap ve credentials doldur.
// EN: Keep ENABLE_TELEGRAM false for Wokwi testing. Set true on real hardware with credentials.
#define ENABLE_TELEGRAM false
#if ENABLE_TELEGRAM
#include <WiFi.h>
#include <WiFiClientSecure.h>
#include <HTTPClient.h>
const char* WIFI_SSID = "YourWiFi";
const char* WIFI_PASS = "YourPassword";
const String TG_BOT_TOKEN = "123456789:AAABBBCCCdddEEE"; // TR: BotFather'dan / EN: from BotFather
const String TG_CHAT_ID = "-1001234567890"; // TR: Chat/kanal ID | EN: Chat/channel ID
#endif
// ============ PIN TANIMLARI / PIN DEFINITIONS ============
#define PIR_PIN 18 // TR: PIR sensor OUT | EN: PIR sensor OUT
#define LED_RED 15 // TR: Alarm LED (kirmizi) | EN: Alarm LED (red)
#define LED_GREEN 4 // TR: Ready LED (yesil) | EN: Ready LED (green)
#define BUZZER_PIN 5 // TR: Aktif buzzer | EN: Active buzzer
// ============ OLED AYARLARI / OLED CONFIG ============
#define OLED_W 128
#define OLED_H 64
#define OLED_ADDR 0x3C
// ============ DAVRANIS / BEHAVIOR ============
#define CALIB_MS 3000UL // TR: Baslangic kalibrasyon (Wokwi 3sn, gercek 30-60sn) | EN: Startup calibration
#define ALARM_HOLD_MS 5000UL // TR: Hareket sonrasi alarm suresi | EN: Alarm duration after motion
#define SIREN_TOGGLE_MS 120 // TR: Siren frekansi (ms) | EN: Siren toggle interval (ms)
#define TG_COOLDOWN_MS 30000UL // TR: Telegram spam engelleme (30sn) | EN: Telegram anti-spam (30 sec)
// ============ NESNELER / OBJECTS ============
Adafruit_SSD1306 oled(OLED_W, OLED_H, &Wire, -1);
// ============ DURUM DEGISKENLERI / STATE VARIABLES ============
bool alarmActive = false;
uint32_t alarmStartMs = 0;
uint32_t lastSirenToggle = 0;
bool sirenState = false;
uint32_t alarmCount = 0;
uint32_t lastTgSentMs = 0;
uint32_t bootMs = 0;
bool calibrating = true;
String lastAlarmStamp = "-";
// ============ YARDIMCILAR / HELPERS ============
void lcdAt(int x, int y, uint8_t size, const String &t) {
oled.setTextSize(size);
oled.setCursor(x, y);
oled.print(t);
}
void centered(uint8_t y, uint8_t size, const String &t) {
int w = t.length() * 6 * size;
int x = (128 - w) / 2;
if (x < 0) x = 0;
lcdAt(x, y, size, t);
}
String fmtDuration(uint32_t ms) {
uint32_t sec = ms / 1000;
uint32_t h = sec / 3600;
uint32_t m = (sec % 3600) / 60;
uint32_t s = sec % 60;
String out = "";
if (h > 0) out += String(h) + "s ";
if (m > 0) out += String(m) + "d ";
out += String(s) + "sn";
return out;
}
// ============ EKRANLAR / SCREENS ============
void drawSplash() {
oled.clearDisplay();
oled.setTextColor(SSD1306_WHITE);
oled.drawRoundRect(2, 2, 124, 60, 6, SSD1306_WHITE);
centered(14, 2, "BlueGrays");
centered(36, 1, "Hareket Alarm");
centered(50, 1, "v1.0 PIR + Siren");
oled.display();
}
void drawCalibrating(uint32_t remaining) {
oled.clearDisplay();
oled.setTextColor(SSD1306_WHITE);
centered(4, 1, "PIR Kalibrasyon");
oled.drawLine(0, 14, 127, 14, SSD1306_WHITE);
centered(24, 2, String(remaining) + "s");
centered(48, 1, "Lutfen bekleyin...");
oled.display();
}
void drawReady() {
oled.clearDisplay();
oled.setTextColor(SSD1306_WHITE);
// TR: Baslik | EN: Title
centered(0, 1, "Hareket Alarm");
oled.drawLine(0, 10, 127, 10, SSD1306_WHITE);
// TR: Buyuk durum | EN: Big status
centered(16, 2, "GUVENLI");
oled.drawLine(0, 42, 127, 42, SSD1306_WHITE);
lcdAt(0, 45, 1, "Son: " + lastAlarmStamp);
lcdAt(0, 55, 1, "Alarm: " + String(alarmCount));
// Ready indicator (sag alt)
oled.fillCircle(120, 60, 3, SSD1306_WHITE);
oled.display();
}
void drawAlarm() {
oled.clearDisplay();
oled.setTextColor(SSD1306_WHITE);
// TR: Cerceve (invert efekt) | EN: Frame (invert-like effect)
oled.drawRect(0, 0, 128, 64, SSD1306_WHITE);
oled.drawRect(2, 2, 124, 60, SSD1306_WHITE);
centered(12, 3, "ALARM!");
centered(40, 1, "HAREKET ALGILANDI");
centered(52, 1, "#" + String(alarmCount));
oled.display();
}
// ============ TELEGRAM ============
#if ENABLE_TELEGRAM
void sendTelegram(const String &msg) {
if (WiFi.status() != WL_CONNECTED) {
Serial.println("[TG] WiFi yok / no WiFi");
return;
}
WiFiClientSecure client;
client.setInsecure(); // TR: Sertifika dogrulamayi atla | EN: Skip cert verify (basit test icin)
HTTPClient https;
String url = "https://api.telegram.org/bot" + TG_BOT_TOKEN + "/sendMessage";
if (https.begin(client, url)) {
https.addHeader("Content-Type", "application/x-www-form-urlencoded");
String payload = "chat_id=" + TG_CHAT_ID + "&text=" + msg;
int code = https.POST(payload);
Serial.printf("[TG] HTTP %d\n", code);
https.end();
}
}
#endif
// ============ SIREN ============
void siren() {
uint32_t now = millis();
if (now - lastSirenToggle >= SIREN_TOGGLE_MS) {
lastSirenToggle = now;
sirenState = !sirenState;
digitalWrite(BUZZER_PIN, sirenState ? HIGH : LOW);
digitalWrite(LED_RED, sirenState ? HIGH : LOW);
}
}
// ============ SETUP ============
void setup() {
Serial.begin(115200);
delay(500);
pinMode(PIR_PIN, INPUT);
pinMode(LED_RED, OUTPUT);
pinMode(LED_GREEN, OUTPUT);
pinMode(BUZZER_PIN, OUTPUT);
digitalWrite(LED_RED, LOW);
digitalWrite(LED_GREEN, LOW);
digitalWrite(BUZZER_PIN, LOW);
Wire.begin();
if (!oled.begin(SSD1306_SWITCHCAPVCC, OLED_ADDR)) {
Serial.println("[X] OLED bulunamadi / not found");
while (1) delay(100);
}
drawSplash();
delay(2000);
#if ENABLE_TELEGRAM
Serial.println("[WiFi] baglaniyor / connecting...");
WiFi.begin(WIFI_SSID, WIFI_PASS);
uint32_t t0 = millis();
while (WiFi.status() != WL_CONNECTED && millis() - t0 < 15000) {
delay(300);
Serial.print(".");
}
Serial.println();
if (WiFi.status() == WL_CONNECTED) {
Serial.println("[WiFi] baglandi / connected: " + WiFi.localIP().toString());
} else {
Serial.println("[WiFi] baglanti basarisiz / failed");
}
#endif
bootMs = millis();
Serial.println("== Sistem baslatildi, kalibrasyon / System started, calibrating ==");
}
// ============ LOOP ============
void loop() {
uint32_t now = millis();
// TR: Kalibrasyon fazi | EN: Calibration phase
if (calibrating) {
uint32_t elapsed = now - bootMs;
if (elapsed >= CALIB_MS) {
calibrating = false;
digitalWrite(LED_GREEN, HIGH);
drawReady();
Serial.println("[OK] Kalibrasyon tamam, sistem hazir / Calibration done, system ready");
} else {
uint32_t remaining = (CALIB_MS - elapsed) / 1000 + 1;
drawCalibrating(remaining);
// TR: Kalibrasyon sirasinda LED'ler alternate blink | EN: LEDs alternate blink during calib
digitalWrite(LED_GREEN, (now / 200) % 2);
delay(100);
return;
}
}
// TR: PIR durumunu oku | EN: Read PIR state
int motion = digitalRead(PIR_PIN);
if (motion == HIGH && !alarmActive) {
// TR: YENI alarm | EN: NEW alarm
alarmActive = true;
alarmStartMs = now;
alarmCount++;
lastAlarmStamp = fmtDuration(now - bootMs) + " once";
digitalWrite(LED_GREEN, LOW);
drawAlarm();
Serial.printf("[!] HAREKET / MOTION #%lu at t=%lu ms\n", (unsigned long)alarmCount, (unsigned long)now);
#if ENABLE_TELEGRAM
if (now - lastTgSentMs > TG_COOLDOWN_MS) {
lastTgSentMs = now;
String msg = "ALARM! Hareket algilandi (motion detected) #" + String(alarmCount);
sendTelegram(msg);
}
#endif
}
if (alarmActive) {
siren();
if (now - alarmStartMs > ALARM_HOLD_MS && motion == LOW) {
// TR: Alarm bitir | EN: End alarm
alarmActive = false;
digitalWrite(BUZZER_PIN, LOW);
digitalWrite(LED_RED, LOW);
digitalWrite(LED_GREEN, HIGH);
lastAlarmStamp = fmtDuration(now - bootMs) + " once";
drawReady();
Serial.println("[OK] Alarm bitti, sisteme donuldu / Alarm ended, back to ready");
}
}
delay(50);
}