If you need to measure tilt, vibration, or motion in a microcontroller project, picking the right acceleration sensor for Arduino comes down to your power budget and whether you also need rotational data. For 90% of pure motion and tap-detection projects, the STMicroelectronics LIS3DH is the default pick. If you are building a drone or balancing robot that requires gyroscopic data alongside acceleration, you need the InvenSense MPU6050.
This guide cuts through the datasheet noise to give you a concrete decision matrix, exact wiring diagrams for 5V logic boards, and fully compilable C++ code with built-in I2C error handling.
The Acceleration Sensor Decision Matrix
Do not buy a sensor until you have matched your physical requirements to the silicon capabilities. Here is the decision path for the four most common modules found on the maker market in 2026.
| Criteria | LIS3DH (3-Axis Accel) | MPU6050 (6-Axis IMU) | ADXL345 (3-Axis Accel) | ADXL335 (Analog Accel) |
|---|---|---|---|---|
| Primary Use Case | Wearables, tap detection, low-power tilt | Drones, balancing robots, AHRS | High-G impact logging, older designs | Simple tilt, no-I2C fallback |
| Interface | I2C / SPI | I2C | I2C / SPI | Analog Voltage |
| Active Current | ~11 µA (Low power mode) | ~3.9 mA | ~140 µA | ~350 µA |
| Hardware Tap Detect? | Yes (Single & Double) | No (Requires MCU polling) | Yes (Single & Double) | No |
| Typical Breakout Price | $9.95 (Adafruit) / $2.50 (Generic) | $4.00 (Generic GY-521) | $12.95 (SparkFun) / $3.00 (Generic) | $5.00 (Generic) |
Hardware Spec Sheet and Parts List
The code and wiring below target the Arduino Nano v3 (ATmega328P) paired with the Adafruit LIS3DH Triple-Axis Accelerometer Breakout. We are using the Adafruit version for the primary build because raw, generic LIS3DH modules (often labeled GY-61 or similar) lack onboard 3.3V voltage regulation and logic-level shifting, which frequently results in fried silicon when connected to 5V Arduino boards.
Required Components
- Microcontroller: Arduino Nano v3 (ATmega328P, 5V logic) or Adafruit Trinket M0 (3.3V logic).
- Sensor: Adafruit LIS3DH Breakout (Product ID: 2809) — includes onboard 3.3V LDO and level shifters.
- Wiring: 22 AWG solid-core jumper wires.
- Prototyping: Half-size solderless breadboard.
- Optional (for raw modules only): BSS138-based bidirectional logic level shifter and 4.7kΩ pull-up resistors.
Wiring the LIS3DH to an Arduino Nano
The LIS3DH operates natively at 1.71V to 3.6V. The Arduino Nano v3 outputs 5V on its I2C pins. The Adafruit breakout handles this via an onboard BSS138 MOSFET level-shifting circuit, but you must wire it exactly as shown below to ensure the high-side and low-side references are correct.
| Adafruit LIS3DH Pin | Arduino Nano v3 Pin | Function & Notes |
|---|---|---|
| VIN | 5V | Powers the onboard 3.3V LDO regulator. |
| 3Vo | Not Connected | Output of the 3.3V LDO. Do not feed this back into the Nano. |
| GND | GND | Common ground reference. |
| SCL | A5 | I2C Clock. Breakout has onboard 10kΩ pull-up to 3.3V. |
| SDA | A4 | I2C Data. Breakout has onboard 10kΩ pull-up to 3.3V. |
| SDO/SA0 | Not Connected | Leave floating for default I2C address (0x18). Tie to 3Vo for 0x19. |
| INT | D2 | Interrupt pin for tap/motion detection (used in extension code). |
VCC pin will instantly destroy the sensor. Furthermore, feeding 5V I2C signals into a 3.3V chip without a level shifter will degrade the internal clamp diodes, leading to intermittent I2C bus lockups. Always use a level shifter for raw modules.
Compilable I2C Code with Error Handling
This sketch initializes the LIS3DH over I2C, sets the measurement range to ±2G (optimal for high-resolution tilt and tap detection), and polls the X, Y, and Z axes at 10Hz. It includes explicit error handling to halt execution and report over Serial if the sensor fails to handshake.
Prerequisites: Install the Adafruit LIS3DH and Adafruit Unified Sensor libraries via the Arduino Library Manager.
#include <Wire.h>
#include <SPI.h>
#include <Adafruit_LIS3DH.h>
#include <Adafruit_Sensor.h>
// --- Pin & Address Definitions ---
// Target Board: Arduino Nano v3 (ATmega328P)
#define I2C_SDA_PIN A4
#define I2C_SCL_PIN A5
#define LIS3DH_I2C_ADDR 0x18 // Default address (SDO pin floating or tied to GND)
// Initialize the sensor object using hardware I2C
Adafruit_LIS3DH lis = Adafruit_LIS3DH();
void setup() {
Serial.begin(115200);
while (!Serial); // Wait for Serial monitor (Native USB boards)
delay(100);
Serial.println(F("LIS3DH Acceleration Sensor Initialization..."));
// Initialize I2C bus and check for sensor presence
if (!lis.begin(LIS3DH_I2C_ADDR)) {
Serial.println(F("ERROR: Could not find LIS3DH sensor! Check wiring and I2C address."));
// Halt execution to prevent reading garbage data or crashing downstream logic
while (1) {
delay(10);
}
}
Serial.println(F("LIS3DH Found and Initialized."));
// Configure sensor parameters
// Range: 2G (highest resolution for earth-gravity tilt), 4G, 8G, or 16G
lis.setRange(LIS3DH_RANGE_2_G);
Serial.print(F("Range = ")); Serial.print(2); Serial.println(F("G"));
// Data Rate: 10Hz (low power), up to 5kHz for high-G impact logging
lis.setDataRate(LIS3DH_DATARATE_10_HZ);
Serial.print(F("Data rate set to: ")); Serial.print(lis.getDataRate()); Serial.println(F(" Hz"));
}
void loop() {
// Read raw ADC values and normalized G-force values
lis.read(); // Triggers a fresh I2C read of the 6 data registers
// Output normalized G-force data
Serial.print(F("X: ")); Serial.print(lis.x_g, 4);
Serial.print(F(" \t Y: ")); Serial.print(lis.y_g, 4);
Serial.print(F(" \t Z: ")); Serial.print(lis.z_g, 4);
Serial.println(F(" G"));
// Calculate static tilt angle relative to Z-axis (useful for inclinometers)
float total_g = sqrt((lis.x_g * lis.x_g) + (lis.y_g * lis.y_g) + (lis.z_g * lis.z_g));
float tilt_angle = acos(lis.z_g / total_g) * 180.0 / PI;
Serial.print(F("Tilt from vertical: ")); Serial.print(tilt_angle, 2); Serial.println(F(" deg"));
Serial.println(F("---"));
delay(100); // 10Hz polling loop match
}
Debugging: "Could not find LIS3DH sensor" Error
The most common failure mode when wiring I2C sensors to Arduino is the bus failing to acknowledge the device address. If your Serial monitor prints the exact error string:
ERROR: Could not find LIS3DH sensor! Check wiring and I2C address.
Do not blindly change the code. Follow this ranked troubleshooting path.
The First 3 Things to Check
- Run the I2C Scanner: Upload the standard Arduino
I2CScannerexample sketch. If the scanner returns no addresses, your hardware bus is dead (missing ground, broken wire, or missing pull-ups). If it returns0x19instead of0x18, your SDO pin is pulled high. - Verify VCC with a Multimeter: Measure the voltage between the breakout's GND and VIN pins. It must read 4.5V to 5.5V. If it reads 3.3V, you have wired it to the Nano's 3V3 pin instead of 5V, and the onboard level shifters will not function correctly.
- Check Pull-Up Resistors: The Arduino Nano's internal I2C pull-ups are ~30kΩ, which is too weak for reliable communication at 400kHz. The Adafruit LIS3DH breakout includes 10kΩ pull-ups to 3.3V. If you are using a raw module without pull-ups, you must add external 4.7kΩ resistors between the SDA/SCL lines and the 3.3V reference.
Ranked Causes for I2C Failure
| Probability | Cause | Fix |
|---|---|---|
| 60% | Missing common ground between Nano and Sensor. | Wire Nano GND to Sensor GND. I2C requires a shared reference. |
| 20% | Incorrect I2C address in code (SDO pin state mismatch). | Change LIS3DH_I2C_ADDR to 0x19 if SDO is tied to 3.3V. |
| 15% | Fried sensor IC due to 5V logic on raw SDA/SCL pins. | Replace sensor. Add BSS138 level shifter for next build. |
| 5% | Bus capacitance too high (wires longer than 30cm). | Drop I2C clock to 100kHz or reduce pull-up resistor to 2.2kΩ. |
Extending and Simplifying Your Build
Once you have raw X/Y/Z data streaming reliably, you can tailor the sensor behavior to your specific application constraints.
How to Extend: Add Hardware Tap Detection
Polling the Z-axis in the loop() to detect a physical knock wastes CPU cycles and prevents the Arduino from entering deep sleep. The LIS3DH has a dedicated hardware click-detection engine. To extend your build into a low-power wake-up switch, wire the INT pin to Arduino D2 and add this configuration block inside setup():
// Configure double-tap detection
// Parameters: (1=single tap, 2=double tap, threshold, time limit, latency, window)
lis.setClick(2, 20, 10, 20, 255);
// Enable interrupt 1 for click events
lis.enableDRDY1(LIS3DH_INT1_CLICK);
You can then attach an interrupt service routine (ISR) to D2, allowing the ATmega328P to sleep via LowPower.powerDown() until the physical tap triggers the INT pin high.
How to Simplify: The Analog Fallback
If your environment is electrically noisy (e.g., near large DC motors or VFDs) and the I2C bus keeps dropping packets despite shielded cables, abandon digital protocols entirely. Switch to the ADXL335. It outputs raw analog voltages proportional to G-force. You simply wire X, Y, and Z to the Nano's A0, A1, and A2 pins and read them with analogRead(). You lose hardware tap detection and configurable ranges, but you gain absolute immunity to I2C bus lockups.
For detailed electrical characteristics and timing diagrams, always refer to the STMicroelectronics LIS3DH Datasheet and the Adafruit LIS3DH Learn Guide. For core I2C protocol timing on the AVR architecture, consult the Arduino Wire Library Reference.






