#include <SPI.h>
#include <TFT_eSPI.h>
#include <ESP32Servo.h>
// استدعاء ملفات صور الوجه
#include "idle.h"
#include "blink.h"
#include "happy.h"
#include "listen_0.h"
#include "listen_1.h"
#include "speak_0.h"
#include "speak_1.h"
// ==========================================
// 1. تحديد المنافذ بناءً على diagram.json
// ==========================================
const int PIN_LED_BLUE = 2;
const int PIN_LED_WHITE = 15;
const int PIN_BTN_REWARD = 4;
const int PIN_BTN_PTT = 5;
const int PIN_PIR_RIGHT = 13;
const int PIN_PIR_LEFT = 14;
const int PIN_SERVO_PAN = 18; // servo2
const int PIN_SERVO_TILT = 25; // servo1
// ==========================================
// 2. الكائنات والمتغيرات
// ==========================================
TFT_eSPI tft = TFT_eSPI();
Servo servoPan;
Servo servoTilt;
enum State { IDLE, LISTEN, SPEAK, PRAISE };
State currentState = IDLE;
unsigned long pttPressStart = 0;
unsigned long stateTimer = 0;
unsigned long lastBlinkTime = 0;
unsigned long lastFrameTime = 0;
unsigned long lastMotionTime = 0;
int frameIndex = 0;
int currentPanAngle = 90;
void setup() {
Serial.begin(115200);
// إعداد الليدات والأزرار
pinMode(PIN_LED_BLUE, OUTPUT);
pinMode(PIN_LED_WHITE, OUTPUT);
digitalWrite(PIN_LED_BLUE, HIGH);
digitalWrite(PIN_LED_WHITE, LOW);
pinMode(PIN_BTN_REWARD, INPUT_PULLUP);
pinMode(PIN_BTN_PTT, INPUT_PULLUP);
pinMode(PIN_PIR_RIGHT, INPUT);
pinMode(PIN_PIR_LEFT, INPUT);
// إعداد محركات السيرفو
servoPan.attach(PIN_SERVO_PAN);
servoTilt.attach(PIN_SERVO_TILT);
servoPan.write(90);
servoTilt.write(90);
// تشغيل وتهيئة الشاشة
tft.init();
tft.setRotation(1);
tft.fillScreen(TFT_BLACK);
// عرض الوجه الأساسي فوراً
tft.pushImage(0, 0, 320, 240, idle);
}
void loop() {
unsigned long now = millis();
// --- أ. زر المكافأة (btn1 على دبوس 4) ---
if (digitalRead(PIN_BTN_REWARD) == LOW && currentState != PRAISE) {
currentState = PRAISE;
tft.pushImage(0, 0, 320, 240, happy);
}
// --- ب. زر التحدث والإسكات (btn2 على دبوس 5) ---
if (currentState != PRAISE) {
bool isPttPressed = (digitalRead(PIN_BTN_PTT) == LOW);
if (isPttPressed && pttPressStart == 0) {
pttPressStart = now;
lastMotionTime = now;
}
else if (isPttPressed && pttPressStart > 0) {
if (now - pttPressStart >= 500 && currentState != LISTEN) {
currentState = LISTEN;
frameIndex = 0;
lastFrameTime = now;
tft.pushImage(0, 0, 320, 240, listen_0);
Serial.println("EVENT: START_RECORDING");
}
}
else if (!isPttPressed && pttPressStart > 0) {
if (currentState == LISTEN) {
currentState = SPEAK;
stateTimer = now;
frameIndex = 0;
lastFrameTime = now;
tft.pushImage(0, 0, 320, 240, speak_0);
Serial.println("EVENT: STOP_RECORDING -> SPEAKING_START");
}
else if (currentState == SPEAK) {
currentState = IDLE;
tft.pushImage(0, 0, 320, 240, idle);
Serial.println("EVENT: MUTE_CANCELLED");
}
pttPressStart = 0;
lastMotionTime = now;
}
}
// --- جـ. أنيميشن الحالات وسرعة الحركة ---
if (currentState == PRAISE) {
digitalWrite(PIN_LED_WHITE, HIGH);
servoTilt.write(90);
delay(200);
for (int i = 0; i < 4; i++) {
servoTilt.write(60);
delay(300);
servoTilt.write(120);
delay(300);
}
servoTilt.write(90);
digitalWrite(PIN_LED_WHITE, LOW);
currentState = IDLE;
tft.pushImage(0, 0, 320, 240, idle);
lastMotionTime = millis();
}
else if (currentState == LISTEN) {
if (now - lastFrameTime > 200) {
frameIndex = !frameIndex;
tft.pushImage(0, 0, 320, 240, frameIndex ? listen_1 : listen_0);
lastFrameTime = now;
}
}
else if (currentState == SPEAK) {
if (now - lastFrameTime > 200) {
frameIndex = !frameIndex;
tft.pushImage(0, 0, 320, 240, frameIndex ? speak_1 : speak_0);
lastFrameTime = now;
}
if (Serial.available()) {
String msg = Serial.readStringUntil('\n');
msg.trim();
if (msg.equalsIgnoreCase("done")) {
currentState = IDLE;
tft.pushImage(0, 0, 320, 240, idle);
}
}
if (now - stateTimer > 30000) {
currentState = IDLE;
tft.pushImage(0, 0, 320, 240, idle);
}
}
else if (currentState == IDLE) {
if (now - lastBlinkTime > 4000) {
tft.pushImage(0, 0, 320, 240, blink);
delay(150);
tft.pushImage(0, 0, 320, 240, idle);
lastBlinkTime = now;
}
}
// --- د. تتبع الحركة PIR ---
int valRight = digitalRead(PIN_PIR_RIGHT);
int valLeft = digitalRead(PIN_PIR_LEFT);
int targetAngle = -1;
if (valRight == HIGH && valLeft == HIGH) targetAngle = 90;
else if (valRight == HIGH) targetAngle = 135;
else if (valLeft == HIGH) targetAngle = 45;
if (targetAngle != -1) {
if (targetAngle != currentPanAngle) {
currentPanAngle = targetAngle;
servoPan.write(currentPanAngle);
}
lastMotionTime = now;
} else {
if (currentPanAngle != 90 && (now - lastMotionTime > 15000)) {
currentPanAngle = 90;
servoPan.write(90);
}
}
delay(10);
}