#include <Wire.h>
#include <LiquidCrystal_I2C.h>
// --- عناوين الـ I2C الافتراضية ---
#define PCF8574_ADDR 0x20
#define EEPROM_ADDR 0x50
// --- خريطة بنات الأردوينو أونو المباشرة (6 أزرار + البازر) ---
#define BUZZER 12
#define BTN_LFT 6
#define BTN_UP 7
#define BTN_DWN 8
#define BTN_ENT 9
#define BTN_BCK 10
#define BTN_RGT 11
// --- خريطة بنات الـ PCF8574 ---
#define PCF_IGN 0 // P0 -> ريليه 1
#define PCF_DOOR 1 // P1 -> ريليه 2
#define PCF_BRAKE 2 // P2 -> ريليه 3
#define PCF_SIREN 3 // P3 -> دخل السرينة (يوصل بالأرضي لقط الإشارة)
#define PCF_SIG2 4 // P4 -> دخل الإشارة 2 (يوصل بالأرضي لقط الإشارة)
#define PCF_ID1 5
#define PCF_ID2 6
#define PCF_ID3 7
LiquidCrystal_I2C lcd(0x27, 20, 4);
int menuIndex = 0;
int lastMenuIndex = -1;
const char* menuItems[] = {"1. AUTO SCAN", "2. MANUAL LEARN", "3. SMART CLEAR", "4. VIEW MEMORY", "5. FACTORY RESET"};
const int menuCount = 5;
bool inSubMenu = false;
struct Action {
unsigned long timestamp;
byte device;
bool state;
};
struct DeviceRecord {
char name[6];
int actionsCount;
};
Action currentMovie[60];
int actionIndex = 0;
unsigned long startRecTime;
// دالة قراءة الأزرار
bool readBtn(int pin) {
if (digitalRead(pin) == LOW) {
delay(30);
if (digitalRead(pin) == LOW) {
tone(BUZZER, 2000, 40);
delay(250);
return true;
}
}
return false;
}
// دوال الـ EEPROM الخارجي AT24C08
void writeExtEEPROM(unsigned int address, byte data) {
byte devAddress = EEPROM_ADDR | ((address >> 8) & 0x07);
byte wordAddress = address & 0xFF;
Wire.beginTransmission(devAddress);
Wire.write(wordAddress);
Wire.write(data);
Wire.endTransmission();
delay(5);
}
byte readExtEEPROM(unsigned int address) {
byte rData = 0xFF;
byte devAddress = EEPROM_ADDR | ((address >> 8) & 0x07);
byte wordAddress = address & 0xFF;
Wire.beginTransmission(devAddress);
Wire.write(wordAddress);
Wire.endTransmission();
Wire.requestFrom((uint8_t)devAddress, (uint8_t)1);
if (Wire.available()) rData = Wire.read();
return rData;
}
void putExtBlock(unsigned int address, byte* data, int size) {
for (int i = 0; i < size; i++) writeExtEEPROM(address + i, data[i]);
}
void getExtBlock(unsigned int address, byte* data, int size) {
for (int i = 0; i < size; i++) data[i] = readExtEEPROM(address + i);
}
// --- دوال الـ PCF8574 ---
byte pcfOutputState = 0xF8;
void writePCF(byte rel_ign_state, byte rel_door_state, byte rel_brake_state) {
pcfOutputState = 0xF8;
if (rel_ign_state == LOW) pcfOutputState &= ~(1 << PCF_IGN);
if (rel_door_state == LOW) pcfOutputState &= ~(1 << PCF_DOOR);
if (rel_brake_state == LOW) pcfOutputState &= ~(1 << PCF_BRAKE);
Wire.beginTransmission(PCF8574_ADDR);
Wire.write(pcfOutputState);
Wire.endTransmission();
}
byte readPCF() {
Wire.beginTransmission(PCF8574_ADDR);
Wire.write(pcfOutputState | 0xF8);
Wire.endTransmission();
Wire.requestFrom((uint8_t)PCF8574_ADDR, (uint8_t)1);
if (Wire.available()) {
return Wire.read();
}
return pcfOutputState;
}
void allOff() {
writePCF(HIGH, HIGH, HIGH);
}
int getPlugID() {
byte pcfData = readPCF();
bool id1 = !(pcfData & (1 << PCF_ID1));
bool id2 = !(pcfData & (1 << PCF_ID2));
bool id3 = !(pcfData & (1 << PCF_ID3));
return (id3 << 2) | (id2 << 1) | id1;
}
void checkAttachedPlug() {
int plugType = getPlugID();
lcd.setCursor(0, 2); lcd.print("DETECTED PLUG: ");
lcd.setCursor(0, 3);
switch(plugType) {
case 1: lcd.print(">> TYPE A: TOYOTA "); break;
case 2: lcd.print(">> TYPE B: HYUNDAI "); break;
case 3: lcd.print(">> TYPE C: NISSAN "); break;
default: lcd.print(">> STANDARD PLUG "); break;
}
delay(1500);
}
void splashScreen() {
lcd.clear();
lcd.setCursor(0, 0); lcd.print("====================");
lcd.setCursor(0, 1); lcd.print(" FARES SYSTEM V12 ");
lcd.setCursor(0, 2); lcd.print(" WOKWI SIMULATION ");
lcd.setCursor(0, 3); lcd.print("====================");
delay(1500);
lcd.clear();
lcd.setCursor(0, 0); lcd.print("SYSTEM INITIALIZING");
lcd.setCursor(0, 1); lcd.print("Loading Threads... ");
for (int i = 0; i < 20; i++) {
lcd.setCursor(i, 2); lcd.print((char)255);
delay(40);
}
checkAttachedPlug();
}
void showMenu() {
lcd.clear();
lcd.setCursor(0, 0); lcd.print("--- MAIN MENU ---");
lcd.setCursor(0, 1); lcd.print("Current Selection: ");
lcd.setCursor(0, 2); lcd.print(" -> "); lcd.print(menuItems[menuIndex]);
lcd.setCursor(0, 3); lcd.print("====================");
}
static bool current_ign = false, current_door = false, current_brake = false;
void recordAction(byte d, bool s) {
if (d == 1) current_ign = s;
if (d == 2) current_door = s;
if (d == 3) current_brake = s;
writePCF(current_ign ? LOW : HIGH, current_door ? LOW : HIGH, current_brake ? LOW : HIGH);
if(actionIndex < 60) {
currentMovie[actionIndex] = {millis() - startRecTime, d, s};
actionIndex++;
}
}
String nameDevice() {
String chars = " ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
char name[6] = " ";
int charPos = 0; int charIdx = 0;
lcd.clear();
lcd.setCursor(0, 0); lcd.print("[ NAME DEVICE ]");
lcd.setCursor(0, 1); lcd.print("Enter 5 Characters:");
while (charPos < 5) {
lcd.setCursor(charPos + 7, 2); lcd.print(chars[charIdx]);
lcd.setCursor(charPos + 7, 2); lcd.cursor();
if (readBtn(BTN_UP)) { charIdx++; if (charIdx >= chars.length()) charIdx = 0; }
if (readBtn(BTN_DWN)) { charIdx--; if (charIdx < 0) charIdx = chars.length() - 1; }
if (readBtn(BTN_RGT)) { charIdx += 5; if (charIdx >= chars.length()) charIdx = 0; }
if (readBtn(BTN_LFT)) { charIdx -= 5; if (charIdx < 0) charIdx = chars.length() - 1; }
if (readBtn(BTN_ENT)) { name[charPos] = chars[charIdx]; charPos++; charIdx = 0; tone(BUZZER, 2500, 50); }
if (digitalRead(BTN_BCK) == LOW) { delay(200); if (charPos > 0) charPos--; else { lcd.noCursor(); return "NONAM"; } }
}
lcd.noCursor(); name[5] = '\0'; return String(name);
}
void dynamicSave() {
String finalName = nameDevice();
int slot = -1;
for(int s=0; s<5; s++) {
DeviceRecord tempDR;
getExtBlock(s * 200, (byte*)&tempDR, sizeof(DeviceRecord));
if(tempDR.actionsCount <= 0 || tempDR.actionsCount > 60) { slot = s; break; }
}
if(slot == -1) slot = 0;
unsigned int addr = slot * 200;
DeviceRecord dr;
finalName.toCharArray(dr.name, 6);
dr.actionsCount = actionIndex;
putExtBlock(addr, (byte*)&dr, sizeof(DeviceRecord));
for(int i=0; i<actionIndex; i++) {
putExtBlock(addr + sizeof(DeviceRecord) + (i * sizeof(Action)), (byte*)¤tMovie[i], sizeof(Action));
}
lcd.clear();
lcd.setCursor(0, 1); lcd.print(" SAVED IN 24C08! ");
lcd.setCursor(0, 2); lcd.print(" SLOT: "); lcd.print(slot+1);
delay(1500);
lastMenuIndex = -1;
}
// --- دالة الفحص اللحظي الفوري للسرينة ---
bool checkSiren() {
byte pcfInput = readPCF();
bool sirenTriggered = !(pcfInput & (1 << PCF_SIREN));
bool sig2Triggered = !(pcfInput & (1 << PCF_SIG2));
if (sirenTriggered || sig2Triggered) {
allOff();
tone(BUZZER, 1500, 800);
lcd.clear();
lcd.setCursor(0, 0); lcd.print("====================");
lcd.setCursor(0, 1); lcd.print(" MATCH FOUND!! ");
lcd.setCursor(0, 2); lcd.print("ENT:SAVE BCK:CLR ");
lcd.setCursor(0, 3); lcd.print("====================");
while(true) {
if (readBtn(BTN_ENT)) { dynamicSave(); return true; }
if (digitalRead(BTN_BCK) == LOW) { delay(200); return true; }
}
}
return false;
}
// 1. الأوتوماتيك السريع (تم إصلاح الأقواس والـ Syntax)
void startAuto() {
lcd.clear();
lcd.setCursor(0, 0); lcd.print("[ AUTO SCAN MODE ]");
lcd.setCursor(0, 1); lcd.print("Select Scan Speed:");
lcd.setCursor(0, 2); lcd.print("UP: FAST (400ms)");
lcd.setCursor(0, 3); lcd.print("ENT: SLOW (800ms)");
int speedDelay = 600;
while (inSubMenu) {
if (readBtn(BTN_UP)) { speedDelay = 400; break; }
if (readBtn(BTN_ENT)) { speedDelay = 800; break; }
if (digitalRead(BTN_BCK) == LOW) { inSubMenu = false; lastMenuIndex = -1; return; }
}
lcd.clear();
lcd.setCursor(0, 0); lcd.print("SCANNING IN PROGRESS");
lcd.setCursor(0, 3); lcd.print("BCK: Abort Scan ");
actionIndex = 0;
startRecTime = millis();
bool dS = true; bool bS = true; bool iS = false;
int pCount = 0;
recordAction(2, dS);
recordAction(3, bS);
unsigned long lastPulseTime = millis();
while(inSubMenu) {
if (checkSiren()) { inSubMenu = false; break; }
if (readBtn(BTN_DWN)) { dS = !dS; recordAction(2, dS); }
if (readBtn(BTN_ENT)) { bS = !bS; recordAction(3, bS); }
if (digitalRead(BTN_BCK) == LOW) { delay(200); inSubMenu = false; break; }
unsigned long now = millis();
if (now - lastPulseTime >= speedDelay) {
iS = !iS;
recordAction(1, iS);
lastPulseTime = now;
if(!iS) {
pCount++;
lcd.setCursor(0, 1); lcd.print("Pulse Count: "); lcd.print(pCount); lcd.print(" ");
lcd.setCursor(0, 2);
lcd.print("DOOR:"); lcd.print(dS?"ON ":"OFF");
lcd.print(" | BRAKE:"); lcd.print(bS?"ON ":"OFF");
}
}
if (pCount >= 20) { break; }
}
allOff();
inSubMenu = false;
lastMenuIndex = -1;
showMenu();
}
// 2. المانوال
void startLearning() {
lcd.clear();
lcd.setCursor(0, 0); lcd.print("[ MANUAL LEARN ]");
lcd.setCursor(0, 1); lcd.print("UP : Toggle IGN");
lcd.setCursor(0, 2); lcd.print("DWN : Toggle DOOR");
lcd.setCursor(0, 3); lcd.print("ENT : Toggle BRAKE");
actionIndex = 0; startRecTime = millis();
bool i=0, d=0, b=0;
current_ign = false; current_door = false; current_brake = false;
allOff();
while(inSubMenu) {
if (readBtn(BTN_UP)) { i=!i; recordAction(1, i); }
if (readBtn(BTN_DWN)) { d=!d; recordAction(2, d); }
if (readBtn(BTN_ENT)) { b=!b; recordAction(3, b); }
if (checkSiren()) break;
if (digitalRead(BTN_BCK) == LOW) break;
}
allOff(); inSubMenu = false; lastMenuIndex = -1; showMenu();
}
// 3. مسح ذكي
void smartClear() {
lcd.clear();
lcd.setCursor(0, 0); lcd.print("[ SMART CLEAR ]");
lcd.setCursor(0, 1); lcd.print("Clearing Codes... ");
current_door = true; current_brake = true; current_ign = false;
writePCF(HIGH, LOW, LOW); delay(300);
int pCount = 0;
while(inSubMenu) {
writePCF(LOW, LOW, LOW); delay(300);
writePCF(HIGH, LOW, LOW); delay(300);
pCount++;
lcd.setCursor(0, 2); lcd.print("Pulse: "); lcd.print(pCount);
byte pcfInput = readPCF();
bool sirenHigh = !(pcfInput & (1 << PCF_SIREN));
bool sig2High = !(pcfInput & (1 << PCF_SIG2));
if (sirenHigh && sig2High) {
delay(400); pcfInput = readPCF();
if (!(pcfInput & (1 << PCF_SIREN))) { tone(BUZZER, 1200, 500); break; }
}
if (pCount >= 30 || digitalRead(BTN_BCK) == LOW) break;
}
allOff(); inSubMenu = false; lastMenuIndex = -1; showMenu();
}
// 4. استعراض الذاكرة
void listMemory() {
lcd.clear();
lcd.setCursor(0, 0); lcd.print("[ VIEW EEPROM 24C08 ]");
int slot = 0;
while(inSubMenu) {
DeviceRecord dr;
getExtBlock(slot * 200, (byte*)&dr, sizeof(DeviceRecord));
lcd.setCursor(0, 1); lcd.print("Slot Number: "); lcd.print(slot+1); lcd.print(" / 5 ");
lcd.setCursor(0, 2);
if(dr.actionsCount > 0 && dr.actionsCount <= 60) {
lcd.print("Device: "); lcd.print(dr.name); lcd.print(" ");
} else {
lcd.print("Status: [EMPTY] ");
}
lcd.setCursor(0, 3); lcd.print("UP/DWN:Nav | ENT:Run");
if(readBtn(BTN_UP)) { slot++; if(slot > 4) slot = 0; }
if(readBtn(BTN_DWN)) { slot--; if(slot < 0) slot = 4; }
if(readBtn(BTN_ENT) && dr.actionsCount > 0) {
lcd.clear();
lcd.setCursor(0, 1); lcd.print(" PLAYING MOVIE ");
lcd.setCursor(0, 2); lcd.print(" PLEASE WAIT.. ");
allOff(); unsigned long pStart = millis();
current_ign = false; current_door = false; current_brake = false;
for(int i=0; i < dr.actionsCount; i++) {
Action a;
getExtBlock((slot * 200) + sizeof(DeviceRecord) + (i * sizeof(Action)), (byte*)&a, sizeof(Action));
while(millis() - pStart < a.timestamp) { if(digitalRead(BTN_BCK) == LOW) goto exitP; }
if (a.device == 1) current_ign = a.state;
if (a.device == 2) current_door = a.state;
if (a.device == 3) current_brake = a.state;
writePCF(current_ign ? LOW : HIGH, current_door ? LOW : HIGH, current_brake ? LOW : HIGH);
}
delay(1000); break;
}
if(digitalRead(BTN_BCK) == LOW) break;
}
exitP:
allOff(); inSubMenu = false; lastMenuIndex = -1; showMenu();
}
// 5. ضبط المصنع
void factoryReset() {
lcd.clear();
lcd.setCursor(0, 0); lcd.print("[ FACTORY RESET ]");
lcd.setCursor(0, 1); lcd.print("Wiping AT24C08... ");
for (unsigned int i = 0; i < 1000; i++) {
writeExtEEPROM(i, 0xFF);
if(i % 100 == 0) { lcd.setCursor(0, 2); lcd.print("Progress: "); lcd.print(i/10); lcd.print("%"); }
}
lcd.clear();
lcd.setCursor(0, 1); lcd.print(" RESET SUCCESS! ");
delay(1500);
inSubMenu = false; lastMenuIndex = -1; showMenu();
}
void setup() {
Wire.begin();
Wire.setClock(400000);
pinMode(BUZZER, OUTPUT);
pinMode(BTN_LFT, INPUT_PULLUP); pinMode(BTN_UP, INPUT_PULLUP); pinMode(BTN_DWN, INPUT_PULLUP);
pinMode(BTN_ENT, INPUT_PULLUP); pinMode(BTN_BCK, INPUT_PULLUP); pinMode(BTN_RGT, INPUT_PULLUP);
allOff();
lcd.init(); lcd.backlight();
splashScreen();
}
void loop() {
if (!inSubMenu) {
if (menuIndex != lastMenuIndex) {
showMenu();
lastMenuIndex = menuIndex;
}
if (readBtn(BTN_UP)) { menuIndex--; if(menuIndex < 0) menuIndex = menuCount - 1; }
if (readBtn(BTN_DWN)) { menuIndex++; if(menuIndex >= menuCount) menuIndex = 0; }
if (readBtn(BTN_ENT)) {
inSubMenu = true;
if (menuIndex == 0) startAuto();
else if (menuIndex == 1) startLearning();
else if (menuIndex == 2) smartClear();
else if (menuIndex == 3) listMemory();
else if (menuIndex == 4) factoryReset();
}
} else {
if (digitalRead(BTN_BCK) == LOW) { delay(100); inSubMenu = false; allOff(); lastMenuIndex = -1; }
}
}