#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <OneWire.h>
#include <DallasTemperature.h>
#include <DHT.h>
#include <Encoder.h> // Библиотека для работы с энкодером
// Пины
#define ONE_WIRE_BUS 2
#define DHTPIN 3
#define HEATER_PIN 9
#define SWITCH_PIN 7
#define BUTTON_SELECT_PIN 4 // Эта кнопка для переключения параметров
#define ENCODER_PIN_A 5 // Пин A энкодера (CLK)
#define ENCODER_PIN_B 6 // Пин B энкодера (DT)
#define ENCODER_BUTTON_PIN 8 // Кнопка энкодера (SW)
#define TOGGLE_BUTTON_PIN 10 // Кнопка включения/выключения системы
#define BUTTON_MODE_PIN 11 // Новая кнопка для переключения режимов экрана
// Настройки
#define DHTTYPE DHT22
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);
OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature sensors(&oneWire);
DHT dht(DHTPIN, DHTTYPE);
// Переменные
float maxTemp = 60.0; // Максимальная температура по умолчанию
int powerLow = 255; // Мощность при температуре < 50°C
int powerMid = 178; // Мощность при температуре 50-60°C
float ds18b20Temp = 0.0, dhtTemp = 0.0, dhtHum = 0.0;
int heaterPower = 0;
bool systemEnabled = false; // Состояние системы (включена/выключена)
// Выбор параметра
enum Parameter { MAX_TEMP, POWER_LOW, POWER_MID };
Parameter selectedParameter = MAX_TEMP;
// Режимы экрана
enum DisplayMode { MODE_INFO, MODE_DS18B20_GRAPH, MODE_DHT_GRAPH };
DisplayMode currentMode = MODE_INFO; // Начальный режим
// Данные для графиков
#define MAX_HISTORY_SIZE 120 // 2 часа для DS18B20 (раз в минуту)
#define MAX_HISTORY_DHT 240 // 4 часа для DHT22 (раз в минуту)
float ds18b20Temps[MAX_HISTORY_SIZE];
float dhtTemps[MAX_HISTORY_DHT];
int ds18b20Index = 0;
int dhtIndex = 0;
// Вспомогательные переменные для энкодера
Encoder encoder(ENCODER_PIN_A, ENCODER_PIN_B);
long encoderPos = 0; // Положение энкодера
long lastEncoderPos = 0; // Для отслеживания изменений положения
// Дебаунсинг кнопки
unsigned long lastDebounceTime = 0;
unsigned long debounceDelay = 50;
// Функция настройки
void setup() {
pinMode(HEATER_PIN, OUTPUT);
pinMode(SWITCH_PIN, INPUT_PULLUP);
pinMode(BUTTON_SELECT_PIN, INPUT_PULLUP);
pinMode(ENCODER_BUTTON_PIN, INPUT_PULLUP); // Подключение кнопки энкодера
pinMode(TOGGLE_BUTTON_PIN, INPUT_PULLUP); // Подключение кнопки включения/выключения
pinMode(BUTTON_MODE_PIN, INPUT_PULLUP); // Новая кнопка для переключения режимов
// Инициализация дисплея
if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
for (;;) {
// Если дисплей не найден, зациклиться
}
}
display.clearDisplay();
display.display();
dht.begin();
sensors.begin();
display.setTextSize(1);
display.setTextColor(SSD1306_WHITE);
}
// Основной цикл
void loop() {
// Обработка кнопки включения/выключения системы
if (buttonPressed(TOGGLE_BUTTON_PIN)) {
systemEnabled = !systemEnabled; // Переключение состояния
}
// Обработка переключения режимов экрана
if (buttonPressed(BUTTON_MODE_PIN)) {
currentMode = static_cast<DisplayMode>((currentMode + 1) % 3); // Переключаем режимы
delay(200); // Задержка для устранения дребезга кнопки
}
// Если система выключена, показываем статус и выходим
if (!systemEnabled) {
heaterPower = 0;
analogWrite(HEATER_PIN, heaterPower);
display.clearDisplay();
display.setCursor(0, 0);
display.print("System disabled");
display.display();
return;
}
// Чтение данных с датчиков
sensors.requestTemperatures();
ds18b20Temp = sensors.getTempCByIndex(0);
dhtTemp = dht.readTemperature();
dhtHum = dht.readHumidity();
// Проверка на корректность данных с DHT
if (isnan(dhtTemp) || isnan(dhtHum)) {
display.clearDisplay();
display.setCursor(0, 0);
display.print("Ошибка чтения с DHT!");
display.display();
return;
}
// Записываем данные в массивы для графиков
if (currentMode == MODE_DS18B20_GRAPH) {
ds18b20Temps[ds18b20Index] = ds18b20Temp;
ds18b20Index = (ds18b20Index + 1) % MAX_HISTORY_SIZE;
}
if (currentMode == MODE_DHT_GRAPH) {
dhtTemps[dhtIndex] = dhtTemp;
dhtIndex = (dhtIndex + 1) % MAX_HISTORY_DHT;
}
// Управление нагревателем
if (digitalRead(SWITCH_PIN) == LOW) {
heaterPower = 0;
} else {
if (dhtTemp < 50) {
heaterPower = powerLow;
} else if (dhtTemp >= 50 && dhtTemp < maxTemp) {
heaterPower = powerMid;
} else {
heaterPower = 0;
}
}
analogWrite(HEATER_PIN, heaterPower);
// Обработка энкодера
encoderPos = encoder.read(); // Чтение текущего положения энкодера
if (encoderPos != lastEncoderPos) {
// Если энкодер был вращен
if (encoderPos > lastEncoderPos) {
modifyParameter(selectedParameter, 1); // Увеличиваем значение
} else {
modifyParameter(selectedParameter, -1); // Уменьшаем значение
}
lastEncoderPos = encoderPos; // Обновляем последнее положение
}
// Обработка кнопки для переключения параметров
if (buttonPressed(BUTTON_SELECT_PIN)) {
selectedParameter = static_cast<Parameter>((selectedParameter + 1) % 3);
}
// Обновление дисплея
updateDisplay();
delay(100);
}
// Функция обновления дисплея
void updateDisplay() {
display.clearDisplay();
if (currentMode == MODE_INFO) {
display.setCursor(0, 0);
display.print("DHT: ");
display.print(dhtTemp, 1);
display.print("C ");
display.print(dhtHum, 1);
display.print("%");
display.setCursor(0, 10);
display.print("Max Temp: ");
display.print(maxTemp, 1);
if (selectedParameter == MAX_TEMP) display.print(" *");
display.setCursor(0, 20);
display.print("Power <50C: ");
display.print(powerLow);
if (selectedParameter == POWER_LOW) display.print(" *");
display.setCursor(0, 30);
display.print("Power 50-60C: ");
display.print(powerMid);
if (selectedParameter == POWER_MID) display.print(" *");
display.setCursor(0, 40);
display.print("Heater Pwr: ");
display.print(heaterPower);
}
else if (currentMode == MODE_DS18B20_GRAPH) {
// Отображаем график для DS18B20 (температура за последние 2 часа)
display.setCursor(0, 0);
display.print("DS18B20 Temp Graph");
// Здесь добавьте код для рисования графика
}
else if (currentMode == MODE_DHT_GRAPH) {
// Отображаем график для DHT22 (температура за последние 4 часа)
display.setCursor(0, 0);
display.print("DHT22 Temp Graph");
// Здесь добавьте код для рисования графика
}
display.display();
}
// Изменение параметров с шагом 5 градусов
void modifyParameter(Parameter param, int change) {
switch (param) {
case MAX_TEMP:
maxTemp = constrain(maxTemp + change * 5, 30.0, 80.0); // Шаг изменения 5 градусов
break;
case POWER_LOW:
powerLow = constrain(powerLow + change * 10, 0, 255);
break;
case POWER_MID:
powerMid = constrain(powerMid + change * 10, 0, 255);
break;
}
}
// Проверка нажатия кнопки с дебаунсингом
bool buttonPressed(int pin) {
if (digitalRead(pin) == LOW) {
if ((millis() - lastDebounceTime) > debounceDelay) {
lastDebounceTime = millis();
return true;
}
}
return false;
}