Four-way switch logic on an Arduino Uno
Three slide switches model the two ends and a reversing intermediate of a three-way plus four-way run, and an Uno lights an LED on their parity; a logic model on low-voltage stand-ins, not house wiring. The whole build, an Arduino Uno and 5 more parts, runs here in your browser on the firmware below; open it in the editor to change the wiring or the code and run it again.
Teaching? Assign it to your class in one click.
Intermediate About 20 minutes to build and run. Runs in your browser. Free, and no account needed.
The parts retail for about $31. On Mokxi they're free.
What you'll learn
- Lights
- School
- Choosing and reading a resistor
- How an LED lights and why it needs a resistor
- Difficulty: Intermediate
- Time: About 20 minutes
- Board: Arduino Uno R3
- Parts: 7
How it works
Three slide switches model the two ends and a reversing intermediate of a three-way plus four-way run, and an Uno lights an LED on their parity; a logic model on low-voltage stand-ins, not house wiring.
Arduino Uno R3 runs the sketch below. The circuit around it: Slide switch, Resistor, 220 Ω, LED, red. pinMode sets SW_A as an input with its pull-up on. digitalRead reads whether SW_A is HIGH or LOW. Serial.begin opens the serial port at 115200 baud. millis reads the time since power-up, so the sketch can time things without stopping.
Parts list
7 parts, plus the jumper wires. They retail for about $31; on Mokxi they're free.
- 1 × Arduino Uno R3In Starter Kit, Claw Machine Kit, Vending Machine Kit.
- 1 × Half breadboard
- 3 × Slide switchIn Logic Lab Kit, Spaceship Panel Kit.
- 1 × Resistor, 220 ΩIn Starter Kit, Claw Machine Kit, Halloween Prop Kit.
- 1 × LED, redIn Starter Kit, Halloween Prop Kit, Logic Lab Kit.
Wiring table
Every connection, pin by pin, read from the circuit itself. Each row joins the pin on the left to the pin on the right, by a jumper wire or a breadboard strip.
| Part and pin | Connects to |
|---|---|
| Slide switch (1) pin 1 | Arduino Uno R3 pin GND |
| Slide switch (2) pin 1 | Arduino Uno R3 pin GND |
| Slide switch (3) pin 1 | Arduino Uno R3 pin GND |
| LED, red pin C | Arduino Uno R3 pin GND |
| Resistor, 220 Ω pin 1 | Arduino Uno R3 pin 13 |
| Slide switch (3) pin 2 | Arduino Uno R3 pin 4 |
| Slide switch (2) pin 2 | Arduino Uno R3 pin 3 |
| Slide switch (1) pin 2 | Arduino Uno R3 pin 2 |
| LED, red pin A | Resistor, 220 Ω pin 2 |
The code
The firmware exactly as the editor opens it. Change a line there and press Run: it compiles in the browser.
// Four-way switch logic: three switches, one lamp, flip any of them.
//
// TEACHING MODEL, NOT HOUSE WIRING. This is a logic model on low-voltage
// stand-ins. It is not a way to wire a building, and nothing here is safe to
// connect to mains or to a real lighting circuit.
//
// pin 2 switch A, the first end (SPDT slide switch)
// pin 3 switch B, the intermediate (see below)
// pin 4 switch C, the second end (SPDT slide switch)
// pin 13 the lamp: a red LED through 220 ohms to GND
//
// Each switch has its common (pin 2 of the slide switch) on the Uno pin and
// one throw on GND. The pins use INPUT_PULLUP, so a switch slid to the GND
// side reads 0 and a switch slid to the open side reads 1.
//
// HOW THE REAL THING WORKS. A three-way run is two SPDT switches joined by
// two traveler wires. Switch 1 feeds the hot to one traveler, switch 2
// listens on one traveler, and the light is on when they match up. Flipping
// either end changes the match, so the light toggles.
//
// A four-way switch goes in the middle of the travelers. It is a DPDT switch
// wired as a reversing switch: in one position it passes the two travelers
// straight through, in the other it swaps them (it crosses them). Swapping
// the travelers is exactly the same as flipping one end switch: whichever
// traveler was live is now the other one. That is why every extra
// intermediate switch flips the parity of the whole circuit, and why you can
// add as many four-ways as you like between the two three-way ends.
//
// So the lamp is the parity (XOR) of all the switch positions:
//
// lamp = A xor B xor C
//
// With an even number of switches at 1 the lamp is off; with an odd number it
// is on.
//
// WHAT THE MODEL DOES. The catalog has no DPDT reversing switch (the DPDT
// part is a relay), so the intermediate is modeled as one more slide switch
// read as a logic input, and the crossing of the travelers is done in code by
// the XOR. The sketch does not wire any traveler; it computes what the
// travelers would deliver.
//
// SERIAL (115200). A truth table for three switches prints once at boot, then
// one line every time any switch flips, like: A=1 B=0 C=1 lamp=0
const int SW_A = 2;
const int SW_B = 3;
const int SW_C = 4;
const int LAMP = 13;
const unsigned long DEBOUNCE_MS = 20;
int stateA = 0;
int stateB = 0;
int stateC = 0;
int lastRaw = -1;
unsigned long changedAt = 0;
// The lamp is the parity of the switch positions.
int lampFor(int a, int b, int c) {
return a ^ b ^ c;
}
int readRaw() {
// 1 when the switch is slid to its open side (the pull-up reads HIGH).
int a = digitalRead(SW_A) == HIGH ? 1 : 0;
int b = digitalRead(SW_B) == HIGH ? 1 : 0;
int c = digitalRead(SW_C) == HIGH ? 1 : 0;
return (a << 2) | (b << 1) | c;
}
void printRow(int a, int b, int c) {
Serial.print("A=");
Serial.print(a);
Serial.print(" B=");
Serial.print(b);
Serial.print(" C=");
Serial.print(c);
Serial.print(" lamp=");
Serial.println(lampFor(a, b, c));
}
void setup() {
Serial.begin(115200);
Serial.println("Mokxi Arduino Uno: four-way switch logic");
Serial.println("lamp = A xor B xor C");
Serial.println("truth table:");
for (int i = 0; i < 8; i++) {
printRow((i >> 2) & 1, (i >> 1) & 1, i & 1);
}
pinMode(SW_A, INPUT_PULLUP);
pinMode(SW_B, INPUT_PULLUP);
pinMode(SW_C, INPUT_PULLUP);
pinMode(LAMP, OUTPUT);
int raw = readRaw();
lastRaw = raw;
stateA = (raw >> 2) & 1;
stateB = (raw >> 1) & 1;
stateC = raw & 1;
digitalWrite(LAMP, lampFor(stateA, stateB, stateC) ? HIGH : LOW);
Serial.print("now: ");
printRow(stateA, stateB, stateC);
}
void loop() {
int raw = readRaw();
if (raw != lastRaw) {
lastRaw = raw;
changedAt = millis();
}
// Believe a new position once it has held for a few milliseconds.
int held = (raw != ((stateA << 2) | (stateB << 1) | stateC)) && (millis() - changedAt >= DEBOUNCE_MS);
if (held) {
stateA = (raw >> 2) & 1;
stateB = (raw >> 1) & 1;
stateC = raw & 1;
digitalWrite(LAMP, lampFor(stateA, stateB, stateC) ? HIGH : LOW);
printRow(stateA, stateB, stateC);
}
}
Code walkthrough
The key functions of the sketch, and what each one does.
setup sketch.ino
Runs once when the board starts. It gets the pins and libraries ready.
void setup() {
Serial.begin(115200);
Serial.println("Mokxi Arduino Uno: four-way switch logic");
Serial.println("lamp = A xor B xor C");
Serial.println("truth table:");
for (int i = 0; i < 8; i++) {
printRow((i >> 2) & 1, (i >> 1) & 1, i & 1);
}
pinMode(SW_A, INPUT_PULLUP);
pinMode(SW_B, INPUT_PULLUP);
pinMode(SW_C, INPUT_PULLUP);
pinMode(LAMP, OUTPUT);
int raw = readRaw();
lastRaw = raw;
stateA = (raw >> 2) & 1;
stateB = (raw >> 1) & 1;
stateC = raw & 1;
digitalWrite(LAMP, lampFor(stateA, stateB, stateC) ? HIGH : LOW);
Serial.print("now: ");
printRow(stateA, stateB, stateC);
}loop sketch.ino
Runs over and over for as long as the board has power. This is where the project does its work.
void loop() {
int raw = readRaw();
if (raw != lastRaw) {
lastRaw = raw;
changedAt = millis();
}
// Believe a new position once it has held for a few milliseconds.
int held = (raw != ((stateA << 2) | (stateB << 1) | stateC)) && (millis() - changedAt >= DEBOUNCE_MS);
if (held) {
stateA = (raw >> 2) & 1;
stateB = (raw >> 1) & 1;
stateC = raw & 1;
digitalWrite(LAMP, lampFor(stateA, stateB, stateC) ? HIGH : LOW);
printRow(stateA, stateB, stateC);
}
}Troubleshooting
The usual mistakes with this circuit, and the fix for each.
- The input reads random values in one position. Slide switch
A switch only connects or disconnects. Add a pull-up or pull-down resistor, or use INPUT_PULLUP, so the pin has a defined level when the switch is open. - The load never turns on when the switch is flipped. Slide switch
Use the center (common) leg and one outer leg. The unused outer leg goes nowhere. Check that the switch is in series with the load. - The LED is dim, burns out, or the pin reads wrong because the resistor value is off. Resistor
Read the color bands again. A 220 ohm to 330 ohm resistor suits a typical LED on 5 V. 10k is a common pull-up or pull-down value. Check with a multimeter if unsure. - A pull-up or pull-down resistor is missing, so the input floats and reads random values. Resistor
Tie the input to 5 V or ground through about 10k. Or use INPUT_PULLUP and wire the switch to ground. - The LED does not light, or it lights once and then dies. LED
LEDs have polarity. The longer leg (anode) goes toward the positive side. Always add a series resistor, about 220 ohms to 1 k ohm on a 5 V pin. - A blue or white LED stays dark on 3.3 V. LED
Blue and white LEDs drop about 3 V, so 3.3 V leaves almost nothing for the resistor. Use a 5 V supply, or a smaller resistor and a red or green LED. - Upload fails or nothing runs after wiring pins 0 and 1 to a module. Arduino Uno R3
Pins 0 and 1 are the USB serial port. Move the module to other pins, or unplug it while uploading. Also check the right board is selected. - Pins misbehave or the board resets when you power motors or servos from it. Arduino Uno R3
Each I/O pin gives about 20 mA, 40 mA max. Power motors and servos from their own supply and join its ground to the Uno's GND.
Change it and keep it
Open it in the editor, change the circuit or the code, and keep your version in a free account.