924 lines
30 KiB
C
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
|