// ============================================================================
// CREATED AND DESIGNED BY: MARVIN A. QUIZZAGAN
// DATE: MARCH 4, 2026
// ARDUINO NANO - INPUT SWITCHING + LATCH ALERT MODULE
//
// FUNCTION SUMMARY
//
// INPUTS
// D9 → Mirrors to A0
// D8 → Mirrors to A1 and used for HOLD command
// D7 → Internal pull-up, inverted output to A2
// D4 → HOLD input
// D2 → HOLD input
//
// OUTPUTS
// A0 → Mirrors D9
// A1 → Mirrors D8
// A2 → Inverted output of D7
//
// D3 → Latched alert output
//
// CONTROL
// Hold D8 + D4 + D2 LOW simultaneously:
//
// If D3 is OFF
// → Hold for LONG_HOLD_ON_MS (5s) → D3 activates
//
// If D3 is ON
// → Hold for LONG_HOLD_OFF_MS (1.5s) → D3 deactivates
//
// ALERT PATTERN (WHEN D3 ACTIVE)
// beep-beep-beep (fast) → pause 3 seconds → repeat
//
// NOTE
// External pull-up resistors required on D9, D8, D4, and D2.
// ============================================================================
const byte IN1 = 9;
const byte OUT1 = A0;
const byte IN2 = 8;
const byte OUT2 = A1;
const byte IN3 = 7;
const byte OUT3 = A2;
const byte HOLD1 = 4;
const byte HOLD2 = 2;
const byte LATCH_OUT = 3;
// ---------------- Hold Timing ----------------
const unsigned long LONG_HOLD_ON_MS = 5000;
const unsigned long LONG_HOLD_OFF_MS = 1500;
// ---------------- Debounce ----------------
const unsigned long DEBOUNCE_MS = 25;
// ---------------- D3 Beep Pattern (FAST 3x + 3s pause) ----------------
const unsigned long BEEP_HIGH_MS = 80; // fast ON time
const unsigned long BEEP_LOW_MS = 80; // fast OFF gap
const unsigned long BEEP_PAUSE_MS = 3000; // pause 3 seconds
// ---------------- States ----------------
bool latchState = false;
bool mustReleaseHold = false;
unsigned long longHoldStartMs = 0;
// ---------------- Stable Read ----------------
bool stableRead(byte pin) {
struct S { byte raw, stable; unsigned long t; };
static S s[20];
byte r = digitalRead(pin);
if (r != s[pin].raw) {
s[pin].raw = r;
s[pin].t = millis();
}
if (millis() - s[pin].t >= DEBOUNCE_MS) {
s[pin].stable = s[pin].raw;
}
return s[pin].stable;
}
// ---------------- D3 Pattern FSM ----------------
enum BeepState : byte { BEEP_IDLE, BEEP_ON, BEEP_OFF, BEEP_PAUSE };
BeepState beepState = BEEP_IDLE;
unsigned long beepT0 = 0;
byte beepCount = 0;
void serviceD3Beep() {
if (!latchState) {
digitalWrite(LATCH_OUT, LOW);
beepState = BEEP_IDLE;
beepCount = 0;
return;
}
unsigned long now = millis();
switch (beepState) {
case BEEP_IDLE:
// start the first beep immediately
digitalWrite(LATCH_OUT, HIGH);
beepState = BEEP_ON;
beepT0 = now;
beepCount = 0;
break;
case BEEP_ON:
if (now - beepT0 >= BEEP_HIGH_MS) {
digitalWrite(LATCH_OUT, LOW);
beepState = BEEP_OFF;
beepT0 = now;
}
break;
case BEEP_OFF:
if (now - beepT0 >= BEEP_LOW_MS) {
beepCount++;
if (beepCount >= 3) {
// done 3 beeps -> pause
beepState = BEEP_PAUSE;
beepT0 = now;
beepCount = 0;
digitalWrite(LATCH_OUT, LOW);
} else {
// next beep
digitalWrite(LATCH_OUT, HIGH);
beepState = BEEP_ON;
beepT0 = now;
}
}
break;
case BEEP_PAUSE:
if (now - beepT0 >= BEEP_PAUSE_MS) {
// restart beep-beep-beep
digitalWrite(LATCH_OUT, HIGH);
beepState = BEEP_ON;
beepT0 = now;
beepCount = 0;
}
break;
}
}
void setLatchedOutput(bool newState) {
latchState = newState;
// D3 is driven by the beeper FSM when latchState is true
if (!latchState) {
digitalWrite(LATCH_OUT, LOW);
beepState = BEEP_IDLE;
beepCount = 0;
}
}
void setup() {
pinMode(IN1, INPUT); // external pull-up
pinMode(IN2, INPUT); // external pull-up
pinMode(IN3, INPUT_PULLUP); // internal pull-up
pinMode(HOLD1, INPUT); // external pull-up
pinMode(HOLD2, INPUT); // external pull-up
pinMode(OUT1, OUTPUT);
pinMode(OUT2, OUTPUT);
pinMode(OUT3, OUTPUT);
pinMode(LATCH_OUT, OUTPUT);
setLatchedOutput(false);
// initialize debounce
stableRead(IN1);
stableRead(IN2);
stableRead(IN3);
stableRead(HOLD1);
stableRead(HOLD2);
}
void loop() {
// -------- Mirror outputs (original perfect logic) --------
digitalWrite(OUT1, stableRead(IN1));
digitalWrite(OUT2, stableRead(IN2));
digitalWrite(OUT3, !stableRead(IN3));
// -------- D3 beep-beep-beep pattern when latched --------
serviceD3Beep();
// -------- Hold detection (unchanged behavior) --------
bool holdOK = (stableRead(HOLD1) == LOW) &&
(stableRead(HOLD2) == LOW);
bool d8Low = (stableRead(IN2) == LOW);
if (mustReleaseHold) {
if (!holdOK || !d8Low) mustReleaseHold = false;
else return;
}
if (holdOK && d8Low) {
if (longHoldStartMs == 0) {
longHoldStartMs = millis();
} else {
unsigned long requiredMs =
latchState ? LONG_HOLD_OFF_MS : LONG_HOLD_ON_MS;
if (millis() - longHoldStartMs >= requiredMs) {
setLatchedOutput(!latchState);
mustReleaseHold = true;
longHoldStartMs = 0;
}
}
} else {
longHoldStartMs = 0;
}
}