Batched vs. Immediate Telemetry Delivery: When to Use Each in Industrial Monitoring [2026]
Every industrial IoT edge gateway faces a fundamental architectural decision for every data point it collects: ship it now, or hold it and ship a batch later?
Get this wrong and you either drown your MQTT broker in tiny messages or you miss a critical alarm because it was sitting in a buffer when the compressor caught fire. This guide covers the engineering behind both approaches, the real-world trade-offs, and a framework for deciding which to use where.
The Two Delivery Modes
Immediate Delivery
The edge device reads a value from the PLC, wraps it in a message, and publishes it to the MQTT broker right now. No waiting, no aggregation. One read → one publish.
Wire format for a single immediately-delivered value:
Header (device type + serial number + timestamp)
└─ Single value group
└─ Tag ID: 0x8001
└─ Status: 0x00 (OK)
└─ Value count: 1
└─ Value: 0x01 (bool, true)
Total payload: ~20 bytes plus MQTT overhead.
Batched Delivery
The edge device reads values from the PLC and adds them to an in-memory batch structure. The batch is only published when either:
- Size threshold is reached (e.g., batch exceeds 4KB)
- Time threshold is reached (e.g., 60 seconds since the batch started collecting)
Wire format for a batch:
Header byte: 0xF7
Number of groups: 3
├─ Group 1 (ts: 1709398800)
│ ├─ Device type + serial
│ ├─ Value count: 12
│ └─ [12 tag values packed]
├─ Group 2 (ts: 1709398805)
│ ├─ Device type + serial
│ ├─ Value count: 8
│ └─ [8 tag values packed]
└─ Group 3 (ts: 1709398810)
├─ Device type + serial
├─ Value count: 15
└─ [15 tag values packed]
Total payload: ~800 bytes for 35 values — versus 35 × 20 = 700 bytes if sent individually, but with 35x fewer MQTT publishes.
Why the Difference Matters
MQTT Publish Overhead
Every MQTT publish involves:
- Fixed header: 2-5 bytes (packet type + remaining length)
- Topic name: variable, typically 20-40 bytes for IoT topics like
devices/{id}/messages/events/ - Payload: your actual data
- QoS 1 handshake: PUBACK round-trip (~50-200ms on cellular networks)
For a 20-byte immediate payload, the MQTT overhead is often larger than the data itself. Batching 35 values into a single publish amortizes that overhead across all values.
Broker Connection Limits
Cloud MQTT brokers (Azure IoT Hub, AWS IoT Core, HiveMQ) enforce per-device message rate limits:
| Broker | Default Limit |
|---|---|
| Azure IoT Hub (S1) | 100 msg/sec/unit |
| AWS IoT Core | 100 msg/sec/thing |
| HiveMQ Cloud | Varies by plan |
With 50 tags polling every 5 seconds, immediate delivery generates 10 messages/second — manageable for one device. But scale to 100 devices and you're at 1,000 msg/sec, which may exceed your broker tier.
Batching those same 50 tags into groups of 10-15 reduces the rate to ~1 message every 5 seconds per device — a 50x reduction.
Cellular Bandwidth Costs
On cellular-connected edge gateways (LTE/5G modems on factory rooftops, remote pump stations, mining equipment), bandwidth is metered. Every MQTT publish includes TLS record overhead (typically 29 bytes for TLS 1.2 record header + MAC).
At 35 publishes per poll cycle vs. 1 publish per cycle, the TLS overhead alone adds up to ~1KB/cycle wasted — roughly 17MB/month of pure overhead on a 5-second poll interval.
When to Use Immediate Delivery
Critical Alarms and Safety Events
Some data points cannot wait for a batch to fill:
- Emergency stop activation
- Safety interlock trips
- Equipment link state changes (PLC communication lost)
- Temperature/pressure exceeding safety thresholds
These should be configured with immediate delivery. The extra bandwidth cost is negligible compared to the value of real-time notification.
In practice, this means tagging specific data points with an "immediate" flag in your edge configuration:
{
"name": "emergency_stop",
"id": 401,
"addr": 401200,
"type": "bool",
"interval": 1,
"compare": true,
"do_not_batch": true
}
Link State Changes
When the edge gateway loses communication with a PLC, that event needs to reach the cloud immediately — not wait in a batch that might never complete because the device generating data went offline.
A robust edge gateway emits a special "link state" tag with immediate delivery whenever the PLC connection drops or recovers. This gives the cloud platform instant visibility into connectivity issues.
Events with Dependent Actions
If the cloud platform needs to react to an event — triggering a notification, updating a dashboard in real-time, or initiating a corrective workflow — the latency of batching is unacceptable.
Rule of thumb: if a human needs to see this value within 5 seconds, use immediate delivery.
When to Use Batched Delivery
Process Variables and Trends
Temperature readings, pressure values, flow rates, motor speeds — these are the bread and butter of industrial monitoring. They change gradually and are primarily used for trending, analytics, and historical queries.
Batching is perfect here:
- Values accumulate every 5-60 seconds
- Batch ships when full or when the timeout fires
- Cloud platform stores them in a time-series database
- Dashboard shows a continuous trend line — nobody notices the 60-second delivery lag
OEE and Production Metrics
Cycle counts, part counts, scrap counts, and runtime hours are all batch-friendly. They change incrementally and are typically analyzed over shifts, hours, or days — not seconds.
Energy and Utility Readings
kWh consumption, water flow totals, compressed air pressure — these change slowly and are analyzed in 15-minute to 1-hour buckets. Batching is the only sensible approach.
The Batch Architecture
A well-designed batch system has several key components:
Group Timestamping
Each "group" within a batch carries its own Unix timestamp from when the PLC values were actually read — not when the batch was published. This ensures time-series accuracy regardless of batch delivery delays.
Batch published at T=100
├─ Group at T=60 → [values from poll at T=60]
├─ Group at T=65 → [values from poll at T=65]
├─ Group at T=70 → [values from poll at T=70]
...
└─ Group at T=100 → [values from poll at T=100]
Size-Based Finalization
The batch monitors its accumulated size. When it approaches the maximum (typically 4-8KB for binary encoding), it finalizes and publishes:
if (current_batch_size + 10% safety margin > max_batch_size):
finalize_and_publish(batch)
start_new_batch()
The 10% safety margin prevents the last group from pushing the batch over the MQTT maximum message size (Azure IoT Hub: 256KB, AWS: 128KB).
Time-Based Finalization
Even if the batch hasn't reached its size limit, it must be published after a configurable timeout (commonly 30-120 seconds). This prevents stale data from sitting in memory indefinitely during low-activity periods:
elapsed = now() - batch_start_time
if (elapsed > batch_timeout_seconds):
finalize_and_publish(batch)
start_new_batch()
Binary vs. JSON Encoding
For bandwidth-constrained deployments, binary encoding reduces batch size by 3-5x compared to JSON:
Binary encoding format:
[0xF7] - Header magic byte (1 byte)
[group_count] - Number of groups (4 bytes, big-endian)
For each group:
[timestamp] - Unix timestamp (4 bytes)
[device_type] - Device identifier (2 bytes)
[serial_number] - Serial number (4 bytes)
[value_count] - Number of values (4 bytes)
For each value:
[tag_id] - Tag identifier (2 bytes)
[status] - Read status: 0=OK (1 byte)
[values_count] - Number of elements (1 byte)
[element_size] - Bytes per element: 1, 2, or 4 (1 byte)
[value_data] - Raw value bytes
Equivalent JSON:
{
"groups": [
{
"ts": 1709398800,
"device_type": 1021,
"serial_number": 123456,
"values": [
{"id": 5, "values": [325]},
{"id": 6, "values": [350]}
]
}
]
}
The binary version of two uint16 values: ~30 bytes. The JSON version: ~150 bytes.
For devices pushing 10KB+ of telemetry per batch over cellular, this difference is significant.
The Buffer Layer: Surviving Disconnects
Between the batch and MQTT sits a critical component: the output buffer. This is where batched and immediate messages land before MQTT publishing, and it's what saves your data during connectivity outages.
Page-Based Buffer Architecture
A production buffer isn't a simple FIFO queue. It uses fixed-size memory pages to provide deterministic behavior:
Total buffer memory: 64KB
Page size: 8KB
Pages available: 8
[Free Pages] ──→ [Work Page] ──→ [Used Pages] ──→ [MQTT Publish]
(writing) (waiting to send) (in flight)
Flow:
- Free page → promoted to work page when data arrives
- Data written to work page until full
- Full work page → moved to used page queue
- When MQTT is connected: first used page → publish
- On PUBACK (delivery confirmed): used page → recycled to free pages
Overflow Behavior
When all pages are full and the MQTT connection is down, the buffer must decide what to do with new data. The two common strategies:
Drop oldest: Recycle the oldest used page (losing historical data) to make room for fresh data. This ensures you always have the most recent readings even during extended outages.
Drop newest: Refuse new data when the buffer is full. This preserves the historical record but means you lose data from the current period.
For most industrial applications, drop oldest is the right choice — the cloud platform cares more about current machine state than historical values that can be interpolated.
Delivery Confirmation
The buffer must track which messages have been successfully published via MQTT QoS 1 (PUBACK). A common pattern:
- Assign each message a packet ID
- Send message, mark as "in flight"
- Wait for PUBACK with matching ID
- On PUBACK: advance read pointer, mark page delivered
- On disconnect: reset "in flight" flag, re-send on reconnect
Only one message should be "in flight" at a time to prevent reordering. This sequential delivery guarantee simplifies the cloud-side processing.
Mixing Both Modes: The Hybrid Pattern
The most effective edge architectures use both modes simultaneously:
| Data Type | Delivery Mode | Interval | Rationale |
|---|---|---|---|
| Temperature setpoint | Batched | 60s | Slow-changing, trend data |
| Motor current | Batched | 5s | Process variable |
| Cycle count | Batched | 10s | Production metric |
| E-stop | Immediate | 1s, on-change | Safety-critical |
| Link state | Immediate | On-change | Connectivity monitoring |
| Alarm word changes | Immediate | 1s, on-change | Operator notification |
Both modes share the same output buffer and MQTT connection. Immediate messages jump ahead of batched pages in the publish queue, ensuring they're delivered first when bandwidth is available.
The On-Change Filter
Most alarm-type tags combine immediate delivery with change detection:
- Interval: Poll every 1 second
- Compare: Only emit when value differs from previous reading
- Delivery: Immediate (bypass batch)
This produces zero bus traffic during normal operation (alarm cleared = no change = no emission) and instant delivery when an alarm fires.
Sizing Your Batch Parameters
Batch Size
Formula:
batch_size = tags_per_group × bytes_per_tag × groups_per_batch
For 50 tags × ~6 bytes/tag (binary) × 12 groups (60-second timeout, 5-second interval):
50 × 6 × 12 = 3,600 bytes ≈ 4KB
A 4KB batch size is a good default for most deployments.
Batch Timeout
Match this to your dashboard refresh expectations:
- Dashboard refreshes every 30 seconds → batch timeout = 30s
- Dashboard refreshes every minute → batch timeout = 60s
- Historical analysis only → batch timeout = 120s
Lower timeouts mean more publishes but fresher data. Higher timeouts mean fewer publishes but more latency.
Buffer Size
Formula:
buffer_size = batch_size × desired_offline_survival_minutes / batch_timeout
For 4KB batches, 60-second timeout, 30 minutes offline survival:
4KB × 30 = 120KB buffer
With 8KB pages, that's 15 pages — more than enough for a typical cellular outage.
Performance Benchmarks
Based on real-world deployments across plastics manufacturing equipment:
| Metric | Immediate Only | Batched Only | Hybrid |
|---|---|---|---|
| MQTT publishes/hour | 7,200 | 60 | ~80 |
| Bandwidth (cellular) | 2.1 MB/hr | 180 KB/hr | 200 KB/hr |
| Alarm latency | under 1s | up to 60s | under 1s |
| Data completeness during outage | 2 minutes | 30 minutes | 30 minutes |
| Broker message rate (100 devices) | 720K/hr | 6K/hr | 8K/hr |
The hybrid approach delivers the best of both worlds: real-time alarm delivery with efficient bandwidth usage for process data.
How machineCDN Handles This
machineCDN's edge gateway implements the hybrid pattern natively. Every tag in the configuration can be marked for batched or immediate delivery. The gateway handles batch sizing, timeout-based finalization, binary encoding, page-buffered MQTT delivery, and automatic overflow management — all on resource-constrained edge hardware running on embedded Linux.
The practical result: critical alarms arrive in under a second, process trends batch efficiently over cellular connections, and no data is lost during connectivity outages of up to 30 minutes.
Conclusion
The "batched vs. immediate" question isn't binary — it's a spectrum that should be configured per-tag based on the data's operational importance.
Use immediate delivery for:
- Safety events and critical alarms
- Connectivity state changes
- Any value that triggers human action
Use batched delivery for:
- Process variables (temperature, pressure, flow)
- Production counters and OEE metrics
- Energy and utility readings
- Any value used primarily for trending
The edge gateway's job is to make this decision transparent: configure once per tag, and let the batch/buffer/publish pipeline handle the rest. The factory floor generates the data. The edge decides how urgently it needs to leave. The cloud stores it all.
Get the classification right, and your IIoT system delivers real-time operational visibility without bankrupting your cellular data plan.