Digital Twin Architecture for Industrial IoT Monitoring Systems
A digital twin transforms raw telemetry from industrial sensors and PLCs into a living computational model that mirrors physical asset behavior in real time. Unlike dashboards that display current values, a properly architected digital twin maintains physics-based state, detects anomalies through model-measurement divergence, and predicts failures hours or days before they manifest. This guide details the embedded and software architecture required to build production digital twin systems for industrial monitoring.
System Architecture Overview
An industrial digital twin system spans four architectural tiers: the physical layer (sensors, actuators, PLCs), the edge layer (protocol translation, local computation), the platform layer (data ingestion, model execution, state management), and the application layer (visualization, alerting, control loop integration). Each tier has distinct latency, reliability, and security requirements.
Data Flow Pipeline
The canonical data flow from physical asset to digital twin state update follows this sequence:
- Sensor acquisition: MEMS accelerometers, RTDs, pressure transducers sample at 1 Hz to 25 kHz depending on parameter type
- Edge aggregation: Microcontroller or gateway applies signal conditioning, decimation, and feature extraction
- Protocol translation: OPC UA or Modbus TCP data mapped to a normalized telemetry schema via MQTT
- Time alignment: Ingestion engine reorders and interpolates multi-source data to a common timestamp
- State update: Physics model ingests aligned data, computes derived state, and updates the twin
- Divergence detection: Residual analysis between predicted and measured values triggers anomaly flags
Sensor Integration and Edge Data Acquisition
The quality of a digital twin is bounded by the quality and coverage of its sensor inputs. Industrial equipment typically requires monitoring across multiple physical domains: vibration (mechanical health), temperature (thermal stress), current/voltage (electrical condition), and process variables (flow, pressure, level).
Multi-Channel DAQ Firmware on STM32H7
The STM32H743 provides the ADC resolution (16-bit), sampling rate (3.6 MSPS aggregate), and DMA capability needed for multi-channel industrial data acquisition. The following firmware implements simultaneous sampling of vibration, temperature, and current channels:
/* daq_engine.c - Multi-channel industrial DAQ on STM32H743 */
#include "stm32h7xx_hal.h"
#include "arm_math.h"
#define VIBRATION_FS 25600 /* 25.6 kHz for bearing defect detection */
#define TEMP_FS 10 /* 10 Hz for thermal monitoring */
#define CURRENT_FS 6400 /* 6.4 kHz for motor current signature */
#define VIB_BLOCK_SIZE 2048 /* FFT block for spectral analysis */
typedef struct {
float32_t vib_buffer[VIB_BLOCK_SIZE];
float32_t temp_buffer[16];
float32_t current_buffer[1024];
uint32_t vib_idx;
uint32_t timestamp_us;
uint8_t vib_block_ready;
} daq_state_t;
static daq_state_t daq;
static arm_rfft_fast_instance_f32 fft_inst;
void daq_init(void) {
arm_rfft_fast_init_f32(&fft_inst, VIB_BLOCK_SIZE);
/* Configure ADC1 for vibration (25.6 kHz via TIM2 trigger) */
hadc1.Init.Resolution = ADC_RESOLUTION_16B;
hadc1.Init.ScanConvMode = ADC_SCAN_DISABLE;
hadc1.Init.ExternalTrigConv = ADC_EXTERNALTRIG_T2_TRGO;
hadc1.Init.DMAContinuousRequests = ENABLE;
HAL_ADC_Init(&hadc1);
/* Configure ADC3 for temperature (RTD via Wheatstone bridge) */
hadc3.Init.Resolution = ADC_RESOLUTION_16B;
hadc3.Init.ScanConvMode = ADC_SCAN_ENABLE;
hadc3.Init.NbrOfConversion = 4; /* 4 RTD channels */
HAL_ADC_Init(&hadc3);
/* Start DMA transfers */
HAL_ADC_Start_DMA(&hadc1,
(uint32_t *)daq.vib_buffer, VIB_BLOCK_SIZE);
}
/* Called from DMA complete ISR when vibration block is full */
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef *hadc) {
if (hadc == &hadc1) {
daq.timestamp_us = __HAL_TIM_GET_COUNTER(&htim5);
daq.vib_block_ready = 1;
}
}
/* Extract vibration spectral features for digital twin */
void compute_vibration_features(twin_features_t *features) {
float32_t fft_output[VIB_BLOCK_SIZE];
float32_t magnitude[VIB_BLOCK_SIZE / 2];
/* Apply Hanning window to reduce spectral leakage */
for (int i = 0; i < VIB_BLOCK_SIZE; i++) {
float32_t w = 0.5f * (1.0f - arm_cos_f32(
2.0f * PI * i / (VIB_BLOCK_SIZE - 1)));
daq.vib_buffer[i] *= w;
}
arm_rfft_fast_f32(&fft_inst, daq.vib_buffer, fft_output, 0);
arm_cmplx_mag_f32(fft_output, magnitude, VIB_BLOCK_SIZE / 2);
/* RMS velocity (ISO 10816 vibration severity) */
float32_t rms;
arm_rms_f32(daq.vib_buffer, VIB_BLOCK_SIZE, &rms);
features->vibration_rms_mms = rms * SENSITIVITY_MV_G
/ (2.0f * PI * features->rpm / 60.0f);
/* Peak magnitude and frequency */
uint32_t peak_idx;
arm_max_f32(magnitude, VIB_BLOCK_SIZE / 2, &features->peak_mag, &peak_idx);
features->peak_freq_hz = (float32_t)peak_idx * VIBRATION_FS / VIB_BLOCK_SIZE;
/* Bearing defect frequencies (BPFO, BPFI, BSF, FTF) */
features->bpfo_amplitude = magnitude[features->bpfo_bin];
features->bpfi_amplitude = magnitude[features->bpfi_bin];
}
Sensor Selection for Industrial Digital Twins
| Parameter | Sensor Type | Part Number | Range | Accuracy | Interface |
|---|---|---|---|---|---|
| Vibration | MEMS Accelerometer | IIS3DWB | +/-16g | 60 ug/rtHz | SPI (26.7 kHz) |
| Temperature | RTD (PT1000) | IST P1K0.232.6W | -50 to 500C | Class A (0.15C) | Analog (bridge) |
| Current | Hall Effect | ACS37220 | 0-50A | 0.5% | Analog / I2C |
| Pressure | Piezoresistive | MS5837-30BA | 0-30 bar | 0.2% FS | I2C |
| Flow | Ultrasonic | TDC1000+TDC7200 | 0.1-12 m/s | 1% | SPI |
| Position | Encoder | AS5047P | 14-bit / rev | 0.06 deg | SPI / ABI |
Edge-to-Cloud Communication Architecture
The communication layer must handle heterogeneous industrial protocols at the plant floor and deliver normalized telemetry to the digital twin engine with deterministic latency. Two protocols dominate this space: OPC UA for structured PLC data and MQTT for lightweight sensor telemetry.
MQTT Telemetry Publisher with QoS Management
Edge gateways aggregate sensor data and publish to the digital twin platform via MQTT. The following implementation handles connection resilience, QoS levels per data criticality, and payload serialization using a compact binary format:
/* mqtt_publisher.c - Industrial telemetry publisher */
#include "mqtt_client.h"
#include "cbor.h"
#include <string.h>
#include <time.h>
#define BROKER_URI "mqtts://twin.example.com:8883"
#define TELEMETRY_TOPIC "dt/plant-01/pump-003/telemetry"
#define ALARM_TOPIC "dt/plant-01/pump-003/alarm"
#define KEEPALIVE_SEC 30
#define RECONNECT_MS 5000
#define MAX_INFLIGHT 10
typedef struct {
mqtt_client_t client;
uint8_t cbor_buf[256];
uint32_t seq_counter;
bool connected;
} publisher_t;
static publisher_t pub;
/* Serialize telemetry as CBOR for compact transport */
static int encode_telemetry(const twin_features_t *feat,
uint8_t *buf, size_t max_len) {
CborEncoder encoder, map;
cbor_encoder_init(&encoder, buf, max_len, 0);
cbor_encoder_create_map(&encoder, &map, 8);
cbor_encode_text_stringz(&map, "ts");
cbor_encode_uint(&map, feat->timestamp_ms);
cbor_encode_text_stringz(&map, "seq");
cbor_encode_uint(&map, pub.seq_counter++);
cbor_encode_text_stringz(&map, "vib_rms");
cbor_encode_float(&map, feat->vibration_rms_mms);
cbor_encode_text_stringz(&map, "peak_hz");
cbor_encode_float(&map, feat->peak_freq_hz);
cbor_encode_text_stringz(&map, "temp_c");
cbor_encode_float(&map, feat->bearing_temp_c);
cbor_encode_text_stringz(&map, "current_a");
cbor_encode_float(&map, feat->motor_current_a);
cbor_encode_text_stringz(&map, "pressure_bar");
cbor_encode_float(&map, feat->discharge_pressure_bar);
cbor_encode_text_stringz(&map, "rpm");
cbor_encode_float(&map, feat->shaft_rpm);
cbor_encoder_close_container(&encoder, &map);
return cbor_encoder_get_buffer_size(&encoder, buf);
}
void publisher_send_telemetry(const twin_features_t *feat) {
if (!pub.connected) return;
int len = encode_telemetry(feat, pub.cbor_buf,
sizeof(pub.cbor_buf));
mqtt_publish(&pub.client, TELEMETRY_TOPIC,
pub.cbor_buf, len,
MQTT_QOS_0, /* Telemetry: at-most-once */
false); /* No retain */
}
void publisher_send_alarm(const char *alarm_id,
float value, float threshold) {
/* Alarms use QoS 1 for guaranteed delivery */
uint8_t buf[128];
CborEncoder encoder, map;
cbor_encoder_init(&encoder, buf, sizeof(buf), 0);
cbor_encoder_create_map(&encoder, &map, 4);
cbor_encode_text_stringz(&map, "alarm_id");
cbor_encode_text_stringz(&map, alarm_id);
cbor_encode_text_stringz(&map, "value");
cbor_encode_float(&map, value);
cbor_encode_text_stringz(&map, "threshold");
cbor_encode_float(&map, threshold);
cbor_encode_text_stringz(&map, "ts");
cbor_encode_uint(&map, (uint64_t)time(NULL) * 1000);
cbor_encoder_close_container(&encoder, &map);
mqtt_publish(&pub.client, ALARM_TOPIC,
buf, cbor_encoder_get_buffer_size(&encoder, buf),
MQTT_QOS_1, /* At-least-once for alarms */
false);
}
Physics-Based Digital Twin Model
The core of a digital twin is its simulation model. For rotating machinery such as centrifugal pumps, a physics-based model captures the relationships between operating parameters (speed, flow, pressure) and internal state (bearing wear, impeller degradation, seal condition). Unlike purely data-driven models, physics models generalize to operating conditions not seen during training.
Centrifugal Pump Twin Model
The following reduced-order model simulates pump hydraulics and mechanical degradation. It updates at 10 Hz, ingesting live sensor data and computing health indicators:
/* pump_twin.c - Centrifugal pump digital twin model */
#include <math.h>
#include <string.h>
typedef struct {
/* Hydraulic state */
float head_m; /* Developed head (meters) */
float flow_m3s; /* Volumetric flow rate */
float efficiency; /* Hydraulic efficiency */
/* Mechanical state */
float bearing_temp_c; /* Predicted bearing temperature */
float vibration_predicted; /* Predicted vibration level */
float seal_leakage_lpm; /* Estimated seal leakage */
/* Degradation indicators */
float impeller_wear; /* 0.0 (new) to 1.0 (replace) */
float bearing_health; /* 1.0 (new) to 0.0 (failure) */
float cavitation_index; /* 0.0 (none) to 1.0 (severe) */
/* Model parameters (from commissioning) */
float head_coeff[3]; /* H = a*Q^2 + b*Q + c (at ref speed) */
float eff_coeff[3]; /* eta = a*Q^2 + b*Q + c */
float rated_speed_rpm;
float bearing_thermal_r; /* Thermal resistance (C/W) */
float bearing_thermal_c; /* Thermal capacitance (J/C) */
} pump_twin_t;
/* Update twin state with new sensor readings */
void pump_twin_update(pump_twin_t *twin,
const twin_features_t *measured,
float dt_sec) {
float n_ratio = measured->shaft_rpm / twin->rated_speed_rpm;
/* Affinity laws: scale pump curve to current speed */
float q_ref = measured->flow_m3s / n_ratio;
float head_predicted = (twin->head_coeff[0] * q_ref * q_ref
+ twin->head_coeff[1] * q_ref
+ twin->head_coeff[2])
* n_ratio * n_ratio
* (1.0f - 0.3f * twin->impeller_wear);
/* Head residual indicates impeller degradation */
float head_residual = measured->discharge_pressure_bar * 10.197f
- head_predicted;
if (head_residual < -0.5f) {
twin->impeller_wear += 0.0001f * fabsf(head_residual) * dt_sec;
if (twin->impeller_wear > 1.0f) twin->impeller_wear = 1.0f;
}
/* Bearing thermal model (first-order lumped parameter) */
float friction_power = 0.001f * measured->shaft_rpm
* (1.0f + 2.0f * (1.0f - twin->bearing_health));
float q_dissipated = (twin->bearing_temp_c - 25.0f)
/ twin->bearing_thermal_r;
twin->bearing_temp_c += (friction_power - q_dissipated)
/ twin->bearing_thermal_c * dt_sec;
/* Bearing health degradation from vibration envelope */
if (measured->bpfi_amplitude > 0.5f
|| measured->bpfo_amplitude > 0.5f) {
float vib_stress = fmaxf(measured->bpfi_amplitude,
measured->bpfo_amplitude);
twin->bearing_health -= 1e-6f * vib_stress * vib_stress * dt_sec;
if (twin->bearing_health < 0.0f) twin->bearing_health = 0.0f;
}
/* NPSH-based cavitation detection */
float npsh_available = measured->suction_pressure_bar * 10.197f
+ measured->suction_head_m
- vapor_pressure_m(measured->fluid_temp_c);
float npsh_required = 3.0f * powf(n_ratio, 2.0f)
* (1.0f + 0.2f * twin->impeller_wear);
twin->cavitation_index = fmaxf(0.0f,
1.0f - npsh_available / npsh_required);
/* Update predicted state */
twin->head_m = head_predicted;
twin->efficiency = (twin->eff_coeff[0] * q_ref * q_ref
+ twin->eff_coeff[1] * q_ref
+ twin->eff_coeff[2])
* (1.0f - 0.15f * twin->impeller_wear);
}
In a textile manufacturing plant deployment, this pump twin model detected bearing inner race degradation 11 days before the maintenance team's scheduled vibration analysis route. The bearing health indicator dropped from 0.82 to 0.61 over 72 hours, triggering a predictive maintenance work order that prevented an unplanned shutdown estimated at $45,000 in lost production.
State Synchronization and Consistency
Maintaining consistency between the physical asset and its digital twin requires careful handling of time alignment, missing data, and conflicting sensor readings. The synchronization engine must handle network partitions gracefully, buffering updates and reconciling state when connectivity is restored.
Time Alignment Buffer
Sensors report at different rates and experience varying network delays. A time alignment buffer collects readings within a configurable window and produces synchronized snapshots for the physics model:
/* time_align.c - Multi-source temporal alignment */
#include <stdint.h>
#include <stdbool.h>
#define MAX_CHANNELS 16
#define BUFFER_DEPTH 64
#define ALIGN_WINDOW_MS 50 /* 50ms alignment tolerance */
typedef struct {
float value;
uint64_t timestamp_ms;
bool valid;
} sample_t;
typedef struct {
sample_t ring[BUFFER_DEPTH];
uint32_t head;
uint32_t sample_rate_hz;
uint64_t last_ts;
} channel_t;
typedef struct {
channel_t channels[MAX_CHANNELS];
uint32_t num_channels;
uint64_t sync_time_ms;
} alignment_engine_t;
/* Interpolate channel value at target timestamp */
static float interpolate_at(channel_t *ch, uint64_t target_ms) {
sample_t *prev = NULL, *next = NULL;
for (int i = 0; i < BUFFER_DEPTH; i++) {
int idx = (ch->head - i - 1 + BUFFER_DEPTH) % BUFFER_DEPTH;
sample_t *s = &ch->ring[idx];
if (!s->valid) continue;
if (s->timestamp_ms <= target_ms) {
prev = s;
break;
}
next = s;
}
if (prev && next) {
float alpha = (float)(target_ms - prev->timestamp_ms)
/ (float)(next->timestamp_ms - prev->timestamp_ms);
return prev->value + alpha * (next->value - prev->value);
}
return prev ? prev->value : (next ? next->value : 0.0f);
}
/* Produce aligned snapshot for digital twin model */
bool alignment_get_snapshot(alignment_engine_t *eng,
float *aligned_values) {
uint64_t now_ms = get_system_time_ms();
/* Use the oldest channel's latest timestamp as sync point */
uint64_t sync = now_ms;
for (uint32_t i = 0; i < eng->num_channels; i++) {
if (eng->channels[i].last_ts < sync)
sync = eng->channels[i].last_ts;
}
if (now_ms - sync > ALIGN_WINDOW_MS * 3)
return false; /* Data too stale */
for (uint32_t i = 0; i < eng->num_channels; i++) {
aligned_values[i] = interpolate_at(&eng->channels[i], sync);
}
eng->sync_time_ms = sync;
return true;
}
Predictive Maintenance Decision Engine
The digital twin's ultimate value lies in converting degradation indicators into actionable maintenance decisions. The decision engine applies configurable thresholds with hysteresis, Remaining Useful Life (RUL) estimation, and maintenance window optimization.
RUL Estimation and Alert Generation
A linear extrapolation of degradation trends provides initial RUL estimates, while more sophisticated implementations use particle filters or Bayesian updating. The threshold framework integrates with existing CMMS (Computerized Maintenance Management Systems) via REST API calls:
- Green (Normal): All health indicators above 0.7, no trend degradation exceeding 0.001/day
- Yellow (Watch): Any indicator between 0.4-0.7, or degradation trend exceeding 0.005/day
- Orange (Plan): Any indicator between 0.2-0.4, RUL estimate below 30 days
- Red (Act): Any indicator below 0.2, RUL estimate below 7 days, or cavitation index above 0.7
The integration between digital twin monitoring and real-time operating systems running on the edge is explored further in our guide to Zephyr RTOS for IoT development. For sensor networks feeding the twin, mesh topologies like Zigbee 3.0 provide reliable in-plant connectivity with self-healing routes around metal obstructions and RF interference.
Scaling Digital Twins Across a Fleet
A single-asset digital twin proves the concept; fleet-scale deployment requires additional architectural patterns. Cross-asset correlation reveals systemic issues (shared cooling water temperature affecting multiple pumps) and enables fleet-level optimization.
Fleet Management Architecture
The platform must support multi-tenancy, model versioning, and compute resource allocation per asset criticality. Key architectural decisions include:
- Model registry: Version-controlled physics model parameters per asset type and serial number
- Hierarchical twins: Asset twins compose into system twins (pump station), which compose into plant twins
- Compute tiering: Critical assets run twins at 10 Hz on dedicated edge hardware; non-critical assets share cloud compute at 0.1 Hz
- Cross-asset correlation: Shared environmental inputs (ambient temperature, supply voltage) propagate to all affected asset twins simultaneously
For edge compute architectures capable of hosting multiple twin instances, WebAssembly runtimes on edge devices offer sandboxed, portable model execution with near-native performance. Where ultra-low-latency networking connects sensors to edge compute, Wi-Fi HaLow (802.11ah) provides sub-GHz wireless backhaul with kilometer-range coverage ideal for large industrial sites.
Frequently Asked Questions
What is a digital twin in the context of industrial IoT?
A digital twin in industrial IoT is a virtual replica of a physical asset that receives real-time sensor data to mirror its state. Unlike static models, it continuously synchronizes through sensor feeds, maintains historical state for trend analysis, and runs physics-based simulations to predict future behavior. This enables condition-based monitoring, predictive maintenance scheduling, and what-if scenario analysis without disrupting production.
How does OPC UA compare to MQTT for digital twin data ingestion?
OPC UA provides rich information modeling with built-in semantics and type systems, ideal for structured PLC/SCADA data. MQTT is lighter weight with minimal overhead, better suited for high-volume telemetry from constrained edge devices. Most production architectures use both: OPC UA at the plant floor for PLC integration and MQTT at the edge-to-cloud boundary for efficient telemetry transport.
What sensor sampling rates are needed for vibration-based predictive maintenance?
Vibration-based predictive maintenance requires sampling at least 2.56 times the maximum frequency of interest. For standard industrial motors, bearing defect frequencies below 10 kHz require 25.6 kHz minimum. MEMS accelerometers like the IIS3DWB support up to 26.7 kHz. For higher frequencies, piezoelectric accelerometers with dedicated DAQ modules are necessary.
Can digital twin simulations run on edge hardware instead of cloud?
Yes, reduced-order physics models and lightweight ML inference run effectively on industrial edge platforms. Even ARM Cortex-A72 gateways handle real-time simulation at 100 Hz for simpler thermal or flow models. Full CFD or FEA simulations still need cloud compute, but surrogate models trained from them run at the edge with sub-10ms latency.
How do you handle clock synchronization across distributed sensor networks?
IEEE 1588 PTPv2 achieves sub-microsecond synchronization for Ethernet-connected devices. GPS-disciplined clocks provide 50-100ns accuracy for wireless nodes. NTP with hardware timestamping reaches 1-10ms where PTP and GPS are unavailable. The twin engine implements a time alignment buffer that reorders and interpolates values to a common time base before feeding the model.