// ============================================================================
// CREATED AND DESIGNED BY: MARVIN A. QUIZZAGAN
// DATE: MARCH 11, 2026 – FINAL VERSION
// SWITCHING CODE FOR Arduino Nano – Alert / Reverse Beeper Controller
//
// INPUTS
// D9 → Mirror to A0 (external pull-up)
// D8 → Mirror to A1 + HOLD command + D6 trigger (external pull-up)
// D7 → Internal pull-up → inverted output to A2
// D4 → HOLD input + D6 trigger (external pull-up)
// D2 → HOLD input (external pull-up)
// D12 → Enable reverse function (LOW = allow D13)
// D13 → Reverse trigger input (internal pull-up)
//
// OUTPUTS
// A0 → Mirrors D9
// A1 → Mirrors D8
// A2 → Inverted D7
// D3 → Alert / reverse beeper
// D6 → Default HIGH, becomes LOW only when D8 + D4 are LOW
// but stays HIGH if D2 is also LOW
//
// CONTROL
// Hold D8 + D4 + D2 LOW:
// OFF → hold 5 sec → ALERT ON
// ON → hold 1.5 sec → ALERT OFF
//
// EXTRA OUTPUT CONTROL
// D8 + D4 LOW for 30 ms, while D2 is HIGH:
// D6 = LOW
// If D8 or D4 becomes HIGH, or D2 becomes LOW:
// D6 = HIGH immediately
//
// ALERT MODE
// beep-beep-beep (fast) → pause 3 sec → repeat
//
// REVERSE MODE (Highest Priority)
// Active when: D12 LOW + D13 LOW
// BEEEP … pause … BEEEP … pause … continuously
//
// NOTE
// External pull-ups required: D9, D8, D4, D2
// Internal pull-ups used: D7, D12, D13
// ============================================================================
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 D13_ENABLE = 12; // D12 = enables D13 function only when LOW
const byte REV_IN = 13; // D13 = reverse trigger, INPUT_PULLUP
const byte LATCH_OUT = 3; // D3 = beeping output
const byte OUT_D6 = 6; // D6 = HIGH by default, LOW only for D8 + D4 condition
// ---------------- 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;
// ---------------- D6 Trigger Timing ----------------
// Sharp trigger for D8 + D4 LOW before D6 goes LOW
const unsigned long D6_TRIGGER_MS = 30;
// ---------------- Normal Latched Alert Pattern ----------------
// beep-beep-beep (fast) -> pause 3 sec -> repeat
const unsigned long ALERT_BEEP_HIGH_MS = 80;
const unsigned long ALERT_BEEP_LOW_MS = 80;
const unsigned long ALERT_BEEP_PAUSE_MS = 3000;
// ---------------- Realistic Truck Reverse Pattern ----------------
// BEEEP ... pause ... BEEEP ... pause ...
const unsigned long REV_BEEP_HIGH_MS = 320;
const unsigned long REV_BEEP_LOW_MS = 680;
// ---------------- Mode Values ----------------
#define MODE_OFF 0
#define MODE_ALERT 1
#define MODE_REVERSE 2
// ---------------- States ----------------
bool latchState = false;
bool mustReleaseHold = false;
unsigned long longHoldStartMs = 0;
// ---------------- D6 State ----------------
unsigned long d6LowStartMs = 0;
bool d6Active = false;
// ---------------- Debounce ----------------
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;
}
// ---------------- Beeper States ----------------
byte currentMode = MODE_OFF;
unsigned long beepT0 = 0;
byte beepCount = 0;
// Alert FSM
byte alertState = 0;
// Reverse FSM
byte reverseState = 0;
// ---------------- Reset FSM ----------------
void resetBeeperFSM() {
digitalWrite(LATCH_OUT, LOW);
alertState = 0;
reverseState = 0;
beepT0 = 0;
beepCount = 0;
}
// ---------------- Beeper Engine ----------------
void serviceD3Beep(byte requestedMode) {
unsigned long now = millis();
if (requestedMode != currentMode) {
currentMode = requestedMode;
resetBeeperFSM();
}
if (currentMode == MODE_OFF) {
digitalWrite(LATCH_OUT, LOW);
}
else if (currentMode == MODE_ALERT) {
switch (alertState) {
case 0:
digitalWrite(LATCH_OUT, HIGH);
alertState = 1;
beepT0 = now;
beepCount = 0;
break;
case 1:
if (now - beepT0 >= ALERT_BEEP_HIGH_MS) {
digitalWrite(LATCH_OUT, LOW);
alertState = 2;
beepT0 = now;
}
break;
case 2:
if (now - beepT0 >= ALERT_BEEP_LOW_MS) {
beepCount++;
if (beepCount >= 3) {
digitalWrite(LATCH_OUT, LOW);
alertState = 3;
beepT0 = now;
beepCount = 0;
} else {
digitalWrite(LATCH_OUT, HIGH);
alertState = 1;
beepT0 = now;
}
}
break;
case 3:
if (now - beepT0 >= ALERT_BEEP_PAUSE_MS) {
digitalWrite(LATCH_OUT, HIGH);
alertState = 1;
beepT0 = now;
}
break;
}
}
else if (currentMode == MODE_REVERSE) {
switch (reverseState) {
case 0:
digitalWrite(LATCH_OUT, HIGH);
reverseState = 1;
beepT0 = now;
break;
case 1:
if (now - beepT0 >= REV_BEEP_HIGH_MS) {
digitalWrite(LATCH_OUT, LOW);
reverseState = 2;
beepT0 = now;
}
break;
case 2:
if (now - beepT0 >= REV_BEEP_LOW_MS) {
digitalWrite(LATCH_OUT, HIGH);
reverseState = 1;
beepT0 = now;
}
break;
}
}
}
// ---------------- Setup ----------------
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(D13_ENABLE, INPUT_PULLUP); // D12 internal pull-up
pinMode(REV_IN, INPUT_PULLUP); // D13 internal pull-up
pinMode(OUT1, OUTPUT);
pinMode(OUT2, OUTPUT);
pinMode(OUT3, OUTPUT);
pinMode(LATCH_OUT, OUTPUT);
pinMode(OUT_D6, OUTPUT);
latchState = false;
resetBeeperFSM();
digitalWrite(OUT_D6, HIGH); // default HIGH
// initialize debounce
stableRead(IN1);
stableRead(IN2);
stableRead(IN3);
stableRead(HOLD1);
stableRead(HOLD2);
stableRead(D13_ENABLE);
stableRead(REV_IN);
}
// ---------------- Main Loop ----------------
void loop() {
// -------- Read stable inputs once --------
bool in1State = stableRead(IN1); // D9
bool in2State = stableRead(IN2); // D8
bool in3State = stableRead(IN3); // D7
bool hold1State = stableRead(HOLD1); // D4
bool hold2State = stableRead(HOLD2); // D2
bool d12State = stableRead(D13_ENABLE); // D12
bool revState = stableRead(REV_IN); // D13
// -------- Mirror outputs --------
digitalWrite(OUT1, in1State);
digitalWrite(OUT2, in2State);
digitalWrite(OUT3, !in3State);
// -------- D6 sharp trigger logic --------
// D6 goes LOW only if:
// D8 == LOW
// D4 == LOW
// D2 == HIGH <-- prevents D6 from activating during D8 + D4 + D2 hold function
//
// D6 returns HIGH immediately if:
// D8 goes HIGH, or D4 goes HIGH, or D2 goes LOW
bool d6Condition = (in2State == LOW) &&
(hold1State == LOW) &&
(hold2State == HIGH);
unsigned long now = millis();
if (d6Condition) {
if (d6LowStartMs == 0) {
d6LowStartMs = now;
} else if (now - d6LowStartMs >= D6_TRIGGER_MS) {
d6Active = true;
}
} else {
d6LowStartMs = 0;
d6Active = false;
}
digitalWrite(OUT_D6, d6Active ? LOW : HIGH);
// -------- D12 controls whether D13 is allowed --------
bool d13Allowed = (d12State == LOW);
bool reverseActive = d13Allowed && (revState == LOW);
// -------- Priority selection for D3 mode --------
byte requestedMode;
if (reverseActive) {
requestedMode = MODE_REVERSE; // highest priority
} else if (latchState) {
requestedMode = MODE_ALERT;
} else {
requestedMode = MODE_OFF;
}
// -------- Service D3 beeper --------
serviceD3Beep(requestedMode);
// -------- Hold detection for D8 + D4 + D2 --------
bool holdOK = (hold1State == LOW) &&
(hold2State == LOW);
bool d8Low = (in2State == 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) {
latchState = !latchState;
mustReleaseHold = true;
longHoldStartMs = 0;
}
}
} else {
longHoldStartMs = 0;
}
}