#ifndef SENSORS_H #define SENSORS_H #include #include #include "Config.h" // ================== تعاریف و ماکروها از کد دوم ================== // تعریف آدرس سنسورها #define BH1750_ADDR 0x23 #define SHT31_ADDR 0x44 // حالتهای اندازه‌گیری BH1750 #define BH1750_CONTINUOUS_HIGH_RES_MODE 0x10 #define BH1750_ONE_TIME_HIGH_RES_MODE 0x20 #define BH1750_POWER_DOWN 0x00 #define BH1750_POWER_ON 0x01 #define BH1750_RESET 0x07 // دستورات SHT31 #define SHT31_MEAS_HIGHREP 0x2400 #define SHT31_MEAS_MEDREP 0x240B #define SHT31_MEAS_LOWREP 0x2416 #define SHT31_SOFTRESET 0x30A2 #define SHT31_HEATER_ENABLE 0x306D #define SHT31_HEATER_DISABLE 0x3066 #define SHT31_STATUS_REG 0xF32D #define SHT31_CLEAR_STATUS 0x3041 #define VREF_ADC 3.3f // ولتاژ مرجع ADC #define ADC_RESOLUTION 4095.0f // رزولوشن ADC 12 بیت #define R1 2190.0f // مقاومت سری اول (اهم) #define R2 3270.0f // مقاومت به زمین (اهم) #define RL_SENSOR 839.0f // مقاومت بار ماژول (مقاومت بین خروجی آنالوگ و GND) #define MQ7_PIN PA2 // پین ADC #define NUM_SAMPLES 10 // تعداد نمونه برای میانگین‌گیری // پارامترهای سنسور MQ7 برای تبدیل به ppm (از دیتاشیت) #define CO_A 1.9f // ضریب A #define CO_B -0.6f // ضریب B #define PREHEAT_TIME_MS (2 * 60 * 1000) // 2 دقیقه پیش‌گرمایش // متغیرهای جهانی float VCC_SENSOR = 4.9f; // ولتاژ تغذیه سنسور MQ7 float MQ7_R0 = 24150.0; //g:14600,14500 //2:24150.0; unsigned long deviceStartTime = 0; bool MQ7sensorPreheated=false; extern bool coSensorConnected; /* ================== ساختار وضعیت سیستم (از کد اول) ================== */ struct SystemStatus { bool sht31OK; bool bh1750OK; uint32_t lastRecovery; uint32_t lastSuccess; uint16_t errorCount; }; extern SystemStatus i2cStatus; /* ================== متغیرهای عمومی ================== */ extern SensorData currentData; extern unsigned long lastSensorRead; /* ================== متغیرهای اختصاصی از کد دوم ================== */ // متغیرهای ذخیره‌سازی مقادیر آخرین خوانش موفق extern float lastLightLevel; extern float lastTemperature; extern float lastHumidity; // متغیرهای تشخیص خطا از کد دوم extern uint8_t bh1750ErrorCount; extern uint8_t sht31ErrorCount; extern const uint8_t MAX_ERRORS; // زمان آخرین اندازه‌گیری موفق از کد دوم extern unsigned long lastBh1750Success; extern unsigned long lastSht31Success; /* ================== توابع عمومی ================== */ void initSensors(); void readSensors(); void updateSensors(); // حلقه اصلی (جایگزین loop) /* ================== توابع I2C مستقیم (از کد دوم) ================== */ // ────────────────────────────────────────────────────── // تابع بررسی وجود دستگاه روی باس I2C (از کد دوم) // ────────────────────────────────────────────────────── inline bool checkDevice(uint8_t address) { Wire.beginTransmission(address); byte error = Wire.endTransmission(); return (error == 0); } // ────────────────────────────────────────────────────── // تابع مقداردهی اولیه BH1750 (از کد دوم) // ────────────────────────────────────────────────────── inline bool initBH1750Direct() { if (!checkDevice(BH1750_ADDR)) { Serial1.println("BH1750 not found!"); i2cStatus.bh1750OK = false; return false; } i2cStatus.bh1750OK = true; bh1750ErrorCount = 0; Serial1.println("BH1750 ready (One-Time mode)"); return true; } bool bh1750SoftReset() { Wire.beginTransmission(BH1750_ADDR); Wire.write(BH1750_POWER_DOWN); if (Wire.endTransmission() != 0) return false; delay(5); Wire.beginTransmission(BH1750_ADDR); Wire.write(BH1750_POWER_ON); if (Wire.endTransmission() != 0) return false; delay(5); // دستور Reset فقط بعد از Power On معتبر است Wire.beginTransmission(BH1750_ADDR); Wire.write(BH1750_RESET); if (Wire.endTransmission() != 0) return false; delay(5); return true; } inline bool readBH1750Direct(float &lightLevel) { if (!i2cStatus.bh1750OK) { if (!initBH1750Direct()) return false; } // 👇 مهم: ریست داخلی قبل از هر خواندن if (!bh1750SoftReset()) { bh1750ErrorCount++; return false; } // ارسال فرمان اندازه‌گیری Wire.beginTransmission(BH1750_ADDR); Wire.write(BH1750_ONE_TIME_HIGH_RES_MODE); byte error = Wire.endTransmission(); if (error != 0) { bh1750ErrorCount++; return false; } delay(180); Wire.requestFrom(BH1750_ADDR, 2); if (Wire.available() == 2) { uint16_t value = Wire.read(); value <<= 8; value |= Wire.read(); lightLevel = value / 1.2; if (lightLevel >= 0 && lightLevel <= 65535) { lastLightLevel = lightLevel; bh1750ErrorCount = 0; i2cStatus.bh1750OK = true; return true; } } // اگر به اینجا رسید یعنی خواندن شکست خورده bh1750ErrorCount++; if (bh1750ErrorCount >= 3) { // تلاش برای ریست کامل سنسور bh1750SoftReset(); delay(50); } if (bh1750ErrorCount >= MAX_ERRORS) { i2cStatus.bh1750OK = false; } return false; } // ────────────────────────────────────────────────────── // تابع مقداردهی اولیه SHT31 (از کد دوم) // ────────────────────────────────────────────────────── inline bool initSHT31Direct() { if (!checkDevice(SHT31_ADDR)) { Serial1.println("SHT31 not found!"); i2cStatus.sht31OK = false; return false; } // ارسال دستور ریست نرم Wire.beginTransmission(SHT31_ADDR); Wire.write(SHT31_SOFTRESET >> 8); Wire.write(SHT31_SOFTRESET & 0xFF); byte error = Wire.endTransmission(); if (error != 0) { i2cStatus.sht31OK = false; Serial1.print("SHT31 reset error: "); Serial1.println(error); return false; } delay(10); // تأخیر بعد از ریست // غیرفعال کردن هیتر (برای مصرف کمتر و عمر طولانی‌تر) Wire.beginTransmission(SHT31_ADDR); Wire.write(SHT31_HEATER_DISABLE >> 8); Wire.write(SHT31_HEATER_DISABLE & 0xFF); error = Wire.endTransmission(); if (error == 0) { i2cStatus.sht31OK = true; sht31ErrorCount = 0; Serial1.println("SHT31 initialized successfully"); return true; } else { i2cStatus.sht31OK = false; Serial1.print("SHT31 init error: "); Serial1.println(error); return false; } } // ────────────────────────────────────────────────────── // تابع خواندن دما و رطوبت از SHT31 (از کد دوم) // ────────────────────────────────────────────────────── inline bool readSHT31Direct(float &temperature, float &humidity) { // اگر سنسور غیرفعال است و هنوز خطاها کم است، سعی در راه‌اندازی مجدد if (!i2cStatus.sht31OK && sht31ErrorCount < MAX_ERRORS) { initSHT31Direct(); } if (!i2cStatus.sht31OK) return false; // ارسال دستور اندازه‌گیری با دقت بالا Wire.beginTransmission(SHT31_ADDR); Wire.write(SHT31_MEAS_HIGHREP >> 8); Wire.write(SHT31_MEAS_HIGHREP & 0xFF); byte error = Wire.endTransmission(); if (error != 0) { sht31ErrorCount++; Serial1.println("Error sending measurement command to SHT31"); return false; } // تأخیر برای تبدیل (حداکثر 15ms برای حالت High Rep) delay(15); // خواندن 6 بایت داده Wire.requestFrom(SHT31_ADDR, 6); if (Wire.available() == 6) { uint16_t tempRaw = Wire.read(); tempRaw <<= 8; tempRaw |= Wire.read(); uint8_t tempCRC = Wire.read(); uint16_t humRaw = Wire.read(); humRaw <<= 8; humRaw |= Wire.read(); uint8_t humCRC = Wire.read(); // تبدیل مقادیر temperature = -45 + (175 * (float)tempRaw / 65535.0); humidity = 100 * (float)humRaw / 65535.0; // اعتبارسنجی مقادیر if (temperature >= -40 && temperature <= 125 && humidity >= 0 && humidity <= 100) { lastTemperature = temperature; lastHumidity = humidity; sht31ErrorCount = 0; lastSht31Success = millis(); return true; } } // در صورت خطا sht31ErrorCount++; Serial1.println("Error reading SHT31"); if (sht31ErrorCount >= MAX_ERRORS) { i2cStatus.sht31OK = false; Serial1.println("SHT31 marked as unavailable"); } return false; } /* ================== توابع I2C Recovery (از کد اول) ================== */ // ────────────────────────────────────────────────────── // پاکسازی باس I2C با Clock Pulse // ────────────────────────────────────────────────────── inline void cleanI2CBus() { pinMode(I2C_SCL_PIN, OUTPUT); pinMode(I2C_SDA_PIN, OUTPUT); digitalWrite(I2C_SCL_PIN, HIGH); digitalWrite(I2C_SDA_PIN, HIGH); delay(5); // ارسال 16 clock pulse for (uint8_t i = 0; i < 16; i++) { digitalWrite(I2C_SCL_PIN, LOW); delayMicroseconds(5); digitalWrite(I2C_SCL_PIN, HIGH); delayMicroseconds(5); } // STOP condition digitalWrite(I2C_SDA_PIN, LOW); delayMicroseconds(5); digitalWrite(I2C_SCL_PIN, HIGH); delayMicroseconds(5); digitalWrite(I2C_SDA_PIN, HIGH); delayMicroseconds(5); pinMode(I2C_SCL_PIN, INPUT); pinMode(I2C_SDA_PIN, INPUT); } // ────────────────────────────────────────────────────── // بازیابی نرم I2C (از کد دوم) // ────────────────────────────────────────────────────── inline void recoverI2CSoft() { Serial1.println("Attempting I2C recovery..."); // آزاد کردن خطوط pinMode(I2C_SDA_PIN, INPUT_PULLUP); pinMode(I2C_SCL_PIN, INPUT_PULLUP); delay(10); // تولید پالس‌های ساعت برای آزادسازی هر دستگاه قفل شده pinMode(I2C_SCL_PIN, OUTPUT); for (int i = 0; i < 9; i++) { digitalWrite(I2C_SCL_PIN, LOW); delayMicroseconds(5); digitalWrite(I2C_SCL_PIN, HIGH); delayMicroseconds(5); } // ایجاد شرط STOP pinMode(I2C_SDA_PIN, OUTPUT); digitalWrite(I2C_SDA_PIN, LOW); delayMicroseconds(5); digitalWrite(I2C_SCL_PIN, HIGH); delayMicroseconds(5); digitalWrite(I2C_SDA_PIN, HIGH); delayMicroseconds(5); // بازگشت به حالت عادی Wire.begin(); Serial1.println("I2C recovery completed"); } // ────────────────────────────────────────────────────── // راه‌اندازی مجدد سنسورهای I2C (منطق کد اول با توابع مستقیم) // ────────────────────────────────────────────────────── inline void initI2CSensors() { i2cStatus.sht31OK = false; i2cStatus.bh1750OK = false; bh1750SoftReset(); delay(10); // ─── SHT31 ─── if (checkDevice(SHT31_ADDR)) { if (initSHT31Direct()) { Serial1.println("[I2C] SHT31 OK"); } } else { Serial1.println("[I2C] SHT31 NOT FOUND"); } // ─── BH1750 ─── if (checkDevice(BH1750_ADDR)) { if (initBH1750Direct()) { Serial1.println("[I2C] BH1750 OK"); } } else { Serial1.println("[I2C] BH1750 NOT FOUND"); } } // ────────────────────────────────────────────────────── // بازیابی کامل I2C (منطق کد اول با بازیابی نرم از کد دوم) // ────────────────────────────────────────────────────── inline void recoverI2C() { uint32_t now = millis(); // جلوگیری از recovery مکرر if (now - i2cStatus.lastRecovery < RECOVERY_COOLDOWN) return; i2cStatus.lastRecovery = now; i2cStatus.errorCount++; if (i2cStatus.errorCount > MAX_ERROR_COUNT) i2cStatus.errorCount = 100; Serial1.println("\n[RECOVERY] Starting I2C recovery..."); recoverI2CSoft(); Wire.end(); delay(50); Wire.setSDA(I2C_SDA_PIN); Wire.setSCL(I2C_SCL_PIN); Wire.begin(); Wire.setClock(100000); // 100kHz Wire.setTimeout(1000); // افزایش timeout برای جلوگیری از hang شدن delay(50); bh1750SoftReset(); delay(20); initI2CSensors(); Serial1.println("[RECOVERY] Complete\n"); } // ────────────────────────────────────────────────────── // بررسی سلامت سنسورها (منطق ترکیبی کد اول و دوم) // ────────────────────────────────────────────────────── inline void healthCheck() { bool sht = checkDevice(SHT31_ADDR); bool bh = checkDevice(BH1750_ADDR); // اگر هیچکدام پاسخ ندادند → recovery کامل if (!sht && !bh) { Serial1.println("[HEALTH] Both sensors failed, starting recovery..."); recoverI2C(); return; } // منطق کد دوم: بررسی دوره‌ای سلامت سنسورها bool shtWasOK = i2cStatus.sht31OK; bool bhWasOK = i2cStatus.bh1750OK; // بررسی SHT31 if (checkDevice(SHT31_ADDR)) { if (!shtWasOK) { Serial1.println("[HEALTH] SHT31 reappeared, reinitializing..."); initSHT31Direct(); } } else if (shtWasOK) { Serial1.println("[HEALTH] SHT31 disappeared!"); i2cStatus.sht31OK = false; } // بررسی BH1750 if (checkDevice(BH1750_ADDR)) { if (!bhWasOK) { Serial1.println("[HEALTH] BH1750 reappeared, reinitializing..."); initBH1750Direct(); } } else if (bhWasOK) { Serial1.println("[HEALTH] BH1750 disappeared!"); i2cStatus.bh1750OK = false; } // اگر هر دو دستگاه از دست رفته‌اند، بازیابی باس I2C if (!i2cStatus.bh1750OK && !i2cStatus.sht31OK) { recoverI2C(); // تلاش مجدد برای مقداردهی اولیه delay(100); initBH1750Direct(); initSHT31Direct(); } } /* ================== توابع MQ7 ================== */ // ────────────────────────────────────────────────────── // کالیبراسیون MQ7 // ────────────────────────────────────────────────────── /*inline void calibrateMQ7() { if(currentData.volage < 1) return; Serial1.println("======================================================================"); Serial1.println("[MQ7] Final calibration with optimized parameters..."); Serial1.print("VCC = "); Serial1.println(currentData.volage, 2); // محاسبه پارامترها float correction_factor = V_AO_SENSOR / V_A2_MICRO; // 1.5 float VDIV_RATIO = V_AO_SENSOR / currentData.volage; // ~0.045 // RL تنظیم شده برای رسیدن به R0 ≈ 3kΩ float optimized_RL = 1500.0; // از 790 به 1500 افزایش دادیم float sumRS = 0; for(int i = 0; i < 50; i++) { int adc = analogRead(MQ7_PIN); // تبدیل ADC به ولتاژ (فرض: مرجع ADC = VCC) float v_a2 = adc * (currentData.volage / 4095.0); // تصحیح برای بدست آوردن ولتاژ واقعی AO سنسور float v_ao = v_a2 * correction_factor; // اطمینان از محدوده منطقی if (v_ao > currentData.volage * 0.8) { v_ao = currentData.volage * 0.8; } if (v_ao < 0.01) v_ao = 0.01; // محاسبه RS float RS = optimized_RL * (currentData.volage / v_ao - 1.0); sumRS += RS; delay(20); } MQ7_R0 = (sumRS / 50.0) / 9.8; calibrationComplete = true; Serial1.print("Optimized R0 = "); Serial1.print(MQ7_R0, 4); Serial1.println("Ω"); // ارزیابی نتیجه if (MQ7_R0 > 2000 && MQ7_R0 < 5000) { Serial1.println("✓ Excellent! R0 in perfect range (2-5kΩ)"); } else if (MQ7_R0 > 1000 && MQ7_R0 < 10000) { Serial1.println("✓ Good! R0 in acceptable range (1-10kΩ)"); } else { Serial1.println("⚠ May need further adjustment"); } // اطلاعات دیباگ Serial1.print("Parameters used - RL: "); Serial1.print(optimized_RL); Serial1.print("Ω, Correction: "); Serial1.print(correction_factor, 2); Serial1.print(", VDIV_RATIO: "); Serial1.println(VDIV_RATIO, 4); Serial1.println("======================================================================"); }*/ // inline void calibrateMQ7_() { // if(currentData.volage<1) // return; // Serial1.println("[MQ7] Starting calibration..."); // float sumRS = 0; // for(int i = 0; i < 50; i++) { // int adc = analogRead(MQ7_PIN); // float v_adc = adc * (currentData.volage / 4095.0);//5.0 -> currentData.volage // float v_ao = v_adc / VDIV_RATIO; // if (v_ao < 0.01) v_ao = 0.01; // float RS = RL * (currentData.volage / v_ao - 1.0);//VCC -> currentData.volage // sumRS += RS; // delay(20); // } // Serial1.print("currentData.volage In MQCalibrRead:"); // Serial1.print(currentData.volage); // MQ7_R0 = (sumRS / 50.0) / 9.8; // calibrationComplete = true; // Serial1.print("======================================================================"); // Serial1.print("[MQ7] Calibration complete. R0 = "); // Serial1.println(MQ7_R0, 4); // } //Serial1.print("======================================================================"); // ────────────────────────────────────────────────────── // خواندن CO با میانگین‌گیری // ────────────────────────────────────────────────────── // ============================ خواندن CO (با مقادیر ثابت) ============================ // inline float readCOImproved__() { // // اگر سنسور هنوز گرم نشده، مقدار -1 برگردان // if (!isMQ7sensorPreheated()) { // return -1.0; // } // // میانگین‌گیری برای کاهش نویز // float sumADC = 0; // for (int i = 0; i < 5; i++) { // sumADC += analogRead(MQ7_PIN); // delay(10); // } // int adc_avg = sumADC / 5.0; // // تبدیل ADC به ولتاژ // float v_a2 = adc_avg * (currentData.volage / 4095.0); // // تصحیح با فاکتور ثابت // float v_ao = v_a2 * CORRECTION_FACTOR_FIXED; // // محدود کردن مقادیر غیرمنطقی // if (v_ao < 0.01) v_ao = 0.01; // if (v_ao > currentData.volage * 0.9) { // v_ao = currentData.volage * 0.9; // } // // محاسبه مقاومت سنسور // float RS = RL_FIXED * (currentData.volage / v_ao - 1.0); // // محاسبه نسبت // float ratio = RS / MQ7_R0; // // فرمول CO برای MQ7 // float ppm = 10.0 * pow(ratio, -1.53); // // فیلتر نویز // if (ppm < 0.1) ppm = 0.0; // ppm = constrain(ppm, 0, 5000); // // چاپ دیباگ (هر 30 ثانیه) // static unsigned long lastPrint = 0; // if (millis() - lastPrint > 30000) { // lastPrint = millis(); // Serial1.print("[CO] "); // Serial1.print(ppm, 2); // Serial1.print(" ppm (ADC:"); // Serial1.print(adc_avg); // Serial1.print(" V_ao:"); // Serial1.print(v_ao, 3); // Serial1.println("V)"); // } // return ppm; // } bool CheckMQ7sensorPreheated() { if (MQ7sensorPreheated) return true; unsigned long uptime = millis() - deviceStartTime; if (uptime >= PREHEAT_TIME_MS) { MQ7sensorPreheated = true; Serial1.println("[MQ7] Sensor preheated and ready"); return true; } int remaining = (PREHEAT_TIME_MS - uptime) / 60000; // به دقیقه if (remaining % 5 == 0) { // هر 5 دقیقه گزارش Serial1.print("[MQ7] Preheating... "); Serial1.print(remaining); Serial1.println(" minutes remaining"); } return false; } void calibrateMQ7() { Serial.println("[MQ7] Starting calibration in clean air..."); deviceStartTime = millis(); } float readSensorVoltage() { uint32_t sum = 0; for (int i = 0; i < NUM_SAMPLES; i++) { sum += analogRead(MQ7_PIN); delay(10); } float adcValue = sum / (float)NUM_SAMPLES; float vAdc = (adcValue / ADC_RESOLUTION) * VREF_ADC; // محاسبه ولتاژ خروجی سنسور با جبران تقسیم ولتاژ (R1 و R2) return vAdc * (R1 + R2) / R2; } float calculateRs(float vOut) { if (vOut <= 0.0f) return 0.0f; return RL_SENSOR * (VCC_SENSOR / vOut - 1.0f); } float calculatePPM(float rs) { float ratio = rs / MQ7_R0; if (ratio <= 0.0f) return 0.0f; return CO_A * pow(ratio, CO_B); } float readCOImproved() { if (!MQ7sensorPreheated) { CheckMQ7sensorPreheated(); return -1.0f; } float vOut = readSensorVoltage(); float rs = calculateRs(vOut); float ppm = calculatePPM(rs); // محدود کردن به بازه معقول ppm = constrain(ppm, 0.0f, 5000.0f); return ppm; } // inline float readCOImproved() { // // میانگین‌گیری برای کاهش نویز // Serial1.print("=======************==============================================================="); // float sumADC = 0; // for (int i = 0; i < 10; i++) { // sumADC += analogRead(MQ7_PIN); // delay(2); // } // int adc_avg = sumADC / 10.0; // // تبدیل ADC به ولتاژ پین A2 (با فرض VCC به عنوان مرجع ADC) // float v_a2 = adc_avg * (currentData.volage / 4095.0); // // تصحیح برای بدست آوردن ولتاژ واقعی AO سنسور (با دیود/مقاومت) // float v_ao = v_a2 * 1.5; // Correction Factor = 1.5 // // محدود کردن مقادیر غیرمنطقی // if (v_ao < 0.01) v_ao = 0.01; // if (v_ao > currentData.volage * 0.9) v_ao = currentData.volage * 0.9; // // محاسبه مقاومت سنسور در گاز // float RS = 1500.0 * (currentData.volage / v_ao - 1.0); // RL = 1500Ω // // اگر کالیبراسیون انجام نشده // if (!calibrationComplete || MQ7_R0 <= 0) { // return -1.0; // } // // محاسبه نسبت // float ratio = RS / MQ7_R0; // // دیباگ اطلاعات // Serial1.print("[MQ7] ADC:"); // Serial1.print(adc_avg); // Serial1.print(" v_a2:"); // Serial1.print(v_a2, 4); // Serial1.print("V v_ao:"); // Serial1.print(v_ao, 4); // Serial1.print("V RS:"); // Serial1.print(RS, 4); // Serial1.print("Ω ratio:"); // Serial1.print(ratio, 4); // // محاسبه ppm CO بر اساس دیتاشیت MQ7 // // فرمول: ppm = A * (RS/R0)^B // // برای MQ7 و CO: A ≈ 10, B ≈ -1.53 // float ppm = 10.0 * pow(ratio, -1.53); // // محدود کردن بازه خروجی // ppm = constrain(ppm, 0, 5000); // Serial1.print(" PPM:"); // Serial1.println(ppm, 4); // return ppm; // } /* ================== خواندن سنسورها (با توابع مستقیم از کد دوم) ================== */ // ────────────────────────────────────────────────────── // خواندن تمام سنسورها با توابع مستقیم // ────────────────────────────────────────────────────── inline void readSensors() { bool success = false; Serial1.println("\n=== Reading Sensors ==="); // ──── 1. خواندن SHT31 با تابع مستقیم ──── float temp, hum; if (readSHT31Direct(temp, hum)) { currentData.temperature = temp; currentData.humidity = hum; Serial1.print("[SHT31] T="); Serial1.print(temp, 1); Serial1.print("°C H="); Serial1.print(hum, 1); Serial1.println("%"); success = true; } else { // استفاده از آخرین مقدار موفق در صورت خطا if (lastTemperature != 0 || lastHumidity != 0) { currentData.temperature = lastTemperature; currentData.humidity = lastHumidity; Serial1.print("[SHT31] Using last values - T="); Serial1.print(lastTemperature, 1); Serial1.print("°C H="); Serial1.print(lastHumidity, 1); Serial1.println("%"); } else { Serial1.println("[SHT31] Read error"); currentData.temperature = NAN; currentData.humidity = NAN; } } // ──── 2. خواندن BH1750 با تابع مستقیم ──── float lux; if (readBH1750Direct(lux)) { currentData.lightLux = lux; Serial1.print("[BH1750] "); Serial1.print(lux, 0); Serial1.println(" lux"); success = true; } else { // استفاده از آخرین مقدار موفق در صورت خطا if (lastLightLevel != 0) { currentData.lightLux = lastLightLevel; Serial1.print("[BH1750] Using last value: "); Serial1.print(lastLightLevel, 0); Serial1.println(" lux"); } else { Serial1.println("[BH1750] Read error"); currentData.lightLux = 0; } } // ──── 3. کالیبراسیون خودکار MQ7 ──── /*if (!calibrationComplete){// && (millis() - systemStartTime >= calibrationPeriod)) { calibrateMQ7(); }*/ // ──── 4. خواندن CO ──── currentData.coPPM = readCOImproved(); int adc = analogRead(MQ7_PIN); coSensorConnected = (adc > 10 && adc < 4085); Serial1.print("[MQ7] "); Serial1.print(currentData.coPPM, 1); Serial1.println(" ppm"); // ──── 5. محاسبه درصدها ──── calculatePercentages(currentData); // ──── 6. ثبت زمان آخرین موفقیت ──── if (success) { i2cStatus.lastSuccess = millis(); } lastSensorRead = millis(); Serial1.println("=== Reading Complete ===\n"); } /* ================== راه‌اندازی اولیه ================== */ // ────────────────────────────────────────────────────── // مقداردهی کامل سیستم // ────────────────────────────────────────────────────── inline void initSensors() { delay(2000); Serial1.println("\n=== Initializing Sensor System ==="); // ──── پاکسازی و راه‌اندازی I2C ──── cleanI2CBus(); Wire.setSDA(I2C_SDA_PIN); Wire.setSCL(I2C_SCL_PIN); Wire.begin(); Wire.setClock(100000); // 100kHz برای پایداری Wire.setTimeout(1000); // افزایش timeout برای جلوگیری از hang شدن delay(100); bh1750SoftReset(); delay(20); // ──── راه‌اندازی سنسورهای I2C ──── initI2CSensors(); // ──── راه‌اندازی MQ7 ──── pinMode(MQ7_PIN, INPUT); analogReadResolution(12); Serial1.println("[MQ7] Analog pin configured"); // ──── اسکن I2C (اختیاری - برای debug) ──── Serial1.println("\n[I2C] Scanning bus..."); byte deviceCount = 0; for(byte addr = 1; addr < 127; addr++) { Wire.beginTransmission(addr); if (Wire.endTransmission() == 0) { Serial1.print(" Device found at 0x"); if (addr < 16) Serial1.print("0"); Serial1.println(addr, HEX); deviceCount++; } } Serial1.print("Total devices: "); Serial1.println(deviceCount); Serial1.println("\n=== System Ready ===\n"); } /* ================== حلقه اصلی (جایگزین loop) ================== */ // ────────────────────────────────────────────────────── // باید در loop() اصلی فراخوانی شود // ────────────────────────────────────────────────────── inline void updateSensors() { static uint32_t lastRead = 0; static uint32_t lastHealth = 0; uint32_t now = millis(); // جلوگیری از overflow if (now < lastRead) lastRead = 0; if (now < lastHealth) lastHealth = 0; // ──── خواندن دوره‌ای سنسورها ──── if (now - lastRead >= READ_INTERVAL) { lastRead = now; readSensors(); } // ──── بررسی سلامت دوره‌ای (هر 30 ثانیه) ──── if (now - lastHealth >= HEALTH_CHECK_INTERVAL) { lastHealth = now; healthCheck(); } } #endif // SENSORS_H