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

  1. De-energize the board: Ensure the Arduino Nano is unplugged from USB before wiring.
  2. 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).
  3. 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.
  4. Wire the Indicator: Place the 220Ω resistor in series with the LED anode. Connect the resistor to D8 and the LED cathode to GND.
  5. 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Ω).
Callout Tip: Simplifying with Internal Pull-Ups
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:

  1. 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 to TILT_PIN.
  2. Reserved Keywords: You used a reserved C++ keyword as a variable name, most commonly int switch = 2;. The word switch is reserved for control flow statements. Fix: Use tiltSwitchPin instead.
  3. 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:

  1. 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.
  2. 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.
  3. 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_DELAY to 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.h library to put the Nano into powerDown sleep 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.