Сегодня я предлагаю вашему вниманию рабочий код, версия №1.3 (стабильная) без расширенного функционала для загрузки на Ардуино.
Инструкция по использованию терморегулятора для инкубатора
- Кнопки управления:
Кнопка "Режим" - переключает между нагревом и охлаждением
Кнопка "+" - увеличивает установленную температуру
Кнопка "-" - уменьшает установленную температуру - Отображение на LCD:
Верхняя строка: текущая температура (NOW) и режим работы (HEAT/COOL)
Нижняя строка: заданная температура (SET) и состояние реле (ON/OFF) - Логика работы:
В режиме нагрева реле включается, когда температура ниже уставки
В режиме охлаждения реле включается, когда температура выше уставки
Основные функции реализованные в этом коде:
Антидребезг кнопок
Плавное обновление дисплея без мерцания
Точность изменения температуры 0.1°C
Два режима работы (нагрев и охлаждение)
Гистерезис для предотвращения частых переключений реле
Этот код обеспечивает базовую функциональность терморегулятора с простым и понятным управлением, без дополнительных сложных функций (более сложные функции и настройки реализованы в последующих версиях кода).
Код микроконтроллера C++:
Просто копируете текст в вставляете в скетч
#include <OneWire.h>
#include <DallasTemperature.h>
#include <LiquidCrystal_I2C.h>
#include <Bounce2.h>
// Пины
#define ONE_WIRE_BUS 2
#define RELAY_PIN 7
#define MODE_BUTTON 8
#define UP_BUTTON 9
#define DOWN_BUTTON 10
// Настройки
#define TEMP_STEP 0.1 // Шаг изменения температуры 0.1°C
#define HYSTERESIS 0.2 // Гистерезис 0.2°C
#define LCD_UPDATE_INTERVAL 200 // Интервал обновления дисплея (мс)
// Инициализация
OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature sensors(&oneWire);
LiquidCrystal_I2C lcd(0x27, 16, 2);
// Антидребезг для кнопок
Bounce modeButton = Bounce();
Bounce upButton = Bounce();
Bounce downButton = Bounce();
// Переменные состояния
float currentTemp = 0;
float setPoint = 37.8;
bool heatingMode = true;
bool relayState = false;
// Переменные для управления дисплеем
float lastShownTemp = -100;
float lastShownSetPoint = -100;
bool lastShownMode = false;
bool lastShownRelayState = false;
unsigned long lastDisplayUpdate = 0;
bool displayInitialized = false;
void setup() {
pinMode(RELAY_PIN, OUTPUT);
digitalWrite(RELAY_PIN, LOW);
// Настройка кнопок
modeButton.attach(MODE_BUTTON, INPUT_PULLUP);
modeButton.interval(25);
upButton.attach(UP_BUTTON, INPUT_PULLUP);
upButton.interval(25);
downButton.attach(DOWN_BUTTON, INPUT_PULLUP);
downButton.interval(25);
// Инициализация датчика температуры
sensors.begin();
sensors.setResolution(12);
// Инициализация дисплея
lcd.init();
lcd.backlight();
// Первоначальное отображение
updateDisplay(true);
}
void loop() {
unsigned long currentMillis = millis();
// Обновление состояния кнопок
modeButton.update();
upButton.update();
downButton.update();
// Обработка кнопок
handleButtons();
// Обновление температуры
static unsigned long lastTempUpdate = 0;
if (currentMillis - lastTempUpdate >= 1000) {
sensors.requestTemperatures();
currentTemp = sensors.getTempCByIndex(0);
controlLogic();
lastTempUpdate = currentMillis;
}
// Плавное обновление дисплея
if (currentMillis - lastDisplayUpdate >= LCD_UPDATE_INTERVAL) {
updateDisplay(false);
lastDisplayUpdate = currentMillis;
}
}
void handleButtons() {
// Переключение режима
if (modeButton.fell()) {
heatingMode = !heatingMode;
}
// Увеличение температуры
if (upButton.fell()) {
setPoint += TEMP_STEP;
}
// Уменьшение температуры
if (downButton.fell()) {
setPoint -= TEMP_STEP;
}
}
void controlLogic() {
if (heatingMode) {
// Режим нагрева
if (currentTemp < (setPoint - HYSTERESIS/2) && !relayState) {
digitalWrite(RELAY_PIN, HIGH);
relayState = true;
} else if (currentTemp >= (setPoint + HYSTERESIS/2) && relayState) {
digitalWrite(RELAY_PIN, LOW);
relayState = false;
}
} else {
// Режим охлаждения
if (currentTemp > (setPoint + HYSTERESIS/2) && !relayState) {
digitalWrite(RELAY_PIN, HIGH);
relayState = true;
} else if (currentTemp <= (setPoint - HYSTERESIS/2) && relayState) {
digitalWrite(RELAY_PIN, LOW);
relayState = false;
}
}
}
void updateDisplay(bool forceFullUpdate) {
// Определяем необходимость обновления каждой части
bool updateTemp = forceFullUpdate || (fabs(currentTemp - lastShownTemp) >= 0.05);
bool updateSetPoint = forceFullUpdate || (fabs(setPoint - lastShownSetPoint) >= 0.05);
bool updateMode = forceFullUpdate || (heatingMode != lastShownMode);
bool updateRelay = forceFullUpdate || (relayState != lastShownRelayState);
// Если нечего обновлять и не принудительное обновление - выходим
if (!forceFullUpdate && !updateTemp && !updateSetPoint && !updateMode && !updateRelay) {
return;
}
// Полное обновление при первом вызове или по требованию
if (forceFullUpdate || !displayInitialized) {
lcd.clear();
lcd.setCursor(0, 0); lcd.print("Now:");
lcd.setCursor(11, 0); lcd.print("");
lcd.setCursor(0, 1); lcd.print("Set:");
lcd.setCursor(11, 1); lcd.print("");
displayInitialized = true;
}
// Обновляем только измененные части
if (updateTemp) {
lcd.setCursor(4, 0);
lcd.print(currentTemp, 1);
lcd.write(0xDF); // Символ градуса
lcd.print("C ");
lastShownTemp = currentTemp;
}
if (updateSetPoint) {
lcd.setCursor(4, 1);
lcd.print(setPoint, 1);
lcd.write(0xDF);
lcd.print("C ");
lastShownSetPoint = setPoint;
}
if (updateMode) {
lcd.setCursor(12, 0);
lcd.print(heatingMode ? "HEAT" : "COOL");
lastShownMode = heatingMode;
}
if (updateRelay) {
lcd.setCursor(12, 1);
lcd.print(relayState ? "ON " : "OFF");
lastShownRelayState = relayState;
}
}