Untitled
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