#include <Wire.h>
#include <LiquidCrystal_I2C.h>
// LCD sozlamalari (0x27 yoki 0x3F bo'lishi mumkin)
LiquidCrystal_I2C lcd(0x27, 16, 2);
// Pin sozlamalari
#define ENCODER_CLK 2
#define ENCODER_DT 3
#define ENCODER_SW 4
#define RELAY1_PIN 8
#define RELAY2_PIN 9
// O'zgaruvchilar
int lastCLK = HIGH;
bool buttonPressed = false;
unsigned long lastButtonTime = 0;
const unsigned long debounceDelay = 50;
unsigned long buttonPressStart = 0;
const unsigned long longPressTime = 800;
// LCD yangilanish nazorati
unsigned long lastLCDUpdate = 0;
const unsigned long lcdUpdateInterval = 200;
String lastLine0 = "";
String lastLine1 = "";
// Rele 1 taymer o'zgaruvchilari
int relay1Hours = 0;
int relay1Minutes = 0;
int relay1Seconds = 0;
unsigned long relay1Duration = 0;
unsigned long relay1Start = 0;
bool relay1Running = false;
bool relay1Active = false;
// Rele 2 taymer o'zgaruvchilari
int relay2Hours = 0;
int relay2Minutes = 0;
int relay2Seconds = 0;
unsigned long relay2Duration = 0;
unsigned long relay2Start = 0;
bool relay2Running = false;
bool relay2Active = false;
// Scrolling text
int scrollPosition = 0;
unsigned long lastScrollTime = 0;
const unsigned long scrollInterval = 300;
String aboutText = "Bu qurilma ikki kanalli taymerli rele tizimi bo'lib, har bir releni mustaqil boshqarish imkonini beradi. Qurilma Arduino Uno va LCD1602 ekrandan foydalanadi. Rotary encoder yordamida vaqtni sozlash va boshqarish amalga oshiriladi. Har bir rele uchun 0 dan 99 daqiqagacha vaqt belgilash mumkin. Dasturchi: Ravshanov M.I. Tizim yuqori aniqlik va ishonchlilikka ega.";
// Maxsus belgilar
byte progressEmpty[8] = {
0b00000,
0b00000,
0b00000,
0b00000,
0b00000,
0b00000,
0b00000,
0b00000
};
byte progressFull[8] = {
0b11111,
0b11111,
0b11111,
0b11111,
0b11111,
0b11111,
0b11111,
0b11111
};
byte progressHalf[8] = {
0b11111,
0b11111,
0b11111,
0b11111,
0b11111,
0b11111,
0b11111,
0b11111
};
byte clockIcon[8] = {
0b00000,
0b01110,
0b10101,
0b10111,
0b10001,
0b01110,
0b00000,
0b00000
};
byte powerIcon[8] = {
0b10000,
0b11000,
0b11100,
0b11110,
0b11100,
0b11000,
0b10000,
0b00000
};
byte stopIcon[8] = {
0b00000,
0b11111,
0b11111,
0b11111,
0b11111,
0b11111,
0b00000,
0b00000
};
enum MenuState {
MENU_MAIN,
MENU_SET_RELAY1,
MENU_SET_RELAY2,
MENU_ABOUT,
MENU_RUNNING
};
MenuState currentState = MENU_MAIN;
int menuSelection = 0; // 0-3: menyu tanlovlari
void setup() {
// LCD boshlash
lcd.init();
lcd.backlight();
// Maxsus belgilar yaratish
lcd.createChar(0, progressEmpty); // Bo'sh progress
lcd.createChar(1, progressFull); // To'liq progress
lcd.createChar(2, progressHalf); // Yarim progress
lcd.createChar(3, clockIcon); // Soat belgisi
lcd.createChar(4, powerIcon); // Quvvat belgisi
lcd.createChar(5, stopIcon); // To'xtash belgisi
// Pin rejimlarini sozlash
pinMode(ENCODER_CLK, INPUT_PULLUP);
pinMode(ENCODER_DT, INPUT_PULLUP);
pinMode(ENCODER_SW, INPUT_PULLUP);
pinMode(RELAY1_PIN, OUTPUT);
pinMode(RELAY2_PIN, OUTPUT);
// Relalarni o'chirish
digitalWrite(RELAY1_PIN, LOW);
digitalWrite(RELAY2_PIN, LOW);
// Boshlang'ich ekran
displayWelcome();
delay(1000);
displayMainMenu();
}
void loop() {
readEncoder();
checkButton();
unsigned long currentMillis = millis();
switch(currentState) {
case MENU_MAIN:
if (currentMillis - lastLCDUpdate >= lcdUpdateInterval) {
updateMainMenu();
lastLCDUpdate = currentMillis;
}
break;
case MENU_SET_RELAY1:
if (currentMillis - lastLCDUpdate >= lcdUpdateInterval) {
updateSetRelay1();
lastLCDUpdate = currentMillis;
}
break;
case MENU_SET_RELAY2:
if (currentMillis - lastLCDUpdate >= lcdUpdateInterval) {
updateSetRelay2();
lastLCDUpdate = currentMillis;
}
break;
case MENU_ABOUT:
if (currentMillis - lastScrollTime >= scrollInterval) {
updateAboutScreen();
lastScrollTime = currentMillis;
}
break;
case MENU_RUNNING:
if (currentMillis - lastLCDUpdate >= lcdUpdateInterval) {
updateRunningDisplay();
lastLCDUpdate = currentMillis;
}
checkTimers();
break;
}
}
void displayWelcome() {
lcd.clear();
lcd.setCursor(1, 0);
lcd.print("VAQT RELELARI");
lcd.setCursor(0, 1);
lcd.print("by Ravshanov M.I");
delay(3000);
}
void displayMainMenu() {
lcd.clear();
lastLine0 = "";
lastLine1 = "";
}
void updateMainMenu() {
String line0 = "";
String line1 = "";
if (menuSelection == 0) {
line0 = ">R1:";
if (relay1Hours < 10) line0 += "0";
line0 += String(relay1Hours) + ":";
if (relay1Minutes < 10) line0 += "0";
line0 += String(relay1Minutes) + ":";
if (relay1Seconds < 10) line0 += "0";
line0 += String(relay1Seconds);
line1 = " R2:";
if (relay2Hours < 10) line1 += "0";
line1 += String(relay2Hours) + ":";
if (relay2Minutes < 10) line1 += "0";
line1 += String(relay2Minutes) + ":";
if (relay2Seconds < 10) line1 += "0";
line1 += String(relay2Seconds);
}
else if (menuSelection == 1) {
line0 = " R1:";
if (relay1Hours < 10) line0 += "0";
line0 += String(relay1Hours) + ":";
if (relay1Minutes < 10) line0 += "0";
line0 += String(relay1Minutes) + ":";
if (relay1Seconds < 10) line0 += "0";
line0 += String(relay1Seconds);
line1 = ">R2:";
if (relay2Hours < 10) line1 += "0";
line1 += String(relay2Hours) + ":";
if (relay2Minutes < 10) line1 += "0";
line1 += String(relay2Minutes) + ":";
if (relay2Seconds < 10) line1 += "0";
line1 += String(relay2Seconds);
}
else if (menuSelection == 2) {
line0 = ">START TIMER";
line1 = " Boshlash";
}
else if (menuSelection == 3) {
line0 = ">TIZIM HAQIDA";
line1 = " Ma'lumot";
}
updateLCDLine(0, line0);
updateLCDLine(1, line1);
}
void updateSetRelay1() {
String line0 = "RELE 1 Vaqt:";
String line1 = " ";
if (relay1Hours < 10) line1 += "0";
line1 += String(relay1Hours) + ":";
if (relay1Minutes < 10) line1 += "0";
line1 += String(relay1Minutes) + ":";
if (relay1Seconds < 10) line1 += "0";
line1 += String(relay1Seconds);
updateLCDLine(0, line0);
updateLCDLine(1, line1);
}
void updateSetRelay2() {
String line0 = "RELE 2 Vaqt:";
String line1 = " ";
if (relay2Hours < 10) line1 += "0";
line1 += String(relay2Hours) + ":";
if (relay2Minutes < 10) line1 += "0";
line1 += String(relay2Minutes) + ":";
if (relay2Seconds < 10) line1 += "0";
line1 += String(relay2Seconds);
updateLCDLine(0, line0);
updateLCDLine(1, line1);
}
void updateAboutScreen() {
int textLen = aboutText.length();
int displayWidth = 16;
// Faqat birinchi qatorda scrolling text
lcd.setCursor(0, 0);
for (int i = 0; i < displayWidth; i++) {
int charIndex = (scrollPosition + i) % textLen;
lcd.print(aboutText[charIndex]);
}
// Ikkinchi qator bo'sh yoki statik ma'lumot
if (lastLine1 != " ") {
lcd.setCursor(0, 1);
lcd.print(" ");
lastLine1 = " ";
}
scrollPosition++;
if (scrollPosition >= textLen) {
scrollPosition = 0;
}
}
void updateRunningDisplay() {
String line0 = "";
String line1 = "";
// Rele 1 holati
if (relay1Running) {
unsigned long elapsed = millis() - relay1Start;
unsigned long remaining = relay1Duration > elapsed ? relay1Duration - elapsed : 0;
int hours = remaining / 3600000;
int mins = (remaining % 3600000) / 60000;
int secs = (remaining % 60000) / 1000;
// Qaysi formatda ko'rsatish
line0 = "";
line0 += (char)4; // Power icon
line0 += "1:";
if (relay1Hours > 0) {
// Soat formatda
if (hours < 10) line0 += "0";
line0 += String(hours) + ":";
if (mins < 10) line0 += "0";
line0 += String(mins);
} else if (relay1Minutes > 0) {
// Daqiqa formatda
if (mins < 10) line0 += "0";
line0 += String(mins) + ":";
if (secs < 10) line0 += "0";
line0 += String(secs);
} else {
// Soniya formatda
if (secs < 10) line0 += "0";
line0 += String(secs) + "s";
}
// Progress bar (5 blok)
int progress = map(elapsed, 0, relay1Duration, 0, 5);
line0 += " ";
for(int i = 0; i < 5; i++) {
if (i < progress) line0 += (char)1; // Full
else line0 += (char)0; // Empty
}
} else {
line0 = "";
line0 += (char)5; // Stop icon
line0 += "1:--:-- -----";
}
// Rele 2 holati
if (relay2Running) {
unsigned long elapsed = millis() - relay2Start;
unsigned long remaining = relay2Duration > elapsed ? relay2Duration - elapsed : 0;
int hours = remaining / 3600000;
int mins = (remaining % 3600000) / 60000;
int secs = (remaining % 60000) / 1000;
// Qaysi formatda ko'rsatish
line1 = "";
line1 += (char)4; // Power icon
line1 += "2:";
if (relay2Hours > 0) {
// Soat formatda
if (hours < 10) line1 += "0";
line1 += String(hours) + ":";
if (mins < 10) line1 += "0";
line1 += String(mins);
} else if (relay2Minutes > 0) {
// Daqiqa formatda
if (mins < 10) line1 += "0";
line1 += String(mins) + ":";
if (secs < 10) line1 += "0";
line1 += String(secs);
} else {
// Soniya formatda
if (secs < 10) line1 += "0";
line1 += String(secs) + "s";
}
// Progress bar (5 blok)
int progress = map(elapsed, 0, relay2Duration, 0, 5);
line1 += " ";
for(int i = 0; i < 5; i++) {
if (i < progress) line1 += (char)1; // Full
else line1 += (char)0; // Empty
}
} else {
line1 = "";
line1 += (char)5; // Stop icon
line1 += "2:--:-- -----";
}
updateLCDLine(0, line0);
updateLCDLine(1, line1);
}
void updateLCDLine(int line, String text) {
String* lastLine = (line == 0) ? &lastLine0 : &lastLine1;
if (text != *lastLine) {
lcd.setCursor(0, line);
lcd.print(" ");
lcd.setCursor(0, line);
lcd.print(text);
*lastLine = text;
}
}
void readEncoder() {
int currentCLK = digitalRead(ENCODER_CLK);
if (currentCLK != lastCLK && currentCLK == LOW) {
int dtValue = digitalRead(ENCODER_DT);
if (currentState == MENU_MAIN) {
if (dtValue == LOW) {
menuSelection++;
if (menuSelection > 3) menuSelection = 0;
} else {
menuSelection--;
if (menuSelection < 0) menuSelection = 3;
}
lastLCDUpdate = 0;
}
else if (currentState == MENU_SET_RELAY1) {
if (dtValue == LOW) {
relay1Seconds += 1;
if (relay1Seconds >= 60) {
relay1Seconds = 0;
relay1Minutes++;
if (relay1Minutes >= 60) {
relay1Minutes = 0;
relay1Hours++;
if (relay1Hours >= 24) relay1Hours = 23;
}
}
} else {
relay1Seconds -= 1;
if (relay1Seconds < 0) {
relay1Seconds = 59;
relay1Minutes--;
if (relay1Minutes < 0) {
relay1Minutes = 59;
relay1Hours--;
if (relay1Hours < 0) {
relay1Hours = 0;
relay1Minutes = 0;
relay1Seconds = 0;
}
}
}
}
lastLCDUpdate = 0;
}
else if (currentState == MENU_SET_RELAY2) {
if (dtValue == LOW) {
relay2Seconds += 1;
if (relay2Seconds >= 60) {
relay2Seconds = 0;
relay2Minutes++;
if (relay2Minutes >= 60) {
relay2Minutes = 0;
relay2Hours++;
if (relay2Hours >= 24) relay2Hours = 23;
}
}
} else {
relay2Seconds -= 1;
if (relay2Seconds < 0) {
relay2Seconds = 59;
relay2Minutes--;
if (relay2Minutes < 0) {
relay2Minutes = 59;
relay2Hours--;
if (relay2Hours < 0) {
relay2Hours = 0;
relay2Minutes = 0;
relay2Seconds = 0;
}
}
}
}
lastLCDUpdate = 0;
}
}
lastCLK = currentCLK;
}
void checkButton() {
int buttonState = digitalRead(ENCODER_SW);
unsigned long currentTime = millis();
if (buttonState == LOW) {
if (!buttonPressed) {
buttonPressed = true;
buttonPressStart = currentTime;
lastButtonTime = currentTime;
} else {
if ((currentTime - buttonPressStart) >= longPressTime) {
handleLongPress();
buttonPressed = false;
lastButtonTime = currentTime;
}
}
} else {
if (buttonPressed && (currentTime - buttonPressStart) < longPressTime) {
if ((currentTime - lastButtonTime) > debounceDelay) {
handleButtonPress();
}
}
buttonPressed = false;
}
}
void handleButtonPress() {
switch(currentState) {
case MENU_MAIN:
if (menuSelection == 0) {
currentState = MENU_SET_RELAY1;
lcd.clear();
lastLine0 = "";
lastLine1 = "";
} else if (menuSelection == 1) {
currentState = MENU_SET_RELAY2;
lcd.clear();
lastLine0 = "";
lastLine1 = "";
} else if (menuSelection == 2) {
startTimers();
} else if (menuSelection == 3) {
currentState = MENU_ABOUT;
scrollPosition = 0;
lcd.clear();
}
break;
case MENU_SET_RELAY1:
case MENU_SET_RELAY2:
case MENU_ABOUT:
currentState = MENU_MAIN;
lcd.clear();
lastLine0 = "";
lastLine1 = "";
displayMainMenu();
break;
case MENU_RUNNING:
break;
}
}
void handleLongPress() {
switch(currentState) {
case MENU_SET_RELAY1:
case MENU_SET_RELAY2:
case MENU_ABOUT:
currentState = MENU_MAIN;
lcd.clear();
lastLine0 = "";
lastLine1 = "";
displayMainMenu();
break;
case MENU_RUNNING:
stopAllTimers();
break;
case MENU_MAIN:
break;
}
}
void startTimers() {
relay1Duration = (relay1Hours * 3600000UL) + (relay1Minutes * 60000UL) + (relay1Seconds * 1000UL);
if (relay1Duration > 0) {
relay1Start = millis();
relay1Running = true;
relay1Active = true;
digitalWrite(RELAY1_PIN, HIGH);
}
relay2Duration = (relay2Hours * 3600000UL) + (relay2Minutes * 60000UL) + (relay2Seconds * 1000UL);
if (relay2Duration > 0) {
relay2Start = millis();
relay2Running = true;
relay2Active = true;
digitalWrite(RELAY2_PIN, HIGH);
}
if (relay1Duration == 0 && relay2Duration == 0) {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Kamida bitta");
lcd.setCursor(0, 1);
lcd.print("vaqt kiriting!");
delay(2000);
lcd.clear();
lastLine0 = "";
lastLine1 = "";
displayMainMenu();
return;
}
currentState = MENU_RUNNING;
lcd.clear();
lastLine0 = "";
lastLine1 = "";
}
void checkTimers() {
if (relay1Running) {
unsigned long elapsed = millis() - relay1Start;
if (elapsed >= relay1Duration) {
relay1Running = false;
relay1Active = false;
digitalWrite(RELAY1_PIN, LOW);
for(int i = 0; i < 2; i++) {
digitalWrite(RELAY1_PIN, HIGH);
delay(150);
digitalWrite(RELAY1_PIN, LOW);
delay(150);
}
lastLCDUpdate = 0;
}
}
if (relay2Running) {
unsigned long elapsed = millis() - relay2Start;
if (elapsed >= relay2Duration) {
relay2Running = false;
relay2Active = false;
digitalWrite(RELAY2_PIN, LOW);
for(int i = 0; i < 2; i++) {
digitalWrite(RELAY2_PIN, HIGH);
delay(150);
digitalWrite(RELAY2_PIN, LOW);
delay(150);
}
lastLCDUpdate = 0;
}
}
if (!relay1Running && !relay2Running &&
(relay1Duration > 0 || relay2Duration > 0)) {
delay(1000);
lcd.clear();
lcd.setCursor(2, 0);
lcd.print("TAYMER");
lcd.setCursor(2, 1);
lcd.print("TUGADI!");
delay(3000);
currentState = MENU_MAIN;
lcd.clear();
lastLine0 = "";
lastLine1 = "";
displayMainMenu();
}
}
void stopAllTimers() {
relay1Running = false;
relay2Running = false;
relay1Active = false;
relay2Active = false;
digitalWrite(RELAY1_PIN, LOW);
digitalWrite(RELAY2_PIN, LOW);
lcd.clear();
lcd.setCursor(1, 0);
lcd.print("TO'XTATILDI!");
lcd.setCursor(0, 1);
lcd.print("Qaytish...");
delay(2000);
currentState = MENU_MAIN;
lcd.clear();
lastLine0 = "";
lastLine1 = "";
displayMainMenu();
}