An Arduino tilt switch acts as a digital inclinometer, closing an internal circuit when tilted past a specific threshold angle. Unlike multi-axis accelerometers, these sensors are binary: they are either open or closed. To use an arduino tilt switch reliably in a project, you must wire it with a pull-up resistor (or enable internal pull-ups) and implement software debouncing to prevent false triggers caused by mechanical contact bounce. The SW-520D and SW-420 are the most common modules on the market, but they behave very differently under vibration.
SW-520D vs SW-420: Choosing the Right Arduino Tilt Switch
Before wiring anything to your microcontroller, you need to select the correct sensor for your physical environment. Tilt switches rely on a conductive rolling ball or mercury droplet that bridges two contacts. The angle of the enclosure and the mass of the ball dictate the trigger point and the mechanical bounce time. Below is a data-dense comparison of the most common tilt and vibration sensors used in embedded projects.
| Sensor Model | Trigger Angle / Type | Max Current Rating | Typical Bounce Time | Best Use Case |
|---|---|---|---|---|
| SW-520D | ~45° Tilt (Directional) | 10 mA @ 24V DC | 2ms - 5ms | Anti-theft alarms, screen rotation triggers |
| SW-420 | ~60° Tilt / High Vibration | 10 mA @ 24V DC | 10ms - 20ms | Knock sensors, washing machine imbalance |
| SW-200D | ~15° Tilt (Highly Sensitive) | 10 mA @ 24V DC | 1ms - 3ms | Leveling tools, precise tilt detection |
| Mercury (SCP-101) | ~30° Tilt (Omnidirectional) | 1A @ 120V AC | 0ms (Liquid bridge) | Legacy/Hazardous: Banned in EU/RoHS |
Expert Note: Never use a raw tilt switch to switch high-current loads like a 12V sump pump or a mains-powered siren. The 10 mA contact rating will weld the internal ball to the electrode, permanently shorting the sensor. Always use the tilt switch to trigger a GPIO pin, and let a MOSFET or mechanical relay handle the heavy load.
Hardware Wiring and Pin Mapping
For this build, we are targeting the Arduino Nano v3 (ATmega328P). The Nano is ideal for compact, battery-powered tilt alarms due to its low sleep-mode current draw. We will use the SW-520D sensor paired with an external 10kΩ pull-up resistor to ensure a clean HIGH signal when the switch is open.
Parts List
- Microcontroller: Arduino Nano v3 (ATmega328P, 5V/16MHz variant)
- Sensor: SW-520D Tilt Switch (bare component or pre-mounted PCB module)
- Resistors: 1x 10kΩ (Pull-up), 1x 220Ω (LED current limiting)
- Indicator: 5mm Red LED
- Wiring: 22 AWG solid core jumper wires
Pin Mapping Table
| Component | Pin / Terminal | Arduino Nano Pin | Notes |
|---|---|---|---|
| SW-520D | Pin 1 (Long Lead) | D2 (Digital Pin 2) | Also connects to 10kΩ pull-up to 5V |
| SW-520D | Pin 2 (Short Lead) | GND | Common ground |
| 10kΩ Resistor | Lead 1 | D2 | Connects to switch signal line |
| 10kΩ Resistor | Lead 2 | 5V (VCC) | Pulls line HIGH when switch is open |
| LED | Anode (Long leg) | D8 | Via 220Ω current limiting resistor |
| LED | Cathode (Short leg) | GND | Common ground |
Wiring Steps
- De-energize the board: Ensure the Arduino Nano is unplugged from USB before wiring.
- Wire the Pull-Up: Insert the 10kΩ resistor into the breadboard. Connect one end to the Nano's 5V pin and the other end to Digital Pin 2 (D2).
- Connect the Sensor: Insert the SW-520D. Connect the long lead to D2 (sharing the node with the pull-up resistor). Connect the short lead to GND.
- Wire the Indicator: Place the 220Ω resistor in series with the LED anode. Connect the resistor to D8 and the LED cathode to GND.
- Verify Continuity: Use a multimeter in continuity mode. With the sensor upright, the meter should read open (OL). Tilt it past 45°; it should beep (< 1Ω).
If you want to eliminate the external 10kΩ resistor, the ATmega328P has built-in 20kΩ-50kΩ pull-up resistors. You can activate these in software using
pinMode(TILT_PIN, INPUT_PULLUP);. This is documented in the official Arduino pinMode reference. However, for high-noise environments (like near AC motors), an external 4.7kΩ to 10kΩ physical resistor provides a stiffer, more noise-immune pull-up.
Compilable C++ Code with Hardware Debounce
Mechanical tilt switches suffer from contact bounce. When the internal ball hits the electrode, it physically bounces microscopically for a few milliseconds, generating dozens of rapid HIGH/LOW transitions. If you read the pin directly with digitalRead(), a single tilt event might register as 15 separate triggers. We solve this using a non-blocking millis() debounce timer, avoiding the blocking delay() function so the main loop remains responsive.
Target Board: Arduino Nano v3 (ATmega328P). No external libraries required.
// Arduino Tilt Switch Debounce Code
// Target: Arduino Nano v3 (ATmega328P)
#define TILT_PIN 2
#define LED_PIN 8
// Debounce configuration
const unsigned long DEBOUNCE_DELAY = 50; // 50ms window to ignore bounce
// State variables
int lastSwitchState = HIGH; // Assume pull-up keeps it HIGH initially
int currentSwitchState = HIGH;
int ledState = LOW;
unsigned long lastDebounceTime = 0;
void setup() {
Serial.begin(115200);
// Configure pins
pinMode(TILT_PIN, INPUT); // Using external 10k pull-up
pinMode(LED_PIN, OUTPUT);
digitalWrite(LED_PIN, ledState);
Serial.println("System initialized. Waiting for tilt...");
}
void loop() {
// Read the raw state of the tilt switch
int reading = digitalRead(TILT_PIN);
// Check if the state has changed (either from bounce or actual tilt)
if (reading != lastSwitchState) {
// Reset the debouncing timer
lastDebounceTime = millis();
}
// If the state has been stable for longer than the DEBOUNCE_DELAY
if ((millis() - lastDebounceTime) > DEBOUNCE_DELAY) {
// If the stable state is different from the current accepted state
if (reading != currentSwitchState) {
currentSwitchState = reading;
// The switch is active LOW (pulled to GND when tilted)
if (currentSwitchState == LOW) {
Serial.println("TILT DETECTED: Sensor triggered!");
ledState = HIGH;
} else {
Serial.println("RESET: Sensor returned to upright.");
ledState = LOW;
}
// Update the physical LED
digitalWrite(LED_PIN, ledState);
}
}
// Save the raw reading for the next loop iteration
lastSwitchState = reading;
}
Debugging: Compilation Errors and False Triggers
When integrating tilt switches into larger codebases, developers frequently encounter both compiler errors and erratic hardware behavior. If your circuit is not behaving as expected, run through these diagnostics.
The "Expected Unqualified-ID" Compilation Error
One of the most common errors beginners face when defining pins for sensors is this exact compiler output:
error: expected unqualified-id before numeric constant
Ranked Causes and Fixes:
- Invalid Variable Naming: You named your pin variable starting with a number, such as
int 2pin = 2;or#define 2TILT 2. C++ identifiers cannot start with a digit. Fix: Rename toTILT_PIN. - Reserved Keywords: You used a reserved C++ keyword as a variable name, most commonly
int switch = 2;. The wordswitchis reserved for control flow statements. Fix: UsetiltSwitchPininstead. - Missing Semicolon on Previous Line: The line immediately above your pin definition is missing a semicolon, causing the compiler to misinterpret the next line. Fix: Check the preceding line.
Hardware Debugging: The First Three Things to Check
If the code compiles and uploads, but the Serial Monitor shows erratic triggers or no triggers at all, check these three physical parameters:
- Verify the Pull-Up Configuration: A floating GPIO pin will act as an antenna, picking up 50/60Hz mains noise and causing random triggers. If you removed the external 10kΩ resistor, ensure your code explicitly calls
pinMode(TILT_PIN, INPUT_PULLUP);. Measure the voltage at D2 with a multimeter; it should read a steady ~5V when upright. - Check Physical Resting Orientation: The SW-520D is highly directional. If it is rotated 90 degrees on the breadboard, the ball may rest on the contacts even when the board is flat. Ensure the flat side of the sensor casing aligns with the intended axis of tilt.
- Measure Mechanical Bounce Time: If you are still seeing double-triggers in the Serial Monitor, your physical sensor might be worn or heavily vibrated, exceeding the 50ms software debounce window. As noted in Jack Ganssle's definitive Guide to Debouncing, heavily vibrated environments may require increasing the
DEBOUNCE_DELAYto 100ms or 200ms, or switching to a solid-state MEMS accelerometer like the MPU-6050.
Extending and Simplifying the Build
Once you have a stable, debounced tilt detection system, you can adapt the architecture for specific production or hobbyist constraints.
How to Simplify for Rapid Prototyping
If you are building a quick proof-of-concept and want to minimize breadboard clutter, drop the external 10kΩ resistor entirely. Change the setup code to pinMode(TILT_PIN, INPUT_PULLUP);. This leverages the ATmega328P's internal silicon resistors. While the internal resistance varies between 20kΩ and 50kΩ per chip, it is more than sufficient to pull a 10-foot wire up to VCC for a simple desk alarm.
How to Extend for Low-Power Battery Security
Polling a GPIO pin in the loop() keeps the microcontroller awake, drawing ~15mA continuously. If you are building a battery-powered shed alarm, you need the Arduino to sleep and wake only when tilted.
To extend this build for ultra-low power:
- Move the tilt switch to Digital Pin 2 (INT0) or Digital Pin 3 (INT1), as these support hardware interrupts on the ATmega328P.
- Replace the polling logic with
attachInterrupt(digitalPinToInterrupt(TILT_PIN), wakeUp, FALLING);. - Implement the
LowPower.hlibrary to put the Nano intopowerDownsleep mode. The physical closing of the tilt switch will trigger the hardware interrupt, instantly waking the CPU to sound a piezo buzzer or transmit an MQTT alert via an ESP8266 co-processor.
By understanding the mechanical limitations of the rolling ball sensor and pairing it with robust non-blocking debounce logic, you can build highly reliable physical security and orientation triggers without the cost or complexity of a 6-axis IMU.






