/*
Arduino: Button 1 latches Relay 1 ON; Button 2 latches Relay 2 ON
- Mutually exclusive "interlock": only one relay ON at a time
- Press Button 1 -> Relay 1 ON, Relay 2 OFF (latched)
- Press Button 2 -> Relay 2 ON, Relay 1 OFF (latched)
- Debounced button inputs
- Works on Uno/Nano/Pro Mini/MEGA (adjust pins if needed)
- Supports active-LOW (common) or active-HIGH relay modules
Wiring (default config below):
- BUTTON1_PIN (D2) -> One side of Button 1; other side -> GND
- BUTTON2_PIN (D3) -> One side of Button 2; other side -> GND
(We use INPUT_PULLUP, so buttons read LOW when pressed)
- RELAY1_PIN (D8) -> Relay module IN1
- RELAY2_PIN (D9) -> Relay module IN2
- Relay module VCC -> 5V (per module spec), GND -> GND
Behavior:
- Latching selection: a press on Button 1 selects Relay 1; a press on Button 2 selects Relay 2.
- Selection stays until the other button is pressed.
*/
#include <Arduino.h>
// ========================= User Configuration =========================
const uint8_t BUTTON1_PIN = 6; // D2
const uint8_t BUTTON2_PIN = 7; // D3
const uint8_t RELAY1_PIN = 8; // D8
const uint8_t RELAY2_PIN = 9; // D9
// Most common relay modules are active-LOW (LOW = ON). If yours is active-HIGH (HIGH = ON), set to true.
const bool RELAY_ACTIVE_HIGH = false;
// Debounce time in milliseconds (typical 20-50 ms)
const unsigned long DEBOUNCE_MS = 30;
// Startup selection: 0 = both OFF, 1 = Relay1 ON, 2 = Relay2 ON
const uint8_t STARTUP_ACTIVE = 0;
// =====================================================================
struct Button {
uint8_t pin;
bool lastReading; // raw reading from last sample
bool stableState; // debounced stable state
unsigned long lastChange; // ms timestamp when raw reading last changed
};
inline uint8_t relayLevel(bool on) {
if (RELAY_ACTIVE_HIGH) {
return on ? HIGH : LOW;
} else {
return on ? LOW : HIGH; // active-LOW: ON=LOW, OFF=HIGH
}
}
inline bool readButtonPressedRaw(uint8_t pin) {
// With INPUT_PULLUP: unpressed=HIGH, pressed=LOW
return digitalRead(pin) == LOW;
}
void initButton(Button &b, uint8_t pin) {
b.pin = pin;
pinMode(b.pin, INPUT_PULLUP);
bool r = readButtonPressedRaw(b.pin);
b.lastReading = r;
b.stableState = r;
b.lastChange = millis();
}
bool pressedEdge(Button &b) {
bool reading = readButtonPressedRaw(b.pin);
if (reading != b.lastReading) {
b.lastReading = reading;
b.lastChange = millis(); // reading changed, start debounce timer
}
bool edge = false;
if ((millis() - b.lastChange) >= DEBOUNCE_MS) {
if (b.stableState != reading) {
b.stableState = reading; // commit debounced state
edge = b.stableState == true; // pressed edge only
}
}
return edge;
}
// Tracks which relay is active: 0=none, 1=relay1, 2=relay2
uint8_t activeSelect = 0;
void applyRelayOutputs() {
digitalWrite(RELAY1_PIN, relayLevel(activeSelect == 1));
digitalWrite(RELAY2_PIN, relayLevel(activeSelect == 2));
}
Button btn1, btn2;
void setup() {
// Initialize buttons
initButton(btn1, BUTTON1_PIN);
initButton(btn2, BUTTON2_PIN);
// Initialize relays
pinMode(RELAY1_PIN, OUTPUT);
pinMode(RELAY2_PIN, OUTPUT);
activeSelect = STARTUP_ACTIVE;
applyRelayOutputs();
// Optional small delay for stabilization
delay(10);
}
void loop() {
// On a debounced press of Button 1: select Relay 1
if (pressedEdge(btn1)) {
activeSelect = 1;
applyRelayOutputs();
}
// On a debounced press of Button 2: select Relay 2
if (pressedEdge(btn2)) {
activeSelect = 2;
applyRelayOutputs();
}
// Tiny delay to reduce CPU noise/jitter (optional)
delay(1);
}
/*
Notes:
- If your relays appear inverted (ON when they should be OFF), toggle RELAY_ACTIVE_HIGH.
- If you want one relay ON by default at power-up, set STARTUP_ACTIVE to 1 or 2.
- Buttons only trigger on the press edge; holding a button does nothing extra.
Safety:
- If switching mains, use proper relay modules with isolation and follow electrical codes.
- Keep low-voltage Arduino circuitry isolated from high-voltage wiring.
*/