Files
Arduino/14041130/TestLCD/Sensors.h
2026-02-19 06:17:44 +03:30

924 lines
30 KiB
C

#ifndef SENSORS_H
#define SENSORS_H
#include <Arduino.h>
#include <Wire.h>
#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