/*
* PROJECT: ORCH_WLC_OUTSEAL_NANO_v3.5
* FINAL PRODUCTION-READY + SERIAL DEBUG LOGGING
* Industrial ringan untuk pompa submersible 400 W + tangki 1000 L
* Kedalaman 6–7 m, pipa 1 inch, sumur Ø120–150 cm
* Fitur:
* - Lockout TOR trip berulang
* - Low voltage hysteresis + averaging
* - Power-restore delay 120 detik
* - Fault restart delay 30 detik (termasuk manual reset)
* - Dry-run 50 menit realistis
* - Watchdog WDTO_4S pola terbaik
* - Logging Serial 115200 baud
*/
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <EEPROM.h>
#include <avr/wdt.h>
// ────────────────────────────────────────────────────────────────
// PIN DEFINITION
// ────────────────────────────────────────────────────────────────
const uint8_t PIN_FLOAT_TANGKI = 4;
const uint8_t PIN_FLOAT_SUMBER = 5;
const uint8_t PIN_TOR_FAULT = 10;
const uint8_t PIN_RESET_BTN = 6;
const uint8_t PIN_SSR = 8;
const uint8_t PIN_BUZZER = 9;
const uint8_t PIN_LED_GREEN = A0;
const uint8_t PIN_LED_YELLOW = A1;
const uint8_t PIN_LED_RED = A2;
const uint8_t PIN_VOLTAGE_READ = A3;
// ────────────────────────────────────────────────────────────────
// TIMING CONSTANTS
// ────────────────────────────────────────────────────────────────
const unsigned long STARTUP_DELAY = 5000UL;
const unsigned long POWER_RESTORE_DELAY =120000UL;
const unsigned long DEBOUNCE_MS = 150UL;
const unsigned long PUMP_COOLDOWN = 90000UL;
const unsigned long MIN_RUN_TIME =120000UL;
const unsigned long MAX_RUN_TIME =3600000UL;
const unsigned long DRY_RUN_TIMEOUT =3000000UL;
const unsigned long TOR_AUTO_RESET_AFTER =900000UL;
const unsigned long SENSOR_INACTIVE_AFTER =10800000UL;
const unsigned long LOCKOUT_DURATION =1800000UL;
const unsigned long LOCKOUT_WINDOW =86400000UL;
const unsigned long SSR_GLITCH_TIMEOUT = 5000UL;
const unsigned long SSR_GLITCH_GUARD = 2000UL;
const unsigned long FAULT_RESTART_DELAY = 30000UL;
const float V_LOW_THRESHOLD = 4.5f;
const float V_HIGH_THRESHOLD = 4.7f;
const unsigned long V_CHECK_INTERVAL = 1000UL;
const uint8_t V_AVG_SAMPLES = 8;
const float VOLTAGE_DIVIDER_RATIO = 3.13f;
const unsigned long LED_GREEN_ON = 800;
const unsigned long LED_GREEN_OFF = 800;
const unsigned long LED_RED_ON = 150;
const unsigned long LED_RED_OFF = 150;
const unsigned long LED_RED_DRY_ON = 100;
const unsigned long LED_RED_DRY_OFF= 100;
const unsigned long HEARTBEAT_INTERVAL = 10000UL;
// ────────────────────────────────────────────────────────────────
// EEPROM
// ────────────────────────────────────────────────────────────────
const uint8_t EE_OVERLOAD = 0;
const uint8_t EE_TRIP_COUNT = 1;
const uint8_t EE_POWERON_LO = 2;
const uint8_t EE_POWERON_HI = 3;
// ────────────────────────────────────────────────────────────────
// STRUCTS
// ────────────────────────────────────────────────────────────────
struct DebouncePin {
uint8_t pin;
bool lastRaw;
bool stable;
unsigned long tChange;
DebouncePin() : pin(0), lastRaw(HIGH), stable(HIGH), tChange(0) {}
void init(uint8_t p) { pin = p; }
};
struct BuzzerCtrl {
bool active;
unsigned long tStart;
uint16_t onMs;
uint16_t offMs;
uint8_t repeats;
uint8_t count;
bool inOn;
BuzzerCtrl() : active(false), tStart(0), onMs(0), offMs(0), repeats(0), count(0), inOn(false) {}
};
struct LedIndicator {
uint8_t pin;
bool isOn;
unsigned long lastChange;
unsigned long onTime;
unsigned long offTime;
bool enabled;
LedIndicator() : pin(0), isOn(false), lastChange(0), onTime(0), offTime(0), enabled(false) {}
void init(uint8_t p, bool en, unsigned long ot, unsigned long oft) {
pin = p;
enabled = en;
onTime = ot;
offTime = oft;
}
};
// ────────────────────────────────────────────────────────────────
// GLOBAL OBJECTS & STATE
// ────────────────────────────────────────────────────────────────
struct SystemState {
bool pumpRunning = false;
bool overload = false;
bool overloadPrev = false;
bool sourceEmpty = false;
bool tankFull = false;
bool sensorFault = false;
bool maxRunFault = false;
bool dryRunFault = false;
bool lockout = false;
bool ssrGlitchFault = false;
bool lowVoltageFault = false;
bool faultRestartWait= false;
} state;
LiquidCrystal_I2C lcd(0x27, 16, 2);
DebouncePin dbTangki, dbSumber, dbTOR, dbReset;
BuzzerCtrl buzzer;
LedIndicator ledGreen, ledYellow, ledRed;
unsigned long systemBootTime = 0;
unsigned long lastPumpStart = 0;
unsigned long lastPumpStop = 0;
unsigned long lastPumpActivity = 0;
unsigned long overloadDetectTime= 0;
unsigned long lastLCD = 0;
unsigned long tankFillStart = 0;
unsigned long lastTORResetTime = 0;
unsigned long lockoutStart = 0;
unsigned long ssrCheckTime = 0;
unsigned long lastVoltageCheck = 0;
unsigned long lastFaultClear = 0;
unsigned long faultRestartStart = 0;
unsigned long lastHeartbeat = 0;
uint8_t tripCounter = 0;
uint8_t torTripInWindow = 0;
uint16_t powerOnCounter= 0;
uint8_t lastScreenMode = 255;
// ────────────────────────────────────────────────────────────────
// SETUP
// ────────────────────────────────────────────────────────────────
void setup() {
Serial.begin(115200);
Serial.println(F("\n=== ORCH_WLC_OUTSEAL_NANO v3.5 START ==="));
Serial.print(F("Build: ")); Serial.println(__DATE__ " " __TIME__);
wdt_enable(WDTO_4S);
pinMode(PIN_SSR, OUTPUT);
pinMode(PIN_BUZZER, OUTPUT);
pinMode(PIN_LED_GREEN, OUTPUT);
pinMode(PIN_LED_YELLOW, OUTPUT);
pinMode(PIN_LED_RED, OUTPUT);
pinMode(PIN_VOLTAGE_READ, INPUT);
digitalWrite(PIN_SSR, LOW);
digitalWrite(PIN_BUZZER, LOW);
digitalWrite(PIN_LED_GREEN, LOW);
digitalWrite(PIN_LED_YELLOW, LOW);
digitalWrite(PIN_LED_RED, LOW);
pinMode(PIN_FLOAT_TANGKI, INPUT_PULLUP);
pinMode(PIN_FLOAT_SUMBER, INPUT_PULLUP);
pinMode(PIN_TOR_FAULT, INPUT_PULLUP);
pinMode(PIN_RESET_BTN, INPUT_PULLUP);
// Inisialisasi debounce
dbTangki.init(PIN_FLOAT_TANGKI);
dbSumber.init(PIN_FLOAT_SUMBER);
dbTOR.init(PIN_TOR_FAULT);
dbReset.init(PIN_RESET_BTN);
// Inisialisasi LED
ledGreen.init(PIN_LED_GREEN, true, LED_GREEN_ON, LED_GREEN_OFF);
ledYellow.init(PIN_LED_YELLOW, true, 0, 0);
ledRed.init(PIN_LED_RED, true, LED_RED_ON, LED_RED_OFF);
lcd.init();
lcd.backlight();
lcd.setCursor(0,0); lcd.print(F("WLC TOR v3.5"));
lcd.setCursor(0,1); lcd.print(F("PROD READY"));
// EEPROM
byte ovl = EEPROM.read(EE_OVERLOAD);
state.overload = (ovl == 1);
tripCounter = EEPROM.read(EE_TRIP_COUNT);
powerOnCounter = (EEPROM.read(EE_POWERON_HI) << 8) | EEPROM.read(EE_POWERON_LO);
if (powerOnCounter < 65535) {
powerOnCounter++;
EEPROM.update(EE_POWERON_LO, powerOnCounter & 0xFF);
EEPROM.update(EE_POWERON_HI, powerOnCounter >> 8);
}
if (state.overload) overloadDetectTime = millis();
systemBootTime = millis();
lastPumpActivity = millis();
tankFillStart = millis();
lastTORResetTime = millis();
startBeep(80, 100, 3);
unsigned long bootWaitStart = millis();
while (millis() - bootWaitStart < STARTUP_DELAY) {
wdt_reset(); // tendang watchdog setiap iterasi
// Optional: bisa tambah animasi LCD atau LED blink di sini jika mau
}
lcd.clear();
Serial.println(F("BOOT: System ready"));
}
// ────────────────────────────────────────────────────────────────
// LOOP
// ────────────────────────────────────────────────────────────────
void loop() {
wdt_reset();
unsigned long now = millis();
// ── Debounce ────────────────────────────────────────────
updateDebounce(dbTangki, now);
updateDebounce(dbSumber, now);
updateDebounce(dbTOR, now);
updateDebounce(dbReset, now);
state.tankFull = (dbTangki.stable == LOW);
state.sourceEmpty = (dbSumber.stable == HIGH);
bool torTripped = (dbTOR.stable == LOW);
bool resetActive = (dbReset.stable == LOW);
// ── Low voltage ─────────────────────────────────────────
static uint32_t vSum = 0;
static uint8_t vSampleCount = 0;
if (now - lastVoltageCheck >= V_CHECK_INTERVAL) {
lastVoltageCheck = now;
vSum += analogRead(PIN_VOLTAGE_READ);
vSampleCount++;
if (vSampleCount >= V_AVG_SAMPLES) {
float adcAvg = vSum / (float)V_AVG_SAMPLES;
float voltage = (adcAvg * 5.0 / 1023.0) * VOLTAGE_DIVIDER_RATIO;
if (state.lowVoltageFault) {
if (voltage > V_HIGH_THRESHOLD) {
state.lowVoltageFault = false;
Serial.print(F("VOLTAGE: Restored to ")); Serial.println(voltage, 2);
}
} else {
if (voltage < V_LOW_THRESHOLD) {
state.lowVoltageFault = true;
Serial.print(F("VOLTAGE: Low detected ")); Serial.println(voltage, 2);
}
}
vSum = 0;
vSampleCount = 0;
}
}
// ── Lockout check ──────────────────────────────────────
if (state.lockout && (now - lockoutStart >= LOCKOUT_DURATION)) {
state.lockout = false;
Serial.println(F("LOCKOUT: Expired"));
}
// ── TOR logic ──────────────────────────────────────────
if (torTripped && !state.overload) {
state.overload = true;
overloadDetectTime = now;
if (tripCounter < 255) {
tripCounter++;
EEPROM.update(EE_TRIP_COUNT, tripCounter);
}
EEPROM.update(EE_OVERLOAD, 1);
stopPump();
startBeep(200, 150, 3);
Serial.print(F("TOR TRIP DETECTED. Count: ")); Serial.println(tripCounter);
}
if (state.overload && !state.overloadPrev) {
if (now - lastTORResetTime >= LOCKOUT_WINDOW) {
torTripInWindow = 1;
lastTORResetTime = now;
} else {
torTripInWindow++;
}
if (torTripInWindow >= 3) {
state.lockout = true;
lockoutStart = now;
stopPump();
startBeep(500, 300, 3);
Serial.println(F("LOCKOUT: Activated (3x TOR trip in 24h)"));
}
}
state.overloadPrev = state.overload;
if (state.overload && (now - overloadDetectTime >= TOR_AUTO_RESET_AFTER) && !torTripped) {
state.overload = false;
EEPROM.update(EE_OVERLOAD, 0);
overloadDetectTime = 0;
Serial.println(F("TOR: Auto-reset"));
}
// ── Manual reset (release edge) ────────────────────────
static bool lastReset = HIGH;
if (!resetActive && lastReset) {
bool cleared = false;
if (state.overload && !torTripped) {
state.overload = false;
EEPROM.update(EE_OVERLOAD, 0);
overloadDetectTime = 0;
cleared = true;
Serial.println(F("MANUAL RESET: TOR cleared"));
}
if (state.maxRunFault) { state.maxRunFault = false; cleared = true; Serial.println(F("MANUAL RESET: Max-run cleared")); }
if (state.sensorFault) { state.sensorFault = false; cleared = true; Serial.println(F("MANUAL RESET: Sensor fault cleared")); }
if (state.dryRunFault) { state.dryRunFault = false; cleared = true; Serial.println(F("MANUAL RESET: Dry-run cleared")); }
if (state.ssrGlitchFault) { state.ssrGlitchFault = false; cleared = true; Serial.println(F("MANUAL RESET: SSR glitch cleared")); }
if (state.lowVoltageFault) { state.lowVoltageFault = false; cleared = true; Serial.println(F("MANUAL RESET: Low voltage cleared")); }
if (cleared) {
state.faultRestartWait = true;
faultRestartStart = now;
lcd.clear();
lastFaultClear = now;
Serial.println(F("MANUAL RESET: Fault(s) cleared, restart delay started"));
}
}
lastReset = resetActive;
// ── Sensor fault ───────────────────────────────────────
static bool prevTank = false, prevSource = true;
bool levelChanged = (state.tankFull != prevTank) || (state.sourceEmpty != prevSource);
if (levelChanged) {
prevTank = state.tankFull;
prevSource = state.sourceEmpty;
}
bool impossible = state.tankFull && state.sourceEmpty;
bool longIdle = (now - lastPumpActivity >= SENSOR_INACTIVE_AFTER);
state.sensorFault = impossible || (longIdle && !levelChanged && !state.pumpRunning && !state.tankFull);
// ── Max run & Dry-run ──────────────────────────────────
if (state.pumpRunning && (now - lastPumpStart >= MAX_RUN_TIME)) {
state.maxRunFault = true;
stopPump();
startBeep(400, 200, 5);
Serial.println(F("MAX RUN FAULT: Pump stopped (overtime)"));
}
if (state.pumpRunning &&
!state.tankFull &&
!state.sourceEmpty &&
(now - tankFillStart >= DRY_RUN_TIMEOUT)) {
state.dryRunFault = true;
stopPump();
startBeep(300, 150, 4);
Serial.println(F("DRY RUN FAULT: Pump stopped (no fill progress)"));
}
// ── SSR glitch detect ──────────────────────────────────
if (!state.pumpRunning && digitalRead(PIN_SSR) == HIGH && (now - lastPumpStop > SSR_GLITCH_GUARD)) {
if (ssrCheckTime == 0) ssrCheckTime = now;
if (now - ssrCheckTime >= SSR_GLITCH_TIMEOUT) {
state.ssrGlitchFault = true;
digitalWrite(PIN_SSR, LOW);
startBeep(400, 200, 5);
Serial.println(F("SSR GLITCH DETECTED: Force OFF"));
}
} else {
ssrCheckTime = 0;
}
// ── Fault restart delay ────────────────────────────────
if (state.faultRestartWait && (now - faultRestartStart >= FAULT_RESTART_DELAY)) {
state.faultRestartWait = false;
Serial.println(F("FAULT RESTART DELAY: Ended"));
}
// ── Kontrol pompa ──────────────────────────────────────
bool startupSafe = (now - systemBootTime >= STARTUP_DELAY);
bool powerRestoreSafe = (now - systemBootTime >= POWER_RESTORE_DELAY);
bool canStart = startupSafe && powerRestoreSafe && !state.lowVoltageFault && !state.faultRestartWait;
if (canStart && !state.lockout && !state.ssrGlitchFault &&
!state.overload && !state.sensorFault &&
!state.maxRunFault && !state.dryRunFault) {
if (state.pumpRunning) {
bool minRunOk = (now - lastPumpStart >= MIN_RUN_TIME);
if ((state.tankFull || state.sourceEmpty) && minRunOk) {
stopPump();
Serial.println(F("PUMP STOP: Tank full or source empty"));
}
lastPumpActivity = now;
}
else {
bool cooldownOk = (lastPumpStop == 0 || now - lastPumpStop >= PUMP_COOLDOWN);
if (!state.tankFull && !state.sourceEmpty && cooldownOk) {
startPump();
Serial.println(F("PUMP START: Normal condition"));
}
}
}
else if (state.pumpRunning) {
stopPump();
Serial.println(F("PUMP FORCE STOP: Fault or inhibit active"));
}
// ── Heartbeat status log ───────────────────────────────
if (now - lastHeartbeat >= HEARTBEAT_INTERVAL) {
lastHeartbeat = now;
Serial.print(F("HEARTBEAT: Pump=")); Serial.print(state.pumpRunning ? "ON" : "OFF");
Serial.print(F(" | Faults="));
if (state.overload) Serial.print("TOR ");
if (state.sensorFault) Serial.print("SENSOR ");
if (state.maxRunFault) Serial.print("MAXRUN ");
if (state.dryRunFault) Serial.print("DRYRUN ");
if (state.lockout) Serial.print("LOCK ");
if (state.ssrGlitchFault) Serial.print("SSRGLITCH ");
if (state.lowVoltageFault) Serial.print("LOWVOLT ");
if (state.faultRestartWait) Serial.print("RESTARTWAIT ");
Serial.println();
}
// ── Update ─────────────────────────────────────────────
updateLed(ledGreen, now);
updateLed(ledYellow, now);
updateLed(ledRed, now);
setLedPatterns();
updateBeep(now);
updateDisplay(now);
}
// ────────────────────────────────────────────────────────────────
// FUNGSI PEMBANTU
// ────────────────────────────────────────────────────────────────
void updateDebounce(DebouncePin &pin, unsigned long now) {
bool reading = digitalRead(pin.pin); // gunakan HIGH/LOW asli
if (reading != pin.lastRaw) {
pin.tChange = now;
pin.lastRaw = reading;
}
if (now - pin.tChange >= DEBOUNCE_MS) {
pin.stable = reading;
}
}
// ────────────────────────────────────────────────
// LOCKOUT WINDOW RESET (konsisten)
// ────────────────────────────────────────────────
void handleTorTrip(unsigned long now, bool torTripped) {
if (torTripped && !state.overload) {
state.overload = true;
overloadDetectTime = now;
tripCounter = (tripCounter + 1) % 256; // rollover aman
EEPROM.update(EE_TRIP_COUNT, tripCounter);
EEPROM.update(EE_OVERLOAD, 1);
stopPump();
startBeep(200, 150, 3);
Serial.print(F("TOR TRIP DETECTED. Count: ")); Serial.println(tripCounter);
}
if (state.overload && !state.overloadPrev) {
if (now - lastTORResetTime >= LOCKOUT_WINDOW) {
torTripInWindow = 0; // reset benar
lastTORResetTime = now;
}
torTripInWindow++;
if (torTripInWindow >= 3) {
state.lockout = true;
lockoutStart = now;
stopPump();
startBeep(500, 300, 3);
Serial.println(F("LOCKOUT: Activated (3x TOR trip in 24h)"));
}
}
state.overloadPrev = state.overload;
}
// ────────────────────────────────────────────────
// SSR GLITCH DETECTION (berbasis state, bukan pin read)
// ────────────────────────────────────────────────
bool ssrState = false; // global tracking SSR
void startPump() {
state.pumpRunning = true;
ssrState = true;
digitalWrite(PIN_SSR, HIGH);
lastPumpStart = millis();
lastPumpActivity = millis();
tankFillStart = millis();
startBeep(120, 80, 1);
Serial.println(F("PUMP START: Normal condition"));
}
void stopPump() {
if (state.pumpRunning) {
state.pumpRunning = false;
ssrState = false;
digitalWrite(PIN_SSR, LOW);
lastPumpStop = millis();
if (state.maxRunFault || state.dryRunFault || state.ssrGlitchFault || state.lowVoltageFault) {
state.faultRestartWait = true;
faultRestartStart = millis();
Serial.println(F("PUMP STOP: Fault restart delay started"));
}
}
Serial.println(F("PUMP STOP: Executed"));
}
void checkSSRGlitch(unsigned long now) {
if (!state.pumpRunning && ssrState) {
if (ssrCheckTime == 0) ssrCheckTime = now;
if (now - ssrCheckTime >= SSR_GLITCH_TIMEOUT) {
state.ssrGlitchFault = true;
ssrState = false;
digitalWrite(PIN_SSR, LOW);
startBeep(400, 200, 5);
Serial.println(F("SSR GLITCH DETECTED: Force OFF"));
}
} else {
ssrCheckTime = 0;
}
}
// ────────────────────────────────────────────────
// MANUAL RESET EDGE DETECTION (jelas)
// ────────────────────────────────────────────────
void handleManualReset(unsigned long now, bool resetActive, bool torTripped) {
static bool lastReset = false;
if (resetActive && !lastReset) { // tombol ditekan
bool cleared = false;
if (state.overload && !torTripped) {
state.overload = false;
EEPROM.update(EE_OVERLOAD, 0);
overloadDetectTime = 0;
cleared = true;
Serial.println(F("MANUAL RESET: TOR cleared"));
}
if (state.maxRunFault) { state.maxRunFault = false; cleared = true; Serial.println(F("MANUAL RESET: Max-run cleared")); }
if (state.sensorFault) { state.sensorFault = false; cleared = true; Serial.println(F("MANUAL RESET: Sensor fault cleared")); }
if (state.dryRunFault) { state.dryRunFault = false; cleared = true; Serial.println(F("MANUAL RESET: Dry-run cleared")); }
if (state.ssrGlitchFault) { state.ssrGlitchFault = false; cleared = true; Serial.println(F("MANUAL RESET: SSR glitch cleared")); }
if (state.lowVoltageFault) { state.lowVoltageFault = false; cleared = true; Serial.println(F("MANUAL RESET: Low voltage cleared")); }
if (cleared) {
state.faultRestartWait = true;
faultRestartStart = now;
lcd.clear();
lastFaultClear = now;
Serial.println(F("MANUAL RESET: Fault(s) cleared, restart delay started"));
}
}
lastReset = resetActive;
}
void updateLed(LedIndicator &led, unsigned long now) {
if (!led.enabled) {
digitalWrite(led.pin, LOW);
led.isOn = false;
return;
}
if (led.onTime == 0 && led.offTime == 0) {
digitalWrite(led.pin, HIGH);
led.isOn = true;
return;
}
if (led.isOn) {
if (now - led.lastChange >= led.onTime) {
digitalWrite(led.pin, LOW);
led.isOn = false;
led.lastChange = now;
}
} else {
if (now - led.lastChange >= led.offTime) {
digitalWrite(led.pin, HIGH);
led.isOn = true;
led.lastChange = now;
}
}
}
// ────────────────────────────────────────────────
// BUZZER HANDLER
// ────────────────────────────────────────────────
void startBeep(uint16_t on, uint16_t off, uint8_t rep) {
if (buzzer.active) return;
buzzer.onMs = on;
buzzer.offMs = off;
buzzer.repeats = rep;
buzzer.count = 0;
buzzer.inOn = true;
buzzer.tStart = millis();
buzzer.active = true;
digitalWrite(PIN_BUZZER, HIGH);
}
void updateBeep(unsigned long now) {
if (!buzzer.active) return;
unsigned long elapsed = now - buzzer.tStart;
if (buzzer.inOn) {
if (elapsed >= buzzer.onMs) {
digitalWrite(PIN_BUZZER, LOW);
buzzer.tStart = now;
buzzer.inOn = false;
}
} else {
if (elapsed >= buzzer.offMs) {
buzzer.count++;
if (buzzer.count >= buzzer.repeats) {
buzzer.active = false;
digitalWrite(PIN_BUZZER, LOW);
} else {
digitalWrite(PIN_BUZZER, HIGH);
buzzer.tStart = now;
buzzer.inOn = true;
}
}
}
}
void setLedPatterns() {
if (state.dryRunFault) {
ledRed.enabled = true;
ledRed.onTime = LED_RED_DRY_ON;
ledRed.offTime = LED_RED_DRY_OFF;
ledGreen.enabled = false;
ledYellow.enabled = false;
}
else if (state.overload || state.sensorFault || state.maxRunFault || state.lockout || state.ssrGlitchFault || state.lowVoltageFault || state.faultRestartWait) {
ledRed.enabled = true;
ledRed.onTime = LED_RED_ON;
ledRed.offTime = LED_RED_OFF;
ledGreen.enabled = false;
ledYellow.enabled = false;
}
else if (state.pumpRunning) {
ledYellow.enabled = true;
ledYellow.onTime = 0;
ledYellow.offTime = 0;
ledGreen.enabled = false;
ledRed.enabled = false;
}
else {
ledGreen.enabled = true;
ledGreen.onTime = LED_GREEN_ON;
ledGreen.offTime = LED_GREEN_OFF;
ledYellow.enabled = false;
ledRed.enabled = false;
}
}
void updateDisplay(unsigned long now) {
if (now - lastLCD < 900) return;
lastLCD = now;
uint8_t screenMode = 0;
if (state.lockout) screenMode = 1;
else if (state.ssrGlitchFault) screenMode = 2;
else if (state.lowVoltageFault) screenMode = 3;
else if (state.dryRunFault) screenMode = 4;
else if (state.overload) screenMode = 5;
else if (state.sensorFault) screenMode = 6;
else if (state.maxRunFault) screenMode = 7;
else if (state.faultRestartWait) screenMode = 8;
else if (state.pumpRunning) screenMode = 9;
else screenMode = 10;
if (screenMode != lastScreenMode) {
lcd.clear();
lastScreenMode = screenMode;
}
lcd.setCursor(0, 0);
if (state.lockout) {
lcd.print(F("LOCKOUT 30 MENIT"));
lcd.setCursor(0,1); lcd.print(F("TOR TRIP BERULANG"));
}
else if (state.ssrGlitchFault) {
lcd.print(F("! SSR GLITCH ! "));
lcd.setCursor(0,1); lcd.print(F("CEK SSR / LOGIKA"));
}
else if (state.lowVoltageFault) {
lcd.print(F("! LOW VOLTAGE ! "));
lcd.setCursor(0,1); lcd.print(F("TUNGGU STABIL "));
}
else if (state.dryRunFault) {
lcd.print(F("! DRY RUN DETECT!"));
lcd.setCursor(0,1); lcd.print(F("CEK SUMUR/AIR "));
}
else if (state.overload) {
lcd.print(F("! TOR OVERLOAD !"));
lcd.setCursor(0,1); lcd.print(F("TRIP:")); lcd.print(tripCounter); lcd.print(F(" RST"));
}
else if (state.sensorFault) {
lcd.print(F("! SENSOR FAULT !"));
lcd.setCursor(0,1); lcd.print(F("CEK KABEL/FLOAT "));
}
else if (state.maxRunFault) {
lcd.print(F("! MAX RUN TIME !"));
lcd.setCursor(0,1); lcd.print(F("CEK KEBOCORAN "));
}
else if (state.faultRestartWait) {
lcd.print(F("FAULT WAIT 30s "));
lcd.setCursor(0,1); lcd.print(F("RESTART DELAY "));
}
else {
lcd.print(state.pumpRunning ? F("POMPA: NYALA ") : F("POMPA: SIAP "));
lcd.setCursor(0,1);
if (state.sourceEmpty) lcd.print(F("SUMBER KOSONG "));
else if (state.tankFull) lcd.print(F("TANGKI PENUH "));
else {
lcd.print(F("NORMAL T")); lcd.print(tripCounter);
lcd.print(F(" P")); lcd.print(powerOnCounter);
lcd.print(F(" "));
}
}
}