Untitled

 avatar
unknown
c_cpp
8 months ago
52 kB
13
Indexable
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <Encoder.h>
#include <EEPROM.h>

#define ENCODER_CLK D5
#define ENCODER_DT  D6
#define ENCODER_SW  D7

#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);

Encoder encoder(ENCODER_CLK, ENCODER_DT);

const int pumpPin[4] = {D0, D3, D4, D9}; // Pump 1 → D0, Pump 2 → D3, Pump 3 → D4, Pump 4 → D9

enum ScreenState {
  SCREEN_HOME,
  SCREEN_MAIN,
  SCREEN_PUMP,
  SCREEN_PUMP_SETTINGS,
  SCREEN_PUMP_SCHEDULE,
  SCREEN_PUMP_MANUAL,
  SCREEN_PUMP_CALIBRATION,
  SCREEN_PUMP_RECALIBRATION,
};

unsigned long lastBlink = 0;
bool blinkState = true;

ScreenState currentScreen = SCREEN_HOME;

int currentPump = 0;
int menuIndex = 0;
long lastEnc = 0;
bool redraw = true;
unsigned long lastButton = 0;

int tankVolume[4] = {500, 500, 500, 500};
float currentVolume[4] = {125, 125, 125, 125};

bool editingTank = false;
long editEnc = 0;

bool editingCurrent = false;
long editCurrentEnc = 0;

bool scheduleEnabled[4] = {true, true, true, true};
bool editingScheduleAvailable = false;
int scheduleEditState = 0;
bool alarmEnabled[4] = {true, true, true, true};
bool editingAlarm = false;
int alarmEditState = 0;

int dosePerDay[4] = {1, 1, 1, 1}; // mặc định mỗi bơm 1 lần/ngày
bool editingDosePerDay = false;
int doseEditValue = 0;

float volumePerDay[4] = {0, 0, 0, 0}; // mặc định 0ml/ngày
bool editingVolumePerDay = false;
float volumeEditValue = 0;

bool editingFillDays = false;
//bool selectedDays[7] = {false, false, false, false, false, false, false}; // Su -> Sa
bool selectedDays[4][7]; // 4 bơm, mỗi bơm có 7 ngày riêng (Su–Sa)
int fillDayIndex = 0;
const char* fillDayLabels[8] = {"Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "OK"};

bool editingStartTime = false;
int startHour[4] = {8, 8, 8, 8};   // mặc định 08:00
int startMinute[4] = {0, 0, 0, 0};
int startTimeEnc = 0;

bool editingEndTime = false;
int endHour[4] = {8, 8, 8, 8};   // mặc định 08:30
int endMinute[4] = {30, 30, 30, 30};
int endTimeEnc = 0;

bool ifEditingStartTime = false;
bool ifEditingEndTime = false;


String pumpName[4] = {"Pump1", "Pump2", "Pump3", "Pump4"};
bool editingName = false;
int nameCharIndex = 0;
int nameCharEnc = 0;
const char nameChars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789 ";
const int nameCharCount = sizeof(nameChars) - 1;

bool editingVolumeFill = false;
float volumeFillValue = 0;
long volumeFillEnc = 0;

bool isManualRunning = false;
unsigned long manualStartTime = 0;
float manualStartVolume = 0;

bool isRecalibrating = false;
unsigned long recalibrationStartTime = 0;
int recalibrationDuration = 60; // giây

bool confirmStartPump = false;
int confirmIndex = 0; // 0 = OK, 1 = Cancel

bool editingFlowRate = false;
long flowRateEnc = 0;
float flowRate = 0.0; // đơn vị ml/m



const char* mainMenu[] = {
    "Pump 1", "Pump 2", "Pump 3", "Pump 4",
    "Date/Time", "WiFi Config", "Home"
  };
  const int mainMenuCount = sizeof(mainMenu) / sizeof(mainMenu[0]);

const char* pumpMainMenu[] = {
  "Settings", "Schedule", "Manual", "Calibration", "Back"
};
const int pumpMainMenuCount = sizeof(pumpMainMenu) / sizeof(pumpMainMenu[0]);

const char* pumpSettingsMenu[] = {
  "Name", "Tank Size", "Current", "Alarm <10%", "Save", "Back"
};
const int pumpSettingsMenuCount = sizeof(pumpSettingsMenu) / sizeof(pumpSettingsMenu[0]);

const char* pumpScheduleMenu[] = {
  "Available", "Doses/day", "Volume/day", "Fill Days",
  "Start time", "End time", "Save", "Back"
};
const int pumpScheduleMenuCount = sizeof(pumpScheduleMenu) / sizeof(pumpScheduleMenu[0]);

const char* pumpManualMenu[] = {
  "Volume fill", "Manual Run", "Back"
};
const int pumpManualMenuCount = sizeof(pumpManualMenu) / sizeof(pumpManualMenu[0]);

const char* calibrationMenu[] = {
  "Guideline", "Recalibration", "Back"
};
const int calibrationMenuCount = sizeof(calibrationMenu) / sizeof(calibrationMenu[0]);

const char* recalibrationMenu[] = {
  "Start Pump", "Flow Rate", "Save", "Cancel and Back"
};
const int recalibrationMenuCount = sizeof(recalibrationMenu) / sizeof(recalibrationMenu[0]);

bool showingGuideline = false;
int guidelineScrollIndex = 0;

bool readButton() {
  if (digitalRead(ENCODER_SW) == LOW && millis() - lastButton > 350) {
    lastButton = millis();
    return true;
  }
  return false;
}

String guidelineLines[] = {
  "1. PLEASE ENSURE:    ",
  "  -Tank filled.      ",
  "  -Tube filled from  ",
  "   tank-> output port",
  "  -Tank under Pump   ",
  "                     ",
  "2. Press START.      ",
  "  -After 5s, the pump",
  "   runs 1 min.       ",
  "                     ",
  "3. Enter FLOW RATE.  ",
  "  -Measure volume out",
  "   by container and  ",
  "   enter n. measured ",
  "   to FLOW-RATE box. ",
  "  -SAVE  = save value",
  "  -CANCEL=skip saving"
};

const int guidelineLineCount = sizeof(guidelineLines) / sizeof(guidelineLines[0]);



//===============VOID PROGRAM ===============

void drawCenteredText(String text, int y) {
  int16_t x1, y1;
  uint16_t w, h;
  display.getTextBounds(text, 0, y, &x1, &y1, &w, &h);
  display.setCursor((SCREEN_WIDTH - w) / 2, y);
  display.print(text);
}

void drawMenuTitle(String title) {
  display.setTextSize(1);
  display.setTextColor(SSD1306_WHITE);
  drawCenteredText(title, 0);
  display.drawLine(0, 10, SCREEN_WIDTH, 10, SSD1306_WHITE);
}

void drawHome() {
  display.clearDisplay();
  display.setTextColor(SSD1306_WHITE);
  display.setTextSize(1);
  drawCenteredText("DOSING PUMP", 0);
  drawCenteredText("12:34  16/10/2025", 12);

  for (int i = 0; i < 4; i++) {
    int y = 32 + (i / 2) * 12;
    int x = (i % 2 == 0) ? 0 : 64;

    bool alarmActive = alarmEnabled[i] && currentVolume[i] <= (tankVolume[i] * 0.1);

    if (alarmActive) {
      if (blinkState) {
        display.fillRect(x, y - 1, 64, 11, SSD1306_WHITE); // nền trắng
        display.setTextColor(SSD1306_BLACK);               // chữ đen
      } else {
        // Không vẽ gì để tạo hiệu ứng nhấp nháy
        continue;
      }
    } else {
      display.setTextColor(SSD1306_WHITE); // chữ trắng
    }

    display.setCursor(x + 2, y);
    display.printf("P%d: %3dml", i + 1, (int)currentVolume[i]);
  }

  display.display();
}


void drawMainMenu() {
  display.clearDisplay();
  drawMenuTitle("Main Menu");

  int visibleCount = 4;
  int startIndex = menuIndex < visibleCount ? 0 : menuIndex - visibleCount + 1;

  for (int i = 0; i < visibleCount && (startIndex + i) < mainMenuCount; i++) {
    int y = 14 + i * 12;
    int itemIndex = startIndex + i;

    if (itemIndex == menuIndex) {
      display.fillRect(0, y - 1, SCREEN_WIDTH, 11, SSD1306_WHITE);
      display.setTextColor(SSD1306_BLACK);
    } else {
      display.setTextColor(SSD1306_WHITE);
    }

    display.setCursor(2, y);
    display.print(mainMenu[itemIndex]);
  }

  display.display();
}

void drawPumpSubMenu(const char* menu[], int count, String title) {
  display.clearDisplay();
  drawMenuTitle(title);

  int visibleCount = 4;
  int startIndex = menuIndex < visibleCount ? 0 : menuIndex - visibleCount + 1;

  for (int i = 0; i < visibleCount && (startIndex + i) < count; i++) {
    int y = 14 + i * 12;
    int itemIndex = startIndex + i;

    bool isSelected = (itemIndex == menuIndex);
    if (isSelected) {
      display.fillRect(0, y - 1, SCREEN_WIDTH, 11, SSD1306_WHITE);
      display.setTextColor(SSD1306_BLACK);
    } else {
      display.setTextColor(SSD1306_WHITE);
    }

    display.setCursor(2, y);
    display.print(menu[itemIndex]);

    // === Hiển thị giá trị bên phải ===
    String value = "";
    
      if (title == "Schedule" && strcmp(menu[itemIndex], "Available") == 0) {
        value = scheduleEnabled[currentPump] ? "ON" : "OFF";
      }
      else if (title == "Schedule" && strcmp(menu[itemIndex], "Doses/day") == 0) {
      int dose = dosePerDay[currentPump];
        value = String(dose) + (dose == 1 ? " time" : " times");
      if (dose == 0) value = "0 time";
      }
      else if (title == "Schedule" && strcmp(menu[itemIndex], "Volume/day") == 0) {
        value = String(volumePerDay[currentPump], 1) + " ml";
      }
      
      else if (title == "Schedule" && strcmp(menu[itemIndex], "Start time") == 0) {
        char buf[6];
        sprintf(buf, "%02d:%02d", startHour[currentPump], startMinute[currentPump]);
        value = String(buf);
      }
      else if (title == "Schedule" && strcmp(menu[itemIndex], "End time") == 0) {
        char buf[6];
        sprintf(buf, "%02d:%02d", endHour[currentPump], endMinute[currentPump]);
        value = String(buf);
      }

      //=========Hiển thị Volume manual=========
      
      else if (title == "Manual" && strcmp(menu[itemIndex], "Volume fill") == 0) {
        value = String(volumeFillValue, 1) + " ml";
      }

      //==== Hiển thị chọn ngày ==========   
      else if (title == "Schedule" && strcmp(menu[itemIndex], "Fill Days") == 0) {
        String dayShort[] = {"S", "2", "3", "4", "5", "6", "7"};
        String selected = "";

        for (int i = 0; i < 7; i++) {
          if (selectedDays[currentPump][i]) {
            if (selected.length() > 0) selected += "";
            selected += dayShort[i];
          }
        }

        value = selected.length() > 0 ? selected : "None";
      }


    if (value.length() > 0) {
      int16_t x1, y1;
      uint16_t w, h;
      display.getTextBounds(value, 0, y, &x1, &y1, &w, &h);
      display.setCursor(SCREEN_WIDTH - w - 2, y);
      display.print(value);
    }
  }

  display.display();
}

void drawTankEditor(int pump) {
  display.clearDisplay();
  drawMenuTitle("Tank Size");
  display.setTextSize(2);
  drawCenteredText(String(tankVolume[pump]) + " ml", 24);
  display.setTextSize(1);
  drawCenteredText("Press to confirm", 52);
  display.display();
}

void drawCurrentEditor(int pump) {
  display.clearDisplay();
  drawMenuTitle("Current Volume");
  display.setTextSize(2);
  drawCenteredText(String(currentVolume[pump], 0) + " ml", 24);
  display.setTextSize(1);
  drawCenteredText("Press to confirm", 52);
  display.display();
}

void drawFillDaysUI() {
  display.clearDisplay();
  drawMenuTitle("Fill Days");

  int boxWidth = 28;
  int boxHeight = 18;
  int spacingX = 2;
  int spacingY = 4;
  int startX = 0;
  int startY = 14;

  for (int i = 0; i < 8; i++) {
    int col = i % 4;
    int row = i / 4;
    int x = startX + col * (boxWidth + spacingX);
    int y = startY + row * (boxHeight + spacingY);

    bool isSelected = (i == fillDayIndex);
    //bool isActive = (i < 7 && selectedDays[i]);
    bool isActive = (i < 7 && selectedDays[currentPump][i]);

    // Vẽ ô
    if (isActive) {
      display.fillRect(x, y, boxWidth, boxHeight, SSD1306_WHITE);
      display.setTextColor(SSD1306_BLACK);
    } else {
      display.drawRect(x, y, boxWidth, boxHeight, SSD1306_WHITE);
      display.setTextColor(SSD1306_WHITE);
    }

    // Vẽ nhãn ngày
    int16_t x1, y1;
    uint16_t w, h;
    display.getTextBounds(fillDayLabels[i], 0, 0, &x1, &y1, &w, &h);
    display.setCursor(x + (boxWidth - w) / 2, y + (boxHeight - h) / 2);
    display.print(fillDayLabels[i]);

    // Vẽ thanh ngang con trỏ
    if (isSelected) {
      int barY = (row == 0) ? y - 3 : y + boxHeight + 1;
      display.fillRect(x, barY, boxWidth, 2, SSD1306_WHITE);
    }
  }

  display.display();
}

void drawVolumeFillEditor() {
  display.clearDisplay();
  drawMenuTitle("Volume Fill");
  display.setTextSize(2);
  drawCenteredText(String(volumeFillValue, 1) + " ml", 24);
  display.setTextSize(1);
  drawCenteredText("Press to confirm", 52);
  display.display();
}

void drawManualRunUI(float remainingVolume) {
  display.clearDisplay();
  drawMenuTitle("Manual Run");
  display.setTextSize(2);
  drawCenteredText(String(remainingVolume, 1) + " ml", 24);
  display.setTextSize(1);
  drawCenteredText("Running...", 52);
  display.display();
}

void drawGuidelineScreen() {
  display.clearDisplay();
  display.setTextSize(1);
  display.setTextColor(SSD1306_WHITE);
  drawMenuTitle("Guideline");

  int maxVisible = 4; // 4 dòng nội dung + 1 dòng GOT IT
  int startIndex = guidelineScrollIndex;

  for (int i = 0; i < maxVisible && (startIndex + i) < guidelineLineCount; i++) {
    int y = 14 + i * 10;
    display.setCursor(2, y);
    display.print(guidelineLines[startIndex + i]);
  }

  // Hiển thị nút GOT IT nếu đã cuộn đến cuối
  if (startIndex + maxVisible >= guidelineLineCount) {
    bool selected = (guidelineScrollIndex == guidelineLineCount);
    int y = 14 + maxVisible * 10;
    if (selected) {
      display.fillRect(0, y - 1, SCREEN_WIDTH, 11, SSD1306_WHITE);
      display.setTextColor(SSD1306_BLACK);
    } else {
      display.setTextColor(SSD1306_WHITE);
    }
    display.setCursor((SCREEN_WIDTH - 48) / 2, y);
    display.print("GOT IT");
  }

  display.display();
}

void drawRecalibrationCountdown(int secondsLeft) {
  display.clearDisplay();
  drawMenuTitle("Recalibration");

  display.setTextSize(1);
  drawCenteredText("Pump " + String(currentPump + 1) + " running...", 20);

  display.setTextSize(2);
  drawCenteredText(String(secondsLeft) + "s", 40);

  display.display();
}

void runRecalibrationProcess() {
  // Đếm ngược 5 giây trước khi bắt đầu
  for (int i = 5; i >= 1; i--) {
    display.clearDisplay();
    drawMenuTitle("Recalibration");
    display.setTextSize(1);
    drawCenteredText("Pump " + String(currentPump + 1), 20);
    drawCenteredText("will start after:", 30);
    display.setTextSize(2);
    drawCenteredText(String(i), 45);
    display.display();
    delay(1000);
  }

  // Bắt đầu chạy bơm và đếm ngược 60 giây
  digitalWrite(pumpPin[currentPump], HIGH); // bật bơm

  for (int i = 60; i >= 0; i--) {
    drawRecalibrationCountdown(i); // cập nhật giao diện mỗi giây
    delay(1000);
  }

  digitalWrite(pumpPin[currentPump], LOW); // tắt bơm

  // Hiển thị thông báo hoàn tất
  display.clearDisplay();
  drawMenuTitle("Recalibration");
  display.setTextSize(1);
  drawCenteredText("Pump " + String(currentPump + 1) + " Finish", 20);
  drawCenteredText("Please enter value", 35);
  drawCenteredText("to FLOW RATE", 45);
  display.display();
  delay(3000);
}

void drawFlowRateEditor() {
  display.clearDisplay();
  drawMenuTitle("Edit Flow Rate");

  display.setTextSize(1);
  drawCenteredText("Set Flow Rate (ml/m)", 20);

  display.setTextSize(2);
  drawCenteredText(String(flowRate, 1), 40); // hiển thị 1 chữ số thập phân
  display.display();
}


void showSavedMessage() {
  display.clearDisplay();
  drawMenuTitle("Recalibration");
  drawCenteredText("SAVED!", 30);
  display.display();
  delay(1000); // giữ thông báo 1 giây
}

void drawConfirmStartPump() {
  display.clearDisplay();
  drawMenuTitle("Confirm");
  drawCenteredText("Start Pump?", 20);

  display.setTextSize(1);

  int boxWidth = 50;
  int boxHeight = 20;
  int gap = 8;
  int startX = (128 - (boxWidth * 2 + gap)) / 2; // tính căn giữa
  int y = 40;

  if (confirmIndex == 0) {
    // OK được chọn
    display.fillRect(startX, y, boxWidth, boxHeight, WHITE);
    display.setTextColor(BLACK);
    display.setCursor(startX + 15, y + 7);
    display.print("OK");

    display.drawRect(startX + boxWidth + gap, y, boxWidth, boxHeight, WHITE);
    display.setTextColor(WHITE);
    display.setCursor(startX + boxWidth + gap + 5, y + 7);
    display.print("Cancel");
  } else {
    // Cancel được chọn
    display.drawRect(startX, y, boxWidth, boxHeight, WHITE);
    display.setTextColor(WHITE);
    display.setCursor(startX + 15, y + 7);
    display.print("OK");

    display.fillRect(startX + boxWidth + gap, y, boxWidth, boxHeight, WHITE);
    display.setTextColor(BLACK);
    display.setCursor(startX + boxWidth + gap + 5, y + 7);
    display.print("Cancel");
  }

  display.display();
}

void setup() {
  pinMode(ENCODER_SW, INPUT_PULLUP);
  Serial.begin(115200);
  if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
    Serial.println(F("OLED failed"));
    while (1);
  }
  display.clearDisplay();
  drawHome();
  EEPROM.begin(512); // hoặc 64 nếu anh dùng ít hơn
  loadPumpSettingsFromEEPROM(); // đọc tên từ EEPROM khi khởi động
  loadScheduleFromEEPROM();

  
  for (int i = 0; i < 4; i++) {
      pinMode(pumpPin[i], OUTPUT);
      digitalWrite(pumpPin[i], LOW); // đảm bảo bơm tắt ban đầu
    }

}

void savePumpSettingsToEEPROM(int pumpIndex) {
  int baseAddr = pumpIndex * 16;

  // Lưu tên (8 ký tự)
  for (int i = 0; i < 8; i++) {
    char c = (i < pumpName[pumpIndex].length()) ? pumpName[pumpIndex][i] : ' ';
    EEPROM.write(baseAddr + i, c);
  }

  // Lưu tankVolume (2 byte)
  EEPROM.write(baseAddr + 8, highByte(tankVolume[pumpIndex]));
  EEPROM.write(baseAddr + 9, lowByte(tankVolume[pumpIndex]));

  // Lưu currentVolume (2 byte)
  int current = (int)currentVolume[pumpIndex];
  EEPROM.write(baseAddr + 10, highByte(current));
  EEPROM.write(baseAddr + 11, lowByte(current));

  //Lưu trạng thái alarmEnabled (1 byte)
  EEPROM.write(baseAddr + 12, alarmEnabled[pumpIndex] ? 1 : 0);

  EEPROM.commit();
}

void loadPumpSettingsFromEEPROM() {
  for (int p = 0; p < 4; p++) {
    int baseAddr = p * 16;
    String name = "";
    for (int i = 0; i < 8; i++) {
      char c = EEPROM.read(baseAddr + i);
      if (c >= 32 && c <= 126) {
        name += c;
      }
    }
    name.trim();
    if (name.length() == 0 || name == "        ") {
      name = "Pump" + String(p + 1);
    }
    pumpName[p] = name;

    // Đọc tankVolume
    int tank = word(EEPROM.read(baseAddr + 8), EEPROM.read(baseAddr + 9));
    if (tank < 0 || tank > 5000) tank = 500;
    tankVolume[p] = tank;

    // Đọc currentVolume
    int current = word(EEPROM.read(baseAddr + 10), EEPROM.read(baseAddr + 11));
    if (current < 0 || current > tank) current = tank;
    currentVolume[p] = current;

    // Đọc trạng thái alarmEnabled
    byte alarmByte = EEPROM.read(baseAddr + 12);
    alarmEnabled[p] = (alarmByte == 1);
  }
}

void saveScheduleToEEPROM(int pumpIndex) {
  int baseAddr = pumpIndex * 32 + 64;

  // Lưu trạng thái Available
  EEPROM.write(baseAddr, scheduleEnabled[pumpIndex] ? 1 : 0);

  // Lưu số lần bơm/ngày
  EEPROM.write(baseAddr + 1, dosePerDay[pumpIndex]);

  // Lưu thể tích bơm/ngày (float -> int x10 để lưu 1 chữ số thập phân)
  int vol = (int)(volumePerDay[pumpIndex] * 10);
  EEPROM.write(baseAddr + 2, highByte(vol));
  EEPROM.write(baseAddr + 3, lowByte(vol));

  // Lưu Fill Days (7 byte)
  for (int i = 0; i < 7; i++) {
    EEPROM.write(baseAddr + 4 + i, selectedDays[pumpIndex][i] ? 1 : 0);
  }

  // Lưu Start time
  EEPROM.write(baseAddr + 11, startHour[pumpIndex]);
  EEPROM.write(baseAddr + 12, startMinute[pumpIndex]);

  // Lưu End time
  EEPROM.write(baseAddr + 13, endHour[pumpIndex]);
  EEPROM.write(baseAddr + 14, endMinute[pumpIndex]);

  EEPROM.commit();
}

void loadScheduleFromEEPROM() {
  for (int p = 0; p < 4; p++) {
    int baseAddr = p * 32 + 64;

    // Đọc trạng thái Available
    scheduleEnabled[p] = (EEPROM.read(baseAddr) == 1);

    // Đọc số lần bơm/ngày
    dosePerDay[p] = EEPROM.read(baseAddr + 1);
    if (dosePerDay[p] < 0 || dosePerDay[p] > 10) dosePerDay[p] = 1;

    // Đọc thể tích bơm/ngày
    int vol = word(EEPROM.read(baseAddr + 2), EEPROM.read(baseAddr + 3));
    volumePerDay[p] = constrain(vol / 10.0, 0.0, 100.0);

    // Đọc Fill Days
    for (int i = 0; i < 7; i++) {
      selectedDays[p][i] = (EEPROM.read(baseAddr + 4 + i) == 1);
    }

    // Đọc Start time
    startHour[p] = EEPROM.read(baseAddr + 11);
    startMinute[p] = EEPROM.read(baseAddr + 12);

    // Đọc End time
    endHour[p] = EEPROM.read(baseAddr + 13);
    endMinute[p] = EEPROM.read(baseAddr + 14);
  }
}



void loop() {
  bool isEditing = editingScheduleAvailable || editingDosePerDay ||
                   editingTank || editingCurrent || editingAlarm || editingName || editingFillDays || editingStartTime || editingEndTime || editingFlowRate;

  //====== CHINH SUA MANUAL VOLUME =========
    if (editingVolumeFill) {
      long newEnc = encoder.read() / 4;
      if (newEnc != volumeFillEnc) {
        volumeFillEnc = newEnc;
        volumeFillValue = volumeFillEnc * 0.5;
        drawVolumeFillEditor();
      }

      if (readButton()) {
        editingVolumeFill = false;
        redraw = true;
        encoder.write(menuIndex * 4); // reset encoder về menu
      }

      return;
    }


  // ===== CHỈNH SỬA TANK =====
  if (editingTank) {
    long newEditEnc = encoder.read() / 4;
    if (newEditEnc != editEnc) {
      int delta = (newEditEnc > editEnc) ? 10 : -10;
      editEnc = newEditEnc;

      tankVolume[currentPump] += delta;
      if (tankVolume[currentPump] < 0) tankVolume[currentPump] = 0;
      if (tankVolume[currentPump] > 5000) tankVolume[currentPump] = 5000;

      if (currentVolume[currentPump] > tankVolume[currentPump])
        currentVolume[currentPump] = tankVolume[currentPump];

      drawTankEditor(currentPump);
    }

    if (readButton()) {
      editingTank = false;
      redraw = true;
    }

    return;
  }

  // ===== CHỈNH SỬA CURRENT =====
  if (editingCurrent) {
    long newEditEnc = encoder.read() / 4;
    if (newEditEnc != editCurrentEnc) {
      int delta = (newEditEnc > editCurrentEnc) ? 1 : -1;
      editCurrentEnc = newEditEnc;

      currentVolume[currentPump] += delta;
      if (currentVolume[currentPump] < 0) currentVolume[currentPump] = 0;
      if (currentVolume[currentPump] > tankVolume[currentPump])
        currentVolume[currentPump] = tankVolume[currentPump];

      drawCurrentEditor(currentPump);
    }

    if (readButton()) {
      editingCurrent = false;
      redraw = true;
    }

    return;
  }

  // ===== CHỈNH SỬA ALARM =====
  if (editingAlarm) {
    long newEditEnc = encoder.read() / 4;
    if (newEditEnc != alarmEditState) {
      alarmEditState = newEditEnc % 2;
      display.clearDisplay();
      drawMenuTitle("Alarm <10%");
      drawCenteredText(alarmEditState == 1 ? "YES" : "NO", 24);
      drawCenteredText("Press to confirm", 52);
      display.display();
    }

    if (readButton()) {
      alarmEnabled[currentPump] = (alarmEditState == 1);
      editingAlarm = false;
      redraw = true;
    }

    return;
  }

  // ===== CHỈNH SỬA NAME =====
  if (editingName) {
    long newEnc = encoder.read() / 4;
    if (newEnc != nameCharEnc) {
      nameCharEnc = newEnc;
      int charIndex = nameCharEnc % nameCharCount;
      char c = nameChars[charIndex];

      String preview = pumpName[currentPump];
      while (preview.length() < 8) preview += " ";
      preview.setCharAt(nameCharIndex, c);

      if (millis() - lastBlink > 300) {
        blinkState = !blinkState;
        lastBlink = millis();
      }

      display.clearDisplay();
      drawMenuTitle("Pump Name");

      display.setTextSize(2);
      int charWidth = 12;
      int startX = (SCREEN_WIDTH - (charWidth * 8)) / 2;
      int charY = 24;

      for (int i = 0; i < 8; i++) {
        display.setCursor(startX + i * charWidth, charY);
        display.print(preview[i]);
      }

      int underlineY = charY + 16;
      for (int i = 0; i < 8; i++) {
        bool isBlinking = (i == nameCharIndex);
        if (!isBlinking || (isBlinking && blinkState)) {
          display.drawLine(startX + i * charWidth, underlineY,
                           startX + i * charWidth + charWidth - 2, underlineY,
                           SSD1306_WHITE);
        }
      }

      display.setTextSize(1);
      drawCenteredText("Press to confirm", 52);
      display.display();
    }

    if (readButton()) {
      int charIndex = nameCharEnc % nameCharCount;
      char c = nameChars[charIndex];

      if (nameCharIndex < 8) {
        if (nameCharIndex < pumpName[currentPump].length()) {
          pumpName[currentPump].setCharAt(nameCharIndex, c);
        } else {
          pumpName[currentPump] += c;
        }

        nameCharIndex++;

        if (nameCharIndex < 8) {
          char currentChar = 'A';
          if (nameCharIndex < pumpName[currentPump].length()) {
            currentChar = pumpName[currentPump].charAt(nameCharIndex);
          }

          nameCharEnc = 0;
          for (int i = 0; i < nameCharCount; i++) {
            if (nameChars[i] == currentChar) {
              nameCharEnc = i;
              break;
            }
          }

          encoder.write(nameCharEnc * 4);
        }
      }

      if (nameCharIndex >= 8) {
        editingName = false;
        redraw = true;
      }
    }

    return;
  }

  // ===== SCHEDULE AVAILABLE =====
  if (editingScheduleAvailable) {
    long newEditEnc = encoder.read() / 4;
    if (newEditEnc != scheduleEditState) {
      scheduleEditState = newEditEnc % 2;
      display.clearDisplay();
      drawMenuTitle("Available");
      drawCenteredText(scheduleEditState == 1 ? "ON" : "OFF", 24);
      drawCenteredText("Press to confirm", 52);
      display.display();
    }

    if (readButton()) {
      scheduleEnabled[currentPump] = (scheduleEditState == 1);
      editingScheduleAvailable = false;
      redraw = true;
    }
    return;
  }

  // ===== SCHEDULE DOSE/DAY =====
 if (editingDosePerDay) {
  long rawEnc = encoder.read();
  long newEnc = rawEnc / 4;
  int newValue = constrain(newEnc, 0, 10);

  if (newValue != doseEditValue) {
    doseEditValue = newValue;

    display.clearDisplay();
    drawMenuTitle("Doses/day");

    String doseText = String(doseEditValue) + (doseEditValue == 1 ? " time" : " times");
    if (doseEditValue == 0) doseText = "0 time";

    drawCenteredText(doseText, 24);
    drawCenteredText("Press to confirm", 52);
    display.display();
  }

  // Chỉ reset nếu vượt giới hạn
  if (newEnc < 0 || newEnc > 10) {
    encoder.write(doseEditValue * 4);
  }

  if (readButton()) {
    dosePerDay[currentPump] = doseEditValue;
    editingDosePerDay = false;
    redraw = true;
    encoder.write(menuIndex * 4); // reset lại cho menu
  }

  return;
}
  //=========Volume/day============
  if (editingVolumePerDay) {
  long rawEnc = encoder.read();
  long newEnc = rawEnc / 4;
  float newValue = constrain(newEnc * 0.5, 0.0, 100.0);

  // Debug
  Serial.print("Raw: ");
  Serial.print(rawEnc);
  Serial.print(" | Enc/4: ");
  Serial.print(newEnc);
  Serial.print(" | Volume: ");
  Serial.println(volumeEditValue);

  if (newValue != volumeEditValue) {
    volumeEditValue = newValue;

    display.clearDisplay();
    drawMenuTitle("Volume/day");
    drawCenteredText(String(volumeEditValue, 1) + " ml", 24);
    drawCenteredText("Press to confirm", 52);
    display.display();
  }

  // Reset encoder nếu vượt giới hạn
  if (newEnc < 0 || newEnc > 200) {
    encoder.write((int)(volumeEditValue * 2) * 4);
  }

  if (readButton()) {
    volumePerDay[currentPump] = volumeEditValue;
    editingVolumePerDay = false;
    redraw = true;
  }

  return;
}



  // ===== XỬ LÝ XOAY ENCODER =====
  if (!isEditing) {
    long newEnc = encoder.read() / 4;
    if (newEnc != lastEnc) {
      int delta = (newEnc > lastEnc) ? 1 : -1;
      lastEnc = newEnc;

      int maxIndex = 0;
      if (currentScreen == SCREEN_MAIN) maxIndex = mainMenuCount;
      else if (currentScreen == SCREEN_PUMP) maxIndex = pumpMainMenuCount;
      else if (currentScreen == SCREEN_PUMP_SETTINGS) maxIndex = pumpSettingsMenuCount;
      else if (currentScreen == SCREEN_PUMP_SCHEDULE) maxIndex = pumpScheduleMenuCount;
      else if (currentScreen == SCREEN_PUMP_MANUAL) maxIndex = pumpManualMenuCount;
      else if (currentScreen == SCREEN_PUMP_CALIBRATION) maxIndex = calibrationMenuCount;
      else if (currentScreen == SCREEN_PUMP_RECALIBRATION) maxIndex = recalibrationMenuCount;

      menuIndex += delta;
      if (menuIndex < 0) menuIndex = maxIndex - 1;
      if (menuIndex >= maxIndex) menuIndex = 0;

      redraw = true;
    }
  }

 // ===== XỬ LÝ NÚT NHẤN =====
  if (readButton()) {
    if (currentScreen == SCREEN_HOME) {
      currentScreen = SCREEN_MAIN;
      menuIndex = 0;
      redraw = true;
    }
    else if (currentScreen == SCREEN_MAIN) {
      const char* item = mainMenu[menuIndex];

      if (strncmp(item, "Pump", 4) == 0) {
        currentPump = menuIndex;
        currentScreen = SCREEN_PUMP;
        menuIndex = 0;
        redraw = true;
      } else if (strcmp(item, "Home") == 0) {
        currentScreen = SCREEN_HOME;
        menuIndex = 0;
        redraw = true;
      }
    }
    else if (currentScreen == SCREEN_PUMP) {
      const char* item = pumpMainMenu[menuIndex];
      if (strcmp(item, "Settings") == 0) {
        currentScreen = SCREEN_PUMP_SETTINGS;
        menuIndex = 0;
        redraw = true;
      }
      else if (strcmp(item, "Schedule") == 0) {
        currentScreen = SCREEN_PUMP_SCHEDULE;
        menuIndex = 0;
        redraw = true;
      }
      else if (strcmp(item, "Manual") == 0) {
        currentScreen = SCREEN_PUMP_MANUAL;
        menuIndex = 0;
        redraw = true;
      }
      else if (strcmp(item, "Manual") == 0) {
        currentScreen = SCREEN_PUMP_MANUAL;
        menuIndex = 0;
        redraw = true;
      }
      
      else if (strcmp(item, "Back") == 0) {
        currentScreen = SCREEN_MAIN;
        menuIndex = 0;
        redraw = true;
      }

      if (strcmp(item, "Calibration") == 0) {
        currentScreen = SCREEN_PUMP_CALIBRATION;
        menuIndex = 0;
        redraw = true;
        return;
      }

    }
//==============SCREEN_PUMP_CALIBRATION

    else if (currentScreen == SCREEN_PUMP_CALIBRATION) {
      const char* item = calibrationMenu[menuIndex];

      if (strcmp(item, "Guideline") == 0) {
        showingGuideline = true;
        guidelineScrollIndex = 0;
        encoder.write(0);
        drawGuidelineScreen();
        return;
      }

      else if (strcmp(item, "Recalibration") == 0) {
        currentScreen = SCREEN_PUMP_RECALIBRATION;
        menuIndex = 0;
        redraw = true;
        return;
      }

      else if (strcmp(item, "Back") == 0) {
        currentScreen = SCREEN_PUMP;
        menuIndex = 0;
        redraw = true;
        return;
      }
    }

    //SCREEN RECALIBRATION
    else if (currentScreen == SCREEN_PUMP_RECALIBRATION) {
      const char* item = recalibrationMenu[menuIndex];
        // xử lý quy trinh bơm 1 phut
      
        if (strcmp(item, "Start Pump") == 0) {
          confirmStartPump = true;
          confirmIndex = 0; // mặc định chọn OK
          drawConfirmStartPump();
          return;
        }


      else if (strcmp(item, "Flow Rate") == 0) {           
        editingFlowRate = true;
          flowRateEnc = flowRate * 2; // bước 0.5 → nhân 2
          encoder.write(flowRateEnc * 4); // reset encoder
          drawFlowRateEditor(); // vào ngay màn hình edit
          return;
        }


      else if (strcmp(item, "Save") == 0) {
        // xử lý lưu giá trị
      }
      else if (strcmp(item, "Cancel and Back") == 0) {
        currentScreen = SCREEN_PUMP_CALIBRATION;
        menuIndex = 1; // quay lại mục Recalibration
        encoder.write(menuIndex * 4);
        redraw = true;
      }
    }


    //SCREEN PUMP SETTINGS


    else if (currentScreen == SCREEN_PUMP_SETTINGS) {
      const char* item = pumpSettingsMenu[menuIndex];
      if (strcmp(item, "Name") == 0) {
        editingName = true;
        nameCharIndex = 0;

        // Lấy ký tự hiện tại tại vị trí đầu tiên
        char currentChar = 'A';
        if (pumpName[currentPump].length() > 0) {
          currentChar = pumpName[currentPump].charAt(0);
        }

        // Tìm vị trí của ký tự đó trong nameChars
        nameCharEnc = 0;
        for (int i = 0; i < nameCharCount; i++) {
          if (nameChars[i] == currentChar) {
            nameCharEnc = i;
            break;
          }
        }

        encoder.write(nameCharEnc * 4); // đặt encoder tại ký tự hiện tại

        // Gọi luôn giao diện chỉnh sửa tên
        String preview = pumpName[currentPump];
        while (preview.length() < 8) preview += " ";
        preview.setCharAt(nameCharIndex, currentChar);

        display.clearDisplay();
        drawMenuTitle("Pump Name");

        display.setTextSize(2);
        int charWidth = 12;
        int startX = (SCREEN_WIDTH - (charWidth * 8)) / 2;
        int charY = 24;

        for (int i = 0; i < 8; i++) {
          display.setCursor(startX + i * charWidth, charY);
          display.print(preview[i]);
        }

        int underlineY = charY + 16;
        for (int i = 0; i < 8; i++) {
          bool isBlinking = (i == nameCharIndex);
          if (!isBlinking || (isBlinking && blinkState)) {
            display.drawLine(startX + i * charWidth, underlineY,
                            startX + i * charWidth + charWidth - 2, underlineY,
                            SSD1306_WHITE);
          }
        }
        display.setTextSize(1);
        drawCenteredText("Press to confirm", 52);
        display.display();
        return;
      }

      

      //EDIT TANK SIZE
      else if (strcmp(item, "Tank Size") == 0) {
        editingTank = true;
        editEnc = encoder.read() / 4;
        drawTankEditor(currentPump);
        return;
      }

      //EDIT TANK CURRENT
      else if (strcmp(item, "Current") == 0) {
        editingCurrent = true;
        editCurrentEnc = encoder.read() / 4;
        drawCurrentEditor(currentPump);
        return;
      }


      else if (strcmp(item, "Alarm <10%") == 0) {
        editingAlarm = true;
        alarmEditState = alarmEnabled[currentPump] ? 1 : 0;
        encoder.write(alarmEditState * 4);
        display.clearDisplay();
        drawMenuTitle("Alarm <10%");
        drawCenteredText(alarmEditState == 1 ? "YES" : "NO", 24);
        drawCenteredText("Press to confirm", 52);
        display.display();
        return;
      }



      else if (strcmp(item, "Save") == 0) {
        savePumpSettingsToEEPROM(currentPump); // Lưu tên bơm vào EEPROM
        // Hiển thị thông báo "SAVED!"
        display.clearDisplay();
        drawMenuTitle("Pump Settings");
        display.setTextSize(2);
        drawCenteredText("SAVED!", 24);
        display.display();
        delay(1000); // Hiển thị trong 1 giây
        currentScreen = SCREEN_PUMP;
        menuIndex = 0;
        redraw = true;
      }


      else if (strcmp(item, "Back") == 0) {
        currentScreen = SCREEN_PUMP;
        menuIndex = 0;
        redraw = true;
      }

      else if (currentScreen == SCREEN_PUMP_CALIBRATION) {
        const char* item = calibrationMenu[menuIndex];

        if (strcmp(item, "Back") == 0) {
          currentScreen = SCREEN_PUMP;
          menuIndex = 0;
          redraw = true;
          return;
        }
      }

    }
    else if (currentScreen == SCREEN_PUMP_SCHEDULE || currentScreen == SCREEN_PUMP_MANUAL) {
      const char* item = (currentScreen == SCREEN_PUMP_SCHEDULE)
                         ? pumpScheduleMenu[menuIndex]
                         : pumpManualMenu[menuIndex];
    

    //========================XỬ LÝ MÀN HÌNH EDIT===============
      // Xử lý chỉnh sửa trạng thái Available
        if (currentScreen == SCREEN_PUMP_SCHEDULE && strcmp(item, "Available") == 0) {
          editingScheduleAvailable = true;
          scheduleEditState = scheduleEnabled[currentPump] ? 1 : 0;
          encoder.write(scheduleEditState * 4);
          display.clearDisplay();
          drawMenuTitle("Available");
          drawCenteredText(scheduleEditState == 1 ? "ON" : "OFF", 24);
          drawCenteredText("Press to confirm", 52);
          display.display();
          return;
        }
          else if (currentScreen == SCREEN_PUMP_SCHEDULE && strcmp(item, "Doses/day") == 0) {
          editingDosePerDay = true;
          doseEditValue = dosePerDay[currentPump];
          encoder.write(doseEditValue * 4);

          display.clearDisplay();
          drawMenuTitle("Doses/day");
          drawCenteredText(String(doseEditValue) + " time/day", 24);
          drawCenteredText("Press to confirm", 52);
          display.display();
          return;
        }

        if (currentScreen == SCREEN_PUMP_SCHEDULE && strcmp(item, "Volume/day") == 0) {
          editingVolumePerDay = true;
          volumeEditValue = volumePerDay[currentPump];
          encoder.write((int)(volumeEditValue * 2) * 4); // mỗi bước 0.5ml

          display.clearDisplay();
          drawMenuTitle("Volume/day");
          drawCenteredText(String(volumeEditValue, 1) + " ml", 24);
          drawCenteredText("Press to confirm", 52);
          display.display();
          return;
        }

        if (currentScreen == SCREEN_PUMP_SCHEDULE && strcmp(item, "Fill Days") == 0) {
          editingFillDays = true;
          fillDayIndex = 0;
          encoder.write(0);
          drawFillDaysUI();
          return;
        }

        // XỬ LÝ TRẠNG THÁI START TIME
       if (currentScreen == SCREEN_PUMP_SCHEDULE && strcmp(item, "Start time") == 0) {
        editingStartTime = true;
        ifEditingStartTime = true;
        startTimeEnc = startHour[currentPump] * 12 + startMinute[currentPump] / 5;
        encoder.write(startTimeEnc * 4);

        display.clearDisplay();
        drawMenuTitle("Start time");
        char buf[6];
        sprintf(buf, "%02d:%02d", startHour[currentPump], startMinute[currentPump]);
        drawCenteredText(String(buf), 24);
        drawCenteredText("Press to confirm", 52);
        display.display();
        return;
      }

      // ======Xử lý màn hình edit END TIME========
      if (currentScreen == SCREEN_PUMP_SCHEDULE && strcmp(item, "End time") == 0) {
        editingEndTime = true;
        ifEditingEndTime = true;
        endTimeEnc = endHour[currentPump] * 12 + endMinute[currentPump] / 5;
        encoder.write(endTimeEnc * 4);

        display.clearDisplay();
        drawMenuTitle("End time");
        char buf[6];
        sprintf(buf, "%02d:%02d", endHour[currentPump], endMinute[currentPump]);
        drawCenteredText(String(buf), 24);
        drawCenteredText("Press to confirm", 52);
        display.display();
        return;
      }

      

//============XỬ LÝ ĐIỀU CHỈNH MÀ HÌNH MANUAL===============
    else if (currentScreen == SCREEN_PUMP_MANUAL) {
      const char* item = pumpManualMenu[menuIndex];

      if (strcmp(item, "Volume fill") == 0) {
        editingVolumeFill = true;
        volumeFillEnc = (long)(volumeFillValue / 0.5);
        encoder.write(volumeFillEnc * 4);
        drawVolumeFillEditor();
        return;
      }
          //========= Manual Runing Item=========
      if (strcmp(item, "Manual Run") == 0) {
          isManualRunning = true;
          manualStartTime = millis();
          manualStartVolume = volumeFillValue;
          return;
        }  
      // Các xử lý khác như Manual Run, Back...
    }




    else if (currentScreen == SCREEN_PUMP_SCHEDULE) {
      const char* item = pumpScheduleMenu[menuIndex];

      if (strcmp(item, "Save") == 0) {
        saveScheduleToEEPROM(currentPump); // Lưu Schedule
        display.clearDisplay();
        drawMenuTitle("Schedule");
        display.setTextSize(2);
        drawCenteredText("SAVED!", 24);
        display.display();
        delay(1000);
        currentScreen = SCREEN_PUMP;
        menuIndex = 0;
        redraw = true;
        return;
      }
    }

//////////////////////KẾT THÚC XỬ LÝ MÀN HÌNH EDIT//////////////////////////////
      if (strcmp(item, "Back") == 0) {
        currentScreen = SCREEN_PUMP;
        menuIndex = 0;
        redraw = true;
      }
    }
  }

  //====== SCHEDULE AVAIBLE==========
  if (editingScheduleAvailable) {
  long newEditEnc = encoder.read() / 4;
  if (newEditEnc != scheduleEditState) {
    scheduleEditState = newEditEnc % 2;
    display.clearDisplay();
    drawMenuTitle("Available");
    drawCenteredText(scheduleEditState == 1 ? "ON" : "OFF", 24);
    drawCenteredText("Press to confirm", 52);
    display.display();
  }

  if (readButton()) {
    scheduleEnabled[currentPump] = (scheduleEditState == 1);
    editingScheduleAvailable = false;
    redraw = true;
  }

  return;
}

 //===============DOSES/DAY============
if (editingDosePerDay) {
  long rawEnc = encoder.read();
  long newEnc = rawEnc / 4;
  int newValue = constrain(newEnc, 0, 10);

  // Debug
  Serial.print("Raw: ");
  Serial.print(rawEnc);
  Serial.print(" | Enc/4: ");
  Serial.print(newEnc);
  Serial.print(" | Dose: ");
  Serial.println(doseEditValue);

  if (newValue != doseEditValue) {
    doseEditValue = newValue;

    display.clearDisplay();
    drawMenuTitle("Doses/day");

    String doseText = String(doseEditValue) + (doseEditValue == 1 ? " time" : " times");
    if (doseEditValue == 0) doseText = "0 time";

    drawCenteredText(doseText, 24);
    drawCenteredText("Press to confirm", 52);
    display.display();
  }

  // Reset encoder nếu vượt giới hạn
  if (newEnc < 0 || newEnc > 10) {
    encoder.write(doseEditValue * 4);
  }

  if (readButton()) {
    dosePerDay[currentPump] = doseEditValue;
    editingDosePerDay = false;
    redraw = true;
  }

  return;
}
  //========FILL DAY============
if (editingFillDays) {
  long rawEnc = encoder.read();
  long newEnc = rawEnc / 4;

  // In ra để debug
  Serial.print("Raw: ");
  Serial.print(rawEnc);
  Serial.print(" | Enc/4: ");
  Serial.print(newEnc);
  Serial.print(" | Index: ");
  Serial.println(fillDayIndex);

  // Nếu vượt giới hạn, reset encoder về đúng vị trí
  if (newEnc < 0 || newEnc > 7) {
    encoder.write(fillDayIndex * 4);
  } else if (newEnc != fillDayIndex) {
    fillDayIndex = newEnc;
    drawFillDaysUI();
  }

  if (readButton()) {
  if (fillDayIndex == 7) {
    editingFillDays = false;
    encoder.write(menuIndex * 4);
    lastEnc = encoder.read() / 4;
    redraw = true;
  } else {
    selectedDays[currentPump][fillDayIndex] = !selectedDays[currentPump][fillDayIndex];
    drawFillDaysUI();
    encoder.write(fillDayIndex * 4);
  }
}

  return;
}


  //======= START TIME =====
if (editingStartTime) {
  long rawEnc = encoder.read();
  long newEnc = rawEnc / 4;

  // Giới hạn trong khoảng 0–287 (00:00 đến 23:55)
  if (newEnc < 0) {
    newEnc = 0;
    encoder.write(0);
  }
  if (newEnc > 287) {
    newEnc = 287;
    encoder.write(287 * 4);
  }

  // Tính giờ và phút mới
  int newHour = newEnc / 12;
  int newMinute = (newEnc % 12) * 5;
  int newTotalMin = newHour * 60 + newMinute;
  int endTotalMin = endHour[currentPump] * 60 + endMinute[currentPump];

  bool valid = (newTotalMin + 5 <= endTotalMin);

  if (newHour != startHour[currentPump] || newMinute != startMinute[currentPump]) {
    startHour[currentPump] = newHour;
    startMinute[currentPump] = newMinute;

    // Reset encoder để tránh tích lũy sai lệch
    encoder.write((newHour * 12 + newMinute / 5) * 4);

    display.clearDisplay();
    drawMenuTitle("Start time");

    char buf[6];
    sprintf(buf, "%02d:%02d", newHour, newMinute);
    drawCenteredText(String(buf), 24);

    if (!valid) {
      display.setTextSize(1);
      drawCenteredText("Please change End time", 44);
    } else {
      drawCenteredText("Press to confirm", 52);
    }

    display.display();
  }

  if (readButton()) {
  if (valid) {
    editingStartTime = false;
    encoder.write(menuIndex * 4);  // Reset encoder về đúng vị trí menu
    lastEnc = encoder.read() / 4;
    redraw = true;
  } else {
    // Hiển thị thông báo lỗi
    display.clearDisplay();
    drawMenuTitle("Start time");
    drawCenteredText("Invalid time", 24);
    drawCenteredText("Please change End time", 44);
    display.display();
    delay(1000);

    // Reset encoder về đúng vị trí chỉnh giờ
    encoder.write((startHour[currentPump] * 12 + startMinute[currentPump] / 5) * 4);
  }
}

  return;
}
  // ======= END TIME ==========
if (editingEndTime && ifEditingEndTime) {
  long rawEnc = encoder.read();
  long newEnc = rawEnc / 4;

  if (newEnc < 0) {
    newEnc = 0;
    encoder.write(0);
  }
  if (newEnc > 287) {
    newEnc = 287;
    encoder.write(287 * 4);
  }

  int newHour = newEnc / 12;
  int newMinute = (newEnc % 12) * 5;
  int newTotalMin = newHour * 60 + newMinute;
  int startTotalMin = startHour[currentPump] * 60 + startMinute[currentPump];
  bool valid = (newTotalMin >= startTotalMin + 5);

  if (newHour != endHour[currentPump] || newMinute != endMinute[currentPump]) {
    endHour[currentPump] = newHour;
    endMinute[currentPump] = newMinute;
    encoder.write((newHour * 12 + newMinute / 5) * 4);

    display.clearDisplay();
    drawMenuTitle("End time");
    char buf[6];
    sprintf(buf, "%02d:%02d", newHour, newMinute);
    drawCenteredText(String(buf), 24);
    if (!valid) {
      display.setTextSize(1);
      drawCenteredText("Please change Start time", 40);
    } else {
      drawCenteredText("Press to confirm", 52);
    }
    display.display();
  }

  if (readButton()) {
    if (valid) {
      editingEndTime = false;
      ifEditingEndTime = false;
      encoder.write(menuIndex * 4);
      lastEnc = encoder.read() / 4;
      redraw = true;
    } else {
      display.clearDisplay();
      drawMenuTitle("End time");
      drawCenteredText("Invalid time", 24);
      drawCenteredText("Please change Start time", 44);
      display.display();
      delay(1000);
      encoder.write((endHour[currentPump] * 12 + endMinute[currentPump] / 5) * 4);
    }
  }

  return;
}

    // ====RUNING MANUAL=========
    if (isManualRunning) {
      unsigned long elapsed = millis() - manualStartTime;

      if (elapsed >= 3000) {
        volumeFillValue = 0;
        isManualRunning = false;

        // Hiển thị thông báo hoàn tất
        display.clearDisplay();
        drawMenuTitle("Manual Run");
        display.setTextSize(2);
        drawCenteredText("Completed!", 24);
        display.setTextSize(1);
        drawCenteredText("Manual Filling Done", 44);
        display.display();
        delay(3000); // Hiển thị trong 1 giây

        // Quay về menu Manual
        currentScreen = SCREEN_PUMP_MANUAL;
        menuIndex = 0;
        redraw = true;
      } else {
        float remaining = manualStartVolume * (1.0 - (float)elapsed / 3000.0);
        volumeFillValue = remaining;
        drawManualRunUI(remaining);
      }

      return;
    }
// ==============GUIDE LINE==============
if (showingGuideline) {
  long newEnc = encoder.read() / 4;
  if (newEnc != guidelineScrollIndex) {
    guidelineScrollIndex = constrain(newEnc, 0, guidelineLineCount);
    drawGuidelineScreen();
  }

  if (readButton() && guidelineScrollIndex == guidelineLineCount) {
  // Hiển thị thông báo chuyển màn hình
  display.clearDisplay();
  drawMenuTitle("Guideline");
  display.setTextSize(1);
  drawCenteredText("Going to the", 20);
  drawCenteredText("Recalibration...", 40);
  display.display();
  delay(1500); // Hiển thị trong 3 giây

  // Chuyển về menu Calibration, chọn Recalibration
  showingGuideline = false;
  currentScreen = SCREEN_PUMP_CALIBRATION;
  menuIndex = 1;
  encoder.write(menuIndex * 4);
  lastEnc = encoder.read() / 4;
  redraw = true;
}
  return;
}

if (isRecalibrating) {
  unsigned long elapsed = (millis() - recalibrationStartTime) / 1000;
  int secondsLeft = recalibrationDuration - elapsed;

  if (secondsLeft >= 0) {
    drawRecalibrationCountdown(secondsLeft);
  } else {
    // Kết thúc
    isRecalibrating = false;
    currentScreen = SCREEN_PUMP_RECALIBRATION;
    menuIndex = 0;
    encoder.write(menuIndex * 4);
    redraw = true;
  }

  return;
}

//====== CHỈNH SỬA FLOW RATE =========
if (editingFlowRate) {
  long newEnc = encoder.read() / 4;
  if (newEnc != flowRateEnc) {
    flowRateEnc = newEnc;

    // Giới hạn từ 0 đến 1999 (tương ứng 999.5 ml)
    if (flowRateEnc < 0) flowRateEnc = 0;
    if (flowRateEnc > 1999) flowRateEnc = 1999;

    flowRate = flowRateEnc * 0.5;
    drawFlowRateEditor(); // Vẽ màn hình chỉnh Flow Rate
  }

  if (readButton()) { // Nhấn SW thoát ngay
    editingFlowRate = false;
    redraw = true;
    encoder.write(menuIndex * 4); // Reset encoder về menu
  }

  return;
}


if (confirmStartPump) {
  long newEnc = encoder.read() / 4;
  if (newEnc != confirmIndex) {
    confirmIndex = newEnc;

    // Giới hạn giá trị
    if (confirmIndex < 0) confirmIndex = 0;
    if (confirmIndex > 1) confirmIndex = 1;

    // Reset encoder về giá trị hợp lệ
    encoder.write(confirmIndex * 4);

    drawConfirmStartPump();
  }

  if (readButton()) {
    if (confirmIndex == 0) {
      confirmStartPump = false;
      isRecalibrating = true;
      runRecalibrationProcess();
    } else {
      confirmStartPump = false;
      currentScreen = SCREEN_PUMP_RECALIBRATION;
      redraw = true;
    }
  }

  return;
}
  // ===== VẼ MÀN HÌNH =====
  if (redraw) {
    redraw = false;
    if (currentScreen == SCREEN_HOME) drawHome();
    else if (currentScreen == SCREEN_MAIN) drawMainMenu();
    else if (currentScreen == SCREEN_PUMP) drawPumpSubMenu(pumpMainMenu, pumpMainMenuCount, "Pump " + String(currentPump + 1));
    else if (currentScreen == SCREEN_PUMP_SETTINGS) drawPumpSubMenu(pumpSettingsMenu, pumpSettingsMenuCount, "Settings");
    else if (currentScreen == SCREEN_PUMP_SCHEDULE) drawPumpSubMenu(pumpScheduleMenu, pumpScheduleMenuCount, "Schedule");
    else if (currentScreen == SCREEN_PUMP_MANUAL) drawPumpSubMenu(pumpManualMenu, pumpManualMenuCount, "Manual");
    else if (currentScreen == SCREEN_PUMP_CALIBRATION) drawPumpSubMenu(calibrationMenu, calibrationMenuCount, "Calibration");
    else if (currentScreen == SCREEN_PUMP_RECALIBRATION) drawPumpSubMenu(recalibrationMenu, recalibrationMenuCount, "Recalibration");
  }
}
Editor is loading...
Leave a Comment