/*
* Toyota Cooling System Control
* Версия 4.5 - с полной поддержкой кондиционера в зимнем режиме
* и защитой от импульсной работы вентиляторов
*/
#include <Wire.h> // Библиотека для I2C-интерфейса
#include <Adafruit_GFX.h> // Графическая библиотека
#include <Adafruit_SSD1306.h> // Библиотека для OLED-дисплея
// Настройки дисплея
#define SCREEN_WIDTH 128 // Ширина OLED-дисплея в пикселях
#define SCREEN_HEIGHT 64 // Высота OLED-дисплея в пикселях
#define OLED_RESET -1 // Пин сброса (не используется)
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
// Иконки (8x8 пикселей)
static const unsigned char PROGMEM fan_icon[] = {
0x18, 0x3C, 0x5A, 0x7E, 0x7E, 0x5A, 0x3C, 0x18 // Графическое представление вентилятора
};
static const unsigned char PROGMEM snowflake_icon[] = {
0x21, 0x42, 0x84, 0x21, 0x84, 0x42, 0x21, 0x00 // Иконка снежинки для зимнего режима
};
static const unsigned char PROGMEM sun_icon[] = {
0x18, 0x5A, 0x3C, 0xE7, 0xE7, 0x3C, 0x5A, 0x18 // Иконка солнца для летнего режима
};
/*************************** ЛОГОТИП ********************************/
// Графический логотип (128x64 пикселя)
static const unsigned char PROGMEM logo[] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xFF, 0xFF, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xFD, 0xE7, 0x87, 0xFF, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0x03, 0xFC, 0xFF, 0x00, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x0F, 0x00, 0xFF, 0xF0, 0x7F, 0xFC, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xFC, 0x40, 0x09, 0xFF, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0x80, 0x00, 0x00, 0x03, 0xF8, 0x04, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x01, 0xF8, 0x01, 0x00, 0x00, 0x00, 0x7E, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x03, 0xC0, 0x01, 0x07, 0x80, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x07, 0x80, 0x02, 0x0F, 0xC1, 0x00, 0x03, 0x80, 0x10, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x01, 0x87, 0x00, 0x04, 0x18, 0x60, 0x00, 0x03, 0x86, 0x08, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x03, 0x87, 0x00, 0x04, 0x30, 0x20, 0x80, 0x03, 0x87, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x07, 0x83, 0x80, 0x04, 0x30, 0x00, 0xC0, 0x07, 0x07, 0x80, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x0F, 0x40, 0xE0, 0x0C, 0x60, 0x10, 0xC0, 0x1C, 0x0B, 0xC0, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x1C, 0x60, 0x08, 0x08, 0x60, 0x00, 0x40, 0x20, 0x19, 0xE0, 0x00, 0x00, 0x00,
0x00, 0x00, 0x08, 0x3C, 0x10, 0x01, 0xD8, 0x40, 0x08, 0x60, 0x00, 0x30, 0xF0, 0x40, 0x00, 0x00,
0x00, 0x00, 0x10, 0x70, 0x0C, 0x00, 0x08, 0xC0, 0x28, 0x40, 0x00, 0xE0, 0x38, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0xF0, 0x07, 0x80, 0x00, 0x00, 0x00, 0x00, 0x07, 0x80, 0x3C, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0xE0, 0x00, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x1C, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0xE0, 0x00, 0x3F, 0x80, 0x00, 0x00, 0x07, 0xE0, 0x00, 0x1C, 0x00, 0x00, 0x00,
0x00, 0x00, 0x01, 0xC0, 0x00, 0x03, 0xF0, 0xFF, 0xFC, 0x3F, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00,
0x00, 0x00, 0x01, 0xC0, 0x00, 0x00, 0x30, 0xFF, 0xFC, 0x30, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00,
0x00, 0x00, 0x01, 0xC0, 0x00, 0x00, 0x10, 0x80, 0x00, 0x20, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x10, 0x80, 0x00, 0x20, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00,
0x00, 0x00, 0x20, 0xC0, 0x00, 0x00, 0x18, 0x80, 0x00, 0x60, 0x00, 0x00, 0x0C, 0x10, 0x00, 0x00,
0x00, 0x00, 0x20, 0xC0, 0x00, 0x00, 0x18, 0xC0, 0x08, 0x60, 0x00, 0x00, 0x1C, 0x10, 0x00, 0x00,
0x00, 0x00, 0x1E, 0x20, 0x00, 0x00, 0x1F, 0xC0, 0x09, 0xE0, 0x00, 0x00, 0x11, 0xE0, 0x00, 0x00,
0x00, 0x00, 0x1F, 0x00, 0x00, 0x00, 0x1F, 0xC0, 0x0B, 0xE0, 0x00, 0x00, 0x03, 0xE0, 0x00, 0x00,
0x00, 0x00, 0x1F, 0x80, 0x00, 0x00, 0x0F, 0xC0, 0x17, 0xC0, 0x00, 0x00, 0x27, 0xC0, 0x00, 0x00,
0x00, 0x00, 0x0F, 0x88, 0x00, 0x00, 0x0F, 0xE0, 0x07, 0xC0, 0x00, 0x00, 0x4F, 0xC0, 0x00, 0x00,
0x00, 0x00, 0x07, 0xC4, 0x00, 0x00, 0x0F, 0xE0, 0x0F, 0xC0, 0x00, 0x01, 0x8F, 0x80, 0x00, 0x00,
0x00, 0x00, 0x03, 0xF0, 0x00, 0x00, 0x0F, 0xD0, 0x0F, 0xC0, 0x00, 0x03, 0x3F, 0x00, 0x00, 0x00,
0x00, 0x00, 0x01, 0xF8, 0x00, 0x00, 0x0F, 0xE8, 0x1F, 0xC0, 0x00, 0x04, 0x7E, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0xFC, 0x40, 0x00, 0x07, 0xE6, 0x9F, 0x80, 0x00, 0x00, 0xFC, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x3F, 0x10, 0x00, 0x07, 0xF8, 0x7F, 0x80, 0x00, 0x63, 0xF8, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x1F, 0xC0, 0x00, 0x03, 0xFF, 0xFF, 0x00, 0x00, 0x0F, 0xE0, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x07, 0xF0, 0x00, 0x03, 0xFF, 0xFE, 0x00, 0x04, 0x3F, 0x80, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x01, 0xFC, 0x38, 0x01, 0xFF, 0xFE, 0x00, 0xF0, 0xFE, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x7F, 0x87, 0xC0, 0xFF, 0xFC, 0x07, 0x87, 0xFC, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x1F, 0xF8, 0x7F, 0x7F, 0xFB, 0xF8, 0x7F, 0xE0, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x03, 0xFF, 0xC1, 0xBF, 0xF4, 0x0F, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x01, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x03, 0xFF, 0xF8, 0x7F, 0xE1, 0xF0, 0x1F, 0x0F, 0xF8, 0x3F, 0xFF, 0x83, 0xE0, 0x00, 0x00,
0x00, 0x03, 0xFF, 0xF8, 0xFF, 0xF0, 0xF8, 0x3E, 0x3F, 0xFE, 0x3F, 0xFF, 0x87, 0xF0, 0x00, 0x00,
0x00, 0x01, 0xFF, 0xF1, 0xF9, 0xFC, 0x78, 0x7C, 0x7F, 0x3F, 0x1F, 0xFF, 0x0F, 0xF0, 0x00, 0x00,
0x00, 0x00, 0x1F, 0x03, 0xE0, 0x7E, 0x7C, 0xF8, 0x7C, 0x0F, 0x80, 0xF0, 0x0F, 0xF8, 0x00, 0x00,
0x00, 0x00, 0x1F, 0x03, 0xC0, 0x3E, 0x3E, 0xF0, 0xF8, 0x07, 0x80, 0xF0, 0x0F, 0x78, 0x00, 0x00,
0x00, 0x00, 0x1F, 0x07, 0xC0, 0x1E, 0x1F, 0xF0, 0xF0, 0x07, 0xC0, 0xF0, 0x1F, 0x7C, 0x00, 0x00,
0x00, 0x00, 0x1E, 0x07, 0x80, 0x1E, 0x0F, 0xE1, 0xF0, 0x07, 0xC1, 0xF0, 0x3E, 0x3C, 0x00, 0x00,
0x00, 0x00, 0x1E, 0x07, 0xC0, 0x1E, 0x07, 0xC0, 0xF0, 0x07, 0xC1, 0xF0, 0x3E, 0x3E, 0x00, 0x00,
0x00, 0x00, 0x1E, 0x07, 0xC0, 0x3E, 0x07, 0x80, 0xF8, 0x07, 0x81, 0xF0, 0x7F, 0xFE, 0x00, 0x00,
0x00, 0x00, 0x1E, 0x03, 0xE0, 0x7C, 0x07, 0x80, 0x7C, 0x1F, 0x81, 0xF0, 0x7F, 0xFF, 0x00, 0x00,
0x00, 0x00, 0x1F, 0x01, 0xFF, 0xFC, 0x07, 0xC0, 0x7F, 0xFF, 0x00, 0xF0, 0xFC, 0x0F, 0x80, 0x00,
0x00, 0x00, 0x1F, 0x00, 0xFF, 0xF0, 0x07, 0xC0, 0x3F, 0xFE, 0x00, 0xF0, 0xF8, 0x0F, 0x80, 0x00,
0x00, 0x00, 0x1F, 0x00, 0x7F, 0xE0, 0x07, 0xC0, 0x0F, 0xF8, 0x00, 0xF1, 0xF0, 0x07, 0xC0, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
static const unsigned char PROGMEM logo2[] = {
0xFF, 0xFF, 0xFF, 0xFF, 0xF3, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xCF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x00, 0x3F, 0xFF, 0xFF, 0xFC, 0x00, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x00, 0x07, 0xFF, 0xFF, 0xE0, 0x00, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x00, 0x03, 0xFF, 0xFF, 0xC0, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x00, 0x03, 0xFF, 0xFF, 0xC0, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x00, 0x01, 0xFF, 0xFF, 0x80, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x01, 0xFF, 0xFF, 0x80, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x01, 0xFF, 0xFF, 0x80, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x01, 0xFF, 0xFF, 0x80, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x01, 0xFF, 0xFF, 0x80, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0x01, 0xFF, 0xFF, 0x80, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0x01, 0xFF, 0xFF, 0x80, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0x01, 0xFF, 0xFF, 0x80, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0, 0x00, 0xFF, 0xFF, 0x00, 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0, 0x00, 0xFF, 0xFF, 0x00, 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x00, 0xFF, 0xFF, 0x00, 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x00, 0xFF, 0xFF, 0x00, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x00, 0xFF, 0xFF, 0x00, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x00, 0x7F, 0xFE, 0x00, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x00, 0x7F, 0xFE, 0x00, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x00, 0x7F, 0xFE, 0x00, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x00, 0x7F, 0xFE, 0x00, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x00, 0x7F, 0xFE, 0x00, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x00, 0x7F, 0xFE, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x00, 0x7F, 0xFE, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x00, 0x7F, 0xFE, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x3F, 0xFC, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x3F, 0xFC, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x3F, 0xFC, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x3F, 0xFC, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x3F, 0xFC, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0x3F, 0xFC, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0x1F, 0xF8, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0, 0x1F, 0xF8, 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0, 0x1F, 0xF8, 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0, 0x1F, 0xF8, 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x1F, 0xF8, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x0F, 0xF0, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x0F, 0xF0, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x0F, 0xF0, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x0F, 0xF0, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x00, 0x00, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x00, 0x00, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x00, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x8E, 0x00, 0x00, 0x71, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x07, 0x00, 0x00, 0xE0, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x07, 0xFF, 0xFF, 0xE0, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0, 0x00, 0xFF, 0xFF, 0x00, 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x01, 0xFF, 0xFF, 0x80, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x00, 0x01, 0xFF, 0xFF, 0x80, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xF8, 0x00, 0x03, 0xFF, 0xFF, 0xC0, 0x00, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xE0, 0x00, 0x07, 0xFF, 0xFF, 0xE0, 0x00, 0x07, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0x00, 0x0F, 0xFF, 0xFF, 0xF0, 0x00, 0x03, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x1F, 0xFF, 0xFF, 0xF8, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFE, 0x00, 0x00, 0x1F, 0xFF, 0xFF, 0xF8, 0x00, 0x00, 0x7F, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFC, 0x00, 0x00, 0x3F, 0xFF, 0xFF, 0xFC, 0x00, 0x00, 0x1F, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xF0, 0x00, 0x00, 0x7F, 0xFF, 0xFF, 0xFE, 0x00, 0x00, 0x0F, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0, 0xFF, 0xFF, 0xFF, 0xFF, 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF1, 0xFF, 0xFF, 0xFF, 0xFF, 0x8F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFB, 0xFF, 0xFF, 0xFF, 0xFF, 0xDF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
};
// Параметры термистора
const int SAMPLE_NUMBER = 10; // Количество измерений для усреднения
const double BALANCE_RESISTOR = 9710.0; // Сопротивление балансного резистора (Ом)
const double MAX_ADC = 1023.0; // Максимальное значение АЦП
const double BETA = 3974.0; // Коэффициент Бета термистора
const double ROOM_TEMP = 303.15; // Комнатная температура в Кельвинах (30°C)
const double RESISTOR_ROOM_TEMP = 2238.0; // Сопротивление термистора при комнатной температуре
// Пороги температур (в градусах Цельсия)
// Летний режим
const int SUMMER_FAN1_ON = 83; // Включение 1-го вентилятора летом
const int SUMMER_FAN2_ON = 87; // Включение 2-го вентилятора летом
const int SUMMER_FAN_OFF = 80; // Выключение вентиляторов летом
// Зимний режим
const int WINTER_FAN1_ON = 90; // Включение 1-го вентилятора зимой
const int WINTER_FAN2_ON = 94; // Включение 2-го вентилятора зимой
const int WINTER_FAN_OFF = 85; // Выключение вентиляторов зимой
const int BREAK_SENS = -50; // Температура при ошибке датчика
const int CONDITIONER_THRESHOLD = 200; // Порог значения кондиционера (меньше = включен)
// Пины подключения
const int THERMISTOR_PIN = A0; // Пин термистора
const int CONDITIONER_PIN = A1; // Пин датчика кондиционера
const int BUTTON_PIN = A3; // Пин кнопки переключения режима
const int RELAY_1 = 11; // Пин реле 1-го вентилятора
const int RELAY_2 = 12; // Пин реле 2-го вентилятора
// Переменные системы
double currentTemperature = 0; // Текущая температура
double conditionerValue = 0; // Значение датчика кондиционера
bool highTempMode = false; // Флаг высокотемпературного режима
bool winterMode = false; // Флаг зимнего режима
bool buttonPressed = false; // Флаг нажатия кнопки
unsigned long lastDebounceTime = 0; // Время последнего нажатия кнопки
const unsigned long debounceDelay = 50; // Задержка антидребезга (мс)
/**
* Отображение графического логотипа при запуске
*/
void showLogo() {
display.clearDisplay();
// Центрирование логотипа на экране
display.drawBitmap(
(SCREEN_WIDTH - 128) / 2,
(SCREEN_HEIGHT - 64) / 2,
logo,
128, 64,
SSD1306_WHITE
);
display.display();
delay(2000); // Показ логотипа 2 секунды
}
void showLogo2() {
display.clearDisplay();
// Центрирование логотипа на экране
display.drawBitmap(
(SCREEN_WIDTH - 128) / 2,
(SCREEN_HEIGHT - 64) / 2,
logo2,
128, 64,
SSD1306_WHITE
);
display.display();
delay(2000); // Показ логотипа 2 секунды
}
void setup() {
Serial.begin(9600); // Инициализация последовательного порта
// Инициализация OLED-дисплея
if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
Serial.println(F("Ошибка инициализации дисплея"));
for(;;); // Бесконечный цикл при ошибке
}
// Настройка пинов
pinMode(RELAY_1, OUTPUT);
pinMode(RELAY_2, OUTPUT);
pinMode(THERMISTOR_PIN, INPUT);
pinMode(CONDITIONER_PIN, INPUT);
pinMode(BUTTON_PIN, INPUT);
// Инициализация реле в выключенном состоянии
digitalWrite(RELAY_1, LOW);
digitalWrite(RELAY_2, LOW);
// Показ заставки при запуске
// Показ логотипов при запуске
showLogo(); // Графический логотип
showLogo2(); // Графический логотип
delay(1500); // Задержка показа заставки
Serial.println(F("Система инициализирована"));
Serial.println(F("Режим по умолчанию: ЛЕТО"));
}
void loop() {
// 1. Чтение данных с датчиков
currentTemperature = readThermistor();
conditionerValue = analogRead(CONDITIONER_PIN);
// 2. Логирование данных в Serial
Serial.print(F("Температура: "));
Serial.print(currentTemperature, 1);
Serial.print(F("°C | Кондиционер: "));
Serial.print(conditionerValue);
Serial.print(F(" ("));
Serial.print(conditionerValue < CONDITIONER_THRESHOLD ? F("ВКЛ") : F("ВЫКЛ"));
Serial.print(F(") | Режим: "));
Serial.println(winterMode ? F("ЗИМА") : F("ЛЕТО"));
// 3. Проверка кнопки переключения режима
checkWinterModeButton();
// 4. Управление вентиляторами
controlFans();
// 5. Обновление информации на дисплее
updateDisplay();
delay(100); // Задержка между циклами (100 мс)
}
/**
* Чтение температуры с термистора
* Возвращает температуру в градусах Цельсия
* При ошибке возвращает значение ниже BREAK_SENS
*/
double readThermistor() {
double average = 0;
// Усреднение нескольких измерений для стабильности
for (int i = 0; i < SAMPLE_NUMBER; i++) {
average += analogRead(THERMISTOR_PIN);
delay(10);
}
average /= SAMPLE_NUMBER;
// Проверка на корректность показаний
if (average <= 0 || average >= 1023) {
Serial.println(F("ОШИБКА ДАТЧИКА ТЕМПЕРАТУРЫ!"));
return BREAK_SENS - 1; // Возврат значения ошибки
}
// Расчет сопротивления термистора
double resistance = BALANCE_RESISTOR * (MAX_ADC / average - 1);
// Расчет температуры по уравнению Стейнхарта-Харта
double steinhart = log(resistance / RESISTOR_ROOM_TEMP) / BETA;
steinhart += 1.0 / ROOM_TEMP;
steinhart = 1.0 / steinhart;
// Конвертация из Кельвинов в Цельсии
return steinhart - 273.15;
}
/**
* Показ информации о текущем режиме и порогах срабатывания
* при переключении между зимним и летним режимами
*/
void showModeSwitchInfo() {
display.clearDisplay();
display.setTextSize(1);
display.setTextColor(SSD1306_WHITE);
// Заголовок с названием режима
display.setCursor(15, 5);
display.print(winterMode ? F("WINTER MODE") : F("SUMMER MODE"));
// Температуры срабатывания для 1-го вентилятора
display.setCursor(2, 20);
display.print(F("FAN1 ON:"));
display.print(winterMode ? WINTER_FAN1_ON : SUMMER_FAN1_ON);
display.print(F("C OFF:"));
display.print(winterMode ? WINTER_FAN_OFF : SUMMER_FAN_OFF);
display.print(F("C"));
// Температуры срабатывания для 2-го вентилятора
display.setCursor(2, 35);
display.print(F("FAN2 ON:"));
display.print(winterMode ? WINTER_FAN2_ON : SUMMER_FAN2_ON);
display.print(F("C OFF:"));
display.print(winterMode ? WINTER_FAN_OFF : SUMMER_FAN_OFF);
display.print(F("C"));
// Подсказка для пользователя
display.setCursor(2, 50);
display.print(F("Mode is activated"));
display.display();
delay(2000); // Показываем 2 секунды
Serial.print(F("Режим изменен на: "));
Serial.println(winterMode ? F("ЗИМА") : F("ЛЕТО"));
}
/**
* Обновление информации на дисплее
*/
void updateDisplay() {
static bool firstRun = true;
if(firstRun) {
showModeSwitchInfo();
firstRun = false;
return;
}
display.clearDisplay();
// 1. Верхняя строка - режим и иконки вентиляторов
display.drawRect(0, 0, SCREEN_WIDTH, 16, SSD1306_WHITE);
// Иконка текущего режима
display.drawBitmap(5, 4, winterMode ? snowflake_icon : sun_icon, 8, 8, SSD1306_WHITE);
// Надпись режима
display.setTextSize(1);
display.setCursor(20, 5);
display.print(winterMode ? F("WINTER") : F("SUMMER"));
// Иконки вентиляторов с номерами
display.drawBitmap(90, 4, fan_icon, 8, 8, digitalRead(RELAY_1) ? SSD1306_WHITE : SSD1306_BLACK);
display.setCursor(100, 5);
display.print(F("1"));
display.drawBitmap(110, 4, fan_icon, 8, 8, digitalRead(RELAY_2) ? SSD1306_WHITE : SSD1306_BLACK);
display.setCursor(120, 5);
display.print(F("2"));
// 2. Центральный блок - температура
display.setTextSize(3);
display.setCursor(18, 20);
if(currentTemperature < BREAK_SENS) {
display.print(F("ERROR"));
} else {
display.print(round(currentTemperature)); // Округление до целого
display.print(F("c"));
// Символ градуса
// display.fillRect(60, 25, 3, 3, SSD1306_WHITE);
// display.setCursor(65, 20);
// display.print(F("C"));
}
// 3. Нижний блок - шкала температуры (0-120°C)
const int segmentWidth = 6; // Ширина сегмента
const int segmentHeight = 10; // Высота сегмента
const int segmentSpacing = 2; // Промежуток между сегментами
const int startX = 16; // Начальная позиция по X
const int startY = 45; // Начальная позиция по Y
const int totalSegments = 12; // Всего сегментов
const int maxScaleTemp = 120; // Максимальная температура шкалы
// Расчет активных сегментов
int activeSegments = currentTemperature < 0 ? 0 :
min(map(round(currentTemperature), 0, maxScaleTemp, 0, totalSegments),
totalSegments);
// Отрисовка шкалы
for(int i = 0; i < totalSegments; i++) {
int x = startX + i * (segmentWidth + segmentSpacing);
// Определение зон шкалы
bool isNormalZone = (i < 8); // 0-80°C: нормальная зона
bool isWarningZone = (i >= 8 && i < 10); // 80-100°C: предупреждение
bool isCriticalZone = (i >= 10); // 100-120°C: критическая зона
// Отрисовка сегмента
if(i < activeSegments) {
if(isCriticalZone) {
// Критическая зона - мигание
if(millis() % 600 < 300) display.fillRect(x, startY, segmentWidth, segmentHeight, SSD1306_WHITE);
}
else if(isWarningZone) {
// Предупреждающая зона - постоянное свечение
display.fillRect(x, startY, segmentWidth, segmentHeight, SSD1306_WHITE);
}
else {
// Нормальная зона
display.fillRect(x, startY, segmentWidth, segmentHeight, SSD1306_WHITE);
}
}
else {
// Неактивные сегменты
display.drawRect(x, startY, segmentWidth, segmentHeight, SSD1306_WHITE);
}
}
// Метки шкалы (0, 60, 120°C)
display.setTextSize(1);
display.setCursor(startX-3, startY+segmentHeight+2);
display.print(F("0"));
display.setCursor(startX + 5*(segmentWidth+segmentSpacing)-5, startY+segmentHeight+2);
display.print(F("60"));
display.setCursor(startX + 11*(segmentWidth+segmentSpacing)-8, startY+segmentHeight+2);
display.print(F("120"));
display.display();
}
/**
* Проверка состояния кнопки переключения режима
* с защитой от дребезга
*/
void checkWinterModeButton() {
int buttonValue = analogRead(BUTTON_PIN);
bool currentState = buttonValue < 500 ? LOW : HIGH;
// Обработка нажатия с антидребезгом
if (currentState == LOW && !buttonPressed) {
if (millis() - lastDebounceTime > debounceDelay) {
winterMode = !winterMode; // Переключение режима
buttonPressed = true;
lastDebounceTime = millis();
// Анимация переключения (3 мигания)
for(int i=0; i<3; i++) {
display.invertDisplay(true);
delay(150);
display.invertDisplay(false);
delay(150);
}
// Показ информации о новом режиме
showModeSwitchInfo();
}
}
// Сброс флага нажатия при отпускании кнопки
if (currentState == HIGH) {
buttonPressed = false;
}
}
/**
* Управление вентиляторами в зависимости от режима и температуры
* с защитой от частых переключений и поддержкой кондиционера
*/
void controlFans() {
static unsigned long lastFanSwitchTime = 0;
const unsigned long minFanOnTime = 30000; // Минимальное время работы вентилятора (30 сек)
bool acActive = conditionerValue < CONDITIONER_THRESHOLD;
unsigned long currentTime = millis();
if (currentTemperature < BREAK_SENS) {
// Аварийный режим - включаем оба вентилятора при ошибке датчика
digitalWrite(RELAY_1, HIGH);
digitalWrite(RELAY_2, HIGH);
highTempMode = false;
Serial.println(F("АВАРИЙНЫЙ РЕЖИМ: Оба вентилятора включены"));
return;
}
if (winterMode) {
// Зимний режим работы с учетом кондиционера
if (currentTemperature > WINTER_FAN2_ON) {
// Высокая температура - включаем оба вентилятора
digitalWrite(RELAY_1, HIGH);
digitalWrite(RELAY_2, HIGH);
highTempMode = true;
lastFanSwitchTime = currentTime;
Serial.println(F("ЗИМА: Высокая температура - Оба вентилятора включены"));
}
else if (currentTemperature > WINTER_FAN1_ON) {
// Средняя температура - включаем 1-й вентилятор
digitalWrite(RELAY_1, HIGH);
// Вентилятор 2 управляется кондиционером с защитой от частых переключений
if (currentTime - lastFanSwitchTime > minFanOnTime) {
digitalWrite(RELAY_2, acActive ? HIGH : LOW);
if (digitalRead(RELAY_2) != (acActive ? HIGH : LOW)) {
lastFanSwitchTime = currentTime;
}
}
highTempMode = false;
Serial.print(F("ЗИМА: Средняя температура - Вентилятор 1 включен, Вентилятор 2 "));
Serial.println(acActive ? F("включен (A/C)") : F("выключен"));
}
else {
// Низкая температура - выключаем 1-й вентилятор, 2-й - по кондиционеру
digitalWrite(RELAY_1, LOW);
// Вентилятор 2 управляется кондиционером с защитой от частых переключений
if (currentTime - lastFanSwitchTime > minFanOnTime) {
digitalWrite(RELAY_2, acActive ? HIGH : LOW);
if (digitalRead(RELAY_2) != (acActive ? HIGH : LOW)) {
lastFanSwitchTime = currentTime;
}
}
highTempMode = false;
Serial.print(F("ЗИМА: Низкая температура - Вентилятор 1 выключен, Вентилятор 2 "));
Serial.println(acActive ? F("включен (A/C)") : F("выключен"));
}
} else {
// Летний режим работы
if (currentTemperature > SUMMER_FAN2_ON) {
digitalWrite(RELAY_1, HIGH);
digitalWrite(RELAY_2, acActive ? HIGH : LOW);
highTempMode = true;
lastFanSwitchTime = currentTime;
Serial.println(F("ЛЕТО: Высокая температура - Оба вентилятора включены"));
}
else if (currentTemperature > SUMMER_FAN1_ON) {
digitalWrite(RELAY_1, HIGH);
digitalWrite(RELAY_2, acActive ? HIGH : LOW);
highTempMode = false;
Serial.print(F("ЛЕТО: Средняя температура - Вентилятор 1 включен, Вентилятор 2 "));
Serial.println(acActive ? F("включен (A/C)") : F("выключен"));
}
else {
digitalWrite(RELAY_1, LOW);
digitalWrite(RELAY_2, acActive ? HIGH : LOW);
highTempMode = false;
Serial.print(F("ЛЕТО: Низкая температура - Вентилятор 1 выключен, Вентилятор 2 "));
Serial.println(acActive ? F("включен (A/C)") : F("выключен"));
}
}
}