IOT ENTERPRISE ©
К плате ARDUINO NANO подключены два датчика температуры. Первый датчик температуры с верху, второй с низу. Так же на плату подключен зуммер для звуковой индикации. Первая кнопка отвечает за переключение режима работы, вторая за переключение вверх или влево, третья кнопка отвечает за переключение вправо или вниз. На управление установлены два выхода, на первом выходе установлен вентилятор, на втором выходе установлен нагревательный элемент. Подключен по i2c OLED дисплей 128*64 пикселя. В нормальном состоянии отображается температура первого датчика, под ним температура второго датчика, далее подним таймер сколько осталось до конца таймера. Время отображается в формате ЧАСЫ:МИНУТА:СЕКУНДЫ. Если таймер не равен нулю, то по нажанию кнопки выбора, включится обязательно выход вентилятора, выход нагревателя включается только в случаи если температура датчиков меньше чем задана программой. Включенный вентелятор и нагреватель будет до тех пор пока не закончится таймер. Если температура верхнего датчика будет выще чем заложено программой, то нагревательный элемент выключается пока температура не снизится на более чем 5 градусов от установленной программой. Нагревательный элемент должен включатся до тех пор пока есть время на таймере, и температура установленная программой больше, чем показания с датчиков температуры. Если разница между верхним и нижним датчиком в процессе работы будет более чем на 10 градусов разной, должно каждые 60 секунд секунд выводится предупреждение, и звучать 5 раз звуковой сигнал об ошибке. По истечению таймера, нагревательный элемент должен выключится, а вентилятор выключится только, когда температура датчиков верхнего и нижнего будет менее 45 градусов. Если задержать кнопку выбора более чем на 5 секунд, то откроется окно настройки температуры, кнопками можно ее с градцией по одному градусу настраивать от 40 до 70 градусов, при нажании кнопки выбора настраивается таймер, таймер настраивается кратно часам, пр и нажатии кнопки выбора мы переходим в рабочий режим, где отображается верхняя и нижняя темепература датчиков, и таймер. При нажании на кнопку выбора, при наличии таймера не равным нулю, включается в работу, а так же таймер начинает отсчитывать обратный отсчет.
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <OneWire.h>
#include <DallasTemperature.h>
// Пины подключения
#define BUZZER_PIN 3 // Пин зумера
#define BUTTON_MODE_PIN 4 // Кнопка режима
#define BUTTON_UP_LEFT_PIN 5 // Кнопка вверх/влево
#define BUTTON_DOWN_RIGHT_PIN 6 // Кнопка вниз/вправо
#define FAN_PIN 7 // Выход вентилятора
#define HEATER_PIN 8 // Выход нагревателя
#define ONE_WIRE_BUS_TOP 9 // Верхний датчик температуры
#define ONE_WIRE_BUS_BOTTOM 10 // Нижний датчик температуры
// Настройки OLED дисплея
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_ADDRESS 0x3C
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);
// Датчики температуры
OneWire oneWireTop(ONE_WIRE_BUS_TOP);
OneWire oneWireBottom(ONE_WIRE_BUS_BOTTOM);
DallasTemperature sensorTop(&oneWireTop);
DallasTemperature sensorBottom(&oneWireBottom);
// Переменные состояния
float tempTop = 0;
float tempBottom = 0;
float targetTemp = 60.0; // Целевая температура по умолчанию
unsigned long timerSeconds = 0; // Таймер в секундах
bool isRunning = false;
bool fanState = false;
bool heaterState = false;
unsigned long lastTempCheck = 0;
unsigned long lastWarningTime = 0;
unsigned long buttonPressTime = 0;
bool inSettings = false;
bool settingTemp = true; // true - настройка температуры, false - настройка таймера
// Настройки
const float TEMP_HYSTERESIS = 5.0; // Гистерезис температуры
const float TEMP_DIFF_WARNING = 10.0; // Разница температур для предупреждения
const float COOL_DOWN_TEMP = 45.0; // Температура охлаждения
const int WARNING_INTERVAL = 60000; // Интервал предупреждений (60 сек)
void setup() {
Serial.begin(9600);
// Инициализация пинов
pinMode(BUZZER_PIN, OUTPUT);
pinMode(BUTTON_MODE_PIN, INPUT_PULLUP);
pinMode(BUTTON_UP_LEFT_PIN, INPUT_PULLUP);
pinMode(BUTTON_DOWN_RIGHT_PIN, INPUT_PULLUP);
pinMode(FAN_PIN, OUTPUT);
pinMode(HEATER_PIN, OUTPUT);
digitalWrite(FAN_PIN, LOW);
digitalWrite(HEATER_PIN, LOW);
// Инициализация дисплея
if(!display.begin(SSD1306_SWITCHCAPVCC, OLED_ADDRESS)) {
Serial.println(F("OLED не найден!"));
for(;;);
}
// Инициализация датчиков температуры
sensorTop.begin();
sensorBottom.begin();
display.clearDisplay();
display.setTextSize(1);
display.setTextColor(SSD1306_WHITE);
display.setCursor(0,0);
display.println(F("Система контроля"));
display.println(F("температуры"));
display.println(F("Запуск..."));
display.display();
delay(2000);
beep(1, 200); // Сигнал запуска
}
void loop() {
unsigned long currentMillis = millis();
// Чтение температуры каждые 2 секунды
if (currentMillis - lastTempCheck >= 2000) {
readTemperatures();
lastTempCheck = currentMillis;
}
// Обработка кнопок
handleButtons();
// Основная логика работы
if (isRunning && timerSeconds > 0) {
processHeatingLogic();
updateTimer();
} else if (timerSeconds == 0 && isRunning) {
processCoolDown();
}
// Проверка разницы температур
checkTemperatureDifference();
// Обновление дисплея
updateDisplay();
delay(100);
}
void readTemperatures() {
sensorTop.requestTemperatures();
sensorBottom.requestTemperatures();
tempTop = sensorTop.getTempCByIndex(0);
tempBottom = sensorBottom.getTempCByIndex(0);
// Проверка на ошибку датчиков
if (tempTop == DEVICE_DISCONNECTED_C) tempTop = 0;
if (tempBottom == DEVICE_DISCONNECTED_C) tempBottom = 0;
}
void handleButtons() {
static unsigned long lastDebounceTime = 0;
const int debounceDelay = 50;
// Кнопка выбора (долгое нажатие для настроек)
if (digitalRead(BUTTON_MODE_PIN) == LOW) {
if (buttonPressTime == 0) {
buttonPressTime = millis();
} else if (millis() - buttonPressTime > 5000 && !inSettings) {
inSettings = true;
settingTemp = true;
beep(2, 300);
buttonPressTime = 0;
}
} else {
if (buttonPressTime > 0) {
if (millis() - buttonPressTime < 5000) {
// Короткое нажатие
if (inSettings) {
if (settingTemp) {
settingTemp = false;
} else {
inSettings = false;
isRunning = true;
}
beep(1, 200);
} else if (timerSeconds > 0) {
isRunning = !isRunning;
beep(1, 200);
}
}
buttonPressTime = 0;
}
}
// Кнопки вверх/вниз только в режиме настроек
if (inSettings) {
if (digitalRead(BUTTON_UP_LEFT_PIN) == LOW && millis() - lastDebounceTime > debounceDelay) {
if (settingTemp) {
targetTemp = constrain(targetTemp + 1, 40, 70);
} else {
timerSeconds += 3600; // +1 час
}
beep(1, 100);
lastDebounceTime = millis();
}
if (digitalRead(BUTTON_DOWN_RIGHT_PIN) == LOW && millis() - lastDebounceTime > debounceDelay) {
if (settingTemp) {
targetTemp = constrain(targetTemp - 1, 40, 70);
} else {
timerSeconds = max(0, timerSeconds - 3600); // -1 час
}
beep(1, 100);
lastDebounceTime = millis();
}
}
}
void processHeatingLogic() {
// Вентилятор всегда включен при работе
digitalWrite(FAN_PIN, HIGH);
fanState = true;
// Логика нагревателя
if (tempTop < targetTemp - TEMP_HYSTERESIS && tempBottom < targetTemp - TEMP_HYSTERESIS) {
digitalWrite(HEATER_PIN, HIGH);
heaterState = true;
} else if (tempTop > targetTemp) {
digitalWrite(HEATER_PIN, LOW);
heaterState = false;
}
}
void processCoolDown() {
// Выключение нагревателя
digitalWrite(HEATER_PIN, LOW);
heaterState = false;
// Выключение вентилятора когда температура ниже 45°C
if (tempTop < COOL_DOWN_TEMP && tempBottom < COOL_DOWN_TEMP) {
digitalWrite(FAN_PIN, LOW);
fanState = false;
isRunning = false;
}
}
void checkTemperatureDifference() {
if (abs(tempTop - tempBottom) > TEMP_DIFF_WARNING) {
if (millis() - lastWarningTime > WARNING_INTERVAL) {
beep(5, 500);
lastWarningTime = millis();
}
}
}
void updateTimer() {
if (isRunning && timerSeconds > 0) {
static unsigned long lastSecond = 0;
if (millis() - lastSecond >= 1000) {
timerSeconds--;
lastSecond = millis();
}
}
}
void updateDisplay() {
display.clearDisplay();
display.setCursor(0,0);
if (inSettings) {
display.println(F("=== НАСТРОЙКИ ==="));
if (settingTemp) {
display.print(F("Температура: "));
display.print(targetTemp);
display.println(F("C"));
display.println(F("Кнопки: +/-1C"));
} else {
display.print(F("Время: "));
display.print(timerSeconds / 3600);
display.println(F(" ч"));
display.println(F("Кнопки: +/-1 ч"));
}
display.println(F("Выбор - далее"));
} else {
display.print(F("Верх: "));
display.print(tempTop);
display.println(F("C"));
display.print(F("Низ: "));
display.print(tempBottom);
display.println(F("C"));
display.print(F("Цель: "));
display.print(targetTemp);
display.println(F("C"));
display.print(F("Время: "));
display.print(formatTime(timerSeconds));
display.setCursor(0, 48);
if (fanState) display.print(F("ВЕНТ "));
if (heaterState) display.print(F("НАГР "));
if (!isRunning && timerSeconds > 0) display.print(F("ПАУЗА"));
}
display.display();
}
String formatTime(unsigned long seconds) {
int hours = seconds / 3600;
int minutes = (seconds % 3600) / 60;
int secs = seconds % 60;
char buffer[9];
sprintf(buffer, "%02d:%02d:%02d", hours, minutes, secs);
return String(buffer);
}
void beep(int count, int duration) {
for (int i = 0; i < count; i++) {
digitalWrite(BUZZER_PIN, HIGH);
delay(duration);
digitalWrite(BUZZER_PIN, LOW);
if (i < count - 1) delay(200);
}
}