Skip to main content
Back to Projects

Industrial IoT Edge Platform

Operating a Multi-Site Data-Logging Fleet in Production

A production fleet of around 20 edge data-logging devices running across 10 industrial sites for different customers, on hardware from different brands. Machine and sensor data is acquired over Modbus, processed in containers at the edge, and streamed to cloud analytics — with one-click OTA updates and store-and-forward resilience.

01

Problem

Industrial sites generate valuable machine data, but cloud connectivity is unreliable and every customer site is different: different device brands, different machines, different sensors worth monitoring. Supporting a growing fleet remotely — keeping devices updated, diagnosing failures, and not losing data during outages — is where most of the real engineering effort lives.

Unreliable Connectivity

Sites drop offline; devices must buffer locally and lose nothing

Heterogeneous Fleet

Different customers, sites, and device brands behind one architecture

Remote Operations

Devices live at customer sites — updates and diagnosis happen from afar

02

Architecture

PLC LayerSiemens S7Allen-BradleyModbus / OPC UAEdge LayerAzure IoT EdgeDocker ContainersInfluxDB + MQTTAdaptersS7 AdapterModbus AdapterOPC UA AdapterCloud LayerIoT HubAnalytics PipelineGrafana DashboardsAlerting SystemLong-term StorageMachine LearningSensorsTemperature, PressureVibration, FlowAnalog/DigitalIndustrial ProtocolsRaw DataMQTTTLS/HTTPSCommands

The system uses a three-tier edge computing architecture. PLCs communicate via industrial protocols (S7, Modbus, OPC UA) to local edge gateways. These gateways aggregate data, filter noise, and perform local analytics. Azure IoT Edge orchestrates containers with store-and-forward resilience for cloud connectivity interruptions.

PLC Layer

  • Siemens S7 protocol adapters
  • Allen-Bradley integration
  • Modbus TCP/RTU support
  • OPC UA for modern devices

Edge Layer

  • Azure IoT Edge runtime
  • Docker container orchestration
  • Local InfluxDB time-series storage
  • Store-and-forward message queue

Cloud Layer

  • Azure IoT Hub for device management
  • Time-series analytics pipeline
  • Grafana dashboards for visualization
  • Alerting and notification system

Protocol Adapters

  • Modular adapter pattern
  • Auto-discovery of new devices
  • Protocol translation to MQTT
  • Health monitoring per adapter
03

Technical Approach

Designed a three-tier architecture: machine layer for data acquisition over Modbus and field sensors, edge layer for containerized collection and local buffering, and cloud layer for long-term analytics. Fleet updates run through Azure Pipelines: each release produces a versioned container image, and an edge tool reads the release number deployed on every device and compares it against the latest build — so version drift is visible at a glance and updating a device is one click. Vendor SDKs are used where specific hardware or tooling requires them.

edge-gateway/edge_module.py
import asyncio
import json
from dataclasses import dataclass, asdict
from typing import Optional
from azure.iot.device import IoTHubModuleClient

@dataclass
class SensorReading:
    device_id: str
    timestamp: float
    value: float
    quality: str  # 'good', 'uncertain', 'bad'
    
class EdgeGateway:
    def __init__(self, connection_string: str):
        self.client = IoTHubModuleClient.create_from_connection_string(
            connection_string
        )
        self.local_buffer = []
        self.cloud_connected = False
        
    async def process_reading(self, reading: SensorReading):
        # Always store locally first
        self.local_buffer.append(asdict(reading))
        
        # Try to send to cloud
        if self.cloud_connected:
            try:
                message = json.dumps(asdict(reading))
                await self.client.send_message(message)
            except Exception:
                self.cloud_connected = False
                self._persist_to_disk(reading)
        else:
            # Store-and-forward pattern
            self._persist_to_disk(reading)
    
    def _persist_to_disk(self, reading: SensorReading):
        # SQLite for local resilience
        with self._get_db() as db:
            db.execute(
                "INSERT INTO pending VALUES (?, ?, ?, ?)",
                (reading.device_id, reading.timestamp, 
                 reading.value, reading.quality)
            )
            
    async def flush_buffer(self):
        # Send buffered messages when connection restored
        if not self.cloud_connected:
            return
            
        pending = self._get_pending_messages()
        for msg in pending:
            await self.client.send_message(json.dumps(msg))
            self._mark_sent(msg['timestamp'])

Key Technical Decisions

Azure IoT Edge Over Custom Solution

Chose Azure IoT Edge for its built-in container orchestration, security features, and managed device provisioning. This reduced operational complexity compared to building a custom edge runtime.

Store-and-Forward Pattern

Implemented SQLite-based local storage for message persistence during network outages. This ensures zero data loss even during extended connectivity interruptions common in manufacturing environments.

Modular Protocol Adapters

Designed each industrial protocol as a separate Docker container. This allows mixing different device types at a single site and enables independent updates without affecting other adapters.

Failure Isolation at Module Boundaries

Learned from a real incident: containers on a device must not trust each other to stay alive. Inter-module communication is wrapped so a neighbor's failure is handled and logged instead of cascading — on a remote device, that is the difference between a log line and a site visit.

04

Operating the Fleet

Building the platform is half the job — the other half is running ~20 devices at 10 sites you cannot walk up to. Fleet operations center on three things: knowing what release every device runs, updating with confidence, and surviving bad networks.

One-Click Updates, Driven by the Release Pipeline

1 · Build

Azure Pipelines turns every release into a versioned container image

2 · Compare

An edge tool reads each device's deployed release number and compares it against the latest build — drift is visible at a glance

3 · Deploy

Updating a device is one click; store-and-forward keeps data safe while modules restart

Incident: When One Container Took Down Another

A communication bug between two containers running on the same device meant that when one module failed, the exception propagated and crashed its neighbor — a single fault took the whole logger down at a remote site.

The fix was not exotic: rework the error handling at the module boundary so a failed call is caught, logged, and retried instead of crashing the caller. The device now degrades gracefully — one module can fail while the rest keeps logging. The lesson stuck: on hardware you cannot reach, failure isolation is a feature, not a refactor.

Hardware as the Interface

Edge work means treating hardware as an interface, not an abstraction: Modbus to talk to machines and gather data, field sensors chosen for what is actually worth monitoring at each site, and vendor SDKs where a specific device or tool requires them. Every brand in the fleet has its own quirks — the architecture absorbs them so the data pipeline does not have to.

05

Tech Stack

Azure IoT Edge
Azure Pipelines (CI/CD + OTA)
Docker
Python
C++
Modbus TCP/RTU
OPC UA
MQTT
InfluxDB
Grafana
06

Results & Outcomes

01

Fleet of ~20 data-logging devices operated in production across 10 sites and multiple customers

02

Heterogeneous hardware — devices from different brands integrated behind one containerized collector architecture

03

One-click OTA updates: Azure Pipelines releases with deployed-vs-latest image comparison per device

04

Hardened failure isolation after a real incident where one container crash cascaded into another — a module failure now degrades gracefully instead of taking down the device

05

Machine and sensor data acquired over Modbus, with vendor SDKs where specific hardware required them

06

Store-and-forward buffering keeps data safe through the network outages that real sites produce

~20
Devices in Production
10
Sites
Multi-brand
Hardware Fleet
1-click
OTA Updates
07

Live Demo

Explore the interactive fleet operations dashboard: device health, alerts, and a Fleet & Updates view showing deployed-vs-latest releases with one-click OTA updates. All data is simulated — the workflows mirror how the real fleet is run.

Open Dashboard Demo