A vibration monitoring sensor is the cheapest early-warning system you can attach to a rotating machine. In this tutorial you will build one from an ESP32 and an accelerometer, compute RMS, peak and crest factor on the device, publish the results over MQTT and trigger alerts when vibration rises above a learned baseline.
Why Vibration Is the First Signal Worth Measuring
Almost every mechanical fault in rotating equipment changes how the machine shakes before it changes anything else. Unbalance, misalignment, loose mounting bolts, worn bearings and cavitation all add energy at characteristic frequencies long before a motor trips or a bearing seizes. Temperature usually rises later, and by the time a technician hears the noise the damage is often well advanced.
That is why vibration sits at the heart of most predictive maintenance programs. The predictive maintenance with IoT overview explains the full strategy; this article focuses on the practical part: getting a reliable vibration number off a machine and into a system that can act on it.
You do not need a spectrum analyzer on day one. Trending two or three simple statistics per machine, every second or every minute, catches a large share of developing faults. The key is consistency: same mounting point, same axis, same calculation, and a history to compare against.
Choosing a Vibration Monitoring Sensor
Sensors range from hobby MEMS boards to industrial piezoelectric accelerometers. The right choice depends on what you want to detect.
| Sensor type | Typical use | Strengths | Limitations |
|---|---|---|---|
| MEMS accelerometer (MPU6050, ADXL345) | Prototypes, fans, pumps, conveyors | Very cheap, digital I2C/SPI output, easy with ESP32 | Limited bandwidth and higher noise floor |
| Industrial MEMS vibration sensor | Permanent monitoring of motors and pumps | Wider bandwidth, rugged housing, often Modbus or IO-Link | Needs a gateway to reach MQTT |
| IEPE / piezoelectric accelerometer | Bearings, gearboxes, high-speed spindles | Wide frequency range, low noise, industry standard | Needs a constant-current supply and a fast ADC |
| Wireless vibration sensor | Hard-to-reach or rotating assets | No cabling, battery powered | Sends summaries, not raw waveforms |
For this tutorial we use an MPU6050 because it costs very little and teaches every concept you need. The same firmware structure applies when you swap in a better accelerometer: sample a window, remove the DC offset, compute features, publish.
Mounting matters more than the chip
A sensor glued to a thin guard plate measures the guard plate. Mount the board as close to the bearing housing as possible, on a rigid surface, with a stud, a strong magnet or industrial epoxy. Record which axis points radially and which axially, and never move the sensor once you have collected a baseline, because a new location means a new baseline.
Hardware and Wiring
You need:
- An ESP32 development board.
- An MPU6050 breakout (GY-521 or similar).
- Four jumper wires and a USB cable.
Wire VCC to 3.3 V, GND to GND, SDA to GPIO 21 and SCL to GPIO 22. Those are the default I2C pins on most ESP32 DevKit boards, as documented in the ESP32 I2C reference.
What the Sensor Computes
Raw acceleration at 800 samples per second would mean 2,400 numbers per second per machine. Sending that over MQTT is possible but rarely useful for trending. Instead the ESP32 reduces each one-second window to a few features:
- RMS (root mean square): the overall energy of the vibration. The single most useful trend value.
- Peak: the largest absolute deviation from the mean in the window. Sensitive to impacts.
- Crest factor: peak divided by RMS. A rising crest factor with stable RMS is a classic early sign of bearing defects, because short impacts raise the peak without adding much energy.
Before computing these, the firmware subtracts the mean of each axis. That removes gravity and sensor offset, so a machine standing still reads close to zero.
Firmware: ESP32 Vibration Monitoring Sensor
The sketch below reads the MPU6050 registers directly with Wire, so it has no extra sensor library dependency. Install PubSubClient from the Arduino Library Manager. Replace the random suffix 7f3a91 with your own so your topics do not collide with anyone else on the public broker.
#include <WiFi.h>
#include <Wire.h>
#include <PubSubClient.h>
#include <math.h>
const char* WIFI_SSID = "YOUR_WIFI";
const char* WIFI_PASS = "YOUR_WIFI_PASSWORD";
const char* MQTT_HOST = "broker.connectmq.com";
const uint16_t MQTT_PORT = 1883;
const char* CLIENT_ID = "vib-sensor-7f3a91";
const char* TOPIC = "connectmq/demo/7f3a91/line1/pump1/vibration";
const uint8_t MPU_ADDR = 0x68;
const int SAMPLE_HZ = 800;
const int N = SAMPLE_HZ; // one-second window
const uint32_t PERIOD_US = 1000000UL / SAMPLE_HZ;
const float LSB_PER_G = 4096.0f; // +/-8 g range
WiFiClient net;
PubSubClient mqtt(net);
float bufX[N], bufY[N], bufZ[N];
void mpuWrite(uint8_t reg, uint8_t val) {
Wire.beginTransmission(MPU_ADDR);
Wire.write(reg);
Wire.write(val);
Wire.endTransmission();
}
bool readAccel(float &x, float &y, float &z) {
Wire.beginTransmission(MPU_ADDR);
Wire.write(0x3B);
if (Wire.endTransmission(false) != 0) return false;
if (Wire.requestFrom(MPU_ADDR, (uint8_t)6) != 6) return false;
int16_t rx = (Wire.read() << 8) | Wire.read();
int16_t ry = (Wire.read() << 8) | Wire.read();
int16_t rz = (Wire.read() << 8) | Wire.read();
x = rx / LSB_PER_G;
y = ry / LSB_PER_G;
z = rz / LSB_PER_G;
return true;
}
void stats(const float* b, float &rms, float &peak) {
double mean = 0;
for (int i = 0; i < N; i++) mean += b[i];
mean /= N;
double sumSq = 0;
peak = 0;
for (int i = 0; i < N; i++) {
double d = b[i] - mean;
sumSq += d * d;
if (fabs(d) > peak) peak = fabs(d);
}
rms = sqrt(sumSq / N);
}
void connectWifi() {
WiFi.mode(WIFI_STA);
WiFi.begin(WIFI_SSID, WIFI_PASS);
while (WiFi.status() != WL_CONNECTED) delay(250);
}
void connectMqtt() {
while (!mqtt.connected()) {
if (!mqtt.connect(CLIENT_ID)) delay(2000);
}
}
void setup() {
Serial.begin(115200);
Wire.begin(21, 22);
Wire.setClock(400000);
mpuWrite(0x6B, 0x00); // wake up
mpuWrite(0x1A, 0x01); // DLPF ~184 Hz accel bandwidth
mpuWrite(0x1C, 0x10); // +/-8 g full scale
connectWifi();
mqtt.setServer(MQTT_HOST, MQTT_PORT);
mqtt.setBufferSize(512);
}
void loop() {
if (WiFi.status() != WL_CONNECTED) connectWifi();
if (!mqtt.connected()) connectMqtt();
mqtt.loop();
uint32_t next = micros();
for (int i = 0; i < N; i++) {
while ((int32_t)(micros() - next) < 0) { }
next += PERIOD_US;
if (!readAccel(bufX[i], bufY[i], bufZ[i])) {
bufX[i] = bufY[i] = bufZ[i] = 0;
}
}
float rx, px, ry, py, rz, pz;
stats(bufX, rx, px);
stats(bufY, ry, py);
stats(bufZ, rz, pz);
float rmsMax = fmaxf(rx, fmaxf(ry, rz));
float peakMax = fmaxf(px, fmaxf(py, pz));
float crest = rmsMax > 0.0001f ? peakMax / rmsMax : 0;
char payload[256];
snprintf(payload, sizeof(payload),
"{\"rms_g\":{\"x\":%.4f,\"y\":%.4f,\"z\":%.4f},"
"\"peak_g\":%.4f,\"crest\":%.2f,\"fs\":%d,\"n\":%d}",
rx, ry, rz, peakMax, crest, SAMPLE_HZ, N);
mqtt.publish(TOPIC, payload);
Serial.println(payload);
}
Open a terminal and watch the values arrive:
mosquitto_sub -h broker.connectmq.com -p 1883 -i viewer-7f3a91 -t "connectmq/demo/7f3a91/#" -v
Tap the table next to the sensor and you will see the peak and crest factor jump while RMS barely moves. Put the sensor on a running fan and RMS settles into a steady band. That steady band is your baseline.
Turning Readings into Alerts
A fixed threshold copied from a datasheet is a poor starting point because every machine has its own normal. A better approach is to learn the baseline for each asset and alert on a multiple of it. The Python subscriber below does exactly that with paho-mqtt 2.x. It collects the first 300 readings as the baseline, then publishes a warning or alarm when RMS exceeds 2x or 3x that baseline for several consecutive windows.
import json
import statistics
import paho.mqtt.client as mqtt
BROKER = "broker.connectmq.com"
PREFIX = "connectmq/demo/7f3a91"
BASELINE_SAMPLES = 300
WARN_FACTOR, ALARM_FACTOR = 2.0, 3.0
CONSECUTIVE = 5
state = {} # per-asset: samples, baseline, streak, level
def on_connect(client, userdata, flags, reason_code, properties):
print("connected:", reason_code)
client.subscribe(f"{PREFIX}/+/+/vibration", qos=1)
def on_message(client, userdata, msg):
data = json.loads(msg.payload)
rms = max(data["rms_g"].values())
asset = "/".join(msg.topic.split("/")[3:5])
s = state.setdefault(asset, {"samples": [], "baseline": None, "streak": 0, "level": "ok"})
if s["baseline"] is None:
s["samples"].append(rms)
if len(s["samples"]) >= BASELINE_SAMPLES:
s["baseline"] = statistics.median(s["samples"])
print(f"{asset} baseline RMS = {s['baseline']:.4f} g")
return
ratio = rms / s["baseline"]
level = "alarm" if ratio >= ALARM_FACTOR else "warning" if ratio >= WARN_FACTOR else "ok"
s["streak"] = s["streak"] + 1 if level != "ok" else 0
if level != s["level"] and (level == "ok" or s["streak"] >= CONSECUTIVE):
s["level"] = level
alert = {"asset": asset, "level": level, "rms_g": round(rms, 4),
"baseline_g": round(s["baseline"], 4), "ratio": round(ratio, 2)}
client.publish(f"{PREFIX}/{asset}/alert", json.dumps(alert), qos=1, retain=True)
print("ALERT", alert)
client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2, client_id="vib-alerts-7f3a91")
client.on_connect = on_connect
client.on_message = on_message
client.connect(BROKER, 1883, keepalive=60)
client.loop_forever()
Three details make this robust in practice. The median baseline ignores the occasional bump during learning. The consecutive-window requirement stops a single dropped tool or forklift pass from paging anyone. And the alert is published as a retained message, so a dashboard that connects later immediately sees the current alarm state of every asset.
If you prefer not to run code for basic thresholds, the ConnectMQ portal includes threshold alert rules and an alerts log. You can point a rule at the RMS field of your vibration topic and see every excursion in the portal, then keep the Python service for the baseline-relative logic.
Moving from Demo to Production
The public MQTT test broker is perfect for this experiment, but everything on it is readable by anyone, and it limits clients to 10 messages per second. For real machines, move to a private broker:
- Sign up at the ConnectMQ portal and create a broker. You receive a generated username, password and client ID.
- Switch the ESP32 to
WiFiClientSecureon port 8883 and pass the credentials tomqtt.connect(CLIENT_ID, USER, PASS). - Adopt an ISA-95 style topic tree such as
acme/plant1/assembly/line1/pump1/vibration, which the portal recognises and groups in its devices view. - Run the alert service under a process manager so it restarts automatically.
A managed MQTT broker removes the work of patching, certificates and capacity planning, which matters once you have dozens of sensors publishing every second.
Troubleshooting Checklist
- All readings are zero: check the I2C address with a scanner sketch. Some boards use 0x69 when AD0 is high.
- RMS looks too high when the machine is off: electrical noise or a loose mount. Re-seat the sensor and keep the cable away from motor power leads.
- MQTT disconnects every few seconds: another client is using the same client ID. Make it unique.
- Values seem capped: you are clipping. Raise the full-scale range from 8 g to 16 g (
0x18in register0x1C, and 2048 LSB per g). - Faults at high frequency are missed: the MPU6050 bandwidth is limited. Use an industrial accelerometer for bearing and gearbox work.
Where to Go Next
Once RMS and crest factor are flowing reliably, add a second signal. Combining vibration with motor current monitoring or bearing temperature monitoring separates electrical from mechanical problems and confirms alarms from two independent sources. When you are ready to put the data in context across the whole plant, the smart manufacturing guide shows how vibration fits into the larger data backbone.
Start Monitoring Vibration on ConnectMQ
ConnectMQ gives every sensor a secure, private MQTT endpoint with TLS, an in-browser client, a live topic explorer and built-in threshold alerts. Create a broker on the free Serverless plan at app.connectmq.com, no credit card required, and move your vibration monitor from the demo topic to production in minutes.
Frequently Asked Questions
What is the best vibration monitoring sensor for a beginner project?
A MEMS accelerometer such as the MPU6050 or ADXL345 on an ESP32 is the easiest starting point. It is cheap, uses I2C and is good enough to trend overall vibration on fans, pumps and conveyors.
Should I send raw vibration waveforms over MQTT?
Usually not for continuous monitoring. Send summary features such as RMS, peak and crest factor every second, and capture short raw bursts only when you need spectral analysis.
How do I set vibration alarm thresholds?
Learn a baseline for each machine while it runs normally, then alert on multiples of that baseline. Require several consecutive windows above the limit to avoid nuisance alarms.
Can I use broker.connectmq.com for real machines?
Use the public broker for testing only, because its topics are readable by anyone. For production, create a private broker with your own credentials in the ConnectMQ portal.



