Smart Manufacturing

Vibration Monitoring Sensor: Build an MQTT Vibration Monitor for Predictive Maintenance

Build a working vibration monitoring sensor from an ESP32 and an accelerometer, compute RMS, peak and crest factor on the device, publish them over MQTT and raise alerts when a machine drifts from its baseline.

Vibration monitoring sensor mounted on a pump motor streaming RMS and peak values to a cloud MQTT dashboard
Run this on your own MQTT brokerGet a private, managed MQTT broker on the Free plan and start from ready-made code samples.
Try it free Code samples No card required

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:

  1. An ESP32 development board.
  2. An MPU6050 breakout (GY-521 or similar).
  3. 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:

  1. Sign up at the ConnectMQ portal and create a broker. You receive a generated username, password and client ID.
  2. Switch the ESP32 to WiFiClientSecure on port 8883 and pass the credentials to mqtt.connect(CLIENT_ID, USER, PASS).
  3. 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.
  4. 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

  1. All readings are zero: check the I2C address with a scanner sketch. Some boards use 0x69 when AD0 is high.
  2. 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.
  3. MQTT disconnects every few seconds: another client is using the same client ID. Make it unique.
  4. Values seem capped: you are clipping. Raise the full-scale range from 8 g to 16 g (0x18 in register 0x1C, and 2048 LSB per g).
  5. 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.

Related articles

OEE vs TEEP vs OOE: Which Metric Should You Use?

OEE, OOE and TEEP share the same performance and quality factors but measure against different time bases. This comparison works through one example and lists the MQTT signals each metric needs.

7 min read

All articles →

Your private MQTT broker is a few clicks away

Create an account, get your broker credentials and connect your first device — no servers to install, patch or scale.