/ Параметры оборудования
#define FAN_PIN D0
#define WATER_SENSOR_PIN D7
#define TEMP_INNER_PIN D5
#define TEMP_EXTERNAL_PIN D6
// Настройки температуры
const float FAN_ON_TEMP = 26.0;
const float FAN_OFF_TEMP = 25.0;
const float HIGH_TEMP_ALARM = 26.5;
// Интервалы времени (мс)
const unsigned long BOT_INTERVAL = 1000;
const unsigned long TEMP_GRAPH_INTERVAL = 3600000;
const unsigned long ALARM_COOLDOWN = 900000;
const unsigned long WATER_ALARM_COOLDOWN = 1800000;
// Объекты компонентов
OneWire innerWire(TEMP_INNER_PIN);
OneWire externalWire(TEMP_EXTERNAL_PIN);
DallasTemperature innerSensor(&innerWire);
DallasTemperature externalSensor(&externalWire);
RTC_DS3231 rtc;
Adafruit_SSD1306 display(128, 64, &Wire);
WiFiClientSecure client;
UniversalTelegramBot bot(BOT_TOKEN, client);
// Глобальные переменные
float innerTemp = 0;
float externalTemp = 0;
bool fanState = false;
bool waterAlarm = false;
bool tempAlarm = false;
bool innerSensorActive = true;
bool externalSensorActive = true;
bool manualFanControl = false; // Флаг ручного управления вентилятором
unsigned long lastBotUpdate = 0;
unsigned long lastTempSend = 0;
unsigned long lastTempAlarm = 0;
unsigned long lastWaterAlarm = 0;
// История температур
const int HISTORY_SIZE = 24;
float tempHistory[HISTORY_SIZE] = {0};
int historyIndex = 0;
// Клавиатура Telegram
const String keyboardJson = R"([
["Вкл вентилятор", "Выкл вентилятор"],
["Статус", "График"],
["Датчик1 Вкл/Выкл", "Датчик2 Вкл/Выкл"]
])";
void setup() {
Serial.begin(115200);
Serial.println("\nСистема аквариумного терморегулятора");
Serial.println("Доступные команды:");
Serial.println("SETTIME ГГГГ-ММ-ДД ЧЧ:ММ:СС - Установка времени");
Serial.println("GETTIME - Получить текущее время");
Serial.println("STATUS - Получить статус системы");
Serial.println("FAN ON/OFF - Ручное управление вентилятором");
Serial.println("SENSORS - Статус датчиков");
pinMode(FAN_PIN, OUTPUT);
pinMode(WATER_SENSOR_PIN, INPUT);
digitalWrite(FAN_PIN, LOW);
// Инициализация OLED
if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
Serial.println(F("OLED ошибка!"));
}
display.clearDisplay();
display.setTextSize(1);
display.setTextColor(WHITE);
display.setCursor(0,0);
display.println("Загрузка...");
display.display();
// Подключение Wi-Fi
WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
display.print("Подключение WiFi");
while (WiFi.status() != WL_CONNECTED) {
delay(500);
display.print(".");
display.display();
Serial.print(".");
}
display.println("\nПодключено!");
display.display();
Serial.println("\nWiFi подключен");
// Инициализация датчиков
innerSensor.begin();
externalSensor.begin();
Wire.begin();
if (!rtc.begin()) {
Serial.println("RTC не найден!");
display.println("RTC ошибка!");
display.display();
}
// Проверка датчиков температуры
if (!checkSensor(innerSensor)) {
Serial.println("Внутр. датчик не найден!");
innerSensorActive = false;
}
if (!checkSensor(externalSensor)) {
Serial.println("Внеш. датчик не найден!");
externalSensorActive = false;
}
// Настройка времени если RTC не инициализирован
if (rtc.lostPower()) {
Serial.println("Настройте время RTC!");
rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
}
// Настройка TLS для Telegram
client.setInsecure();
// Отправка приветственного сообщения
bot.sendMessage(CHAT_ID, "Аквариумный терморегулятор запущен!", "");
bot.sendMessageWithReplyKeyboard(CHAT_ID, "Выберите действие:", "", keyboardJson, true);
}
bool checkSensor(DallasTemperature &sensor) {
DeviceAddress addr;
return sensor.getDeviceCount() > 0 && sensor.getAddress(addr, 0);
}
void loop() {
// Обработка Telegram
if (millis() - lastBotUpdate > BOT_INTERVAL) {
int numNewMessages = bot.getUpdates(bot.last_message_received + 1);
handleTelegram(numNewMessages);
lastBotUpdate = millis();
}
// Чтение температуры
readTemperatures();
// Управление вентилятором
controlFan();
// Проверка тревог
checkAlarms();
// Сохранение температуры в историю
if (millis() - lastTempSend > TEMP_GRAPH_INTERVAL) {
saveTemperature();
sendTemperatureData();
lastTempSend = millis();
}
// Обновление дисплея
updateDisplay();
// Обработка команд Serial
handleSerialCommands();
}
void readTemperatures() {
if (innerSensorActive) {
innerSensor.requestTemperatures();
innerTemp = innerSensor.getTempCByIndex(0);
}
if (externalSensorActive) {
externalSensor.requestTemperatures();
externalTemp = externalSensor.getTempCByIndex(0);
}
delay(100);
}
void controlFan() {
if (manualFanControl) {
// В режиме ручного управления автоматика не работает
return;
}
if (!fanState && innerTemp >= FAN_ON_TEMP) {
fanState = true;
digitalWrite(FAN_PIN, HIGH);
bot.sendMessage(CHAT_ID, "Вентилятор ВКЛ: " + String(innerTemp, 1) + "°C", "");
Serial.println("Вентилятор автоматически включен");
}
else if (fanState && innerTemp <= FAN_OFF_TEMP) {
fanState = false;
digitalWrite(FAN_PIN, LOW);
bot.sendMessage(CHAT_ID, "Вентилятор ВЫКЛ: " + String(innerTemp, 1) + "°C", "");
Serial.println("Вентилятор автоматически выключен");
}
}
void checkAlarms() {
// Проверка высокой температуры
if (innerTemp >= HIGH_TEMP_ALARM && millis() - lastTempAlarm > ALARM_COOLDOWN) {
String msg = "?? ВНИМАНИЕ! Высокая температура: " + String(innerTemp,1) + "°C";
bot.sendMessage(CHAT_ID, msg, "");
Serial.println(msg);
lastTempAlarm = millis();
tempAlarm = true;
}
else {
tempAlarm = false;
}
// Проверка уровня воды
if (digitalRead(WATER_SENSOR_PIN) == HIGH) {
if (millis() - lastWaterAlarm > WATER_ALARM_COOLDOWN) {
String msg = "?? КРИТИЧЕСКО! Отсутствует вода!";
bot.sendMessage(CHAT_ID, msg, "");
Serial.println(msg);
lastWaterAlarm = millis();
waterAlarm = true;
}
}
else {
waterAlarm = false;
}
}
void saveTemperature() {
tempHistory[historyIndex] = innerTemp;
historyIndex = (historyIndex + 1) % HISTORY_SIZE;
}
void sendTemperatureData() {
String data = "?? Температура за последние " + String(HISTORY_SIZE) + " ч:\n";
DateTime now = rtc.now();
int currentHour = now.hour();
for (int i = 0; i < HISTORY_SIZE; i++) {
int idx = (historyIndex + i) % HISTORY_SIZE;
int hour = (currentHour - i + 24) % 24;
if (tempHistory[idx] != 0) {
data += String(hour) + ":00 - " + String(tempHistory[idx], 1) + "°C\n";
}
}
bot.sendMessage(CHAT_ID, data, "");
}
void updateDisplay() {
display.clearDisplay();
display.setCursor(0,0);
// Температуры
display.print("Внутр: ");
if (innerSensorActive) display.print(innerTemp,1);
else display.print("--");
display.println("C");
display.print("Внеш: ");
if (externalSensorActive) display.print(externalTemp,1);
else display.print("--");
display.println("C");
// Дата и время
DateTime now = rtc.now();
display.printf("%02d.%02d.%04d\n", now.day(), now.month(), now.year());
display.printf("%02d:%02d:%02d\n", now.hour(), now.minute(), now.second());
// Статус вентилятора
display.print(fanState ? "ВЕНТ ВКЛ" : "ВЕНТ ВЫКЛ");
if (manualFanControl) display.print(" (РУЧН)");
display.println();
// Тревоги
if (waterAlarm) display.println("! НЕТ ВОДЫ !");
if (tempAlarm) display.println("! ПЕРЕГРЕВ !");
display.display();
}
void handleTelegram(int numNewMessages) {
for (int i = 0; i < numNewMessages; i++) {
String chat_id = bot.messages[i].chat_id;
String text = bot.messages[i].text;
if (text == "/start" || text == "Меню") {
bot.sendMessageWithReplyKeyboard(chat_id, "Выберите действие:", "", keyboardJson, true);
}
else if (text == "Вкл вентилятор") {
digitalWrite(FAN_PIN, HIGH);
fanState = true;
manualFanControl = true;
bot.sendMessage(chat_id, "Вентилятор принудительно ВКЛ (автоматика отключена)", "");
Serial.println("Вентилятор включен через Telegram (ручное управление)");
}
else if (text == "Выкл вентилятор") {
digitalWrite(FAN_PIN, LOW);
fanState = false;
manualFanControl = false;
bot.sendMessage(chat_id, "Вентилятор принудительно ВЫКЛ (автоматика включена)", "");
Serial.println("Вентилятор выключен через Telegram (включена автоматика)");
}
else if (text == "Статус") {
String status = "?? Внутр. темп: " + String(innerTemp,1) + "°C\n";
status += "?? Внеш. темп: " + String(externalTemp,1) + "°C\n";
status += "?? Вентилятор: " + String(fanState ? "ВКЛ" : "ВЫКЛ");
status += manualFanControl ? " (РУЧНОЕ УПР)\n" : " (АВТОМАТИКА)\n";
status += "?? Вода: " + String(waterAlarm ? "ОТСУТСТВУЕТ! ??" : "норма ?");
bot.sendMessage(chat_id, status, "");
}
else if (text == "График") {
sendTemperatureData();
}
else if (text == "Датчик1 Вкл/Выкл") {
innerSensorActive = !innerSensorActive;
String msg = innerSensorActive ? "? Внутр. датчик ВКЛ" : "? Внутр. датчик ВЫКЛ";
bot.sendMessage(chat_id, msg, "");
Serial.println(msg);
}
else if (text == "Датчик2 Вкл/Выкл") {
externalSensorActive = !externalSensorActive;
String msg = externalSensorActive ? "? Внеш. датчик ВКЛ" : "? Внеш. датчик ВЫКЛ";
bot.sendMessage(chat_id, msg, "");
Serial.println(msg);
}
}
}
void handleSerialCommands() {
if (Serial.available()) {
String input = Serial.readStringUntil('\n');
input.trim();
if (input.startsWith("SETTIME")) {
// Формат: SETTIME ГГГГ-ММ-ДД ЧЧ:ММ:СС
if (input.length() >= 19) {
int y = input.substring(8,12).toInt();
int m = input.substring(13,15).toInt();
int d = input.substring(16,18).toInt();
int hh = input.substring(19,21).toInt();
int mm = input.substring(22,24).toInt();
int ss = input.substring(25,27).toInt();
rtc.adjust(DateTime(y, m, d, hh, mm, ss));
Serial.println("Время установлено!");
} else {
Serial.println("Ошибка формата! Используйте: SETTIME ГГГГ-ММ-ДД ЧЧ:ММ:СС");
}
}
else if (input == "GETTIME") {
DateTime now = rtc.now();
Serial.printf("Текущее время: %02d.%02d.%04d %02d:%02d:%02d\n",
now.day(), now.month(), now.year(),
now.hour(), now.minute(), now.second());
}
else if (input == "STATUS") {
Serial.println("\n=== СТАТУС СИСТЕМЫ ===");
Serial.printf("Внутр. температура: %.1f°C\n", innerTemp);
Serial.printf("Внеш. температура: %.1f°C\n", externalTemp);
Serial.printf("Вентилятор: %s (%s)\n",
fanState ? "ВКЛ" : "ВЫКЛ",
manualFanControl ? "РУЧНОЕ УПР" : "АВТОМАТИКА");
Serial.printf("Датчик воды: %s\n", digitalRead(WATER_SENSOR_PIN) ? "НЕТ ВОДЫ!" : "норма");
Serial.printf("Внутр. датчик: %s\n", innerSensorActive ? "работает" : "отключен");
Serial.printf("Внеш. датчик: %s\n", externalSensorActive ? "работает" : "отключен");
}
else if (input == "FAN ON") {
digitalWrite(FAN_PIN, HIGH);
fanState = true;
manualFanControl = true;
Serial.println("Вентилятор принудительно включен (автоматика отключена)");
}
else if (input == "FAN OFF") {
digitalWrite(FAN_PIN, LOW);
fanState = false;
manualFanControl = false;
Serial.println("Вентилятор принудительно выключен (автоматика включена)");
}
else if (input == "SENSORS") {
Serial.println("\n=== СОСТОЯНИЕ ДАТЧИКОВ ===");
Serial.printf("Внутр. датчик: %s (%.1f°C)\n",
innerSensorActive ? "активен" : "отключен", innerTemp);
Serial.printf("Внеш. датчик: %s (%.1f°C)\n",
externalSensorActive ? "активен" : "отключен", externalTemp);
Serial.printf("Датчик воды: %s\n",
digitalRead(WATER_SENSOR_PIN) ? "НЕТ ВОДЫ!" : "норма");
}
else if (input == "HELP") {
Serial.println("\nДоступные команды:");
Serial.println("SETTIME ГГГГ-ММ-ДД ЧЧ:ММ:СС - Установка времени");
Serial.println("GETTIME - Текущее время");
Serial.println("STATUS - Статус системы");
Serial.println("FAN ON/OFF - Ручное управление вентилятором");
Serial.println("SENSORS - Состояние датчиков");
Serial.println("HELP - Справка по командам");
}
else {
Serial.println("Неизвестная команда. Введите HELP для списка команд");
}
}
}
Это для этого. Работает ведь
Прикрепленные изображения