472 lines
12 KiB
Plaintext
472 lines
12 KiB
Plaintext
#ifndef SENSORS_H
|
|
#define SENSORS_H
|
|
|
|
#include <Arduino.h>
|
|
#include <Wire.h>
|
|
#include "Config.h"
|
|
|
|
#define VCC 4.63
|
|
#define RL 10000.0
|
|
#define VDIV_RATIO 0.681
|
|
#define I2C_TIMEOUT_MS 100
|
|
|
|
// تعریف پینها
|
|
#define BH1750_SDA_PIN PB7
|
|
#define BH1750_SCL_PIN PB6
|
|
#define SHT31_SDA_PIN PB9
|
|
#define SHT31_SCL_PIN PB8
|
|
|
|
#define SHT31_ADDR 0x44
|
|
#define BH1750_ADDR 0x23
|
|
|
|
// متغیرهای خارجی
|
|
extern SensorData currentData;
|
|
extern bool sht31Connected;
|
|
extern bool lightSensorConnected;
|
|
extern bool coSensorConnected;
|
|
extern bool calibrationComplete;
|
|
extern unsigned long systemStartTime;
|
|
extern unsigned long lastSensorRead;
|
|
extern float MQ7_R0;
|
|
extern TwoWire Wire1;
|
|
extern const long calibrationPeriod;
|
|
|
|
// توابع
|
|
void initSensors();
|
|
void readSensors();
|
|
void checkSensorStatus();
|
|
|
|
// -------------------- پاکسازی باس I2C --------------------
|
|
inline void i2cBusClear(uint8_t scl_pin, uint8_t sda_pin, bool isWire1 = false) {
|
|
Serial1.print("Clearing I2C bus on pins SCL:");
|
|
Serial1.print(scl_pin);
|
|
Serial1.print(" SDA:");
|
|
Serial1.println(sda_pin);
|
|
|
|
// آزاد کردن باس
|
|
if (isWire1) {
|
|
Wire1.end();
|
|
} else {
|
|
Wire.end();
|
|
}
|
|
|
|
delay(50);
|
|
|
|
// تنظیم پینها به صورت دستی
|
|
pinMode(scl_pin, OUTPUT_OPEN_DRAIN);
|
|
pinMode(sda_pin, OUTPUT_OPEN_DRAIN);
|
|
|
|
digitalWrite(sda_pin, HIGH);
|
|
digitalWrite(scl_pin, HIGH);
|
|
delayMicroseconds(10);
|
|
|
|
// تولید پالس کلاک برای رهاسازی باس
|
|
for (int i = 0; i < 18; i++) {
|
|
digitalWrite(scl_pin, LOW);
|
|
delayMicroseconds(5);
|
|
digitalWrite(scl_pin, HIGH);
|
|
delayMicroseconds(5);
|
|
}
|
|
|
|
// ایجاد شرط STOP
|
|
digitalWrite(sda_pin, LOW);
|
|
delayMicroseconds(5);
|
|
digitalWrite(scl_pin, HIGH);
|
|
delayMicroseconds(5);
|
|
digitalWrite(sda_pin, HIGH);
|
|
delayMicroseconds(5);
|
|
|
|
delay(100);
|
|
|
|
// راهاندازی مجدد
|
|
if (isWire1) {
|
|
Wire1.setSDA(sda_pin);
|
|
Wire1.setSCL(scl_pin);
|
|
Wire1.begin();
|
|
Wire1.setClock(50000); // سرعت پایین برای پایداری
|
|
} else {
|
|
Wire.setSDA(sda_pin);
|
|
Wire.setSCL(scl_pin);
|
|
Wire.begin();
|
|
Wire.setClock(50000); // سرعت پایین برای پایداری
|
|
}
|
|
|
|
delay(100);
|
|
}
|
|
|
|
// -------------------- کالیبراسیون MQ7 --------------------
|
|
inline void calibrateMQ7() {
|
|
Serial1.println("Calibrating MQ7...");
|
|
|
|
float sumRS = 0;
|
|
for(int i = 0; i < 50; i++) { // کاهش از 100 به 50
|
|
int adc = analogRead(MQ7_PIN);
|
|
float v_adc = adc * (5.0 / 4095.0);
|
|
float v_ao = v_adc / VDIV_RATIO;
|
|
|
|
if (v_ao < 0.01) v_ao = 0.01;
|
|
float RS = RL * (VCC / v_ao - 1.0);
|
|
sumRS += RS;
|
|
delay(20); // کاهش تاخیر
|
|
}
|
|
|
|
MQ7_R0 = (sumRS / 50.0) / 9.8;
|
|
calibrationComplete = true;
|
|
|
|
Serial1.print("MQ7 R0 calibrated: ");
|
|
Serial1.println(MQ7_R0);
|
|
}
|
|
|
|
// -------------------- خواندن CO --------------------
|
|
inline float readCOImproved() {
|
|
float sumVoltage = 0;
|
|
for (int i = 0; i < 10; i++) { // کاهش از 20 به 10
|
|
int adc = analogRead(MQ7_PIN);
|
|
float v_adc = adc * (5.0 / 4095.0);
|
|
sumVoltage += v_adc;
|
|
delay(1);
|
|
}
|
|
|
|
float v_avg = sumVoltage / 10.0;
|
|
if (v_avg < 0.01) v_avg = 0.01;
|
|
if (v_avg > 4.99) v_avg = 4.99;
|
|
|
|
float v_ao = v_avg / VDIV_RATIO;
|
|
float RS = RL * (VCC / v_ao - 1.0);
|
|
float ratio = RS / MQ7_R0;
|
|
float ppm = 0;
|
|
|
|
if (ratio > 0) {
|
|
ppm = 100.0 * pow(ratio, -1.53);
|
|
ppm = constrain(ppm, 0, 5000);
|
|
}
|
|
|
|
if (!calibrationComplete) {
|
|
ppm = -ppm;
|
|
}
|
|
|
|
return ppm;
|
|
}
|
|
|
|
// -------------------- خواندن SHT31 (سادهشده) --------------------
|
|
inline bool readSHT31(float &temperature, float &humidity) {
|
|
static uint8_t errorCount = 0;
|
|
static bool needsReset = false;
|
|
|
|
// اگر نیاز به بازنشانی داریم
|
|
if (needsReset) {
|
|
Serial1.println("Resetting SHT31 I2C bus...");
|
|
i2cBusClear(SHT31_SCL_PIN, SHT31_SDA_PIN, false);
|
|
needsReset = false;
|
|
delay(100);
|
|
}
|
|
|
|
// تست اتصال اولیه
|
|
Wire.beginTransmission(SHT31_ADDR);
|
|
uint8_t connError = Wire.endTransmission();
|
|
|
|
if (connError != 0) {
|
|
Serial1.print("SHT31 connection error: ");
|
|
Serial1.println(connError);
|
|
errorCount++;
|
|
|
|
if (errorCount >= 2) {
|
|
needsReset = true;
|
|
errorCount = 0;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// ارسال دستور اندازهگیری (کوتاه)
|
|
Wire.beginTransmission(SHT31_ADDR);
|
|
if (Wire.write(0x2C) != 1) { // دستور سریع
|
|
Serial1.println("SHT31 write failed");
|
|
return false;
|
|
}
|
|
if (Wire.write(0x06) != 1) { // تکرار بالا
|
|
Serial1.println("SHT31 write failed");
|
|
return false;
|
|
}
|
|
uint8_t writeError = Wire.endTransmission();
|
|
|
|
if (writeError != 0) {
|
|
Serial1.print("SHT31 write error: ");
|
|
Serial1.println(writeError);
|
|
errorCount++;
|
|
|
|
if (errorCount >= 2) {
|
|
needsReset = true;
|
|
errorCount = 0;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// تاخیر اندازهگیری
|
|
delay(20); // برای حالت High Repeatability
|
|
|
|
// خواندن داده
|
|
uint8_t bytesRead = Wire.requestFrom(SHT31_ADDR, (uint8_t)6, (uint8_t)true);
|
|
|
|
if (bytesRead != 6) {
|
|
Serial1.print("SHT31 read error, bytes: ");
|
|
Serial1.println(bytesRead);
|
|
|
|
// خالی کردن بافر
|
|
while (Wire.available()) {
|
|
Wire.read();
|
|
}
|
|
|
|
errorCount++;
|
|
if (errorCount >= 2) {
|
|
needsReset = true;
|
|
errorCount = 0;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// خواندن بایتها
|
|
uint8_t data[6];
|
|
for (int i = 0; i < 6; i++) {
|
|
data[i] = Wire.read();
|
|
}
|
|
|
|
// بررسی CRC (سادهشده)
|
|
uint16_t tRaw = (data[0] << 8) | data[1];
|
|
uint16_t hRaw = (data[3] << 8) | data[4];
|
|
|
|
// تبدیل مقادیر
|
|
temperature = -45.0 + 175.0 * tRaw / 65535.0;
|
|
humidity = 100.0 * hRaw / 65535.0;
|
|
|
|
// اعتبارسنجی
|
|
if (isnan(temperature) || isnan(humidity) ||
|
|
temperature < -40 || temperature > 125 ||
|
|
humidity < 0 || humidity > 100) {
|
|
Serial1.println("SHT31 invalid data");
|
|
return false;
|
|
}
|
|
|
|
errorCount = 0; // ریست شمارنده خطا
|
|
return true;
|
|
}
|
|
|
|
// -------------------- خواندن BH1750 --------------------
|
|
inline bool readBH1750(float &lux) {
|
|
static bool initialized = false;
|
|
static uint8_t errorCount = 0;
|
|
|
|
// مقداردهی اولیه
|
|
if (!initialized) {
|
|
Serial1.println("Initializing BH1750...");
|
|
Wire1.setSDA(BH1750_SDA_PIN);
|
|
Wire1.setSCL(BH1750_SCL_PIN);
|
|
Wire1.begin();
|
|
Wire1.setClock(50000); // سرعت پایین
|
|
delay(100);
|
|
|
|
// پاور آن
|
|
Wire1.beginTransmission(BH1750_ADDR);
|
|
Wire1.write(0x01);
|
|
if (Wire1.endTransmission() != 0) {
|
|
Serial1.println("BH1750 power on failed");
|
|
return false;
|
|
}
|
|
delay(10);
|
|
|
|
// تنظیم حالت
|
|
Wire1.beginTransmission(BH1750_ADDR);
|
|
Wire1.write(0x13); // Continuous H-Resolution Mode 0.5lx
|
|
if (Wire1.endTransmission() != 0) {
|
|
Serial1.println("BH1750 mode set failed");
|
|
return false;
|
|
}
|
|
|
|
initialized = true;
|
|
delay(180); // زمان اولیه برای اندازهگیری
|
|
}
|
|
|
|
// خواندن داده
|
|
uint8_t bytesRead = Wire1.requestFrom(BH1750_ADDR, (uint8_t)2);
|
|
|
|
if (bytesRead != 2) {
|
|
Serial1.print("BH1750 read error, bytes: ");
|
|
Serial1.println(bytesRead);
|
|
errorCount++;
|
|
|
|
if (errorCount >= 3) {
|
|
initialized = false;
|
|
errorCount = 0;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// خواندن بایتها
|
|
uint8_t highByte = Wire1.read();
|
|
uint8_t lowByte = Wire1.read();
|
|
|
|
uint16_t raw = (highByte << 8) | lowByte;
|
|
lux = raw / 1.2f;
|
|
|
|
errorCount = 0;
|
|
return true;
|
|
}
|
|
|
|
// -------------------- بررسی وضعیت سنسورها --------------------
|
|
inline void checkSensorStatus() {
|
|
// فقط چاپ وضعیت فعلی
|
|
Serial1.println("--- Sensor Status ---");
|
|
Serial1.print("SHT31: ");
|
|
Serial1.println(sht31Connected ? "OK" : "ERROR");
|
|
Serial1.print("BH1750: ");
|
|
Serial1.println(lightSensorConnected ? "OK" : "ERROR");
|
|
Serial1.print("MQ7: ");
|
|
Serial1.println(coSensorConnected ? "OK" : "ERROR");
|
|
}
|
|
|
|
// -------------------- خواندن همه سنسورها --------------------
|
|
inline void readSensors() {
|
|
Serial1.println("\n=== Reading Sensors ===");
|
|
|
|
// اول BH1750 را بخوان (چون کار میکند)
|
|
float lux;
|
|
if (readBH1750(lux)) {
|
|
currentData.lightLux = lux;
|
|
lightSensorConnected = true;
|
|
Serial1.print("BH1750: ");
|
|
Serial1.print(lux, 0);
|
|
Serial1.println(" lux");
|
|
} else {
|
|
currentData.lightLux = 0;
|
|
lightSensorConnected = false;
|
|
Serial1.println("BH1750: FAILED");
|
|
}
|
|
|
|
// سپس SHT31
|
|
float temperature = NAN, humidity = NAN;
|
|
bool sht31Read = readSHT31(temperature, humidity);
|
|
|
|
if (sht31Read && !isnan(temperature) && !isnan(humidity)) {
|
|
currentData.temperature = temperature;
|
|
currentData.humidity = humidity;
|
|
sht31Connected = true;
|
|
Serial1.print("SHT31: T=");
|
|
Serial1.print(temperature, 1);
|
|
Serial1.print("C, H=");
|
|
Serial1.print(humidity, 1);
|
|
Serial1.println("%");
|
|
} else {
|
|
currentData.temperature = NAN;
|
|
currentData.humidity = NAN;
|
|
sht31Connected = false;
|
|
Serial1.println("SHT31: FAILED");
|
|
}
|
|
|
|
// کالیبراسیون MQ7
|
|
if (!calibrationComplete && (millis() - systemStartTime >= calibrationPeriod)) {
|
|
calibrateMQ7();
|
|
calibrationComplete = true;
|
|
}
|
|
|
|
// خواندن 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");
|
|
|
|
// محاسبه درصدها
|
|
calculatePercentages(currentData);
|
|
|
|
lastSensorRead = millis();
|
|
Serial1.println("=== Sensor Readings Complete ===\n");
|
|
|
|
// اگر SHT31 کار نمیکند، سیستم را مسدود نکن
|
|
if (!sht31Connected) {
|
|
Serial1.println("Warning: SHT31 not responding, continuing anyway...");
|
|
}
|
|
}
|
|
|
|
// -------------------- مقداردهی اولیه --------------------
|
|
inline void initSensors() {
|
|
Serial1.println("Initializing sensors...");
|
|
|
|
// راهاندازی I2C1 برای SHT31 (سرعت پایین)
|
|
Wire.setSDA(SHT31_SDA_PIN);
|
|
Wire.setSCL(SHT31_SCL_PIN);
|
|
Wire.begin();
|
|
Wire.setClock(50000); // 50kHz برای پایداری
|
|
Wire.setTimeout(100);
|
|
|
|
// راهاندازی I2C2 برای BH1750
|
|
Wire1.setSDA(BH1750_SDA_PIN);
|
|
Wire1.setSCL(BH1750_SCL_PIN);
|
|
Wire1.begin();
|
|
Wire1.setClock(50000); // 50kHz
|
|
Wire1.setTimeout(100);
|
|
|
|
// پیکربندی MQ7
|
|
pinMode(MQ7_PIN, INPUT);
|
|
analogReadResolution(12);
|
|
|
|
delay(1000);
|
|
|
|
// اسکن آدرسهای I2C
|
|
Serial1.println("Scanning I2C1 (SHT31)...");
|
|
byte error;
|
|
int found1 = 0;
|
|
for(byte addr = 1; addr < 127; addr++) {
|
|
Wire.beginTransmission(addr);
|
|
error = Wire.endTransmission();
|
|
if (error == 0) {
|
|
Serial1.print("Found device at 0x");
|
|
if (addr < 16) Serial1.print("0");
|
|
Serial1.println(addr, HEX);
|
|
found1++;
|
|
}
|
|
}
|
|
Serial1.print("I2C1 devices: ");
|
|
Serial1.println(found1);
|
|
|
|
Serial1.println("Scanning I2C2 (BH1750)...");
|
|
int found2 = 0;
|
|
for(byte addr = 1; addr < 127; addr++) {
|
|
Wire1.beginTransmission(addr);
|
|
error = Wire1.endTransmission();
|
|
if (error == 0) {
|
|
Serial1.print("Found device at 0x");
|
|
if (addr < 16) Serial1.print("0");
|
|
Serial1.println(addr, HEX);
|
|
found2++;
|
|
}
|
|
}
|
|
Serial1.print("I2C2 devices: ");
|
|
Serial1.println(found2);
|
|
|
|
// تست اولیه سریع
|
|
Serial1.println("Quick sensor test...");
|
|
float lux;
|
|
if (readBH1750(lux)) {
|
|
lightSensorConnected = true;
|
|
Serial1.print("BH1750 OK: ");
|
|
Serial1.print(lux);
|
|
Serial1.println(" lux");
|
|
}
|
|
|
|
delay(100);
|
|
|
|
float temp, hum;
|
|
if (readSHT31(temp, hum)) {
|
|
sht31Connected = true;
|
|
Serial1.print("SHT31 OK: ");
|
|
Serial1.print(temp);
|
|
Serial1.print("C ");
|
|
Serial1.print(hum);
|
|
Serial1.println("%");
|
|
}
|
|
|
|
Serial1.println("Sensor init done");
|
|
}
|
|
|
|
#endif // SENSORS_H |