433 lines
11 KiB
C++
433 lines
11 KiB
C++
#include <Wire.h>
|
|
|
|
// تعریف پینهای I2C
|
|
#define I2C_SDA PB9
|
|
#define I2C_SCL PB8
|
|
|
|
// آدرس سنسورها
|
|
#define BH1750_ADDR 0x23
|
|
#define SHT31_ADDR 0x44
|
|
|
|
// حالتهای اندازهگیری BH1750
|
|
#define BH1750_CONTINUOUS_HIGH_RES_MODE 0x10
|
|
#define BH1750_ONE_TIME_HIGH_RES_MODE 0x20
|
|
|
|
// دستورات 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
|
|
|
|
// متغیرهای ذخیرهسازی مقادیر
|
|
float lastLightLevel = 0.0;
|
|
float lastTemperature = 0.0;
|
|
float lastHumidity = 0.0;
|
|
|
|
// متغیرهای تشخیص خطا
|
|
bool bh1750Available = false;
|
|
bool sht31Available = false;
|
|
uint8_t bh1750ErrorCount = 0;
|
|
uint8_t sht31ErrorCount = 0;
|
|
const uint8_t MAX_ERRORS = 5;
|
|
|
|
// زمان آخرین اندازهگیری موفق
|
|
unsigned long lastBh1750Success = 0;
|
|
unsigned long lastSht31Success = 0;
|
|
|
|
// تابع بازیابی نرم I2C (بدون ریست سختافزاری)
|
|
void recoverI2C() {
|
|
Serial1.println("Attempting I2C recovery...");
|
|
|
|
// آزاد کردن خطوط
|
|
pinMode(I2C_SDA, INPUT_PULLUP);
|
|
pinMode(I2C_SCL, INPUT_PULLUP);
|
|
delay(1000);
|
|
|
|
// تولید پالسهای ساعت برای آزادسازی هر دستگاه قفل شده
|
|
pinMode(I2C_SCL, OUTPUT);
|
|
for (int i = 0; i < 9; i++) {
|
|
digitalWrite(I2C_SCL, LOW);
|
|
delayMicroseconds(5);
|
|
digitalWrite(I2C_SCL, HIGH);
|
|
delayMicroseconds(5);
|
|
}
|
|
|
|
// ایجاد شرط STOP
|
|
pinMode(I2C_SDA, OUTPUT);
|
|
digitalWrite(I2C_SDA, LOW);
|
|
delayMicroseconds(5);
|
|
digitalWrite(I2C_SCL, HIGH);
|
|
delayMicroseconds(5);
|
|
digitalWrite(I2C_SDA, HIGH);
|
|
delayMicroseconds(5);
|
|
|
|
// بازگشت به حالت عادی
|
|
Wire.begin();
|
|
|
|
Serial1.println("I2C recovery completed");
|
|
}
|
|
|
|
// تابع بررسی وجود دستگاه روی باس I2C
|
|
bool checkDevice(uint8_t address) {
|
|
Wire.beginTransmission(address);
|
|
byte error = Wire.endTransmission();
|
|
return (error == 0);
|
|
}
|
|
|
|
bool initBH1750() {
|
|
if (!checkDevice(BH1750_ADDR)) {
|
|
Serial1.println("BH1750 not found!");
|
|
bh1750Available = false;
|
|
return false;
|
|
}
|
|
|
|
bh1750Available = true;
|
|
bh1750ErrorCount = 0;
|
|
Serial1.println("BH1750 ready (One-Time mode)");
|
|
return true;
|
|
}
|
|
|
|
bool readBH1750(float &lightLevel) {
|
|
|
|
if (!bh1750Available && bh1750ErrorCount < MAX_ERRORS) {
|
|
if (!initBH1750()) return false;
|
|
}
|
|
|
|
if (!bh1750Available) return false;
|
|
|
|
// ارسال فرمان One-Time High Resolution
|
|
Wire.beginTransmission(BH1750_ADDR);
|
|
Wire.write(BH1750_ONE_TIME_HIGH_RES_MODE);
|
|
byte error = Wire.endTransmission();
|
|
|
|
if (error != 0) {
|
|
bh1750ErrorCount++;
|
|
Serial1.println("BH1750 command error");
|
|
return false;
|
|
}
|
|
|
|
// زمان تبدیل (حداکثر 180ms طبق دیتاشیت)
|
|
delay(180);
|
|
|
|
// درخواست 2 بایت داده
|
|
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;
|
|
lastBh1750Success = millis();
|
|
return true;
|
|
}
|
|
}
|
|
|
|
// در صورت خطا
|
|
bh1750ErrorCount++;
|
|
Serial1.println("Error reading BH1750");
|
|
|
|
if (bh1750ErrorCount >= MAX_ERRORS) {
|
|
bh1750Available = false;
|
|
Serial1.println("BH1750 marked as unavailable");
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
// تابع مقداردهی اولیه BH1750
|
|
bool __initBH1750() {
|
|
if (!checkDevice(BH1750_ADDR)) {
|
|
Serial1.println("BH1750 not found!");
|
|
bh1750Available = false;
|
|
return false;
|
|
}
|
|
|
|
// ارسال دستور اندازهگیری پیوسته
|
|
Wire.beginTransmission(BH1750_ADDR);
|
|
Wire.write(BH1750_CONTINUOUS_HIGH_RES_MODE);
|
|
byte error = Wire.endTransmission();
|
|
|
|
if (error == 0) {
|
|
bh1750Available = true;
|
|
bh1750ErrorCount = 0;
|
|
Serial1.println("BH1750 initialized successfully");
|
|
return true;
|
|
} else {
|
|
bh1750Available = false;
|
|
Serial1.print("BH1750 init error: ");
|
|
Serial1.println(error);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// تابع خواندن نور از BH1750
|
|
bool __readBH1750(float &lightLevel) {
|
|
if (!bh1750Available && bh1750ErrorCount < MAX_ERRORS) {
|
|
if (initBH1750()) {
|
|
delay(180); // تأخیر برای اولین اندازهگیری
|
|
}
|
|
}
|
|
|
|
if (!bh1750Available) return false;
|
|
|
|
// درخواست 2 بایت داده
|
|
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;
|
|
lastBh1750Success = millis();
|
|
return true;
|
|
}
|
|
}
|
|
|
|
// در صورت خطا
|
|
bh1750ErrorCount++;
|
|
Serial1.println("Error reading BH1750");
|
|
|
|
if (bh1750ErrorCount >= MAX_ERRORS) {
|
|
bh1750Available = false;
|
|
Serial1.println("BH1750 marked as unavailable");
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
// تابع مقداردهی اولیه SHT31
|
|
bool initSHT31() {
|
|
if (!checkDevice(SHT31_ADDR)) {
|
|
Serial1.println("SHT31 not found!");
|
|
sht31Available = false;
|
|
return false;
|
|
}
|
|
|
|
// ارسال دستور ریست نرم
|
|
Wire.beginTransmission(SHT31_ADDR);
|
|
Wire.write(SHT31_SOFTRESET >> 8);
|
|
Wire.write(SHT31_SOFTRESET & 0xFF);
|
|
byte error = Wire.endTransmission();
|
|
|
|
if (error != 0) {
|
|
sht31Available = 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) {
|
|
sht31Available = true;
|
|
sht31ErrorCount = 0;
|
|
Serial1.println("SHT31 initialized successfully");
|
|
return true;
|
|
} else {
|
|
sht31Available = false;
|
|
Serial1.print("SHT31 init error: ");
|
|
Serial1.println(error);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// تابع خواندن دما و رطوبت از SHT31
|
|
bool readSHT31(float &temperature, float &humidity) {
|
|
if (!sht31Available && sht31ErrorCount < MAX_ERRORS) {
|
|
initSHT31();
|
|
}
|
|
|
|
if (!sht31Available) 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) {
|
|
sht31Available = false;
|
|
Serial1.println("SHT31 marked as unavailable");
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
// تابع بررسی دورهای سلامت باس I2C
|
|
void checkI2CHealth() {
|
|
static unsigned long lastCheck = 0;
|
|
unsigned long currentMillis = millis();
|
|
|
|
// هر 30 ثانیه بررسی کن
|
|
if (currentMillis - lastCheck >= 30000) {
|
|
lastCheck = currentMillis;
|
|
|
|
bool bh1750WasAvailable = bh1750Available;
|
|
bool sht31WasAvailable = sht31Available;
|
|
|
|
// بررسی BH1750
|
|
if (checkDevice(BH1750_ADDR)) {
|
|
if (!bh1750WasAvailable) {
|
|
Serial1.println("BH1750 reappeared, reinitializing...");
|
|
initBH1750();
|
|
}
|
|
} else if (bh1750WasAvailable) {
|
|
Serial1.println("BH1750 disappeared!");
|
|
bh1750Available = false;
|
|
}
|
|
|
|
// بررسی SHT31
|
|
if (checkDevice(SHT31_ADDR)) {
|
|
if (!sht31WasAvailable) {
|
|
Serial1.println("SHT31 reappeared, reinitializing...");
|
|
initSHT31();
|
|
}
|
|
} else if (sht31WasAvailable) {
|
|
Serial1.println("SHT31 disappeared!");
|
|
sht31Available = false;
|
|
}
|
|
|
|
// اگر هر دو دستگاه از دست رفتهاند، بازیابی باس I2C
|
|
if (!bh1750Available && !sht31Available) {
|
|
recoverI2C();
|
|
|
|
// تلاش مجدد برای مقداردهی اولیه
|
|
delay(100);
|
|
initBH1750();
|
|
initSHT31();
|
|
}
|
|
}
|
|
}
|
|
|
|
void setup() {
|
|
Serial1.begin(115200);
|
|
Serial1.println("Starting STM32 with BH1750 and SHT31...");
|
|
|
|
// مقداردهی اولیه I2C
|
|
Wire.setSDA(I2C_SDA);
|
|
Wire.setSCL(I2C_SCL);
|
|
Wire.begin();
|
|
Wire.setClock(100000); // 100kHz برای پایداری بهتر
|
|
|
|
// افزایش timeout برای جلوگیری از hang شدن
|
|
Wire.setTimeout(1000);
|
|
|
|
delay(1000); // تأخیر برای پایدار شدن
|
|
|
|
// مقداردهی اولیه سنسورها
|
|
initBH1750();
|
|
initSHT31();
|
|
|
|
Serial1.println("Setup completed");
|
|
}
|
|
|
|
void loop() {
|
|
static unsigned long lastRead = 0;
|
|
unsigned long currentMillis = millis();
|
|
|
|
// خواندن هر 2 ثانیه
|
|
if (currentMillis - lastRead >= 2000) {
|
|
lastRead = currentMillis;
|
|
|
|
// خواندن BH1750
|
|
float light;
|
|
if (readBH1750(light)) {
|
|
Serial1.print("Light: ");
|
|
Serial1.print(light);
|
|
Serial1.println(" lux");
|
|
} else {
|
|
Serial1.print("Using last BH1750 value: ");
|
|
Serial1.print(lastLightLevel);
|
|
Serial1.println(" lux");
|
|
}
|
|
delay(500);
|
|
// خواندن SHT31
|
|
float temp, hum;
|
|
if (readSHT31(temp, hum)) {
|
|
Serial1.print("Temperature: ");
|
|
Serial1.print(temp);
|
|
Serial1.print(" °C, Humidity: ");
|
|
Serial1.print(hum);
|
|
Serial1.println(" %");
|
|
} else {
|
|
Serial1.print("Using last SHT31 values - Temp: ");
|
|
Serial1.print(lastTemperature);
|
|
Serial1.print(" °C, Hum: ");
|
|
Serial1.print(lastHumidity);
|
|
Serial1.println(" %");
|
|
}
|
|
|
|
Serial1.println("---");
|
|
}
|
|
|
|
// بررسی سلامت دورهای
|
|
checkI2CHealth();
|
|
|
|
// تأخیر کوتاه برای جلوگیری از overload
|
|
delay(10);
|
|
} |