Arduino Activity 5: Tilt Sensor

Arduino Activity 5: Tilt Sensor

by ACROBOTIC Industries


Time to complete: 2–5min; Level of difficulty: Beginner

In this activity we’ll build an electronic circuit with a tilt sensor that will control the on/off state of an LED using our Arduino board.

List of Materials

  • 1 x Arduino Uno R3
  • 1 x USB-A to USB-B Male / Male Cable
  • 1 x 5mm Color LED Set (10-Pack)
  • 1 x Resistor 1/4W Through Hole (1KOhm)
  • 1 x Tilt Sensor (Ball Switch)
  • 1 x Jumper Wires Male/Male - 10-pack
  • 1 x Mounting Plate for Arduino Uno
  • 1 x Half-Sized Solderless Breadboard

Obtaining the Code

For this activity, we'll be loading a program to our Arduino Uno board, it's available in our Github repository:

We can open a new window in the Arduino environment and copy-paste the code.

How Tilt Sensors Work

These ball switch-type tilt sensors are a type of digital switch that change state from open to closed based on its inclination relative to the earth's gravitational field vector.  Electrically, they behave much like the push button we've used in the past where contacts are conducting when the switch closes, and not conducting when it opens.  Mechanically, they usually consist of a hollow shell (typically cylindrical) and a conductive free mass inside (typically a blob of mercury or a rolling, metallic ball); on one end of the shell the sensor has two or more conductive elements that become shorted when the sensor's orientation is such that the free mass inside rolls and makes physical contact with them.

Tilt Sensor Gutted

Depending on the shape and size of the sensor's shell, and the weight of the free mass inside, the angle at which a sensor opens/closes is different.  This, of course, is not as precise as say accelerometer-based inclinometers, but they offer a very inexpensive solution for detecting when something tilts past a set angle.  In addition, they can switch power on and off (like any other switch) without the additional circuitry that typical accelerometer-based inclinometers need.

The ball switch tilt sensor we'll use on this tutorial features:

  • Type: Cylindrical 
  • Size: 4mm (0.16") diameter & 12mm (0.45") long 
  • Sensitivity range: > ±15 degrees 
  • Lifetime: 50,000+ cycles (switches) 
  • Power supply: Up to 24V, switching less than 5mA

Testing the Tilt Sensor's Operation

Using a Digital Multimeter tool, we can quickly test the operation of this sensor.  Selecting the continuity option, we can connect each test probe to one of the sensor leads.

Open Tilt Sensor Tested with Voltmeter

Next, we notice that when the sensor is upright, the mulitmeter reads a low resistance value.  This means that the 2 leads are in contact, and thus the switch is closed. 

Closed Tilt Sensor Tested with Voltmeter

As we gently incline the sensor from its upright position, there comes a moment when the Mutltimeter indicates a large reading of resistance.  At this point the switch is open, and the angle at which the transition occurs determines the tilt threshold of the sensor.

Example Project: Tilt-activated LED Candlelight

These little sensors can be seen out in the wild at restaurants or cafes in which flameless candles are used for decoration.  Although some will have standard slide switch for powering it on or off, a number of these tabletop LED candlelights include a ball switch. Simply flip them over to turn them on or off.

Tilt Activated LED Candles

We also carry in our store a custom DIY Kit for building your own flameless candles!

Wiring: Tilt Detector

The wiring for this exercise is identical to the one we used for the Push Button tutorial.  We connect one pin of the tilt sensor to GND (ground) using one of the side columns on the breadboard.  Then, we connect the other pin to pin 2 on the Arduino board using a jumper wire connected to the same row as the sensor pin.

Wiring Diagram for Arduino Tilt Sensor Activity

We'll use the same circuit for the LED as in previous tutorials.  The LED, as usual, will serve as a visual indicator for when the tilt detector switches states (open or closed).

Interrupts: A Brief Introduction

As their name implies interrupts—in the context of software and embedded electronics hardware—are signals emitted to the processor that indicate an event requiring immediate attention.  When these signals are generated (whether by software or hardware) the processor stops the normal execution of its current program, saves its state, and executes an Interrupt Service Routine (ISR) to handle the event.  After the event is handled, the processor resumes the normal execution of its program until a new event occurs. 

There are two types of interrupts: hardware (a.k.a external) interrupts and software interrupts.  In this activity, we'll be using a hardware interrupt to detect a change in the state of the tilt sensor.  The interrupt will trigger an ISR that will wake up the microcontroller and return to the normal execution of the loop() function.

The main advantage in this case is that we do not have to worry about monitoring the state of pin 2 inside the loop() function (as we did previously using the digitalRead() function).  This way, our program doesn't waste any time polling the pin, allowing it to perform (almost) any other task without us worrying whether the event will get detected (it always will!).  In fact, the Tilt Detector program sets the microcontroller to a low-power (sleep) mode, saving power until an interrupt is emitted.

Check out the full list of interrupt vectors on the AVR Libc package's page.

Code Walkthrough: Tilt Detector

Given that the tilt sensor behaves much like the push button in our previous tutorials, we take the opportunity to introduce the concept of interrupts, and explore the microcontroller's low-power (sleep) modes.  For using the low-power features of the microcontroller we'll need to include an external library available through our default installation of the Arduino IDE:


As for interrupts, the microcontroller on the Arduino Uno has two external/hardware ones: number 0 on pin 2 and number 1 on pin 3.  We use the function attachInterrupt() to specify another function—no arguments/parameters allowed—that'll be called when the interrupt occurs (referred to as an Interrupt Service Routine or ISR for short).  In this case, the function enableSleep() will be called when the state of pin 2 (interrupt 0) changes from HIGH to LOW or viceversa.  This is set up inside the setup() function by:

attachInterrupt(0, enableSleep, CHANGE);

In the loop() function we make our microcontroller go to sleep until an interrupt event occurs.  To do this we make use of the variable sleep_flag. The variable is initialized to true, making the program call the user-defined function system_sleep()

  // Check whether the flag has been enabled by the ISR (true for the first 
  // run)
  if (sleep_flag)
    system_sleep(); // function to configure and execute low power mode

The function system_sleep() is responsible for configuring and putting the microcontroller into low-power (i.e., sleep) mode:

void system_sleep()
  set_sleep_mode(SLEEP_MODE_PWR_DOWN); // Set sleep mode
  digitalWrite(led_pin, LOW); // Make sure the LED is turned off
  sleep_mode(); // System sleeps here
  // Code resumes here on wake!
  sleep_flag = false; //Reset sleep flag on wake

When the interrupt event occurs, 2 things should happen: the sleep_flag variable should be set to true inside the ISR, and the code should return to execution right after the sleep_mode() function call.  We know that the ISR should occur first, thus we expect for the sleep_flag variable to be set to true, and then the code should resume inside the user-defined function system_sleep().

// Named Interrupt Service Routine attached to int.0 will
// set the sleep_flag variable to true, thus the main
void enableSleep()
  sleep_flag = true;

At this point the variable sleep_flag is set to false and the code returns to the loop() function.  If our logic is sound, the system_sleep() function call should now be ignored, and the loop() function code should make the LED display a heartbeat animation until a new interrupt occurs.

  // Code only gets to this point if/when the sleep flag is false, indicating
  // that an interrupt event was generated
  analogWrite(led_pin, value); //set the current LED brightness
  delay(step_delay_ms); //hold the value for a short time
  value+=step_size; //increase the current value for the next iteration

  // if we've reached the maximum or minimum value, change direction of
  // change in brightness (i.e., increasing -> decreasing or viceversa)
  if( (value>value_max)||(value<0) )
    step_size *= -1;
    value+=step_size; // ensure that value stays within bounds
    if(value == 0)
      delay(cycle_delay_ms); // hold 1 second off starting each cycle


Noticing some erratic behavior when the sensor changes from open to closed? Make sure to read about Debouncing in our Push Button tutorial for more info!

Leave a comment

  • Please note, comments must be approved before they are published

$1 Days
$2 Hours
$3 Minutes
$4 Second
{ "en":{ "general": { "field": { "required": "Required", "actions": "Actions", "top_btn": "Top" }, "accessibility": { "skip_to_content": "Skip to content", "close_modal": "Close (esc)" }, "meta": { "tags": "Tagged \"[[ tags ]]\"", "page": "Page [[ page ]]" }, "404": { "title": "404 Page Not Found", "subtext": "The page you requested does not exist.", "link": "Continue shopping" }, "pagination": { "previous": "Previous", "next": "Next", "current_page": "Page [[ current ]] of [[ total ]]" }, "password_page": { "opening_soon": "Opening Soon", "login_form_heading": "Enter store using password", "login_form_password_label": "Password", "login_form_password_placeholder": "Your password", "login_form_submit": "Enter", "signup_form_email_label": "Email", "signup_form_success": "We will send you an email right before we open!", "admin_link_html": "Are you the store owner? Log in here<\/a>", "password_link": "Enter using password", "powered_by_shopify_html": "This shop will be powered by [[ shopify ]]" }, "social": { "share_on_facebook": "Share", "share_on_twitter": "Tweet", "share_on_pinterest": "Pin it", "alt_text": { "share_on_facebook": "Share on Facebook", "share_on_twitter": "Tweet on Twitter", "share_on_pinterest": "Pin on Pinterest" } }, "search": { "no_results_html": "Your search for \"[[ terms ]]\" did not yield any results.", "results_with_count": { "one": "[[ count ]] result for \"[[ terms ]]\"", "other": "[[ count ]] results for \"[[ terms ]]\"" }, "title": "Search our site", "placeholder": "Search", "submit": "Submit", "close": "Close search" }, "newsletter_form": { "newsletter_email": "Join our mailing list", "email_placeholder": "Email address", "confirmation": "Thanks for subscribing", "submit": "Subscribe", "show_me_text": "Do not show me again" }, "filters": { "show_more": "Show More", "show_less": "Show Less" }, "breadcrumbs": { "home": "Home", "create_account": "Create account", "account": "Account", "addresses": "Addresses" }, "item": { "remove": "Remove Item" } }, "sections": { "header": { "top_header_login": "Login", "top_header_register": "Register", "top_header_wishlist": "Wish list", "register_dropdown": "No account? Create one here", "forgot": "Forgot password", "all_collection": "All Collections", "world_wide_delivery": "Worldwide delivery", "shipping_text": "Free UK Delivery on orders over £ 100", "hot_line": "Hot line" }, "menu": { "mobile_menu_tab": "Menu", "mobile_account_tab": "Account", "mobile_settings_tab": "Settings" }, "slideshow": { "next_slide": "Next slide", "previous_slide": "Previous slide", "pause_slideshow": "Pause slideshow", "play_slideshow": "Play slideshow", "play_video": "Play video", "close_video": "Close video" }, "map": { "get_directions": "Get directions", "address_error": "Error looking up that address", "address_no_results": "No results for that address", "address_query_limit_html": "You have exceeded the Google API usage limit. Consider upgrading to a Premium Plan<\/a>.", "auth_error_html": "There was a problem authenticating your Google Maps account. Create and enable the JavaScript API<\/a> and Geocoding API<\/a> permissions of your app." } }, "blogs": { "article": { "view_all": "View all", "all_topics": "All topics", "by_author": "by [[ author ]]", "posted_in": "Posted in", "read_more": "Read more", "back_to_blog": "Back to [[ title ]]" }, "comments": { "title": "Leave a comment", "name": "Name", "email": "Email", "message": "Message", "post": "Post comment", "moderated": "Please note, comments must be approved before they are published", "success_moderated": "Your comment was posted successfully. We will publish it in a little while, as our blog is moderated.", "success": "Your comment was posted successfully! Thank you!", "comments_with_count": { "one": "[[ count ]] comment", "other": "[[ count ]] comments" } } }, "cart": { "general": { "title": "Your cart", "note": "Add a note to your order", "remove": "Remove", "subtotal": "Subtotal", "savings": "You're saving", "shipping_at_checkout": "Shipping & taxes calculated at checkout", "update": "Update", "checkout": "Process Check out", "empty": "Your cart is currently empty.", "cookies_required": "Enable cookies to use the shopping cart", "edit": "Edit", "cancel": "Cancel", "continue_shopping": "Continue shopping", "recently_added_item": "Recently added item(s)", "remove_item": "Remove This Item", "view_and_edit_cart": "View and edit cart", "clear": "Clear cart", "empty_page_title": "Shopping Cart is Empty", "here": "here", "empty_continue_html": "Click here to continue shopping.", "processing": "Processing...", "items_count_label" : "[[ count ]] item(s) in your cart", "ok" : "Ok" }, "label": { "product": "Product", "price": "Price", "quantity": "Quantity", "total": "Total", "total_item": "Total item", "sub_total_top": "Cart Subtotal" } }, "collections": { "general": { "view_all": "View all", "clear_all": "Clear All", "no_matches": "Sorry, there are no products in this collection", "items_with_count": { "one": "[[ count ]] product", "other": "[[ count ]] products" }, "load_more": "Load More", "sidebar_btn": "Filter by" }, "sorting": { "title": "Sort by", "manual": "Featured", "best_selling": "Best Selling", "title_ascending": "Alphabetically, A-Z", "title_descending": "Alphabetically, Z-A", "price_ascending": "Price, low to high", "price_descending": "Price, high to low", "created_descending": "Date, new to old", "created_ascending": "Date, old to new" }, "filters": { "title_tags": "Filter", "all_tags": "All products", "categories": "categories", "title": "Filter", "color": "Color", "size": "Size", "brand": "Brand", "price": "Price", "green": "Green", "blue": "Blue", "red": "Red", "pink": "Pink", "black": "Black", "purple": "Purple", "white": "White", "orange": "Orange" }, "product_item": { "quick_shop": "Quick View", "compare": "Compare", "wishlist": "Add to Wishlist" } }, "contact": { "form": { "name": "Name", "email": "Email", "phone": "Phone Number", "message": "Message", "submit": "Submit", "post_success": "Thanks for contacting us. We'll get back to you as soon as possible.", "address": "Address", "telephone": "Telephone", "title": "Write us", "required": "Required" } }, "customer": { "account": { "title": "My Account", "details": "Account Details", "view_addresses": "View Addresses", "return": "Return to Account Details" }, "activate_account": { "title": "Activate Account", "subtext": "Create your password to activate your account.", "password": "Password", "password_confirm": "Confirm Password", "submit": "Activate Account", "cancel": "Decline Invitation" }, "addresses": { "title": "Your Addresses", "default": "Default", "add_new": "Add a New Address", "edit_address": "Edit address", "first_name": "First Name", "last_name": "Last Name", "company": "Company", "address1": "Address1", "address2": "Address2", "city": "City", "country": "Country", "province": "Province", "zip": "Postal\/Zip Code", "phone": "Phone", "set_default": "Set as default address", "add": "Add Address", "update": "Update Address", "cancel": "Cancel", "edit": "Edit", "delete": "Delete", "delete_confirm": "Are you sure you wish to delete this address?" }, "login": { "title": "Login", "desc": "If you have an account, sign in with your email address.", "email": "Email", "password": "Password", "forgot_password": "Forgot your password?", "sign_in": "Sign In", "guest_title": "Continue as a guest", "guest_continue": "Continue" }, "orders": { "title": "Order History", "order_number": "Order", "date": "Date", "payment_status": "Payment Status", "fulfillment_status": "Fulfillment Status", "total": "Total", "none": "You haven't placed any orders yet." }, "order": { "title": "Order [[ name ]]", "date": "Placed on [[ date ]]", "cancelled": "Order Cancelled on [[ date ]]", "cancelled_reason": "Reason: [[ reason ]]", "billing_address": "Billing Address", "payment_status": "Payment Status", "shipping_address": "Shipping Address", "fulfillment_status": "Fulfillment Status", "discount": "Discount", "shipping": "Shipping", "tax": "Tax", "product": "Product", "sku": "SKU", "price": "Price", "quantity": "Quantity", "total": "Total", "fulfilled_at": "Fulfilled [[ date ]]", "subtotal": "Subtotal" }, "recover_password": { "title": "Reset your password", "email": "Email", "submit": "Submit", "cancel": "Cancel", "subtext": "We will send you an email to reset your password.", "success": "We've sent you an email with a link to update your password." }, "reset_password": { "title": "Reset account password", "subtext": "Enter a new password for [[ email ]]", "password": "Password", "password_confirm": "Confirm Password", "submit": "Reset Password" }, "register": { "title": "Create Account", "first_name": "First Name", "last_name": "Last Name", "email": "Email", "password": "Password", "submit": "Create", "desc": "Creating an account is easy. Just fill in the form below." } }, "homepage": { "onboarding": { "product_title": "Your product's name", "product_description": "This area is used to describe your product’s details. Tell customers about the look, feel, and style of your product. Add details on color, materials used, sizing, and where it was made.", "collection_title": "Your collection's name", "blog_title": "Your post's title", "blog_excerpt": "Your store hasn’t published any blog posts yet. A blog can be used to talk about new product launches, tips, or other news you want to share with your customers. You can check out Shopify’s ecommerce blog for inspiration and advice for your own store and blog.", "blog_author": "Author name", "no_content": "This section doesn’t currently include any content. Add content to this section using the sidebar." } }, "layout": { "navigation": { "search": "Search", "toggle": "expand\/collapse", "expand": "expand", "collapse": "collapse", "all_categories": "All Categories" }, "cart": { "title": "Cart", "items_count": { "one": "item", "other": "items" } }, "customer": { "account": "Account", "log_out": "Log out", "logout": "Log out", "log_in": "Log in", "create_account": "Create account", "sign_up": "Sign up", "wishlist": "Wishlist" }, "footer": { "social_platform": "[[ name ]] on [[ platform ]]" }, "list_page": { "grid": "Grid", "list": "List" } }, "products": { "product": { "regular_price": "Regular price", "sold_out": "Sold out", "unavailable": "Unavailable", "on_sale": "Sale", "quantity": "Quantity", "add_to_cart": "Add to cart", "back_to_collection": "Back to [[ title ]]", "related_title": "Related Products", "qty_increase": "Increase", "qty_decrease": "Decrease", "deal_days": "Days", "deal_hours": "Hours", "deal_minutes": "Minutes", "deal_second": "Second", "select_option": "Select Option", "add_to_wishlist": "Add to Wishlist", "add_to_review": "Add to review", "compare_success_msg": "[[ product_title ]] has added to comparing box successful", "compare_exist_msg": "[[ product_title ]] is exist in comparing box", "compare_cart_msg": "[[ product_title ]] has added to shopping cart", "compare_remove_msg": "[[ product_title ]] has removed from comparing box", "compare_remove_msg": "[[ product_title ]] has removed from comparing box", "comparing_box": "Comparing box", "compare_no_items": "There is no items in comparing box", "wishlist_success_msg": "[[ product_title ]] has added to wishlist successful", "wishlist_exist_msg": "[[ product_title ]] is exist in wishlist", "wishlist_cart_msg": "[[ product_title ]] has added to shopping cart", "wishlist_box": "Wishlist", "wishlist_remove_msg": "[[ product_title ]] has removed from wishlist", "wislist_no_items": "There is no items in wishlist", "upsell_cart_msg": "\"[[ product_title ]]\" has added to shopping cart", "upsell_block_title": "Frequently bought with \"[[ product_title ]]\"", "upsell_cart_qty": "[[ count ]] item(s)", "upsell_product_page_title": "You may also like these products", "upsell_checkout_btn": "Checkout", "share": "Share product", "share_on_facebook": "Share on Facebook", "share_on_twitter": "Share on Twitter", "share_on_pinterest": "Share on Pinterest", "share_on_google": "Share on Google+", "share_on_linkedin": "Share on LinkedIn", "availability": "Availability", "in_stock": "In Stock", "out_of_stock": "Out of stock", "quick_overview": "Quick Overview", "details": "Details", "reviews": "Reviews", "first_review": "Be the first review", "tags": "Product Tags", "size_chart": "Size Chart", "options": "Options", "vendor": "Vendor", "features": "Features", "sale_left_text": "[[ sales ]] SOLD. HURRY! ONLY A FEW LEFT!", "checkout_text": "Secured and trusted checkout with" }, "upsell": { "recommend_text": "Someone purchased a", "minute_ago": "minutes ago" } }, "gift_cards": { "issued": { "title_html": "Here's your [[ value ]] gift card for [[ shop ]]!", "subtext": "Your gift card", "disabled": "Disabled", "expired": "Expired on [[ expiry ]]", "active": "Expires on [[ expiry ]]", "redeem_html": "Use this code at checkout to redeem your [[ value ]] gift card", "shop_link": "Start shopping", "print": "Print this gift card", "remaining_html": "[[ balance ]] left", "add_to_apple_wallet": "Add to Apple Wallet" } }, "date_formats": { "month_day_year": "%B %d, %Y" } } }