# Real-Time Vibration Monitoring with ThingsBoard and Calculated Fields

This guide shows how to build a complete end-to-end industrial vibration monitoring system from a real Balluff condition monitoring sensor, through a virtual PLC, to a live ThingsBoard dashboard with ISO 10816-compliant alarms.

Unlike the [previous article](https://wiki.codeandcompile.com/product-reviews/smart-platforms/thingsboard/thingsboard-edge-setup), which used simulated telemetry from Node-RED, **this guide uses real vibration data from a physical sensor** connected via IO-Link and streamed live to [ThingsBoard](https://thingsboard.io/?utm_source=inf\&utm_medium=rjvr\&utm_campaign=artcl2) using OPC UA and MQTT.

{% embed url="<https://files.gitbook.com/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FLd2M9UNfMTnw9DjDdZJz%2Fuploads%2FNzPNDdsCpAdzyeZhuBgj%2FThingsBoard.mp4?alt=media&token=201aa1e1-d9c1-4626-96f6-2e09e8e71c7e>" %}

**By the end of this guide, you will have:**

* Real X/Y/Z vibration RMS data streaming from a Balluff condition monitoring sensor
* A **reComputer vPLC** reading sensor data via Modbus TCP/IP over IO-Link
* A **reComputer R1100** bridging data from the vPLC (OPC UA) to ThingsBoard's MQTT Broker using Node-RED
* [**ThingsBoard Calculated Fields**](https://thingsboard.io/docs/pe/user-guide/calculated-fields/?utm_source=inf\&utm_medium=rjvr\&utm_campaign=artcl2) compute ISO 10816 vibration severity and machine health status in real time
* A professional dashboard with a color-coded gauge, alarms, and a dynamic machine health card

***

## Hardware & Software Used

### Hardware

| Component                         | Model                                    |
| --------------------------------- | ---------------------------------------- |
| Edge Computer (vPLC host)         | Seeed reComputer R2100 (OpenPLC Runtime) |
| Edge Gateway and ThingsBoard Host | Seeed reComputer R1100                   |
| IO-Link Master                    | Balluff BNI00L3                          |
| Condition Monitoring Sensor       | Balluff BCM R15E-001-DI00-01,5-S4        |

### Software

* OpenPLC vPLC Runtime (on reComputer R2100)
* Node-RED (on reComputer R1100)
* ThingsBoard Community Edition (on reComputer R1100)

***

## System Architecture

Before diving into the steps, it's helpful to understand how data flows through the system.

```
[Balluff BCM Sensor]
        │
        │  IO-Link
        ▼
[Balluff IO-Link Master — BNI00L3]
        │
        │  Modbus TCP/IP
        ▼
[reComputer R2100 — OpenPLC vPLC]
   - Reads X/Y/Z VRMS + Temperature
   - Byte-swaps Big-Endian floats
   - Exposes values via OPC UA Server
        │
        │  OPC UA
        ▼
[reComputer R1100 — Node-RED]
   - OPC UA Client reads live values
   - Publishes JSON payload via MQTT
        │
        │  MQTT (port 1883)
        ▼
[reComputer R1100 — ThingsBoard]
   - Receives telemetry
   - Runs Calculated Fields (Vibration Severity, Machine Status)
   - Triggers ISO 10816 alarms
   - Displays live dashboard
```

{% hint style="info" %}
💡 **Why two edge devices?** The vPLC (reComputer R2100 running OpenPLC) is dedicated to real-time scan-cycle control and sensor I/O. A separate computer, R1100, handles the edge communication. This separation keeps the PLC runtime clean and follows industrial best practices for separating control logic from data forwarding.
{% endhint %}

***

### Step 1: Hardware Setup (vPLC + IO-Link)

This step is covered in detail in the [Virtual PLC interfacing with IO-Link Master](https://wiki.codeandcompile.com/product-reviews/smart-platforms/virtual-plcs/autonomy-edge-openplc-redefined/virtual-plc-interfacing-with-io-link-master) article.

In summary, the vPLC:

1. Connects to the **Balluff IO-Link Master** over Modbus TCP/IP
2. Reads X/Y/Z VRMS and Temperature from registers `IW1`–`IW8`
3. Uses a **custom C++ WTOR function block** to byte-swap Big-Endian 32-bit floats into usable `REAL` values
4. Stores the results in `rX_VRMS`, `rY_VRMS`, `rZ_VRMS`, and `rTemp`

{% hint style="info" %}
**Key point:** The Balluff sensor sends each float split across 2 × 16-bit Modbus registers in Big-Endian format. A direct cast to `REAL` gives garbage values — the byte swap is mandatory. The WTOR function block handles this automatically.
{% endhint %}

Once the vPLC is running and reading correct values, proceed to Step 2.

***

### Step 2: Expose vPLC Data via OPC UA

The vPLC exposes its runtime variables as an OPC UA Server. This allows the reComputer R1100 to read live values without changing the PLC program.

{% hint style="info" %}
OpenPLC Runtime includes a built-in OPC UA server. No additional configuration is required; variables declared in the PLC program are automatically available as OPC UA nodes.
{% endhint %}

Note the OPC UA server address of the vPLC; you will need this in the next step:

```
opc.tcp://192.168.100.10:4840
```

Replace `192.168.100.10` with the actual IP address of your vPLC.

***

### Step 3: Bridge OPC UA to MQTT using Node-RED&#x20;

The reComputer R1100 runs **Node-RED** as the middleware layer. It acts as an OPC UA client that reads vibration values from the vPLC and publishes them to ThingsBoard's MQTT broker every second.

{% stepper %}
{% step %}

### Install the OPC UA node in Node-RED

Open the Node-RED palette manager and install:

```
node-red-contrib-opcua
```

{% endstep %}

{% step %}

### Build the flow

<figure><img src="/files/AFBLvzejZSUnnsWhRXam" alt="" width="563"><figcaption></figcaption></figure>

The flow consists of three parts:

<details>

<summary><strong>Trigger (every 1 second)</strong></summary>

Use an **Inject** node set to repeat every 1 second.

</details>

<details>

<summary><strong>OPC UA Read</strong></summary>

Add an **OPC UA Client** node and configure it:

* Endpoint: `opc.tcp://192.168.100.10:4840`
* Action: `READ`
* Node IDs to read:
  * `ns=4;i=1`
  * `ns=4;i=2`
  * `ns=4;i=3`
  * `ns=4;i=4`
  * `ns=4;i=5`
  * `ns=4;i=6`
  * `ns=4;i=7`

{% hint style="info" %}
The exact node ID format depends on your OpenPLC version. Use the OPC UA browser (built into the OpcUa-Client node) to confirm the correct node paths for your setup.
{% endhint %}

</details>
{% endstep %}

{% step %}

### Create a Device Profile in ThingsBoard

{% hint style="info" %}
Device profiles in ThingsBoard enable administrators to define and centrally manage common settings for multiple devices. This greatly simplifies the management of a large number of similar devices, making it especially valuable in IoT solutions where numerous devices share identical configurations and behaviors.

Learn more about the device profile [here](https://thingsboard.io/docs/user-guide/device-profiles/)
{% endhint %}

Go to **Profiles → Device Profiles** in the left menu → click **+** → Create new device profile

<figure><img src="/files/CUAGwyF70nFnC2UmGKzp" alt="" width="563"><figcaption></figcaption></figure>
{% endstep %}

{% step %}

### Create a new device

Create a new device and link that to the device profile created above. In our case, the new device is named '**OpenPLC**', linked to the device profile '**Vibration Sensor'**.&#x20;

<figure><img src="/files/UpOJ2LbdfZYpyYszrXr6" alt="" width="563"><figcaption></figcaption></figure>

### How to Link your device to the "Vibration Sensor" device Profile

**Step 1:** Go to **Entities → Devices**&#x20;

**Step 2:** Click on **OpenPLC (your device)** to open its details

**Step 3:** Click the **pencil/edit icon** (✏️) in the top right of the details panel

**Step 4:** Find the **"Device profile"** field — currently shows `default`

**Step 5:** Click on it and select **"Vibration Sensor"** from the dropdown

<figure><img src="/files/A8bq1J0IYFriVfzNR6Wr" alt="" width="563"><figcaption></figcaption></figure>

**Step 6:** Click **Save / Apply changes**

<figure><img src="/files/bIGIDcJUnPGS6oU2RpWG" alt="" width="563"><figcaption></figcaption></figure>
{% endstep %}

{% step %}

### Format and publish the PLC data to the MQTT Broker

Add a **Function** node in the Node-RED to format the payload:

```javascript
// Build payload
msg.payload = {
    // OpenPLC
    plc_rThresholdHigh: msg.payload["ns=2;i=6"].toFixed(3),
    plc_rThresholdLow: msg.payload["ns=2;i=5"].toFixed(3),
    plc_rSensortemp: msg.payload["ns=2;i=7"].toFixed(3),
    plc_iVibrationState: msg.payload["ns=2;i=4"],
    plc_rSensorX_vrms: msg.payload["ns=2;i=1"].toFixed(3),
    plc_rSensorY_vrms: msg.payload["ns=2;i=2"].toFixed(3),
    plc_rSensorZ_vrms: msg.payload["ns=2;i=3"].toFixed(3),
};
return msg;
```

Add an **MQTT Out** node with the ThingsBoard connection details as shown below.

| Setting       | Value                                                                                                                                                                                                                             |
| ------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Broker        | <p><code>192.168.0.227</code> <br>(IP of reComputer R1100)</p>                                                                                                                                                                    |
| Port          | `1883`                                                                                                                                                                                                                            |
| Topic         | `v1/devices/me/telemetry`                                                                                                                                                                                                         |
| Username      | Your ThingsBoard device username. Check [this](https://wiki.codeandcompile.com/product-reviews/smart-platforms/thingsboard/thingsboard-edge-setup#get-thingsboard-mqtt-connection-details) step to learn how to get the username. |
| Password      | *(leave blank)*                                                                                                                                                                                                                   |
| {% endstep %} |                                                                                                                                                                                                                                   |

{% step %}

### Verify data is arriving in ThingsBoard

Go to your ThingsBoard device → **Latest Telemetry** tab. You should see the four keys updating every second:

<figure><img src="/files/LOolIVbM5X5gddVLJQu4" alt="" width="455"><figcaption></figcaption></figure>
{% endstep %}
{% endstepper %}

***

### Step 4: Create Calculated Fields in ThingsBoard

This is where the raw sensor data becomes actionable. ThingsBoard v4.0 introduced **Calculated Fields**, a built-in logic layer that transforms incoming telemetry in real time without any external scripts or rule chains.

We will create two calculated fields:

1. **Overall Vibration Severity:** combines X/Y/Z into a single ISO 10816 metric
2. **Machine Status:** classifies the severity into a human-readable health state

{% hint style="info" %}
Best Practice Note: Instead of attaching a calculated field to a specific device (e.g., IoT1), it is recommended to attach it to a Device Profile. This way, the calculated field automatically applies to all devices sharing that profile, making your setup scalable and maintainable without duplicating configuration.
{% endhint %}

***

{% stepper %}
{% step %}

### Create the calculated fields

Go to **Profiles → Device Profiles** **→ Open your device profile ('Vibration Sensor')**

Navigate to the **Calculated Fields** tab inside the profile. Click **+** and create a calculated field

<figure><img src="/files/el3weWuBnTyVWfOVv4V4" alt=""><figcaption></figcaption></figure>
{% endstep %}

{% step %}

### Calculated Field 1: Overall Vibration Severity (ISO 10816)

Navigate to your Left tree menu→ **Calculated Fields** tab → click **+**.

**General settings:**

| Setting | Value                                            |
| ------- | ------------------------------------------------ |
| Title   | Overall Vibration Severity (ISO 10816 compliant) |
| Type    | Script                                           |

Add three arguments:

| Argument name       | Type             | Key                 |
| ------------------- | ---------------- | ------------------- |
| plc\_rSensorX\_vrms | Latest telemetry | `plc_rSensorX_vrms` |
| plc\_rSensorY\_vrms | Latest telemetry | `plc_rSensorY_vrms` |
| plc\_rSensorZ\_vrms | Latest telemetry | `plc_rSensorZ_vrms` |

<figure><img src="/files/Y2nxgoZoRquHV3eznUTp" alt=""><figcaption></figcaption></figure>

**Add this Script:**

```javascript
var vibration_severity = Math.sqrt(plc_rSensorX_vrms * 
    plc_rSensorX_vrms + plc_rSensorY_vrms * plc_rSensorY_vrms + 
    plc_rSensorZ_vrms * plc_rSensorZ_vrms);
return {
    "vibration_severity":  toFixed(vibration_severity,3)
}
```

**Output: Time series**

{% hint style="info" %}
**Why vector magnitude?** Using `sqrt(X² + Y² + Z²)` rather than the maximum individual axis, it gives you the true Euclidean magnitude of the vibration vector. This is the correct approach for ISO 10816 compliance and eliminates false negatives,  a situation where all three axes are below threshold individually, but combined vibration is dangerously high.
{% endhint %}
{% endstep %}

{% step %}

### Calculated Field 2: Machine Status

Navigate to **Calculated Fields** → click **+** again.

**General settings:**

| Setting | Value          |
| ------- | -------------- |
| Title   | Machine Status |
| Type    | Script         |

**Arguments:**

<table><thead><tr><th width="189.6666259765625">Argument name</th><th>Type</th><th width="191.6666259765625">Key</th><th>Default value</th></tr></thead><tbody><tr><td>vibration_severity</td><td>Latest telemetry</td><td><code>vibration_severity</code></td><td><code>0</code></td></tr></tbody></table>

<figure><img src="/files/EypIGN2jI6zjvFaYPxY6" alt=""><figcaption></figcaption></figure>

> ⚠️ Setting a default value of `0` is important. Without it, if `vibration_severity` has not yet been computed, the argument receives `null`, the comparison fails, and the status incorrectly shows `CRITICAL`.

**Add this Script:**

```javascript
if (vibration_severity < 2.3) {
        return { "machine_status": "HEALTHY" };
    } else if (vibration_severity < 4.5) {
        return { "machine_status": "WARNING" };
    } else if (vibration_severity < 7.1) {
        return { "machine_status": "CRITICAL" };
    } else {
        return { "machine_status": "EMERGENCY" };
    }
```

<figure><img src="/files/ob5Kp9hcwFDrjaVSe9Ul" alt=""><figcaption></figcaption></figure>

**ISO 10816-3 zone reference:**

<table><thead><tr><th width="90.33331298828125">Zone</th><th width="155.3333740234375">Severity range</th><th width="142.6666259765625">Status</th><th>Meaning</th></tr></thead><tbody><tr><td>A</td><td>0 – 2.3 mm/s</td><td>HEALTHY</td><td>Good - new or recently serviced</td></tr><tr><td>B</td><td>2.3 – 4.5 mm/s</td><td>WARNING</td><td>Acceptable - monitor closely</td></tr><tr><td>C</td><td>4.5 – 7.1 mm/s</td><td>CRITICAL</td><td>Unsatisfactory - schedule maintenance</td></tr><tr><td>D</td><td>> 7.1 mm/s</td><td>EMERGENCY</td><td>Unacceptable - risk of damage</td></tr></tbody></table>
{% endstep %}
{% endstepper %}

***

### Step 5: Configure Alarms

Navigate to your **Device Profile** → **Alarm Rules** tab.

Create three alarm rules based on `vibration_severity`:

<figure><img src="/files/h8XCWaHEMtV23HUdFcVP" alt="" width="563"><figcaption></figcaption></figure>

***

#### Alarm 1: Motor Vibration Warning

| Setting   | Value                               |
| --------- | ----------------------------------- |
| Condition | `return (vibration_severity > 2.3)` |

<figure><img src="/files/YUUBXRZ8nvFRsoJAvyd0" alt="" width="563"><figcaption></figcaption></figure>

**Additional info:**

{% hint style="warning" %}
\[WARNING] Motor Vibration entering Zone B (ISO 10816) Severity: ${vibration\_severity} mm/s | Threshold: 2.3 mm/s Axis breakdown — X: ${plc\_rSensorX\_vrms} mm/s | Y: ${plc\_rSensorY\_vrms} mm/s | Z: ${plc\_rSensorZ\_vrms} mm/s Action: Monitor closely. Schedule inspection if sustained.
{% endhint %}

***

#### Alarm 2: Motor Vibration Critical

| Setting   | Value                               |
| --------- | ----------------------------------- |
| Condition | `return (vibration_severity > 4.5)` |

<figure><img src="/files/WGpS3Yw9NFmcqoyL9Jmc" alt="" width="563"><figcaption></figcaption></figure>

**Additional info:**

{% hint style="danger" %}
\[CRITICAL] Motor Vibration in Zone C — Maintenance Required (ISO 10816) Severity: ${vibration\_severity} mm/s | Threshold: 4.5 mm/s Axis breakdown — X: ${plc\_rSensorX\_vrms} mm/s | Y: ${plc\_rSensorY\_vrms} mm/s | Z: ${plc\_rSensorZ\_vrms} mm/s Action: Reduce load and schedule immediate inspection.
{% endhint %}

***

#### Alarm 3: Motor Vibration Emergency

| Setting   | Value                              |
| --------- | ---------------------------------- |
| Condition | `return (vibration_severity > 7.1` |

<figure><img src="/files/FKz1EslrHIV6ANJTXoWn" alt="" width="563"><figcaption></figcaption></figure>

**Additional info:**

{% hint style="danger" %}
\[EMERGENCY] Motor Vibration in Zone D — Shutdown Risk (ISO 10816) Severity: ${vibration\_severity\_r} mm/s | Threshold: 7.1 mm/s Axis breakdown — X: ${plc\_rSensorX\_vrms\_r} mm/s | Y: ${plc\_rSensorY\_vrms\_r} mm/s | Z: ${plc\_rSensorZ\_vrms\_r} mm/s Action: Stop machine immediately. Risk of mechanical damage.
{% endhint %}

{% hint style="success" %}
**Why use `vibration_severity` for alarms instead of individual axes?**&#x20;

Alarming on individual X/Y/Z values creates two problems: false negatives (all axes below threshold but combined severity is dangerous) and false positives (one axis spikes from a single knock while overall severity stays in Zone A). The combined severity metric is ISO-compliant and gives a much cleaner alarm signal.
{% endhint %}

***

### Step 6: Build the Dashboard

{% stepper %}
{% step %}

### Machine Health card (HTML Value Card)

<figure><img src="/files/SkAwvYr2rytemMU078QX" alt=""><figcaption></figcaption></figure>

Add widget → **Cards** → **HTML Value Card**

Type: Entity

Data key: `machine_status`

Navigate to 'Appearance'. Paste the following into the HTML section:

```html
<!DOCTYPE html>
<html>
<head></head>
<body style="margin:0; padding:0;">
<div class="card" id="statusCard">
  <div class="content">
    <div style="text-align:center; width:100%;">
      <div style="font-size:13px; color:white; opacity:0.8;">Machine Health</div>
      <div style="font-size:28px; font-weight:bold; color:white;">${machine_status}</div>
    </div>
  </div>
</div>

<script>
  var status = '${machine_status}';
  var card = document.getElementById('statusCard');
  if (status === 'HEALTHY') {
    card.style.backgroundColor = '#4CAF50';
  } else if (status === 'WARNING') {
    card.style.backgroundColor = '#FFEB3B';
  } else if (status === 'CRITICAL') {
    card.style.backgroundColor = '#FF9800';
  } else {
    card.style.backgroundColor = '#F44336';
  }
</script>
</body>
</html>
```

Replace the CSS section with:

```css
.card {
    width: 100%;
    height: 100%;
    box-sizing: border-box;
    border: none;
}

.card .content {
    padding: 20px;
    display: flex;
    flex-direction: row;
    align-items: center;
    justify-content: center;
    height: 100%;
}
```

{% endstep %}

{% step %}

### Widget 1: Warning Card

<figure><img src="/files/5VNbSkLKsVVzjuVg9ZPZ" alt="" width="220"><figcaption></figcaption></figure>

1. Click **+ Add widget** → **Cards** → **Alarm count**
2. Configure:

| Setting               | Value                           |
| --------------------- | ------------------------------- |
| Label                 | Warnings                        |
| Alarm severity filter | Warning                         |
| Alarm status filter   | Active                          |
| Icon                  | Warning triangle, Size 20px     |
| Icon color            | White                           |
| Icon background       | `#2E7D6B` (dark teal, as shown) |
| {% endstep %}         |                                 |

{% step %}

### Widget 2: Critical Alarms Card

Same steps as above but:

| Setting               | Value                |
| --------------------- | -------------------- |
| Label                 | Critical Alarms      |
| Alarm severity filter | Critical             |
| Icon background       | `#C62828` (dark red) |
| {% endstep %}         |                      |

{% step %}

### Widget 3: Motor Image

* Click **+ Add widget** → **Traditional SCADA Fluid system**→ **Right motor pump**

| Setting        | Value                                                                      |
| -------------- | -------------------------------------------------------------------------- |
| Running        | `True`                                                                     |
| Warning State  | <p>Use alarm status: Warning<br>Alarm Type: Motor Vibration</p>            |
| Critical State | <p>Use alarm status: Critical and Major<br>Alarm Type: Motor Vibration</p> |
| {% endstep %}  |                                                                            |

{% step %}

### Vibration Severity Gauge (ISO 10816 bands)

<figure><img src="/files/JMZPSh2pLtJIDjkVxxHU" alt="" width="355"><figcaption></figcaption></figure>

Add widget → **Gauges** → **Speed Gauge**

Type: Entity

Antity Alias: OpenPLC

Data key: `vibration_severity`

Configure the colour bands to match ISO 10816 zones in the Scale settings:

<figure><img src="/files/1WeXG3it7enKEmk6hutt" alt="" width="563"><figcaption></figcaption></figure>

| From | To   | Colour             |
| ---- | ---- | ------------------ |
| 0    | 2.3  | `#4CAF50` (green)  |
| 2.3  | 4.5  | `#FFEB3B` (yellow) |
| 4.5  | 7.1  | `#FF9800` (orange) |
| 7.1  | 10.0 | `#F44336` (red)    |

Additional settings:

| Setting       | Value                            |
| ------------- | -------------------------------- |
| Min           | `0`                              |
| Max           | `10`                             |
| Decimals      | `1`                              |
| Units         | `mm/s`                           |
| Title         | `Vibration Severity (ISO 10816)` |
| {% endstep %} |                                  |

{% step %}

### Tri-Axial Vibration Chart

<figure><img src="/files/s5TAzrUqaySE4zatiXxj" alt="" width="357"><figcaption></figcaption></figure>

Add widget → **Charts** → **Time series chart**

Data keys: `plc_rSensorX_vrms`, `plc_rSensorY_vrms`, `plc_rSensorZ_vrms`

<div><figure><img src="/files/iLk4HX8RJiPYvzUHBfRb" alt=""><figcaption></figcaption></figure> <figure><img src="/files/GqbceuPiiyxwe0AK6Bg7" alt=""><figcaption></figcaption></figure></div>
{% endstep %}

{% step %}

### Motor Temperature Trend

<figure><img src="/files/3YogTZb3ZiTAQKgDyb01" alt="" width="355"><figcaption></figcaption></figure>

Similarly, you can also make a trend of Motor Temperature
{% endstep %}

{% step %}

### Vibration Severity Trend Chart

<figure><img src="/files/gLKN2K8Uvl3lItUMFpMb" alt="" width="353"><figcaption></figcaption></figure>

Add widget → **Charts** → **Time series chart**

Data key: `vibration_severity`

Add threshold lines at `2.3`, `4.5`, and `7.1` mm/s so operators can immediately see how close the severity is to each ISO zone boundary.

<figure><img src="/files/ZGMNMKsAXTncJKYMUn5K" alt="" width="563"><figcaption></figcaption></figure>
{% endstep %}

{% step %}

### Add Alarm Table

<figure><img src="/files/7Ct1Kjj6ert5PnBlFB2u" alt="" width="563"><figcaption></figcaption></figure>

Add widget → **Tables**→ **Alarms table**

| Setting             | Value                                                      |
| ------------------- | ---------------------------------------------------------- |
| Time window         | Use widget time window                                     |
| Alarm source        | Entity Alias > MachineData                                 |
| Alarm Status list   | <p>Active<br>Cleared<br>Acknowledged<br>Unacknowledged</p> |
| Alarm Severity list | <p>Critical<br>Major<br>Warning</p>                        |
| Alarm type list     | Motor Vibration                                            |
| {% endstep %}       |                                                            |

{% step %}

### Final dashboard layout

Arrange the widgets as follows for the clearest operator view:

```
┌─────────────────┬──────────────────────────┬──────────────────────────┐
│ Machine Health  │ Tri-Axial Vibration       │ Motor Temperature Trend  │
│ (colour card)   │ (X/Y/Z time series)       │ (line chart)             │
│                 │                           │                          │
│ Warnings: 0     ├──────────────────────────┼──────────────────────────┤
│                 │ Vibration Severity        │ Vibration Severity       │
│ Critical: 0     │ (trend chart with         │ (ISO 10816 gauge)        │
│                 │  ISO threshold lines)     │                          │
│ Motor image     │                           │                          │
│                 ├───────────────────────────┴──────────────────────────┤
│ Temperature     │ Alarms table (full width)                            │
└─────────────────┴──────────────────────────────────────────────────────┘
```

{% embed url="<https://files.gitbook.com/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FLd2M9UNfMTnw9DjDdZJz%2Fuploads%2FJeHp6035YL84GuBWzJBG%2FAufzeichnung%202026-03-23%20150309.mp4?alt=media&token=b0c1e2f5-9d16-455a-9fa7-52b1426de29a>" %}

{% hint style="success" %}
**Reading order:**&#x20;

The left column gives instant health status. The centre column shows the vibration detail over time. The right column shows the temperature and the ISO-calibrated gauge. The alarms table at the bottom provides a full audit history.
{% endhint %}
{% endstep %}
{% endstepper %}

***

### How It All Connects

Looking back at the full pipeline:

1. The **Balluff BCM sensor** measures tri-axial vibration (X/Y/Z RMS) and temperature continuously
2. The **Balluff IO-Link Master** exposes sensor data over Modbus TCP
3. The **reComputer vPLC** reads registers every scan cycle, byte-swaps Big-Endian floats, and exposes clean `REAL` values via its OPC UA server
4. **Node-RED on the reComputer R1100** polls the OPC UA server every second and publishes a JSON payload to ThingsBoard via MQTT
5. **ThingsBoard Calculated Fields** compute vibration severity (`sqrt(X²+Y²+Z²)`) and classify machine health (HEALTHY / WARNING / CRITICAL / EMERGENCY) in real time, no external scripts needed
6. **Alarm rules** fire when severity crosses ISO 10816 zone boundaries, with structured alarm messages including rounded axis values
7. The **dashboard** gives operators instant visual feedback through a colour-coded health card, ISO-banded gauge, and trend charts

***

## Resources <a href="#download-my-dashboard" id="download-my-dashboard"></a>

#### ThingsBoard Dashboard

{% file src="/files/e8lBXpG6XCS20kEf5TXg" %}

{% hint style="info" %}
To learn how to import a dashboard, kindly refer to the last article here [ThingsBoard (Edge Setup)](/product-reviews/smart-platforms/thingsboard/thingsboard-edge-setup.md)
{% endhint %}

## ♥️ Work With Me

I regularly test **industrial automation and IIoT devices**. If you’d like me to **review your product** or showcase it in my courses and YouTube channel:

📧 Email: <rajvir@codeandcompile.com> or drop me a message on [LinkedIn](https://www.linkedin.com/in/singhrajvir/)


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://wiki.codeandcompile.com/product-reviews/smart-platforms/thingsboard/real-time-vibration-monitoring-with-thingsboard-and-calculated-fields.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
