#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <WiFi.h>
#include "ThingSpeak.h"
#include "DHT.h"
// ===== أزمنة =====
const unsigned long SENSE_INTERVAL_MS = 2000; // قراءة DHT + PIR كل 2s
const unsigned long POST_INTERVAL_MS = 20000; // رفع ThingSpeak كل 20s
unsigned long lastSense = 0, lastPost = 0, lastHeader = 0;
// ===== DHT22 =====
#define DHTTYPE DHT22
#define DHT_PIN 4
DHT dht(DHT_PIN, DHTTYPE);
// ===== OLED =====
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_RESET -1
#define SCREEN_ADDRESS 0x3C
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
// ===== PIR =====
const int PIR_PIN = 14; // وصّل OUT الحساس هنا
const bool PIR_ACTIVE_HIGH = true; // لو حسّاسك عكسي خلِّها false
const bool PIR_USE_PULLDOWN = true;// في Wokwi يخلي الاستجابة مباشرة
#if 1 // محاكي Wokwi
const unsigned long PIR_WARMUP_MS = 0; // لا تنتظر إحماء
#else // هاردوير حقيقي
const unsigned long PIR_WARMUP_MS = 30000; // مهلة إحماء 30s
#endif
unsigned long bootMs = 0;
// ===== لمبات الإنذار =====
#define LED_TH 13 // إنذار حرارة/رطوبة
#define LED_PIR 26 // إنذار حركة
// ===== WiFi & ThingSpeak =====
const char* WIFI_NAME = "Wokwi-GUEST"; // غيّرها على الهاردوير
const char* WIFI_PASSWORD = ""; // مثال: "Q!Sameer189"
WiFiClient client;
unsigned long myChannelNumber = 3062120; // رقم القناة
const char* myApiKey = "EVCS62STUJ12ZUE3"; // Write API Key
// ===== عتبات =====
const float TEMP_HIGH = 40.0f;
const float HUM_HIGH = 80.0f;
static void connectWiFi() {
if (WiFi.status() == WL_CONNECTED) return;
Serial.printf("Connecting to WiFi %s ...\n", WIFI_NAME);
WiFi.mode(WIFI_STA);
WiFi.setSleep(false);
WiFi.begin(WIFI_NAME, WIFI_PASSWORD);
uint32_t t0 = millis();
while (WiFi.status() != WL_CONNECTED && millis() - t0 < 15000) {
delay(250);
Serial.print(".");
}
Serial.println();
if (WiFi.status() == WL_CONNECTED) {
Serial.print("WiFi OK, IP: ");
Serial.println(WiFi.localIP());
} else {
Serial.println("WiFi failed.");
}
}
static void printHeader() {
Serial.println(F("Time(s) | Temp | Hum | Mot | LED13(TH) | LED26(PIR) | WiFi"));
Serial.println(F("--------+--------+--------+-----+-----------+------------+------"));
}
static void printStatus(unsigned long now, float t, float h, bool motion, bool thAlert) {
// أسطر قصيرة وثابتة العرض لتجنب اللفّ القبيح
Serial.printf("%7lu | %6.1fC | %6.1f%% | %-3s | %d | %d | %s\n",
now / 1000, t, h, motion ? "YES" : "NO",
thAlert ? 1 : 0, motion ? 1 : 0,
(WiFi.status() == WL_CONNECTED) ? "OK" : "DOWN");
Serial.println();
}
void setup() {
Serial.begin(115200);
Serial.println("ESP32 + DHT22 + PIR + ThingSpeak");
// I2C على ESP32: SDA=21, SCL=22
Wire.begin(21, 22);
// OLED
if (!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) {
Serial.println("SSD1306 allocation failed");
for (;;);
}
display.clearDisplay();
display.setTextSize(2);
display.setTextColor(SSD1306_WHITE);
display.setCursor(0, 0); display.println("DHT22");
display.setCursor(0, 30); display.println("ESP32");
display.display();
delay(800);
// DHT
dht.begin();
// PIR
pinMode(PIR_PIN, PIR_USE_PULLDOWN ? INPUT_PULLDOWN : INPUT);
// لمبات
pinMode(LED_TH, OUTPUT);
pinMode(LED_PIR, OUTPUT);
digitalWrite(LED_TH, LOW);
digitalWrite(LED_PIR, LOW);
bootMs = millis();
// WiFi + ThingSpeak
connectWiFi();
ThingSpeak.begin(client);
printHeader();
}
void loop() {
connectWiFi();
unsigned long now = millis();
// اطبع الهيدر كل 30 ثانية
if (now - lastHeader >= 30000) {
printHeader();
lastHeader = now;
}
// قراءة كل 2 ثواني
if (now - lastSense >= SENSE_INTERVAL_MS) {
lastSense = now;
float h = dht.readHumidity();
float t = dht.readTemperature();
if (isnan(h) || isnan(t)) {
Serial.println("DHT read failed");
} else {
// RAW من الحساس
int raw = digitalRead(PIR_PIN);
bool motionLogic = PIR_ACTIVE_HIGH ? (raw == HIGH) : (raw == LOW);
bool motion = (now - bootMs >= PIR_WARMUP_MS) && motionLogic;
// لمبة الحرارة/الرطوبة (GPIO13) — مستقلة
bool thAlert = (t > TEMP_HIGH) || (h > HUM_HIGH);
digitalWrite(LED_TH, thAlert ? HIGH : LOW);
// لمبة الحركة (GPIO26) — مستقلة
digitalWrite(LED_PIR, motion ? HIGH : LOW);
// OLED
display.clearDisplay();
display.setTextSize(2);
display.setCursor(0, 10); display.print("T: "); display.print(t, 1); display.print("C");
display.setCursor(0, 35); display.print("H: "); display.print(h, 1); display.print("%");
display.setTextSize(1);
display.setCursor(0, 56); display.print("Motion: "); display.print(motion ? "YES" : "NO");
display.display();
// Serial منسّق
printStatus(now, t, h, motion, thAlert);
// رفع ThingSpeak كل 20 ثانية
if (now - lastPost >= POST_INTERVAL_MS && WiFi.status() == WL_CONNECTED) {
ThingSpeak.setField(1, t);
ThingSpeak.setField(2, h);
ThingSpeak.setField(3, motion ? 1 : 0); // اختياري: حالة الحركة
int x = ThingSpeak.writeFields(myChannelNumber, myApiKey);
if (x == 200) Serial.println("ThingSpeak: OK");
else Serial.println(String("ThingSpeak error: ") + x);
Serial.println("---");
lastPost = now;
}
}
}
delay(10); // حلقة خفيفة
}