Wemos D1 Mini ile Gelişmiş Bisiklet ve Motosiklet Alarm Sistemi (V2)
Bisiklet ve motosiklet kullanıcıları için güvenlik her zaman birinci önceliktir. Piyasada satılan standart alarmlar bazen yetersiz kalabilir veya sadece yerel olarak ses çıkarır. Bu yazımızda, Wemos D1 Mini kullanarak; hem internet üzerinden Telegram bildirimi gönderen, hem RF kumanda ile kontrol edilebilen, hem de kablosuz yardımcı siren (Slave) desteği sunan profesyonel bir alarm sistemi yapıyoruz.
Dilerseniz bisikletin üzerindeki ana (Master) cihaza buzzer takmayarak sistemi “Sessiz Alarm” modunda kullanabilirsiniz. Bu durumda sarsıntı algılandığında bisiklet ses çıkarmaz; hırsız sistemin farkına varmazken, anlık olarak telefonunuza Telegram bildirimi gelir ve evinizdeki Slave cihaz siren çalmaya başlar. Bu özellik, hırsızı ürkütmeden suçüstü yakalamanıza veya bisikletin yerini sessizce takip etmenize olanak tanır.
Bu sistemin en büyük özelliği, 3 aşamalı akıllı algoritması ve derin uyku modu sayesinde pil tasarrufu sağlamasıdır.
Projenin Öne Çıkan Özellikleri
-
- 3 Aşamalı Akıllı Alarm: İlk sarsıntılarda kısa uyarılar, ısrar devam ederse 10 saniyelik siren ve 9. sarsıntıda asla susmayan “Kalıcı Alarm”.
-
- Telegram Entegrasyonu: Alarm tetiklendiğinde veya kurulduğunda anlık bildirim.
-
- Web Panel: Herhangi bir uygulama indirmeden tarayıcı üzerinden ayar yapma ve durum izleme.
-
- RF Kumanda Öğrenme: Mevcut 433MHz kumandalarınızı sisteme tanıtma modu.
-
- Slave (Yardımcı) Siren: WiFi üzerinden ana cihazla haberleşen, evin içine veya başka bir yere koyabileceğiniz kablosuz siren desteği.
-
- Derin Uyku (Deep Sleep): Alarm kapalıyken 10 saniye hareketsizlikte uykuya dalarak pil ömrünü uzatır.
Gerekli Malzemeler
Ana Cihaz (Master) İçin:
-
- 1x Wemos D1 Mini (ESP8266)
-
- 1x SW-420 Titreşim/Eğim Sensörü
-
- 1x 433MHz RF Alıcı Modül (XY-MK-5V veya benzeri)
-
- 1x 433MHz RF Kumanda
-
- 1x 5V Röle Kartı
-
- 1x Aktif Buzzer
-
- 2x LED (Kırmızı ve Yeşil)
Yardımcı Cihaz (Slave) İçin:
-
- 1x Wemos D1 Mini (veya ESP-01)
-
- 1x Aktif Buzzer
Devre Bağlantısı (Master)
| Bileşen | Wemos D1 Mini Pin |
| Buzzer (+) | D1 (GPIO5) |
| Titreşim Sensörü (OUT) | D2 (GPIO4) |
| RF Alıcı (DATA) | D4 (GPIO2) |
| Röle (IN) | D6 (GPIO12) |
| Yeşil LED (Açık) | D7 (GPIO13) |
| Kırmızı LED (Kapalı) | D8 (GPIO15) |
Web Sayfa Tasarım Görünümü

Master (Ana Cihaz) Kodları
Aşağıdaki kod, sistemin beynidir. WiFi yönetimi, web arayüzü ve Telegram botu işlemlerini gerçekleştirir.codeC++
#include <Arduino.h>
#include <RCSwitch.h>
#include <ESP8266WiFi.h>
#include <WiFiManager.h>
#include <WiFiClientSecure.h>
#include <ArduinoJson.h>
#include <ESP8266WebServer.h>
#include <UniversalTelegramBot.h>
#include <EEPROM.h>
#include <WiFiUdp.h>
// HTML sayfa (PROGMEM'de saklanır - RAM tasarrufu için)
const char INDEX_HTML[] PROGMEM = R"rawliteral(
<!DOCTYPE html>
<html lang="tr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Bisiklet Alarm</title>
<style>
*{margin:0;padding:0;box-sizing:border-box}
body{font-family:Arial,sans-serif;background:linear-gradient(135deg,#1e3c72 0%,#2a5298 50%,#7e22ce 100%);min-height:100vh;padding:20px}
.container{max-width:800px;margin:0 auto}
.header{text-align:center;color:#fff;margin-bottom:30px;padding:20px;background:rgba(255,255,255,0.1);border-radius:15px;backdrop-filter:blur(10px)}
.header-logo{max-width:150px;height:auto;margin-bottom:15px;filter:drop-shadow(0 4px 6px rgba(0,0,0,0.3))}
.header h1{font-size:2.2em;margin-bottom:10px;text-shadow:2px 2px 4px rgba(0,0,0,0.3);font-weight:bold}
.header p{font-size:1.1em;text-shadow:1px 1px 2px rgba(0,0,0,0.3)}
.card{background:#fff;border-radius:10px;padding:20px;margin-bottom:20px;box-shadow:0 5px 15px rgba(0,0,0,0.2)}
.card h2{color:#667eea;margin-bottom:15px;border-bottom:2px solid #099cf1ff;padding-bottom:10px}
.status-box{display:flex;justify-content:space-between;padding:10px;background:#f8f9fa;border-radius:5px;margin-bottom:10px}
.status-value{padding:5px 15px;border-radius:20px;font-weight:bold}
.status-active{background:#28a745;color:#fff}
.status-inactive{background:#dc3545;color:#fff}
.form-group{margin-bottom:15px}
.form-group label{display:block;margin-bottom:5px;font-weight:600}
.form-group input{width:100%;padding:10px;border:2px solid #e0e0e0;border-radius:5px;font-size:1em}
.btn{padding:10px 20px;border:none;border-radius:5px;font-size:1em;cursor:pointer;margin:5px;font-weight:600}
.btn-primary{background:#667eea;color:#fff}
.btn-success{background:#28a745;color:#fff}
.btn-danger{background:#dc3545;color:#fff}
.btn-warning{background:#ffc107;color:#333}
.alert{padding:10px;border-radius:5px;margin-bottom:15px;display:none}
.alert-success{background:#d4edda;color:#155724}
.alert-error{background:#f8d7da;color:#721c24}
.info-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));gap:10px;margin-top:10px}
.info-item{background:#f8f9fa;padding:10px;border-radius:5px;text-align:center}
.info-item-label{font-size:0.9em;color:#666;margin-bottom:5px}
.info-item-value{font-size:1.1em;font-weight:bold}
.footer{background:rgba(255,255,255,0.1);backdrop-filter:blur(10px);padding:20px;border-radius:10px;margin-top:30px;display:flex;justify-content:space-between;align-items:center;color:#fff}
.footer-left{text-align:left;font-size:0.9em}
.footer-right img{max-width:120px;height:auto;filter:drop-shadow(0 2px 4px rgba(0,0,0,0.3))}
</style>
</head>
<body>
<div class="container">
<div class="header">
<img src="https://kirmiziyuz.com/kirmizilogo.svg" alt="Logo" class="header-logo">
<h1>Motor-Bisiklet Alarm Sistemi</h1>
</div>
<div class="card">
<h2>
Sistem Durumu</h2>
<div class="status-box">
<span>Alarm:</span>
<span class="status-value" id="alarmStatus">...</span>
</div>
<div class="status-box">
<span>Siren:</span>
<span class="status-value" id="sirenStatus">Kapalı</span>
</div>
<div class="status-box">
<span>Titreşim Sayacı:</span>
<span class="status-value" id="vibrationCount">0/9</span>
</div>
<div class="status-box">
<span>WiFi:</span>
<span class="status-value" id="wifiStatus">...</span>
</div>
<div class="info-grid">
<div class="info-item">
<div class="info-item-label">IP</div>
<div class="info-item-value" id="ip">-</div>
</div>
<div class="info-item">
<div class="info-item-label">SSID</div>
<div class="info-item-value" id="ssid">-</div>
</div>
<div class="info-item">
<div class="info-item-label">Sinyal</div>
<div class="info-item-value" id="rssi">-</div>
</div>
<div class="info-item">
<div class="info-item-label">Çalışma</div>
<div class="info-item-value" id="uptime">-</div>
</div>
</div>
</div>
<div class="card">
<h2>
Web Panel Giriş Bilgileri</h2>
<div id="loginAlert" class="alert"></div>
<div class="form-group">
<label>Kullanıcı Adı:</label>
<input type="text" id="webUsername" placeholder="admin">
</div>
<div class="form-group">
<label>Şifre:</label>
<input type="password" id="webPassword" placeholder="••••••••">
</div>
<button class="btn btn-primary" onclick="saveLoginSettings()">
Giriş Bilgilerini Kaydet</button>
<div style="margin-top:10px;padding:10px;background:#fff3cd;border-radius:5px;font-size:0.9em;color:#856404">
<strong>
Uyarı:</strong> Şifreyi değiştirdikten sonra yeniden giriş yapmanız gerekecek!
</div>
</div>
<div class="card">
<h2>
Telegram Ayarları</h2>
<div id="alert" class="alert"></div>
<div class="form-group">
<label>Chat ID:</label>
<input type="text" id="chatID" placeholder="123456789">
</div>
<div class="form-group">
<label>Bot Token:</label>
<input type="text" id="botToken" placeholder="123456:ABCdef...">
</div>
<button class="btn btn-primary" onclick="saveSettings()">
Kaydet</button>
<button class="btn btn-success" onclick="testNotification()">
Test</button>
</div>
<div class="card">
<h2>
Bildirim Ayarları</h2>
<div style="margin-bottom:15px">
<label style="display:block;margin-bottom:5px"><input type="checkbox" id="notifyArm"> Alarm açıldığında bildir</label>
<label style="display:block;margin-bottom:5px"><input type="checkbox" id="notifyDisarm"> Alarm kapandığında bildir</label>
<label style="display:block;margin-bottom:5px"><input type="checkbox" id="notifyTrigger"> Alarm tetiklendiğinde bildir</label>
<label style="display:block;margin-bottom:5px"><input type="checkbox" id="notifySleep"> Uyku moduna geçerken bildir</label>
<label style="display:block;margin-bottom:5px"><input type="checkbox" id="notifyKeepAlive"> 12 saatlik durum raporu gönder</label>
</div>
</div>
<div class="card">
<h2>
Mesaj Şablonları</h2>
<div class="form-group">
<label>Sistem Başlangıcı:</label>
<input type="text" id="msgStart" placeholder="Sistem aktif mesajı">
</div>
<div class="form-group">
<label>Alarm Açık:</label>
<input type="text" id="msgArmed" placeholder="Alarm açıldı mesajı">
</div>
<div class="form-group">
<label>Alarm Kapalı:</label>
<input type="text" id="msgDisarmed" placeholder="Alarm kapandı mesajı">
</div>
<div class="form-group">
<label>Alarm Tetiklendi:</label>
<input type="text" id="msgTriggered" placeholder="Hareket algılandı mesajı">
</div>
<div class="form-group">
<label>Uyku Modu:</label>
<input type="text" id="msgSleep" placeholder="Uyku modu mesajı">
</div>
<div class="form-group">
<label>Test Mesajı:</label>
<input type="text" id="msgTest" placeholder="Test mesajı">
</div>
<button class="btn btn-primary" onclick="saveMessages()">
Mesajları Kaydet</button>
</div>
<div class="card">
<h2>
RF Kumanda Ayarları</h2>
<div id="rfAlert" class="alert"></div>
<div class="status-box">
<span>Son Alınan RF Kod:</span>
<span class="status-value status-inactive" id="lastRFCode">-</span>
</div>
<div class="form-group">
<label>Alarm Açma Kodu:</label>
<input type="text" id="rfArmCode" placeholder="14244580">
</div>
<div class="form-group">
<label>Alarm Kapama Kodu:</label>
<input type="text" id="rfDisarmCode" placeholder="14244577">
</div>
<button class="btn btn-warning" onclick="startRFLearn()">
Öğrenme Modunu Başlat (15sn)</button>
<button class="btn btn-success" onclick="useLastRFAsArm()">
Son Kodu Açma Olarak Kullan</button>
<button class="btn btn-danger" onclick="useLastRFAsDisarm()">
Son Kodu Kapama Olarak Kullan</button>
<button class="btn btn-primary" onclick="saveRFCodes()">
RF Kodlarını Kaydet</button>
<div style="margin-top:15px;padding:10px;background:#f8f9fa;border-radius:5px;font-size:0.9em">
<strong>
Nasıl Kullanılır:</strong><br>
1. "Öğrenme Modunu Başlat" butonuna tıklayın<br>
2. 15 saniye içinde kumandanızın butonuna basın<br>
3. "Son Alınan RF Kod" alanında kodunuz görünecek<br>
4. Kodu "Açma" veya "Kapama" olarak atayın<br>
5. "RF Kodlarını Kaydet" butonuna basın
</div>
</div>
<div class="card">
<h2>
Kontroller</h2>
<button class="btn btn-success" onclick="armSystem()">
Aktif Et</button>
<button class="btn btn-warning" onclick="disarmSystem()">
Pasif Et</button>
<button class="btn btn-danger" onclick="resetSystem()">
Sıfırla</button>
<button class="btn btn-primary" onclick="restartSystem()">
Yeniden Başlat</button>
</div>
<div class="footer">
<div class="footer-left">
Copyright © 2026 Adem KIRMIZIYÜZ
</div>
<div class="footer-right">
<img src="https://kirmiziyuz.com/kirmizilogo.svg" alt="Logo">
</div>
</div>
</div>
<script>
function updateStatus(){
fetch('/api/status').then(r=>r.json()).then(d=>{
document.getElementById('alarmStatus').textContent=d.armed?'Aktif':'Pasif';
document.getElementById('alarmStatus').className='status-value '+(d.armed?'status-active':'status-inactive');
// Siren durumu
let sirenText='Kapalı';
let sirenClass='status-inactive';
if(d.permanentAlarm){
sirenText='
KALICI ALARM!';
sirenClass='status-active';
}else if(d.temporarySiren){
sirenText='
Geçici ('+d.sirenRemaining+'s)';
sirenClass='status-active';
}else if(d.relayActive){
sirenText='Röle Açık';
sirenClass='status-active';
}
document.getElementById('sirenStatus').textContent=sirenText;
document.getElementById('sirenStatus').className='status-value '+sirenClass;
// Titreşim sayacı
document.getElementById('vibrationCount').textContent=d.vibrationCount+'/9';
document.getElementById('vibrationCount').className='status-value '+(d.vibrationCount>=3?'status-active':'status-inactive');
document.getElementById('wifiStatus').textContent=d.wifi?'Bağlı':'Yok';
document.getElementById('wifiStatus').className='status-value '+(d.wifi?'status-active':'status-inactive');
document.getElementById('ip').textContent=d.ip||'-';
document.getElementById('ssid').textContent=d.ssid||'-';
document.getElementById('rssi').textContent=d.rssi?(d.rssi+' dBm'):'-';
const s=d.uptime;
document.getElementById('uptime').textContent=s?Math.floor(s/3600)+'s '+Math.floor((s%3600)/60)+'d':'-';
}).catch(e=>console.error(e));
}
function loadSettings(){
fetch('/api/settings').then(r=>r.json()).then(d=>{
document.getElementById('webUsername').value=d.webUsername||'';
document.getElementById('webPassword').value='';
document.getElementById('chatID').value=d.chatid||'';
document.getElementById('botToken').value=d.token||'';
document.getElementById('notifyArm').checked=d.notifyArm||false;
document.getElementById('notifyDisarm').checked=d.notifyDisarm||false;
document.getElementById('notifyTrigger').checked=d.notifyTrigger||false;
document.getElementById('notifySleep').checked=d.notifySleep||false;
document.getElementById('notifyKeepAlive').checked=d.notifyKeepAlive||true;
document.getElementById('msgStart').value=d.msgStart||'';
document.getElementById('msgArmed').value=d.msgArmed||'';
document.getElementById('msgDisarmed').value=d.msgDisarmed||'';
document.getElementById('msgTriggered').value=d.msgTriggered||'';
document.getElementById('msgSleep').value=d.msgSleep||'';
document.getElementById('msgTest').value=d.msgTest||'';
}).catch(e=>console.error(e));
}
function saveLoginSettings(){
const username=document.getElementById('webUsername').value;
const password=document.getElementById('webPassword').value;
if(!username){showLoginAlert('Kullanıcı adı boş olamaz!','error');return;}
if(password&&password.length<4){showLoginAlert('Şifre en az 4 karakter olmalı!','error');return;}
const data={webUsername:username};
if(password)data.webPassword=password;
fetch('/api/login-settings',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify(data)})
.then(r=>r.json()).then(d=>{
if(d.success){
showLoginAlert('Giriş bilgileri kaydedildi! Yeniden giriş yapmanız gerekiyor...','success');
setTimeout(()=>window.location.reload(),2000);
}else{
showLoginAlert('Hata!','error');
}
}).catch(()=>showLoginAlert('Bağlantı hatası!','error'));
}
function showLoginAlert(msg,type){
const a=document.getElementById('loginAlert');
a.textContent=msg;
a.className='alert alert-'+type;
a.style.display='block';
setTimeout(()=>a.style.display='none',5000);
}
function updateRFCodes(){
fetch('/api/rf-codes').then(r=>r.json()).then(d=>{
document.getElementById('rfArmCode').value=d.armCode||'';
document.getElementById('rfDisarmCode').value=d.disarmCode||'';
const lastCode=d.lastReceived||'0';
document.getElementById('lastRFCode').textContent=lastCode!='0'?lastCode:'Henüz alınmadı';
document.getElementById('lastRFCode').className='status-value '+(lastCode!='0'?'status-active':'status-inactive');
if(d.learningMode){
document.getElementById('rfAlert').textContent='
Öğrenme modu aktif! Kumandaya basın...';
document.getElementById('rfAlert').className='alert alert-error';
document.getElementById('rfAlert').style.display='block';
}else{
document.getElementById('rfAlert').style.display='none';
}
}).catch(e=>console.error(e));
}
function startRFLearn(){
if(confirm('RF öğrenme modu başlatılsın mı?\n\n15 saniye içinde kumandanızın butonuna basın.')){
fetch('/api/rf-learn',{method:'POST'}).then(r=>r.json()).then(d=>{
if(d.success){
showRFAlert('Öğrenme modu başlatıldı! 15 saniye içinde kumandaya basın.','success');
}
});
}
}
function useLastRFAsArm(){
const lastCode=document.getElementById('lastRFCode').textContent;
if(lastCode!='Henüz alınmadı'&&lastCode!='-'){
document.getElementById('rfArmCode').value=lastCode;
showRFAlert('Son alınan kod "Açma" olarak atandı!','success');
}else{
showRFAlert('Önce öğrenme modunu başlatın ve kumandaya basın!','error');
}
}
function useLastRFAsDisarm(){
const lastCode=document.getElementById('lastRFCode').textContent;
if(lastCode!='Henüz alınmadı'&&lastCode!='-'){
document.getElementById('rfDisarmCode').value=lastCode;
showRFAlert('Son alınan kod "Kapama" olarak atandı!','success');
}else{
showRFAlert('Önce öğrenme modunu başlatın ve kumandaya basın!','error');
}
}
function saveRFCodes(){
const armCode=document.getElementById('rfArmCode').value;
const disarmCode=document.getElementById('rfDisarmCode').value;
if(!armCode||!disarmCode){showRFAlert('Lütfen her iki kodu da girin!','error');return;}
fetch('/api/rf-codes',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({armCode,disarmCode})})
.then(r=>r.json()).then(d=>{
showRFAlert(d.success?'RF kodları kaydedildi!':'Hata!',d.success?'success':'error');
}).catch(()=>showRFAlert('Bağlantı hatası!','error'));
}
function showRFAlert(msg,type){
const a=document.getElementById('rfAlert');
a.textContent=msg;
a.className='alert alert-'+type;
a.style.display='block';
setTimeout(()=>a.style.display='none',5000);
}
function saveSettings(){
const chatid=document.getElementById('chatID').value;
const token=document.getElementById('botToken').value;
if(!chatid||!token){showAlert('Lütfen tüm alanları doldurun!','error');return;}
const notifyArm=document.getElementById('notifyArm').checked;
const notifyDisarm=document.getElementById('notifyDisarm').checked;
const notifyTrigger=document.getElementById('notifyTrigger').checked;
const notifySleep=document.getElementById('notifySleep').checked;
const notifyKeepAlive=document.getElementById('notifyKeepAlive').checked;
fetch('/api/settings',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({chatid,token,notifyArm,notifyDisarm,notifyTrigger,notifySleep,notifyKeepAlive})})
.then(r=>r.json()).then(d=>{
showAlert(d.success?'Ayarlar kaydedildi!':'Hata!',d.success?'success':'error');
}).catch(()=>showAlert('Bağlantı hatası!','error'));
}
function saveMessages(){
const msgStart=document.getElementById('msgStart').value;
const msgArmed=document.getElementById('msgArmed').value;
const msgDisarmed=document.getElementById('msgDisarmed').value;
const msgTriggered=document.getElementById('msgTriggered').value;
const msgSleep=document.getElementById('msgSleep').value;
const msgTest=document.getElementById('msgTest').value;
fetch('/api/settings',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({msgStart,msgArmed,msgDisarmed,msgTriggered,msgSleep,msgTest})})
.then(r=>r.json()).then(d=>{
showAlert(d.success?'Mesajlar kaydedildi!':'Hata!',d.success?'success':'error');
}).catch(()=>showAlert('Bağlantı hatası!','error'));
}
function testNotification(){
if(confirm('Test bildirimi gönderilsin mi?')){
fetch('/api/test-notification').then(r=>r.json()).then(d=>{
showAlert(d.success?'Bildirim gönderildi!':'Gönderilemedi!',d.success?'success':'error');
});
}
}
function armSystem(){fetch('/api/arm',{method:'POST'}).then(()=>{updateStatus();alert('Alarm aktif!');})}
function disarmSystem(){if(confirm('Alarmı pasif et?'))fetch('/api/disarm',{method:'POST'}).then(()=>{updateStatus();alert('Alarm pasif!');})}
function resetSystem(){
if(confirm('
FABRİKA AYARLARINA DÖN\n\nSilinecekler:\n• WiFi ayarları\n• Web panel kullanıcı/şifre\n• Telegram Chat ID ve Bot Token\n• Bildirim ayarları\n• Özel mesajlar\n\nDevam edilsin mi?')){
if(confirm('
SON UYARI!\n\nTÜM KAYITLI AYARLAR SİLİNECEK!\nSistem AP moduna dönecek.\n\nEmin misiniz?')){
fetch('/api/reset',{method:'POST'}).then(()=>{
alert('
Fabrika ayarlarına dönülüyor...\n\nSistem yeniden başlayacak.\nAP modunda bağlanın:\nSSID: Discovery_Bisiklet_Alarmı\nŞifre: 12345678');
});
}}}
function restartSystem(){if(confirm('Yeniden başlatılsın mı?'))fetch('/api/restart',{method:'POST'}).then(()=>alert('Yeniden başlatılıyor...'))}
function showAlert(msg,type){
const a=document.getElementById('alert');
a.textContent=msg;
a.className='alert alert-'+type;
a.style.display='block';
setTimeout(()=>a.style.display='none',5000);
}
window.onload=()=>{updateStatus();loadSettings();updateRFCodes();setInterval(updateStatus,5000);setInterval(updateRFCodes,2000);};
</script>
</body>
</html>
)rawliteral";
// Pin tanımlamaları
#define CLOSE_LED_PIN D8 // GPIO15 - Alarm kapalı LED
#define OPEN_LED_PIN D7 // GPIO13 - Alarm açık LED
#define TILT_SENSOR_PIN D2 // GPIO4 - Eğim sensörü (HIGH=algılandı)
#define BUZZER_PIN D1 // GPIO5 - Buzzer
#define RELAY_PIN D6 // GPIO12 - Röle
#define RC_RECEIVER_PIN D4 // GPIO2 - RF Alıcı
// RF Kumanda kodları (EEPROM'dan yüklenecek)
unsigned long rfCodeArm = 14244580; // Alarm açma kodu
unsigned long rfCodeDisarm = 14244577; // Alarm kapama kodu
// RF öğrenme modu
bool rfLearningMode = false; // RF öğrenme modu aktif mi?
unsigned long rfLearningStartTime = 0; // Öğrenme modu başlangıç zamanı
const unsigned long RF_LEARNING_TIMEOUT = 15000; // 15 saniye öğrenme süresi
unsigned long lastReceivedRFCode = 0; // Son alınan RF kodu (web sayfasında gösterilecek)
unsigned long lastRFCodeTime = 0; // Son RF kodu zamanı
// Telegram Bot ayarları
String botToken = "";
String chatID = "";
// Web panel giriş bilgileri
String webUsername = "admin";
String webPassword = "admin";
// Bildirim ayarları (hangi durumlarda bildirim gönderilecek)
bool notifyOnArm = true;
bool notifyOnDisarm = true;
bool notifyOnTrigger = true;
bool notifyOnSleep = true;
bool notifyKeepAlive = true; // 12 saatlik durum raporu
// Özel mesajlar (Telegram için emoji'li) - PROGMEM'de saklanır (EEPROM'a kaydedilmez!)
String msgSystemStart = "
Bisiklet Alarm Sistemi Aktif!\n\nSistem basariyla baslatildi.";
String msgAlarmArmed = "
Alarm Acik\n\nSistem aktif edildi.";
String msgAlarmDisarmed = "
Alarm Kapali\n\nSistem devre disi.";
String msgAlarmTriggered = "
ALARM TETIKLENDI!\n\n
Bisiklet hareket algilandi!\n
Role 10 saniye aktif";
String msgSleepMode = "
Uyku Moduna Geciliyor\n\n10 saniye hareketsizlik. Sistem uykuya aliniyor...";
String msgTest = "
Test\n\nSistem calisiyor!";
// Alarm durumu
bool alarmActive = false;
bool systemArmed = false;
unsigned long lastMotionTime = 0;
unsigned long startTime = 0;
unsigned long relayOnTime = 0;
const unsigned long RELAY_DURATION = 10000; // Röle 10 saniye açık kalır
bool relayIsOn = false;
bool waitingForTiltRelease = false; // Tilt tekrar tetiklenmesini engelle
const unsigned long DEEP_SLEEP_TIMEOUT = 10000; // 10 saniye hareketsizlik
// 3 AŞAMALI ALARM SİSTEMİ
// 1-2: Dit-dit (kısa ikaz)
// 3: 10 saniye ötsün
// 4-5: Dit-dit (kısa ikaz)
// 6: 10 saniye ötsün
// 7-8: Dit-dit (kısa ikaz)
// 9: SÜREKLI ALARM (reset atılsa bile devam eder, sadece kumanda ile kapanır)
int vibrationCount = 0; // Titreşim sayacı (0-9)
unsigned long lastVibrationTime = 0; // Son titreşim zamanı
const unsigned long VIBRATION_RESET_TIME = 30000; // 30 saniye sonra sayaç sıfırlanır
// Geçici siren (3. ve 6. tetiklemede 10sn)
bool temporarySirenActive = false;
unsigned long temporarySirenStartTime = 0;
const unsigned long TEMPORARY_SIREN_DURATION = 10000; // 10 saniye
// Kalıcı alarm (9. tetiklemede, EEPROM'da saklanır)
bool alarmTriggeredPermanent = false;
RCSwitch mySwitch = RCSwitch();
WiFiManager wifiManager;
ESP8266WebServer server(80);
WiFiClientSecure secureClient;
UniversalTelegramBot *bot = nullptr;
WiFiUDP udp; // UDP broadcast için
// Telegram bot mesaj kontrol zamanlaması
unsigned long lastBotCheckTime = 0;
// BOT_CHECK_INTERVAL kaldırıldı - loop'ta 5000ms kullanılıyor (RF hızı için)
// Startup mesajı gönderildi mi? (sadece 1 kere gönderilecek)
bool startupMessageSent = false;
// Keep-alive için günlük durum raporu
unsigned long lastKeepAliveTime = 0;
const unsigned long KEEP_ALIVE_INTERVAL = 12 * 60 * 60 * 1000; // 12 saat (ms) - daha sık
// WiFiManager parametreleri (sadece web panel giriş bilgileri)
char saved_username[20] = "admin";
char saved_password[20] = "admin";
// Fonksiyon prototipleri
void sendTelegramNotification(String message);
void handleAlarmTrigger();
void checkTiltSensor();
void checkRFSignals();
void updateLEDs();
void buzzerBeepOpen();
void buzzerBeepClose();
void armAlarm();
void disarmAlarm();
void saveConfigCallback();
void setupWebServer();
void loadSettings();
void saveSettings();
void saveAlarmState();
void initTelegramBot();
void handleTelegramMessages();
void sendTelegramHelp(String chatId);
void notifySlaveDevices(bool alarmActive); // UDP broadcast için
// WiFiManager ayarları kaydedildiğinde çağrılacak
bool shouldSaveConfig = false;
void saveConfigCallback() {
Serial.println("Ayarlar kaydedilecek");
shouldSaveConfig = true;
}
void setup() {
Serial.begin(115200);
Serial.println("\n=== Bisiklet Alarm Sistemi Başlatılıyor ===");
startTime = millis();
lastMotionTime = millis(); // Deep sleep için başlangıç zamanı
lastKeepAliveTime = millis(); // Keep-alive için başlangıç zamanı
// Pin yapılandırması
pinMode(CLOSE_LED_PIN, OUTPUT);
pinMode(OPEN_LED_PIN, OUTPUT);
pinMode(BUZZER_PIN, OUTPUT);
pinMode(RELAY_PIN, OUTPUT);
pinMode(TILT_SENSOR_PIN, INPUT_PULLUP); // PULLUP ile gürültü azalır
// Başlangıç durumu: LED'ler ayarlanacak (EEPROM'dan yüklendikten sonra)
digitalWrite(BUZZER_PIN, LOW);
digitalWrite(RELAY_PIN, LOW);
// İlk kurulum kontrolü - EEPROM'da magic number var mı?
EEPROM.begin(1024);
byte magic1 = EEPROM.read(1020);
byte magic2 = EEPROM.read(1021);
byte magic3 = EEPROM.read(1022);
// Magic number: 0x42='B', 0x53='S', 0x32='2' (BS2 = Versiyon 2)
// FARKLI magic number = yeni firmware = EEPROM temizlenir
if (magic1 != 0x42 || magic2 != 0x53 || magic3 != 0x32) {
Serial.println("
YENİ FİRMWARE ALGILANDI - EEPROM temizleniyor...");
Serial.print(" Eski magic: ");
Serial.print(magic1, HEX);
Serial.print(" ");
Serial.print(magic2, HEX);
Serial.print(" ");
Serial.println(magic3, HEX);
// EEPROM'u tamamen temizle (blank state)
for (int i = 0; i < 1024; i++) {
EEPROM.write(i, 0xFF);
}
// YENİ Magic number yaz (BS2 = Versiyon 2)
EEPROM.write(1020, 0x42); // 'B'
EEPROM.write(1021, 0x53); // 'S'
EEPROM.write(1022, 0x32); // '2' (Versiyon 2)
EEPROM.commit();
Serial.println("EEPROM temizlendi! (Tum eski ayarlar silindi)");
Serial.println("AP moduna geciliyor...");
Serial.println(" SSID: Discovery_Bisiklet_Alarmi");
Serial.println(" Sifre: 12345678");
// WiFi ayarlarını temizle (AP moduna dönmesi için)
WiFi.disconnect(true);
wifiManager.resetSettings();
delay(50); // 100ms -> 50ms (hızlandırıldı)
} else {
Serial.println("Ayni firmware versiyonu - ayarlar korunuyor");
}
EEPROM.end();
// EEPROM'dan ayarları oku (basit key-value çiftleri)
loadSettings();
// LED'leri alarm durumuna göre ayarla
if (systemArmed) {
digitalWrite(CLOSE_LED_PIN, LOW);
digitalWrite(OPEN_LED_PIN, HIGH);
} else {
digitalWrite(CLOSE_LED_PIN, HIGH);
digitalWrite(OPEN_LED_PIN, LOW);
}
// Kalıcı alarm durumunu kontrol et (EEPROM'dan yüklendi)
if (alarmTriggeredPermanent) {
Serial.println("
KALICI ALARM AKTİF! (Önceki oturumdan devam ediyor)");
Serial.print("
Titreşim sayacı: ");
Serial.print(vibrationCount);
Serial.println("/9");
Serial.println("
Siren SÜREKLI çalıyor - RF kumanda ile kapatılabilir!");
// Sireni aç (sürekli çalsın)
digitalWrite(BUZZER_PIN, HIGH);
}
Serial.println("Kaydedilmiş ayarlar:");
Serial.println("Chat ID: " + chatID);
if (botToken.length() > 0) {
Serial.println("Bot Token: ***");
} else {
Serial.println("Bot Token: Yok");
}
// Alarm durumu EEPROM'dan yüklendi (loadSettings'te)
Serial.print("
Alarm durumu: ");
Serial.println(systemArmed ? "AÇIK (önceki oturumdan)" : "KAPALI (önceki oturumdan)");
if (chatID.length() == 0 || botToken.length() == 0) {
Serial.println("
Telegram ayarları eksik - bildirim gönderilmeyecek");
}
// RF modülünü başlat
mySwitch.enableReceive(RC_RECEIVER_PIN);
Serial.println("RF alıcı hazır (D4/GPIO2)");
Serial.println("Eğim sensörü hazır (D2/GPIO4)");
// WiFiManager özel parametreleri (sadece web panel giriş bilgileri)
WiFiManagerParameter custom_username("webuser", "Web Panel Kullanici Adi", saved_username, 20);
WiFiManagerParameter custom_password("webpass", "Web Panel Sifre", saved_password, 20);
// Parametreleri WiFiManager'a ekle
wifiManager.addParameter(&custom_username);
wifiManager.addParameter(&custom_password);
// Ayar kaydetme callback'i
wifiManager.setSaveConfigCallback(saveConfigCallback);
// HIZLI BAŞLATMA İÇİN AYARLAR
wifiManager.setConfigPortalTimeout(180); // 300 -> 180 saniye (3 dakika)
wifiManager.setConnectTimeout(5); // WiFi bağlantı timeout: 5 saniye (daha hızlı)
wifiManager.setConnectRetries(2); // 2 deneme (daha hızlı)
// WiFi bağlantısı
Serial.println("WiFi baglantisi kuruluyor...");
// buzzerBeepOpen(); // KALDIRILDI - hızlı başlatma için
// WiFiManager otomatik bağlantı veya AP modu
if (!wifiManager.autoConnect("Discovery_Bisiklet_Alarmı", "12345678")) {
Serial.println("WiFi baglantisi basarisiz!");
ESP.restart();
}
Serial.println("WiFi baglantisi basarili!");
Serial.print("IP Adresi: ");
Serial.println(WiFi.localIP());
// Ayarlar kaydedildiyse
if (shouldSaveConfig) {
Serial.println("Yeni web panel ayarları kaydediliyor...");
webUsername = String(custom_username.getValue());
webPassword = String(custom_password.getValue());
saveSettings();
Serial.println("Web panel giriş bilgileri kaydedildi!");
Serial.println("Telegram ayarlarını web panelden yapabilirsiniz.");
}
// Telegram Bot'u başlat
initTelegramBot();
// Web server'ı başlat
setupWebServer();
// buzzerBeepOpen(); // KALDIRILDI - hızlı başlatma için
// Sistem hazır
Serial.println("=== Sistem Hazir ===");
Serial.print("Alarm durumu: ");
Serial.println(systemArmed ? "AÇIK" : "KAPALI");
Serial.println("Web arayuzu: http://" + WiFi.localIP().toString());
// Telegram bildirimi ASYNC olarak gönderilecek (loop'ta)
// Startup'ta BLOCKING işlem yapmıyoruz (WDT reset'i önlemek için)
}
void loop() {
// RF sinyallerini öncelikli kontrol et (en hızlı tepki için)
checkRFSignals();
server.handleClient();
updateLEDs();
// İlk açılışta Telegram mesajı gönder (sadece 1 kere, non-blocking)
if (!startupMessageSent && chatID.length() > 0 && botToken.length() > 0 && millis() > 2000) {
String startupMsg = msgSystemStart + "\n\n";
startupMsg += "━━━━━━━━━━━━━━━━━━━━\n";
startupMsg += "
Alarm: " + String(systemArmed ? "
Aktif" : "
Pasif") + "\n";
if (alarmTriggeredPermanent) {
startupMsg += "
KALICI ALARM AKTİF!\n";
}
startupMsg += "\n
KULLANILABILIR KOMUTLAR:\n\n";
startupMsg += "/arm veya /ac\n";
startupMsg += " → Alarmı aktif et\n\n";
startupMsg += "/disarm veya /kapat\n";
startupMsg += " → Alarmı pasif et\n\n";
startupMsg += "/status veya /durum\n";
startupMsg += " → Sistem durumunu göster\n\n";
startupMsg += "/reset veya /fabrika\n";
startupMsg += " → Fabrika ayarlarına dön\n\n";
startupMsg += "/help veya /yardim\n";
startupMsg += " → Bu yardım mesajı\n\n";
startupMsg += "━━━━━━━━━━━━━━━━━━━━\n";
startupMsg += "
3 Aşamalı Alarm:\n";
startupMsg += "• 1-2 tetik: Dit-dit (ikaz)\n";
startupMsg += "• 3. tetik: 10sn siren\n";
startupMsg += "• 4-5 tetik: Dit-dit (ikaz)\n";
startupMsg += "• 6. tetik: 10sn siren\n";
startupMsg += "• 7-8 tetik: Dit-dit (ikaz)\n";
startupMsg += "• 9. tetik: SÜREKLI ALARM!\n\n";
startupMsg += "
Kalıcı alarm reset atılsa\n";
startupMsg += "bile devam eder! Sadece RF\n";
startupMsg += "kumanda ile kapatılabilir!";
sendTelegramNotification(startupMsg);
startupMessageSent = true;
Serial.println("
Startup mesajı gönderildi");
}
// Telegram mesajlarını kontrol et (5 saniye aralıklarla - daha az sık)
if (millis() - lastBotCheckTime > 5000) {
handleTelegramMessages();
lastBotCheckTime = millis();
}
// Keep-alive: Durum raporu gönder (12 saatte 1 kere)
if (notifyKeepAlive && chatID.length() > 0 && botToken.length() > 0 &&
millis() - lastKeepAliveTime > KEEP_ALIVE_INTERVAL) {
String keepAliveMsg = "
DURUM RAPORU\n\n";
keepAliveMsg += "
Alarm: " + String(systemArmed ? "
Aktif" : "
Pasif") + "\n";
if (alarmTriggeredPermanent) {
keepAliveMsg += "
KALICI ALARM AKTİF!\n";
}
// Çalışma süresi
unsigned long uptimeSec = (millis() - startTime) / 1000;
unsigned long days = uptimeSec / 86400;
unsigned long hours = (uptimeSec % 86400) / 3600;
unsigned long minutes = (uptimeSec % 3600) / 60;
keepAliveMsg += "
Çalışma: ";
if (days > 0) keepAliveMsg += String(days) + "g ";
if (hours > 0) keepAliveMsg += String(hours) + "s ";
keepAliveMsg += String(minutes) + "d\n";
// WiFi bilgisi
keepAliveMsg += "
WiFi: " + WiFi.SSID() + "\n";
keepAliveMsg += "
Sinyal: " + String(WiFi.RSSI()) + " dBm\n";
// Titreşim sayacı
if (vibrationCount > 0) {
keepAliveMsg += "
Titreşim: " + String(vibrationCount) + "/9\n";
}
keepAliveMsg += "\n
Bot aktif ve hazır!\n";
keepAliveMsg += "\n
Komutlar: /arm /disarm /status /reset /help";
sendTelegramNotification(keepAliveMsg);
lastKeepAliveTime = millis();
Serial.println("
Keep-alive mesajı gönderildi (12 saatlik rapor)");
}
if (WiFi.status() != WL_CONNECTED) {
Serial.println("WiFi kesildi!");
wifiManager.autoConnect("BisiketAlarm_AP", "12345678");
}
// RF öğrenme modu timeout kontrolü
if (rfLearningMode && (millis() - rfLearningStartTime > RF_LEARNING_TIMEOUT)) {
rfLearningMode = false;
Serial.println("
RF öğrenme modu zaman aşımı (15 saniye doldu)");
}
// Tilt sensörü kontrolü
int tiltState = digitalRead(TILT_SENSOR_PIN);
if (systemArmed) {
// Titreşim sayacını sıfırlama kontrolü (30 saniye geçtiyse)
if (vibrationCount > 0 && vibrationCount < 9 && (millis() - lastVibrationTime > VIBRATION_RESET_TIME)) {
Serial.println("
Sayaç sıfırlandı (30 saniye hareketsizlik)");
vibrationCount = 0;
saveAlarmState();
}
// Alarm açıkken hareket algılandığında
// ÖNEMLI: Geçici siren veya kalıcı alarm aktifken YENİ titreşim SAYMA!
if (tiltState == HIGH && !waitingForTiltRelease && !temporarySirenActive && !alarmTriggeredPermanent) {
// Titreşim algılandı!
vibrationCount++;
lastVibrationTime = millis();
waitingForTiltRelease = true;
// Her titreşimde alarm durumunu kaydet (EEPROM'a)
saveAlarmState();
Serial.print("
TİTREŞİM ALGILANDI! Sayaç: ");
Serial.print(vibrationCount);
Serial.println("/9");
// 3 AŞAMALI ALARM SİSTEMİ
if (vibrationCount == 1 || vibrationCount == 2 ||
vibrationCount == 4 || vibrationCount == 5 ||
vibrationCount == 7 || vibrationCount == 8) {
// 1-2, 4-5, 7-8: Dit-dit (kısa ikaz - araba kapısı sesi gibi)
// BUZZER MUTLAKA ÇALSIN!
for (int i = 0; i < 2; i++) {
digitalWrite(BUZZER_PIN, HIGH);
delay(100);
digitalWrite(BUZZER_PIN, LOW);
delay(100);
}
Serial.println("KISA IKAZ: Dit-dit (buzzer caldi)");
// Slave cihazlara kısa dit-dit bildirimi gönder
notifySlaveDevices(true);
delay(250); // Dit-dit süresi (2x100ms + 2x100ms = 200ms + buffer)
notifySlaveDevices(false); // Hemen kapat
// Telegram bildirimi
if (notifyOnTrigger) {
String message = "
Titresim Ikazi!\n\n";
message += "
Sayac: " + String(vibrationCount) + "/9\n";
message += "
Kisa ikaz (dit-dit)";
sendTelegramNotification(message);
}
}
else if (vibrationCount == 3 || vibrationCount == 6) {
// 3. ve 6. tetikleme: 10 saniye ötsün
temporarySirenActive = true;
temporarySirenStartTime = millis();
digitalWrite(BUZZER_PIN, HIGH);
Serial.println("
GEÇİCİ ALARM: 10 saniye siren başladı");
// Slave cihazlara geçici alarm bildirimi gönder (10 saniye çalacak)
notifySlaveDevices(true);
// Telegram bildirimi
if (notifyOnTrigger) {
String message = "
Geçici Alarm!\n\n";
message += "
Sayaç: " + String(vibrationCount) + "/9\n";
message += "
10 saniye siren çalıyor";
sendTelegramNotification(message);
}
}
else if (vibrationCount >= 9) {
// 9. tetikleme: KALICI ALARM (reset atılsa bile devam eder!)
alarmTriggeredPermanent = true;
digitalWrite(BUZZER_PIN, HIGH);
// Röleyi 10 saniye için aç
digitalWrite(RELAY_PIN, HIGH);
relayIsOn = true;
relayOnTime = millis();
// EEPROM'a kaydet (reset sonrası da devam etsin)
saveAlarmState();
Serial.println("!!! KALICI ALARM TETIKLENDI !!!");
Serial.println("Siren SUREKLI calacak!");
Serial.println("Reset atilsa bile devam eder!");
Serial.println("Sadece RF kumanda veya web panel ile kapatilabilir!");
// Slave cihazlara kalıcı alarm bildirimi gönder
notifySlaveDevices(true);
// Telegram bildirimi - MUTLAKA GONDER!
if (notifyOnTrigger && chatID.length() > 0 && botToken.length() > 0) {
String message = "

KALICI ALARM !!!\n\n";
message += "
9. titresim algilandi!\n";
message += "
Siren surekli caliyor!\n";
message += "
Kumanda ile kapatin!";
sendTelegramNotification(message);
Serial.println("Telegram mesaji gonderildi (9. alarm)");
} else {
Serial.println("UYARI: Telegram ayarlari eksik, mesaj gonderilemedi!");
}
}
}
// Tilt sensörü LOW olunca tekrar hazır
if (tiltState == LOW && waitingForTiltRelease) {
delay(50); // Debounce - HIZLANDIRILDI (önceden 100ms)
if (digitalRead(TILT_SENSOR_PIN) == LOW) {
waitingForTiltRelease = false;
Serial.println("Sensor hazir (titresim bitti)");
}
}
// Geçici siren kontrolü (3. ve 6. tetiklemede 10sn)
if (temporarySirenActive) {
unsigned long elapsed = millis() - temporarySirenStartTime;
if (elapsed >= TEMPORARY_SIREN_DURATION) {
// 10 saniye doldu
temporarySirenActive = false;
Serial.println("
Geçici siren tamamlandı (10 saniye)");
// Slave cihazlara geçici alarm bitti bildirimi gönder (kalıcı alarm yoksa)
if (!alarmTriggeredPermanent) {
notifySlaveDevices(false);
}
// ÖNEMLI: Kalıcı alarm yoksa buzzer'ı kapat
if (!alarmTriggeredPermanent) {
digitalWrite(BUZZER_PIN, LOW);
Serial.println("Buzzer kapatildi (kalici alarm yok)");
} else {
Serial.println("Buzzer ACIK kaliyor (kalici alarm var!)");
}
}
}
// Röle 10 saniye açık kaldıysa kapat (BUZZER'A DOKUNMA!)
if (relayIsOn && (millis() - relayOnTime > RELAY_DURATION)) {
digitalWrite(RELAY_PIN, LOW);
relayIsOn = false;
Serial.println("Role kapatildi (10 saniye doldu)");
// BUZZER'I KAPATMA! Kalıcı alarm varsa çalmaya devam etmeli
}
}
// KALICI ALARM KONTROLÜ - EN ÖNEMLİ!
// systemArmed durumundan bağımsız!
// Reset sonrası da çalmalı, sadece disarmAlarm() fonksiyonu ile kapanmalı
if (alarmTriggeredPermanent) {
digitalWrite(BUZZER_PIN, HIGH);
// Her loop'ta kontrol et ve açık tut
static unsigned long lastPermanentAlarmLog = 0;
if (millis() - lastPermanentAlarmLog > 5000) {
Serial.println("!!! KALICI ALARM AKTIF - BUZZER HIGH !!!");
lastPermanentAlarmLog = millis();
}
}
if (!systemArmed) {
// Alarm kapalıyken röleyi kapat
if (relayIsOn) {
digitalWrite(RELAY_PIN, LOW);
relayIsOn = false;
}
// Deep sleep kontrolü (alarm kapalıyken)
// lastMotionTime'ı güncelle (sadece değiştiğinde log)
static bool lastTiltState = LOW;
if (tiltState == HIGH && lastTiltState == LOW) {
lastMotionTime = millis();
Serial.println("
Hareket algilandi - deep sleep zamanlayici sifirlandi");
lastTiltState = HIGH;
} else if (tiltState == LOW && lastTiltState == HIGH) {
Serial.println("
Hareket durdu");
lastTiltState = LOW;
}
// Debug: Her 5 saniyede bir kalan süreyi göster
static unsigned long lastDebugTime = 0;
if (millis() - lastDebugTime > 5000) {
unsigned long elapsed = millis() - lastMotionTime;
unsigned long remaining = (DEEP_SLEEP_TIMEOUT > elapsed) ? (DEEP_SLEEP_TIMEOUT - elapsed) / 1000 : 0;
Serial.print("
Deep sleep: ");
Serial.print(remaining);
Serial.print(" saniye kaldi (Tilt: ");
Serial.print(tiltState == HIGH ? "HIGH" : "LOW");
Serial.println(")");
lastDebugTime = millis();
}
if (millis() - lastMotionTime > DEEP_SLEEP_TIMEOUT) {
Serial.println("
10 saniye hareketsizlik - Derin uykuya geciliyor...");
// Uyku modu bildirimi gönder (opsiyonel)
if (notifyOnSleep && chatID.length() > 0 && botToken.length() > 0) {
sendTelegramNotification(msgSleepMode);
delay(1000); // Bildirimin gönderilmesi için kısa bekle
}
digitalWrite(RELAY_PIN, LOW);
digitalWrite(BUZZER_PIN, LOW);
digitalWrite(CLOSE_LED_PIN, LOW);
digitalWrite(OPEN_LED_PIN, LOW);
delay(100);
Serial.println("
Deep sleep aktif - D0-RST ile uyanir");
ESP.deepSleep(0); // D0-RST kısa devre ile uyanır
}
}
delay(5); // ÇOK HIZLI RF TEPKI için 5ms (önceden 10ms)
}
void initTelegramBot() {
if (botToken.length() > 0) {
secureClient.setInsecure();
if (bot != nullptr) {
delete bot;
}
bot = new UniversalTelegramBot(botToken, secureClient);
Serial.println("Telegram Bot başlatıldı");
}
}
void setupWebServer() {
// Ana sayfa
server.on("/", HTTP_GET, []() {
if (!server.authenticate(webUsername.c_str(), webPassword.c_str())) {
return server.requestAuthentication();
}
server.send_P(200, "text/html", INDEX_HTML);
});
// API: Sistem durumu
server.on("/api/status", HTTP_GET, []() {
if (!server.authenticate(webUsername.c_str(), webPassword.c_str())) {
return server.requestAuthentication();
}
StaticJsonDocument<400> doc;
doc["armed"] = systemArmed;
doc["wifi"] = WiFi.status() == WL_CONNECTED;
doc["ip"] = WiFi.localIP().toString();
doc["ssid"] = WiFi.SSID();
doc["rssi"] = WiFi.RSSI();
doc["uptime"] = (millis() - startTime) / 1000;
doc["relayActive"] = relayIsOn;
doc["vibrationCount"] = vibrationCount;
doc["temporarySiren"] = temporarySirenActive;
doc["permanentAlarm"] = alarmTriggeredPermanent;
// Geçici siren için kalan süre
if (temporarySirenActive) {
unsigned long elapsed = millis() - temporarySirenStartTime;
unsigned long remaining = (TEMPORARY_SIREN_DURATION > elapsed) ? (TEMPORARY_SIREN_DURATION - elapsed) / 1000 : 0;
doc["sirenRemaining"] = remaining;
} else {
doc["sirenRemaining"] = 0;
}
String response;
serializeJson(doc, response);
server.send(200, "application/json", response);
});
// API: Ayarları getir
server.on("/api/settings", HTTP_GET, []() {
if (!server.authenticate(webUsername.c_str(), webPassword.c_str())) {
return server.requestAuthentication();
}
StaticJsonDocument<1024> doc;
doc["webUsername"] = webUsername;
doc["chatid"] = chatID;
doc["token"] = botToken;
doc["notifyArm"] = notifyOnArm;
doc["notifyDisarm"] = notifyOnDisarm;
doc["notifyTrigger"] = notifyOnTrigger;
doc["notifySleep"] = notifyOnSleep;
doc["notifyKeepAlive"] = notifyKeepAlive;
doc["msgStart"] = msgSystemStart;
doc["msgArmed"] = msgAlarmArmed;
doc["msgDisarmed"] = msgAlarmDisarmed;
doc["msgTriggered"] = msgAlarmTriggered;
doc["msgSleep"] = msgSleepMode;
doc["msgTest"] = msgTest;
String response;
serializeJson(doc, response);
server.send(200, "application/json", response);
});
// API: Ayarları kaydet
server.on("/api/settings", HTTP_POST, []() {
if (!server.authenticate(webUsername.c_str(), webPassword.c_str())) {
return server.requestAuthentication();
}
if (server.hasArg("plain")) {
StaticJsonDocument<1024> doc;
deserializeJson(doc, server.arg("plain"));
chatID = doc["chatid"] | chatID;
botToken = doc["token"] | botToken;
notifyOnArm = doc["notifyArm"] | notifyOnArm;
notifyOnDisarm = doc["notifyDisarm"] | notifyOnDisarm;
notifyOnTrigger = doc["notifyTrigger"] | notifyOnTrigger;
notifyOnSleep = doc["notifySleep"] | notifyOnSleep;
notifyKeepAlive = doc["notifyKeepAlive"] | notifyKeepAlive;
// Mesaj şablonları runtime'da değişir ama EEPROM'a kaydedilmez (UTF-8 sorunu)
if (doc.containsKey("msgStart")) msgSystemStart = doc["msgStart"].as<String>();
if (doc.containsKey("msgArmed")) msgAlarmArmed = doc["msgArmed"].as<String>();
if (doc.containsKey("msgDisarmed")) msgAlarmDisarmed = doc["msgDisarmed"].as<String>();
if (doc.containsKey("msgTriggered")) msgAlarmTriggered = doc["msgTriggered"].as<String>();
if (doc.containsKey("msgSleep")) msgSleepMode = doc["msgSleep"].as<String>();
if (doc.containsKey("msgTest")) msgTest = doc["msgTest"].as<String>();
saveSettings();
initTelegramBot();
server.send(200, "application/json", "{\"success\":true}");
Serial.println("Ayarlar güncellendi (mesajlar sadece runtime)");
} else {
server.send(400, "application/json", "{\"success\":false}");
}
});
// API: Web panel giriş bilgilerini değiştir
server.on("/api/login-settings", HTTP_POST, []() {
if (!server.authenticate(webUsername.c_str(), webPassword.c_str())) {
return server.requestAuthentication();
}
if (server.hasArg("plain")) {
StaticJsonDocument<300> doc;
deserializeJson(doc, server.arg("plain"));
if (doc.containsKey("webUsername")) {
String newUsername = doc["webUsername"].as<String>();
if (newUsername.length() > 0 && newUsername.length() < 20) {
webUsername = newUsername;
}
}
if (doc.containsKey("webPassword")) {
String newPassword = doc["webPassword"].as<String>();
if (newPassword.length() >= 4 && newPassword.length() < 20) {
webPassword = newPassword;
}
}
saveSettings();
server.send(200, "application/json", "{\"success\":true}");
Serial.println("Web panel giriş bilgileri güncellendi");
Serial.print(" Yeni kullanıcı adı: ");
Serial.println(webUsername);
} else {
server.send(400, "application/json", "{\"success\":false}");
}
});
// API: Alarmı aktif et
server.on("/api/arm", HTTP_POST, []() {
if (!server.authenticate(webUsername.c_str(), webPassword.c_str())) {
return server.requestAuthentication();
}
armAlarm();
server.send(200, "application/json", "{\"success\":true}");
});
// API: Alarmı pasif et
server.on("/api/disarm", HTTP_POST, []() {
if (!server.authenticate(webUsername.c_str(), webPassword.c_str())) {
return server.requestAuthentication();
}
disarmAlarm();
server.send(200, "application/json", "{\"success\":true}");
});
// API: Test bildirimi
server.on("/api/test-notification", HTTP_GET, []() {
if (!server.authenticate(webUsername.c_str(), webPassword.c_str())) {
return server.requestAuthentication();
}
sendTelegramNotification(msgTest);
server.send(200, "application/json", "{\"success\":true}");
});
// API: Fabrika ayarları
server.on("/api/reset", HTTP_POST, []() {
if (!server.authenticate(webUsername.c_str(), webPassword.c_str())) {
return server.requestAuthentication();
}
Serial.println("
Fabrika ayarlarına dönülüyor...");
// EEPROM'u tamamen temizle (0xFF = blank state)
EEPROM.begin(1024);
for (int i = 0; i < 1024; i++) {
EEPROM.write(i, 0xFF);
}
EEPROM.commit();
EEPROM.end();
Serial.println("
EEPROM temizlendi (0xFF)");
// WiFi ayarlarını sıfırla
wifiManager.resetSettings();
Serial.println("
WiFi ayarları sıfırlandı");
server.send(200, "application/json", "{\"success\":true}");
delay(1000);
Serial.println("
Sistem yeniden başlatılıyor...");
ESP.restart();
});
// API: Yeniden başlat
server.on("/api/restart", HTTP_POST, []() {
if (!server.authenticate(webUsername.c_str(), webPassword.c_str())) {
return server.requestAuthentication();
}
server.send(200, "application/json", "{\"success\":true}");
delay(1000);
ESP.restart();
});
// API: RF öğrenme modunu başlat
server.on("/api/rf-learn", HTTP_POST, []() {
if (!server.authenticate(webUsername.c_str(), webPassword.c_str())) {
return server.requestAuthentication();
}
rfLearningMode = true;
rfLearningStartTime = millis();
lastReceivedRFCode = 0;
server.send(200, "application/json", "{\"success\":true,\"message\":\"RF öğrenme modu başlatıldı\"}");
Serial.println("
RF öğrenme modu başlatıldı (15 saniye)");
});
// API: RF kodlarını getir
server.on("/api/rf-codes", HTTP_GET, []() {
if (!server.authenticate(webUsername.c_str(), webPassword.c_str())) {
return server.requestAuthentication();
}
StaticJsonDocument<300> doc;
doc["armCode"] = String(rfCodeArm);
doc["disarmCode"] = String(rfCodeDisarm);
doc["lastReceived"] = String(lastReceivedRFCode);
doc["learningMode"] = rfLearningMode;
// Öğrenme modu timeout kontrolü
if (rfLearningMode && (millis() - rfLearningStartTime > RF_LEARNING_TIMEOUT)) {
rfLearningMode = false;
doc["learningMode"] = false;
}
String response;
serializeJson(doc, response);
server.send(200, "application/json", response);
});
// API: RF kodlarını kaydet
server.on("/api/rf-codes", HTTP_POST, []() {
if (!server.authenticate(webUsername.c_str(), webPassword.c_str())) {
return server.requestAuthentication();
}
if (server.hasArg("plain")) {
StaticJsonDocument<300> doc;
deserializeJson(doc, server.arg("plain"));
if (doc.containsKey("armCode")) {
String armStr = doc["armCode"].as<String>();
rfCodeArm = strtoul(armStr.c_str(), NULL, 10);
}
if (doc.containsKey("disarmCode")) {
String disarmStr = doc["disarmCode"].as<String>();
rfCodeDisarm = strtoul(disarmStr.c_str(), NULL, 10);
}
saveSettings();
Serial.println("RF kodları güncellendi:");
Serial.print(" Açma kodu: ");
Serial.println(rfCodeArm);
Serial.print(" Kapama kodu: ");
Serial.println(rfCodeDisarm);
server.send(200, "application/json", "{\"success\":true}");
} else {
server.send(400, "application/json", "{\"success\":false}");
}
});
server.begin();
Serial.println("Web server başlatıldı");
}
void loadSettings() {
EEPROM.begin(1024);
int addr = 0;
// EEPROM'dan string okuma helper fonksiyonu - MUTLAKA maxLen kadar address ilerletir
auto readString = [](int &address, char* dest, int maxLen) -> bool {
bool hasValidData = false;
int startAddr = address;
for (int i = 0; i < maxLen; i++) {
char c = EEPROM.read(startAddr + i);
// 0xFF veya 0x00 görürsen string'i bitir
if (c == (char)0xFF || c == 0) {
dest[i] = 0;
break;
}
dest[i] = c;
hasValidData = true;
}
dest[maxLen - 1] = 0; // Güvenlik için
// KRITIK: MUTLAKA maxLen kadar address'i ilerlet
address = startAddr + maxLen;
return hasValidData;
};
// Username (20 bytes) - addr = 0
if (readString(addr, saved_username, 20)) {
webUsername = String(saved_username);
}
if (webUsername.length() == 0) webUsername = "admin";
// Password (20 bytes) - addr = 20
if (readString(addr, saved_password, 20)) {
webPassword = String(saved_password);
}
if (webPassword.length() == 0) webPassword = "admin";
// Chat ID (20 bytes) - addr = 40
char tempChatID[21];
if (readString(addr, tempChatID, 20)) {
chatID = String(tempChatID);
}
// Token (50 bytes) - addr = 60
char tempToken[51];
if (readString(addr, tempToken, 50)) {
botToken = String(tempToken);
}
// Notification flags (1 byte) - addr = 110
byte flags = EEPROM.read(addr++);
if (flags != 0xFF) {
notifyOnArm = flags & 0x01;
notifyOnDisarm = flags & 0x02;
notifyOnTrigger = flags & 0x04;
notifyOnSleep = flags & 0x08;
notifyKeepAlive = flags & 0x10; // 5. bit
}
// Alarm triggered flag (1 byte) - addr = 111
byte alarmFlag = EEPROM.read(addr++);
if (alarmFlag != 0xFF) {
alarmTriggeredPermanent = alarmFlag;
}
// Vibration count (1 byte) - addr = 112
byte vibCount = EEPROM.read(addr++);
if (vibCount != 0xFF && vibCount <= 9) {
vibrationCount = vibCount;
}
// System armed state (1 byte) - addr = 113 (YENİ!)
byte armedState = EEPROM.read(addr++);
if (armedState != 0xFF) {
systemArmed = (armedState == 1);
}
// RF Codes - ARM (4 bytes) - addr = 114 (adres kaydı!)
unsigned long armCode = 0;
armCode |= ((unsigned long)EEPROM.read(addr++)) << 24;
armCode |= ((unsigned long)EEPROM.read(addr++)) << 16;
armCode |= ((unsigned long)EEPROM.read(addr++)) << 8;
armCode |= ((unsigned long)EEPROM.read(addr++));
if (armCode != 0 && armCode != 0xFFFFFFFF) rfCodeArm = armCode;
// RF Codes - DISARM (4 bytes) - addr = 118
unsigned long disarmCode = 0;
disarmCode |= ((unsigned long)EEPROM.read(addr++)) << 24;
disarmCode |= ((unsigned long)EEPROM.read(addr++)) << 16;
disarmCode |= ((unsigned long)EEPROM.read(addr++)) << 8;
disarmCode |= ((unsigned long)EEPROM.read(addr++));
if (disarmCode != 0 && disarmCode != 0xFFFFFFFF) rfCodeDisarm = disarmCode;
// MESAJ ŞABLONLARI EEPROM'A KAYDEDİLMİYOR (UTF-8 encoding sorunu)
// Default değerler kod içinde tanımlı
EEPROM.end();
}
void saveSettings() {
EEPROM.begin(1024);
// EEPROM Memory Map:
// 0-19: webUsername (20 bytes)
// 20-39: webPassword (20 bytes)
// 40-59: chatID (20 bytes)
// 60-109: botToken (50 bytes)
// 110: notification flags (1 byte)
// 111: alarm triggered permanent (1 byte)
// 112: vibration count (1 byte)
// 113: system armed state (1 byte)
// 114-117: RF code ARM (4 bytes)
// 118-121: RF code DISARM (4 bytes)
// 1020-1022: magic number (3 bytes)
int addr = 0;
// Username (20 bytes)
for (int i = 0; i < 20; i++) {
if (i < (int)webUsername.length()) {
EEPROM.write(addr++, webUsername[i]);
} else {
EEPROM.write(addr++, 0);
}
}
// Password (20 bytes)
for (int i = 0; i < 20; i++) {
if (i < (int)webPassword.length()) {
EEPROM.write(addr++, webPassword[i]);
} else {
EEPROM.write(addr++, 0);
}
}
// Chat ID (20 bytes)
for (int i = 0; i < 20; i++) {
if (i < (int)chatID.length()) {
EEPROM.write(addr++, chatID[i]);
} else {
EEPROM.write(addr++, 0);
}
}
// Token (50 bytes)
for (int i = 0; i < 50; i++) {
if (i < (int)botToken.length()) {
EEPROM.write(addr++, botToken[i]);
} else {
EEPROM.write(addr++, 0);
}
}
// Notification flags (1 byte) - addr = 110
byte flags = 0;
if (notifyOnArm) flags |= 0x01;
if (notifyOnDisarm) flags |= 0x02;
if (notifyOnTrigger) flags |= 0x04;
if (notifyOnSleep) flags |= 0x08;
if (notifyKeepAlive) flags |= 0x10; // 5. bit
EEPROM.write(addr++, flags);
// Alarm triggered flag (1 byte) - addr = 111
EEPROM.write(addr++, alarmTriggeredPermanent ? 1 : 0);
// Vibration count (1 byte) - addr = 112
EEPROM.write(addr++, vibrationCount);
// System armed state (1 byte) - addr = 113 (YENİ!)
EEPROM.write(addr++, systemArmed ? 1 : 0);
// RF Codes - ARM (4 bytes) - addr = 114 (adres kaydı!)
EEPROM.write(addr++, (rfCodeArm >> 24) & 0xFF);
EEPROM.write(addr++, (rfCodeArm >> 16) & 0xFF);
EEPROM.write(addr++, (rfCodeArm >> 8) & 0xFF);
EEPROM.write(addr++, rfCodeArm & 0xFF);
// RF Codes - DISARM (4 bytes) - addr = 118
EEPROM.write(addr++, (rfCodeDisarm >> 24) & 0xFF);
EEPROM.write(addr++, (rfCodeDisarm >> 16) & 0xFF);
EEPROM.write(addr++, (rfCodeDisarm >> 8) & 0xFF);
EEPROM.write(addr++, rfCodeDisarm & 0xFF);
// MESAJ ŞABLONLARI EEPROM'A KAYDEDİLMİYOR (UTF-8 encoding sorunu)
// Default değerler kod içinde tanımlı
// Magic number yaz (BS2 = Versiyon 2)
EEPROM.write(1020, 0x42); // 'B'
EEPROM.write(1021, 0x53); // 'S'
EEPROM.write(1022, 0x32); // '2' (Versiyon 2)
EEPROM.commit();
EEPROM.end();
Serial.println("
Tüm ayarlar EEPROM'a kaydedildi");
Serial.print(" Chat ID: ");
Serial.println(chatID.length() > 0 ? chatID : "(boş)");
Serial.print(" Bot Token: ");
Serial.println(botToken.length() > 0 ? "***" : "(boş)");
Serial.print(" Bildirimler: Arm=");
Serial.print(notifyOnArm ? "✓" : "✗");
Serial.print(" Disarm=");
Serial.print(notifyOnDisarm ? "✓" : "✗");
Serial.print(" Trigger=");
Serial.print(notifyOnTrigger ? "✓" : "✗");
Serial.print(" Sleep=");
Serial.println(notifyOnSleep ? "✓" : "✗");
}
// Alarm durumunu kaydet (EEPROM'a - reset sonrası da devam etsin)
void saveAlarmState() {
EEPROM.begin(1024);
// Alarm triggered flag (1 byte) - addr = 111
EEPROM.write(111, alarmTriggeredPermanent ? 1 : 0);
// Vibration count (1 byte) - addr = 112
EEPROM.write(112, vibrationCount);
// System armed state (1 byte) - addr = 113 (YENİ!)
EEPROM.write(113, systemArmed ? 1 : 0);
EEPROM.commit();
EEPROM.end();
Serial.print("
Alarm durumu kaydedildi: Tetik=");
Serial.print(alarmTriggeredPermanent ? "AKTİF" : "PASİF");
Serial.print(" Titreşim=");
Serial.print(vibrationCount);
Serial.print("/9 Sistem=");
Serial.println(systemArmed ? "AÇIK" : "KAPALI");
}
void armAlarm() {
systemArmed = true;
digitalWrite(CLOSE_LED_PIN, LOW);
digitalWrite(OPEN_LED_PIN, HIGH);
buzzerBeepOpen();
lastMotionTime = millis();
// Alarm durumunu EEPROM'a kaydet
saveAlarmState();
Serial.println("
Alarm açıldı");
if (notifyOnArm) {
sendTelegramNotification(msgAlarmArmed);
}
}
void disarmAlarm() {
systemArmed = false;
alarmActive = false;
digitalWrite(CLOSE_LED_PIN, HIGH);
digitalWrite(OPEN_LED_PIN, LOW);
digitalWrite(BUZZER_PIN, LOW);
digitalWrite(RELAY_PIN, LOW);
relayIsOn = false;
// Titreşim sayacını sıfırla
vibrationCount = 0;
// Geçici sireni kapat
temporarySirenActive = false;
// KALICI ALARMI KAPAT (Sadece bu fonksiyonla kapatılır!)
alarmTriggeredPermanent = false;
// EEPROM'a kaydet
saveAlarmState();
buzzerBeepClose();
// Deep sleep için zamanlayıcıyı başlat
lastMotionTime = millis();
Serial.println("
Alarm tamamen kapatıldı (RF kumanda/web panel ile)");
Serial.println("
10 saniye sonra derin uykuya geçecek (hareket yoksa)");
// Slave cihazlara alarm kapatıldı bildirimi gönder
notifySlaveDevices(false);
if (notifyOnDisarm) {
sendTelegramNotification(msgAlarmDisarmed);
}
}
void updateLEDs() {
// Alarm durumunu LED'lerle göster
if (systemArmed) {
digitalWrite(CLOSE_LED_PIN, LOW);
digitalWrite(OPEN_LED_PIN, HIGH); // Alarm açık = açık LED yanar
} else {
digitalWrite(CLOSE_LED_PIN, HIGH); // Alarm kapalı = kapalı LED yanar
digitalWrite(OPEN_LED_PIN, LOW);
}
}
void buzzerBeepOpen() {
// ALARM AÇILDI - 2x hızlı bip (200ms açık, 100ms kapalı) - HIZLI!
Serial.println(">>> BUZZER ACMA SESI <<<");
digitalWrite(BUZZER_PIN, LOW);
delay(10);
for (int i = 0; i < 2; i++) {
digitalWrite(BUZZER_PIN, HIGH);
delay(200);
digitalWrite(BUZZER_PIN, LOW);
delay(100);
}
}
void buzzerBeepClose() {
// ALARM KAPANDI - 1x kısa bip (150ms) - ÇOK HIZLI!
Serial.println(">>> BUZZER KAPAMA SESI <<<");
digitalWrite(BUZZER_PIN, LOW);
delay(10);
digitalWrite(BUZZER_PIN, HIGH);
delay(150);
digitalWrite(BUZZER_PIN, LOW);
}
void checkRFSignals() {
if (mySwitch.available()) {
unsigned long receivedValue = mySwitch.getReceivedValue();
if (receivedValue != 0) {
Serial.print("RF Kod: ");
Serial.println(receivedValue);
// Son alınan kodu kaydet (web sayfasında gösterilecek)
lastReceivedRFCode = receivedValue;
lastRFCodeTime = millis();
// RF öğrenme modu aktifse, sadece kodu göster
if (rfLearningMode) {
Serial.print("
ÖĞRENME MODU: RF Kod alındı: ");
Serial.println(receivedValue);
}
// Normal mod: Kumanda kontrolü
else {
// Alarm açma kodu
if (receivedValue == rfCodeArm && !systemArmed) {
Serial.println(">>> RF: ALARM ACILIYOR <<<");
armAlarm();
// delay KALDIRILDI - hızlı tepki için
}
// Alarm kapama kodu
else if (receivedValue == rfCodeDisarm && systemArmed) {
Serial.println(">>> RF: ALARM KAPATILIYOR <<<");
disarmAlarm();
// delay KALDIRILDI - hızlı tepki için
}
else {
Serial.println("Bilinmeyen RF kodu");
}
}
}
mySwitch.resetAvailable();
}
}
void handleAlarmTrigger() {
// BU FONKSİYON ARTIK KULLANILMIYOR - Basit alarm sistemi
// Hareket algılandığında direkt röle açılıyor (loop içinde)
}
void sendTelegramNotification(String message) {
Serial.println("\n=== TELEGRAM BİLDİRİMİ ===");
// WiFi kontrolü
if (WiFi.status() != WL_CONNECTED) {
Serial.println("
HATA: WiFi bağlı değil!");
Serial.print(" WiFi durumu: ");
Serial.println(WiFi.status());
return;
}
Serial.println("
WiFi bağlı");
// Chat ID kontrolü
if (chatID.length() == 0) {
Serial.println("
HATA: Chat ID boş!");
Serial.println(" Web panelden Chat ID'yi girin");
return;
}
Serial.print("
Chat ID: ");
Serial.println(chatID);
// Bot Token kontrolü
if (botToken.length() == 0) {
Serial.println("
HATA: Bot Token boş!");
Serial.println(" Web panelden Bot Token'ı girin");
return;
}
Serial.print("
Bot Token: ");
Serial.print(botToken.substring(0, 10));
Serial.println("...");
// Bot kontrolü
if (bot == nullptr) {
Serial.println("
HATA: Bot başlatılmamış!");
Serial.println(" Bot yeniden başlatılıyor...");
initTelegramBot();
if (bot == nullptr) {
Serial.println("
Bot başlatılamadı!");
return;
}
}
Serial.println("
Bot hazır");
// Mesaj gönder
Serial.println("Mesaj gonderiliyor...");
Serial.print(" Mesaj uzunlugu: ");
Serial.print(message.length());
Serial.println(" karakter");
bool sent = bot->sendMessage(chatID, message, "Markdown");
if (sent) {
Serial.println("
BAŞARILI: Telegram mesajı gönderildi!");
} else {
Serial.println("
BAŞARISIZ: Telegram mesajı gönderilemedi!");
Serial.println(" Olası sebepler:");
Serial.println(" - Bot Token yanlış");
Serial.println(" - Chat ID yanlış");
Serial.println(" - Bot ile sohbet başlatılmamış");
Serial.println(" - İnternet bağlantısı yavaş");
}
Serial.println("=========================\n");
}
void handleTelegramMessages() {
// Bot veya ayarlar yoksa çık
if (bot == nullptr || chatID.length() == 0 || botToken.length() == 0) {
return;
}
// Yeni mesajları al
int numNewMessages = bot->getUpdates(bot->last_message_received + 1);
for (int i = 0; i < numNewMessages; i++) {
String chat_id = String(bot->messages[i].chat_id);
String text = bot->messages[i].text;
String from_name = bot->messages[i].from_name;
Serial.println("\n=== TELEGRAM MESAJI ALINDI ===");
Serial.println("Gönderen: " + from_name);
Serial.println("Chat ID: " + chat_id);
Serial.println("Mesaj: " + text);
// Sadece kayıtlı Chat ID'den gelen mesajları işle (güvenlik)
if (chat_id != chatID) {
Serial.println("
UYARI: Yetkisiz Chat ID! Mesaj işlenmedi.");
bot->sendMessage(chat_id, "
Yetkisiz erişim!\n\nBu bot sadece kayıtlı kullanıcı için çalışır.", "");
continue;
}
// Komutları işle
if (text == "/start") {
sendTelegramHelp(chat_id);
}
else if (text == "/help" || text == "/yardim") {
sendTelegramHelp(chat_id);
}
else if (text == "/arm" || text == "/ac") {
if (!systemArmed) {
armAlarm();
bot->sendMessage(chat_id, "
Alarm Aktif Edildi\n\n
Sistem şimdi korumalı.\nTitreşim algılamaya başladı.", "");
} else {
bot->sendMessage(chat_id, "
Alarm zaten aktif.", "");
}
}
else if (text == "/disarm" || text == "/kapat") {
if (systemArmed) {
disarmAlarm();
bot->sendMessage(chat_id, "
Alarm Pasif Edildi\n\n
Sistem devre dışı.\nHareket algılama durduruldu.", "");
} else {
bot->sendMessage(chat_id, "
Alarm zaten pasif.", "");
}
}
else if (text == "/status" || text == "/durum") {
String statusMsg = "
SİSTEM DURUMU\n\n";
// Alarm durumu
statusMsg += "
Alarm: ";
statusMsg += systemArmed ? "
Aktif" : "
Pasif";
statusMsg += "\n";
// Röle durumu
statusMsg += "� Röle: ";
statusMsg += relayIsOn ? "
Açık (10sn)" : "Kapalı";
statusMsg += "\n";
// WiFi bilgisi
statusMsg += "
WiFi: " + WiFi.SSID() + "\n";
statusMsg += "
Sinyal: " + String(WiFi.RSSI()) + " dBm\n";
statusMsg += "
IP: " + WiFi.localIP().toString() + "\n";
// Çalışma süresi
unsigned long uptimeSec = (millis() - startTime) / 1000;
statusMsg += "
Çalışma: " + String(uptimeSec / 3600) + "s " + String((uptimeSec % 3600) / 60) + "d";
bot->sendMessage(chat_id, statusMsg, "");
}
else if (text == "/reset" || text == "/fabrika") {
String confirmMsg = "
FABRİKA AYARLARINA DÖN\n\n";
confirmMsg += "Bu işlem şunları silecek:\n";
confirmMsg += "• WiFi ayarları\n";
confirmMsg += "• Telegram ayarları\n";
confirmMsg += "• RF kodları\n";
confirmMsg += "• Tüm özel mesajlar\n\n";
confirmMsg += "
Sistem AP moduna dönecek!\n\n";
confirmMsg += "Onaylamak için tekrar '/reset_onay' yazın.";
bot->sendMessage(chat_id, confirmMsg, "");
}
else if (text == "/reset_onay") {
bot->sendMessage(chat_id, "
Fabrika ayarlarına dönülüyor...\n\nSistem yeniden başlayacak.\nAP modunda bağlanın:\n\nSSID: Discovery_Bisiklet_Alarmı\nŞifre: 12345678", "");
delay(2000); // Mesajın gitmesi için bekle
// EEPROM'u temizle
EEPROM.begin(1024);
for (int i = 0; i < 1024; i++) {
EEPROM.write(i, 0xFF);
}
EEPROM.commit();
EEPROM.end();
// WiFi ayarlarını sıfırla
wifiManager.resetSettings();
Serial.println("
Telegram'dan fabrika sıfırlama yapıldı");
delay(1000);
ESP.restart();
}
else {
String unknownMsg = "
Bilinmeyen komut: " + text + "\n\n";
unknownMsg += "Kullanılabilir komutlar için /help yazın.";
bot->sendMessage(chat_id, unknownMsg, "");
}
Serial.println("
Komut işlendi");
Serial.println("==============================\n");
}
}
void sendTelegramHelp(String chat_id) {
String helpMsg = "
BİSİKLET ALARM SİSTEMİ\n\n";
helpMsg += "
KULLANILABILIR KOMUTLAR:\n\n";
helpMsg += "/arm veya /ac\n";
helpMsg += " → Alarmı aktif et\n\n";
helpMsg += "/disarm veya /kapat\n";
helpMsg += " → Alarmı pasif et\n\n";
helpMsg += "/status veya /durum\n";
helpMsg += " → Sistem durumunu göster\n\n";
helpMsg += "/reset veya /fabrika\n";
helpMsg += " → Fabrika ayarlarına dön\n\n";
helpMsg += "/help veya /yardim\n";
helpMsg += " → Bu yardım mesajı\n\n";
helpMsg += "━━━━━━━━━━━━━━━━━━━━\n";
helpMsg += "
3 Aşamalı Alarm:\n";
helpMsg += "• 1-2 tetik: Dit-dit (ikaz)\n";
helpMsg += "• 3. tetik: 10sn siren\n";
helpMsg += "• 4-5 tetik: Dit-dit (ikaz)\n";
helpMsg += "• 6. tetik: 10sn siren\n";
helpMsg += "• 7-8 tetik: Dit-dit (ikaz)\n";
helpMsg += "• 9. tetik: SÜREKLI ALARM!\n\n";
helpMsg += "
Kalıcı alarm reset atılsa\n";
helpMsg += "bile devam eder! Sadece RF\n";
helpMsg += "kumanda ile kapatılabilir!";
bot->sendMessage(chat_id, helpMsg, "");
}
// Slave cihazlara UDP broadcast ile alarm durumu gönder
void notifySlaveDevices(bool alarmActive) {
if (WiFi.status() == WL_CONNECTED) {
udp.beginPacket("255.255.255.255", 8888); // Broadcast to port 8888
if (alarmActive) {
udp.write("ALARM_ON");
Serial.println("
Slave'lere ALARM_ON gönderildi (UDP broadcast)");
} else {
udp.write("ALARM_OFF");
Serial.println("
Slave'lere ALARM_OFF gönderildi (UDP broadcast)");
}
udp.endPacket();
} else {
Serial.println("
WiFi bağlı değil - Slave bildirim gönderilemedi");
}
}
(Not: Master kodunda bulunan HTML arayüzü, cihazın IP adresine girdiğinizde karşınıza çıkacak olan modern yönetim panelini oluşturur.)
Slave (Yardımcı Siren) Kodları
Slave cihaz, Master cihazdan gelen UDP paketlerini dinler. Eğer hırsızlık girişimi olursa, ana cihaz WiFi üzerinden “ALARM_ON” komutu gönderir ve evin içindeki bu cihaz da ötmeye başlar.codeC++
/*
* SLAVE CIHAZ KODU - Sadece Buzzer
*
* Bu kod ikinci bir ESP8266'ya yüklenecek
* Master cihazdan UDP broadcast ile alarm sinyali alır
* Sadece buzzer çalıştırır, başka bir işlevi yok
*
* Bağlantılar:
* - D1 (GPIO5): Buzzer (+)
* - GND: Buzzer (-)
*
* Kullanım:
* 1. WiFi ayarlarını güncelleyin (ssid, password)
* 2. Bu kodu ikinci ESP8266'ya yükleyin
* 3. Master cihaz ile aynı WiFi ağına bağlayın
* 4. Master alarm çaldığında bu cihaz da çalacak
*/
#include <ESP8266WiFi.h>
#include <WiFiUdp.h>
// WiFi ayarları - GÜNCELLEYIN!
const char* ssid = "WiFi_AGINIZIN_ADI";
const char* password = "WIFI_SIFRENIZ";
// Pin tanımları
#define BUZZER_PIN D1 // GPIO5
// UDP ve alarm durumu
WiFiUDP udp;
bool alarmActive = false;
unsigned long lastAlarmTime = 0;
void setup() {
Serial.begin(115200);
Serial.println("\n=== SLAVE CIHAZ BAŞLATIYOR ===");
// Buzzer pin ayarı
pinMode(BUZZER_PIN, OUTPUT);
digitalWrite(BUZZER_PIN, LOW);
// WiFi bağlantısı
Serial.print("WiFi'ye bağlanıyor: ");
Serial.println(ssid);
WiFi.begin(ssid, password);
// Bağlantı bekleme (timeout: 30 saniye)
int attempts = 0;
while (WiFi.status() != WL_CONNECTED && attempts < 60) {
delay(500);
Serial.print(".");
attempts++;
}
if (WiFi.status() == WL_CONNECTED) {
Serial.println("\n
WiFi bağlandı!");
Serial.print("IP Adresi: ");
Serial.println(WiFi.localIP());
Serial.print("Sinyal Gücü: ");
Serial.print(WiFi.RSSI());
Serial.println(" dBm");
} else {
Serial.println("\n
WiFi bağlantısı başarısız!");
Serial.println("Lütfen WiFi ayarlarını kontrol edin");
return;
}
// UDP dinlemeye başla
if (udp.begin(8888)) {
Serial.println("
UDP Port 8888 dinleniyor");
Serial.println("=== SLAVE CIHAZ HAZIR ===");
Serial.println("Master cihazdan alarm sinyali bekleniyor...\n");
} else {
Serial.println("
UDP başlatılamadı!");
}
// Başlangıç test sesi (3x kısa bip)
for (int i = 0; i < 3; i++) {
digitalWrite(BUZZER_PIN, HIGH);
delay(100);
digitalWrite(BUZZER_PIN, LOW);
delay(100);
}
}
void loop() {
// WiFi bağlantısını kontrol et
if (WiFi.status() != WL_CONNECTED) {
Serial.println("
WiFi bağlantısı kesildi! Yeniden bağlanıyor...");
WiFi.reconnect();
delay(5000);
return;
}
// UDP mesaj kontrolü
int packetSize = udp.parsePacket();
if (packetSize > 0) {
char packet[16];
int len = udp.read(packet, sizeof(packet) - 1);
packet[len] = '\0'; // String sonlandırıcı
Serial.print("
UDP mesaj alındı: ");
Serial.print(packet);
Serial.print(" (");
Serial.print(udp.remoteIP());
Serial.println(")");
// Alarm ON sinyali
if (strcmp(packet, "ALARM_ON") == 0) {
if (!alarmActive) {
alarmActive = true;
lastAlarmTime = millis();
Serial.println("
ALARM BAŞLADI! Buzzer aktif");
}
}
// Alarm OFF sinyali
else if (strcmp(packet, "ALARM_OFF") == 0) {
if (alarmActive) {
alarmActive = false;
digitalWrite(BUZZER_PIN, LOW);
Serial.println("
Alarm durdu! Buzzer pasif");
}
}
else {
Serial.println("
Bilinmeyen UDP mesajı");
}
}
// Alarm aktifse buzzer çal
if (alarmActive) {
digitalWrite(BUZZER_PIN, HIGH);
// Her 10 saniyede bir durum mesajı
if (millis() - lastAlarmTime > 10000) {
Serial.println("
Alarm devam ediyor... (Buzzer çalıyor)");
lastAlarmTime = millis();
}
}
// CPU'yu rahatlatmak için kısa delay
delay(10);
}
Kurulum ve Ayarlar
1. WiFi Bağlantısı
Cihazı ilk kez çalıştırdığınızda veya WiFi ayarlarını bulamadığında “Discovery_Bisiklet_Alarmı” isimli bir AP (Erişim Noktası) oluşturur. Telefonunuzla bu ağa bağlanın (Şifre: 12345678) ve çıkan ekranda kendi ev/iş WiFi bilgilerinizi girin.
2. Web Panel ve Telegram
Cihaz WiFi’ye bağlandığında aldığı IP adresini seri port ekranından görebilirsiniz. Tarayıcıya bu IP’yi yazarak yönetim paneline girin (Varsayılan: admin / admin).
-
- Telegram Ayarları: BotFather üzerinden aldığınız Tokenı ve kendi Chat IDnizi buraya yazıp kaydedin.
-
- RF Öğrenme: “Öğrenme Modunu Başlat” butonuna tıklayıp 15 saniye içinde kumandanızın tuşlarına basarak kumandanızı sisteme tanıtın.
3. Alarm Çalışma Mantığı (3 AŞAMA)
Sistem, hırsızları caydırmak için kademeli bir mantıkla çalışır:
-
- 1-2. Sarsıntı: “Dit-dit” şeklinde kısa uyarı sesi (Yanlış tetiklemeler için).
-
- 3. ve 6. Sarsıntı: 10 saniye boyunca siren çalar ve Telegram’dan bildirim gönderir.
-
- 9. Sarsıntı (KRİTİK): Kalıcı alarm devreye girer. Cihaz resetlense bile susmaz. Sadece kayıtlı kumanda veya web panel üzerinden kapatılabilir.
Önemli Notlar
-
- Pil Kullanımı: Alarm devredeyken cihaz sürekli WiFi’ye bağlı kalır. Bu nedenle kaliteli bir 18650 pil veya powerbank kullanmanızı öneririm.
-
- Anten: RF alıcının çekim mesafesini artırmak için DATA pininin yanındaki boşluğa 17cm’lik bir bakır tel lehimlemeyi unutmayın.
Bu proje ile bisikletiniz artık sadece kilitlere değil, internetin gücüne de emanet! Sorularınızı yorum kısmından iletebilirsiniz.
