#include <Keypad.h>
#include <LiquidCrystal_I2C.h>
#include "HX711.h"
// ================ تعريف الأجهزة والمكونات ==================
// LCD I2C
LiquidCrystal_I2C lcd(0x27, 16, 2);
// Keypad setup
const byte ROWS = 4;
const byte COLS = 4;
char keys[ROWS][COLS] = {
{'1','2','3','A'},
{'4','5','6','B'},
{'7','8','9','C'},
{'*','0','#','D'}
};
byte rowPins[ROWS] = {9, 8, 7, 6};
byte colPins[COLS] = {5, 4, 3, 2};
Keypad keypad = Keypad(makeKeymap(keys), rowPins, colPins, ROWS, COLS);
// HX711 setup
#define DOUT A2
#define CLK A3
HX711 scale;
// Joystick
#define JOY_X A0
#define JOY_Y A1
// Outputs
#define BUZZER 11 // خرج البوزر (يمكن ضبطه إذا احتجت)
#define LED 13 // LED متصل بالمنفذ 13
#define RESET_BTN 12 // زر إعادة الضبط (قفل الخزنة)
// ================ إعدادات كلمة المرور والحالات ==================
String password = "1"; // كلمة المرور المطلوبة
String inputPassword = ""; // لتخزين الإدخال من Keypad
// حالات النظام
bool isUnlocked = false; // حالة فتح الخزنة
bool alertActive = false; // حالة التنبيه
float baselineWeight = 0; // الوزن الابتدائي بعد tare()
const float ALLOWED_LOAD = 4.0; // الحد الأعلى للفرق المسموح به بين الوزن الحالي والـ baseline
// متغيرات للتحكم في وميض LED في حالة التنبيه (Non-Blocking)
unsigned long alertPreviousMillis = 0;
const unsigned long alertInterval = 300; // فترة وميض LED (300 مللي ثانية)
bool alertToggle = false;
// ===================== الدوال =====================
// دالة لمعالجة إدخال كلمة المرور من لوحة المفاتيح
void processKeypadInput() {
char key = keypad.getKey();
if (key) {
Serial.print("Key pressed: ");
Serial.println(key);
// مفتاح التأكيد
if (key == '#') {
Serial.print("Input Password: ");
Serial.println(inputPassword);
if (inputPassword == password) {
isUnlocked = true;
lcd.clear();
lcd.print("Safe Opened!");
// عند الفتح، سنترك LED في حالة HIGH كدليل على أن الخزنة مفتوحة
digitalWrite(LED, HIGH);
noTone(BUZZER);
} else {
lcd.clear();
lcd.print("Incorrect Code");
tone(BUZZER, 1000, 500);
delay(1000);
lcd.clear();
lcd.print("safe is secured");
}
inputPassword = "";
}
// مفتاح المسح
else if (key == '*') {
inputPassword = "";
lcd.setCursor(0, 1);
lcd.print(" ");
}
// أي مفتاح آخر يُضاف إلى كلمة المرور
else {
if (inputPassword.length() < 16) {
inputPassword += key;
lcd.setCursor(0, 1);
// عرض "*" بدلاً من الحرف المُدخل
for (int i = 0; i < inputPassword.length(); i++) {
lcd.print("*");
}
}
}
}
}
// دالة لفحص التلاعب عبر قراءة الوزن وحالة الـ Joystick
void checkForTampering() {
float currentWeight = scale.get_units(5);
int joyX = analogRead(JOY_X);
int joyY = analogRead(JOY_Y);
// لطباعة القيم على Serial (مفيد للتصحيح)
Serial.print("Current Weight: ");
Serial.print(currentWeight);
Serial.print(" | Baseline: ");
Serial.println(baselineWeight);
// إذا كانت الخزنة مغلقة ولم يحدث تنبيه بعد، نفحص التلاعب
if (!isUnlocked && !alertActive) {
// نقارن الفرق بين الوزن الحالي والـ baseline بالإضافة للتحقق من حالتي Joystick
if ((currentWeight - baselineWeight) > ALLOWED_LOAD ||
abs(currentWeight - baselineWeight) > 0.5 ||
joyX < 400 || joyX > 600 ||
joyY < 400 || joyY > 600) {
alertActive = true;
lcd.clear();
lcd.print("Warning: Tampering!");
// عند بدء التنبيه، نوقف LED (ولنبدأ وميضه باستخدام updateAlert)
digitalWrite(LED, LOW);
Serial.println("Tampering detected!");
tone(BUZZER, 2000); // نشغل البوزر تلقائياً عند بدء التنبيه
alertPreviousMillis = millis(); // بدء عد التوقيت للوميض
}
}
}
// دالة لتحديث وميض LED في حالة التنبيه باستخدام millis (غير متزامن)
void updateAlert() {
if (alertActive) {
unsigned long currentMillis = millis();
if (currentMillis - alertPreviousMillis >= alertInterval) {
alertPreviousMillis = currentMillis;
alertToggle = !alertToggle;
digitalWrite(LED, alertToggle ? HIGH : LOW);
}
}
}
// دالة لإعادة ضبط الخزنة عند الضغط على زر RESET
void resetSafe() {
bool resetPressed = digitalRead(RESET_BTN) == LOW;
// إذا كانت الخزنة مفتوحة أو هناك تنبيه واضغط زر RESET
if ((isUnlocked || alertActive) && resetPressed) {
// يمكن إضافة وميض LED للإشارة إلى إعادة الضبط
digitalWrite(LED, HIGH);
delay(500);
digitalWrite(LED, LOW);
// إعادة الحالة إلى الوضع الطبيعي (مقفولة)
isUnlocked = false;
alertActive = false;
lcd.clear();
lcd.print("safe is secured");
digitalWrite(LED, LOW);
// إعادة ضبط الوزن الأساسي
scale.tare();
baselineWeight = scale.get_units(3);
Serial.print("New Baseline: ");
Serial.println(baselineWeight);
// مهلة بسيطة لتفادي ارتداد زر RESET
delay(1000);
}
}
void setup() {
// إعداد المخارج والمداخل
pinMode(BUZZER, OUTPUT);
pinMode(LED, OUTPUT);
pinMode(RESET_BTN, INPUT_PULLUP);
lcd.init();
lcd.backlight();
Serial.begin(9600);
// إعداد جهاز HX711 والمعايرة
scale.begin(DOUT, CLK);
delay(1000);
scale.set_scale(); // اضبط معامل المعايرة المناسب لوحدة الوزن
scale.tare();
baselineWeight = scale.get_units(3);
Serial.print("Baseline Weight: ");
Serial.println(baselineWeight);
// عرض رسالة البدء عند قفل الخزنة
lcd.setCursor(0, 0);
lcd.print("safe is secured");
digitalWrite(LED, LOW);
}
void loop() {
// أولاً، نتحقق من زر RESET لإعادة الضبط إذا كانت الخزنة مفتوحة أو في حالة تنبيه
resetSafe();
// إذا لم تكن الخزنة مفتوحة ولا في حالة تنبيه، نعالج إدخال لوحة المفاتيح وفحص التلاعب
if (!isUnlocked && !alertActive) {
processKeypadInput();
checkForTampering();
}
// تحديث حالة التنبيه (وميض LED والبوزر إذا كان التنبيه مفعل)
updateAlert();
}