// --- ЦИФРОВЫЕ ВЫХОДЫ (Управление ключами светодиодов) ---
const int PIN_LED_IR = 2;
const int PIN_LED_BLUE = 3;
const int PIN_ALARM = 4; // Выход сигнала "Пожар"
// --- АНАЛОГОВЫЕ ВХОДЫ (Сигналы с операционных усилителей) ---
const int PIN_ADC_DIRECT = A0; // Прямой канал
const int PIN_ADC_REVERSE = A1; // Обратный канал
const int PIN_ADC_REF = A2; // Опорный канал (диагностика)
// --- ГЛОБАЛЬНЫЕ ПЕРЕМЕННЫЕ (Хранение оцифрованных значений) ---
// Прямой канал
int dir_bg = 0, dir_ir = 0, dir_blue = 0;
// Обратный канал
int rev_bg = 0, rev_ir = 0, rev_blue = 0;
// Опорный канал
int ref_bg = 0, ref_ir = 0, ref_blue = 0;
// --- ТАЙМИНГИ И СОСТОЯНИЯ ---
unsigned long previousMillis = 0;
const long INTERVAL = 2000; // Измерение и вывод каждые 2 секунды
int state = 0; // 0 - замер ИК, 1 - замер Синего
// --- КАЛИБРОВОЧНЫЕ ПОРОГИ ---
const float SMOKE_RATIO_MIN = 1.5; // Минимальное отношение Синий/ИК для дыма
const float STEAM_RATIO_MIN = 0.8; // Нижняя граница для пара/тумана
const float STEAM_RATIO_MAX = 1.2; // Верхняя граница для пара/тумана
const int NOISE_FLOOR = 20; // Порог отсечения шума
const int REF_MIN_LEVEL = 100; // Минимальный уровень для исправного светодиода
void performMeasurement(int ledPin, int &out_dir, int &out_rev, int &out_ref) {
// Включаем целевой светодиод
digitalWrite(ledPin, HIGH);
delay(2);
// Для работы в симуляторе просто считываем прямое значение с потенциометров
out_dir = analogRead(PIN_ADC_DIRECT);
out_rev = analogRead(PIN_ADC_REVERSE);
out_ref = analogRead(PIN_ADC_REF);
// Выключаем светодиод
digitalWrite(ledPin, LOW);
}
void analyzeAerosol() {
// Проверяем прямой канал на наличие минимального сигнала
if (dir_ir > NOISE_FLOOR && dir_blue > NOISE_FLOOR) {
// Вычисляем коэффициент рассеяния
float ratio = (float)dir_blue / (float)dir_ir;
Serial.print("Коэффициент рассеяния: ");
Serial.println(ratio);
if (ratio >= SMOKE_RATIO_MIN) {
Serial.println("ОБНАРУЖЕН ДЫМ! ПОЖАР!");
digitalWrite(PIN_ALARM, HIGH);
}
else if (ratio > STEAM_RATIO_MIN && ratio < STEAM_RATIO_MAX) {
Serial.println("Обнаружен пар или пыль. Тревога заблокирована.");
digitalWrite(PIN_ALARM, LOW);
}
} else {
// Чистый воздух
digitalWrite(PIN_ALARM, LOW);
}
}
void checkDiagnostics() {
// Проверка опорного канала (сигнал независим от среды)
if (ref_ir < REF_MIN_LEVEL) {
Serial.println("ОШИБКА: Деградация или обрыв ИК-светодиода!");
}
if (ref_blue < REF_MIN_LEVEL) {
Serial.println("ОШИБКА: Деградация или обрыв Синего светодиода!");
}
}
void setup() {
Serial.begin(115200);
// Настройка выводов
pinMode(PIN_LED_IR, OUTPUT);
pinMode(PIN_LED_BLUE, OUTPUT);
pinMode(PIN_ALARM, OUTPUT);
// Стартовое состояние
digitalWrite(PIN_LED_IR, LOW);
digitalWrite(PIN_LED_BLUE, LOW);
digitalWrite(PIN_ALARM, LOW);
Serial.println("Инициализация датчика завершена.");
}
void loop() {
unsigned long currentMillis = millis();
// Неблокирующий таймер на 500 мс
if (currentMillis - previousMillis >= INTERVAL) {
previousMillis = currentMillis;
// Конечный автомат
if (state == 0) {
// Такт 1: Замер инфракрасного канала
Serial.println("--- Активация ИК тракта ---");
performMeasurement(PIN_LED_IR, dir_ir, rev_ir, ref_ir);
state = 1; // Переход к следующему такту
} else if (state == 1) {
// Такт 2: Замер синего канала
Serial.println("--- Активация Синего тракта ---");
performMeasurement(PIN_LED_BLUE, dir_blue, rev_blue, ref_blue);
// После сбора данных за оба такта производим анализ
analyzeAerosol();
checkDiagnostics();
state = 0; // Возврат в начало
}
}
// Здесь контроллер может выполнять другие задачи
// (например, мигать дежурным зеленым светодиодом),
// пока ждет истечения следующих 500 мс.
}