If you need to measure vibration with a microcontroller, skip the cheap SW-420 tilt switches and use the ADXL345 digital accelerometer. When building an ESP32 vibration sensor for actual machine fault detection—like identifying bearing wear or motor imbalance via FFT analysis—you need 13-bit resolution and sampling rates up to 3200 Hz. The ADXL345 delivers exactly that over a stable I2C or SPI bus, making it the definitive choice for serious embedded condition monitoring.
Time to Build: 45 minutes
The ESP32 Vibration Sensor Decision Matrix
Before buying parts, you need to match the sensor to your actual physical requirement. Here is the decision path for choosing the right transducer for your ESP32 project.
| Sensor Type | Best Application | Resolution / Max Freq | Verdict |
|---|---|---|---|
| SW-420 (Vibration Switch) | Simple knock alarms, washing machine 'done' alerts | Digital (High/Low) / N/A | Reject for data. Use only for binary triggers. |
| Piezo Film (LDT0-028K) | High-frequency acoustic emission, glass break | Analog / 100kHz+ | Reject. Requires complex op-amp conditioning for ESP32 ADC. |
| MPU6050 (6-Axis IMU) | Orientation tracking, drone stabilization | 16-bit / 1kHz max | Overkill and too slow. 1kHz limits Nyquist to 500Hz. |
| ADXL345 (Digital Accelerometer) | Machine health, motor bearing FFT, structural vibe | 13-bit / 3200 Hz | DEFAULT PICK. Best balance of precision, speed, and ESP32 compatibility. |
The Decision: Unless you are strictly building a binary 'knock' detector, buy the ADXL345. It natively supports the ESP32's 3.3V logic without level shifters and provides the high-frequency data required for meaningful mechanical diagnostics.
Hardware Spec Sheet and Pin Mapping
Below is the exact bill of materials and wiring map for this build. Prices reflect typical 2026 maker-market averages.
Parts List
- Microcontroller: ESP32-WROOM-32 DevKit V1 (38-pin variant) — ~$6.00
- Sensor: ADXL345 Breakout Board (GY-291 variant, ensure it has the 3.3V voltage regulator onboard) — ~$3.50
- Passives: 2x 4.7kΩ pull-up resistors (if your specific breakout board lacks them)
- Mechanical: 2x M3 brass standoffs and Loctite 495 (cyanoacrylate) for rigid coupling
Pin Mapping Table
We are using the default I2C bus for the ESP32-WROOM-32. Do not use GPIO 12 (MTDI) for I2C SDA, as it has an internal pull-up that causes boot failures on some ESP32 modules.
| ADXL345 Pin | ESP32-WROOM-32 GPIO | Notes |
|---|---|---|
| VCC | 3V3 | Do NOT connect to 5V (VIN). The sensor logic is strictly 3.3V. |
| GND | GND | Common ground required. |
| CS | 3V3 | Tie HIGH to force I2C mode. If left floating, SPI/I2C state is undefined. |
| SDO | GND | Tie LOW to set I2C address to 0x53. Tie HIGH for 0x1D. |
| SDA | GPIO 21 | Default ESP32 I2C SDA. Add 4.7kΩ pull-up to 3V3 if needed. |
| SCL | GPIO 22 | Default ESP32 I2C SCL. Add 4.7kΩ pull-up to 3V3 if needed. |
| INT1 / INT2 | Not Connected | We are using polling for this baseline code. See 'Extending' for interrupts. |
Wiring and Assembly Steps
Software can't fix bad physics. The mechanical coupling of your ESP32 vibration sensor to the target machine is just as critical as the I2C wiring.
- Verify Breakout Voltage: Check the silkscreen on your ADXL345 GY-291 board. If it only says '3.3V', connect VCC to the ESP32 3V3 pin. If it says '3-5V', it has an onboard LDO, and you can safely use the ESP32 VIN (5V) pin, though 3V3 is preferred to reduce noise.
- Wire the I2C Bus: Connect SDA to GPIO 21 and SCL to GPIO 22. Keep these wires under 30cm (12 inches) to minimize parasitic capacitance, which degrades the I2C rise time at high clock speeds.
- Set the I2C Address: Solder a jumper wire from the SDO pad to the GND pad on the ADXL345 breakout. This hard-locks the I2C address to
0x53. - Force I2C Mode: Solder a jumper from the CS (Chip Select) pad to the VCC/3V3 pad. This disables the SPI interface. Leaving CS floating is the #1 cause of intermittent sensor dropouts.
- Mechanical Mounting: Do not use double-sided foam tape. Foam acts as a low-pass mechanical filter, absorbing the high-frequency bearing faults you want to measure. Use a dab of cyanoacrylate (super glue) to bond the brass standoff directly to the motor casing, then screw the sensor board to the standoff.
Compilable ESP32 Code with Error Handling
This code targets the ESP32 Dev Module board variant in the Arduino IDE (or PlatformIO). It uses the Adafruit unified sensor libraries. It includes explicit I2C initialization, sensor verification, and a high-speed sampling loop that prevents the ESP32's task watchdog from triggering.
Required Libraries: Install Adafruit ADXL345 and Adafruit Unified Sensor via the Arduino Library Manager. Reference the Adafruit ADXL345 Unified Sensor Library repository for version compatibility.
#include <Wire.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_ADXL345_U.h>
// --- PIN DEFINITIONS ---
#define PIN_I2C_SDA 21
#define PIN_I2C_SCL 22
#define I2C_FREQ_HZ 400000 // 400kHz Fast Mode
// --- SENSOR CONFIG ---
#define ADXL345_ADDRESS (0x53) // SDO tied to GND
#define SAMPLE_RATE_HZ 800 // Safe rate for serial printing
#define READ_INTERVAL_MS (1000 / SAMPLE_RATE_HZ)
// Assign a unique ID to this sensor
Adafruit_ADXL345_Unified accel = Adafruit_ADXL345_Unified(12345);
unsigned long lastReadTime = 0;
void setup() {
Serial.begin(115200);
// Wait for serial monitor to connect (useful for native USB boards, harmless on UART)
while (!Serial && millis() < 3000) { delay(10); }
Serial.println("ESP32 ADXL345 Vibration Monitor");
Serial.println("-------------------------------");
// Explicitly initialize I2C with defined pins and frequency
// Reference: Espressif ESP32 Arduino I2C API
Wire.begin(PIN_I2C_SDA, PIN_I2C_SCL);
Wire.setClock(I2C_FREQ_HZ);
// Initialize the sensor with error handling
if (!accel.begin(ADXL345_ADDRESS)) {
Serial.println("[FATAL] Could not find ADXL345 on I2C bus.");
Serial.println("Check wiring, ensure CS is tied HIGH and SDO is tied LOW.");
while (1) {
delay(1000); // Halt execution, blink LED if available
}
}
// Configure for high-resolution vibration data
// Range: +/- 16g provides the widest dynamic range for machine faults
accel.setRange(ADXL345_RANGE_16_G);
// Data Rate: 800Hz (Adequate for general motor bearing faults)
// Note: Serial printing at 800Hz will drop frames. See 'Extending' for SD logging.
accel.setDataRate(ADXL345_DATARATE_800_HZ);
Serial.println("Sensor initialized. Streaming X, Y, Z acceleration (m/s^2)...");
}
void loop() {
unsigned long currentMillis = millis();
if (currentMillis - lastReadTime >= READ_INTERVAL_MS) {
lastReadTime = currentMillis;
sensors_event_t event;
if (accel.getEvent(&event)) {
// Output CSV format for easy ingestion by Python/Serial Plotter
Serial.print(event.acceleration.x);
Serial.print(",");
Serial.print(event.acceleration.y);
Serial.print(",");
Serial.println(event.acceleration.z);
} else {
Serial.println("[ERROR] Failed to read sensor event. I2C bus may be locked.");
}
// Feed the watchdog timer to prevent panic during high-speed polling
yield();
}
}
Debugging: First Three Things to Check When It Fails
When working with high-frequency I2C polling on the ESP32, you will eventually hit bus lockups or watchdog panics. If your serial monitor spits out the following exact error string:
[E][Wire.cpp:499] requestFrom(): i2cRead returned Error 263 (ESP_ERR_TIMEOUT)
Or if the ESP32 reboots with Guru Meditation Error: Core 1 panic'ed (Interrupt wdt timeout on CPU1), follow this ranked troubleshooting path:
1. Missing or Incorrect I2C Pull-Up Resistors (Most Likely)
The Cause: The I2C specification requires pull-up resistors on SDA and SCL. Many cheap GY-291 ADXL345 breakouts omit these to save $0.02 in manufacturing. Without them, the ESP32's internal weak pull-ups (typically 45kΩ) are too weak to pull the bus high fast enough at 400kHz, resulting in Error 263 timeouts.
The Fix: Solder two 4.7kΩ resistors between the SDA/3V3 and SCL/3V3 pads on the breakout board. Drop the Wire.setClock() to 100000 (100kHz) temporarily to verify if bus speed was the culprit.
2. Floating CS or SDO Pins (Address Instability)
The Cause: The ADXL345 uses the CS pin to determine if it should listen to SPI or I2C. If CS is left unconnected, electromagnetic noise from the motor you are measuring will induce a voltage on the floating pin, causing the chip to randomly switch to SPI mode mid-transaction.
The Fix: Physically verify with a multimeter that the CS pad reads a solid 3.3V and the SDO pad reads a solid 0V (GND). Do not rely on internal pull-ups for these configuration pins.
3. Starving the Task Watchdog (Software Fault)
The Cause: If you attempt to sample at 3200Hz inside a tight while() loop or an Interrupt Service Routine (ISR) without yielding control back to the FreeRTOS scheduler, the ESP32's hardware watchdog will assume the core has locked up and trigger a panic reboot.
The Fix: Never put I2C reads inside an ISR. Use the non-blocking millis() approach shown in the code above, and always include a yield() or vTaskDelay(1) if you are pushing the sampling rate above 1000Hz.
Extending and Simplifying the Build
Depending on your end goal, you may need to scale this architecture up for industrial logging or down for a simple smart-home trigger.
How to Simplify (The 'Washing Machine' Alert)
If you realize you don't actually need FFT data and just want to know when a compressor kicks on or a washing machine finishes, abandon the ADXL345. Buy a SW-420 vibration switch module ($1.00). Wire its Digital Out pin to ESP32 GPIO 4. Use the ESP32's attachInterrupt() to trigger a boolean flag on the falling edge. This eliminates I2C debugging entirely and reduces your code to 20 lines.
How to Extend (High-Speed SD Logging & MQTT)
Serial printing bottlenecks at roughly 400Hz. To capture the full 3200Hz Nyquist bandwidth required for high-speed spindle monitoring:
- Add an SD Card: Wire a MicroSD breakout via SPI (GPIO 5 for CS, GPIO 18 SCK, GPIO 19 MISO, GPIO 23 MOSI).
- Use the FIFO Buffer: The ADXL345 has a 32-level internal FIFO buffer. Configure the
INT1pin to trigger when the FIFO is full (Watermark mode). ConnectINT1to ESP32 GPIO 4. - Read in Bursts: Write an ISR that sets a flag when
INT1goes high. In the main loop, read all 32 samples via high-speed SPI (switch from I2C to SPI for the ADXL345 to support burst reads) and write them to the SD card in a single block. - Analyze Offline: Export the CSV to Python and use
scipy.fftto identify specific fault frequencies (e.g., Ball Pass Frequency Outer race) based on your motor's RPM.
By selecting the right sensor for the physical reality of your machine and respecting the ESP32's I2C hardware quirks, you can build a vibration monitoring system that rivals commercial $500 predictive maintenance pucks.






