Industrial Data Collection with AWS IoT Core: From ZMA Devices to Cloud
Introduction
Moving industrial data to the cloud is the cornerstone of Industry 4.0 and predictive maintenance processes. However, data collected from sensors in the field typically communicate using traditional protocols like Modbus RTU or Modbus TCP. An IoT Gateway is required to transfer this data to cloud platforms like AWS IoT Core.
In this article, we examine in detail how data collected via Modbus from our ZMA Data Acquisition and GDT Digital Transmitter devices is transferred to AWS IoT Core through an IoT Gateway.
Architecture Overview
┌─────────────────────────────────────────────────────────────┐
│ Factory / Field │
│ │
│ ┌─────────┐ Modbus RTU/TCP ┌──────────────┐ │
│ │ ZMA-4 │◄────────────────────►│ │ │
│ │ Device │ │ IoT Gateway │ │
│ └─────────┘ │ (Linux Box) │ │
│ │ │ │
│ ┌─────────┐ Modbus RTU/TCP │ - Modbus │ │
│ │ GDT │◄────────────────────►│ Client │ │
│ │ Trans. │ │ - MQTT │ │
│ └─────────┘ │ Client │ │
│ │ - TLS/SSL │ │
│ └───────┬──────┘ │
└───────────────────────────────────────────┼───────────────┘
│
│ MQTT over TLS
│ Port 8883
▼
┌────────────────┐
│ AWS Cloud │
│ │
│ ┌──────────┐ │
│ │ IoT Core │ │
│ └────┬─────┘ │
│ │ │
│ ┌────▼─────┐ │
│ │Timestream│ │
│ └──────────┘ │
└────────────────┘
Important Note: IoT Gateway Requirement
ZMA and GDT devices communicate via Modbus RTU (RS485) and Modbus TCP (Ethernet) protocols. AWS IoT Core uses the MQTT protocol. An IoT Gateway device is needed to perform protocol conversion between these two worlds.
Gateway Options
- Raspberry Pi / Jetson Nano: Linux-based, custom script with Python/Node.js
- Industrial IoT Gateways: Ready solutions like Moxa, Advantech, Siemens
- Our HMI Panel: Can function as both HMI and gateway on Embedded Linux
- PLC + OPC UA: Cloud connection via PLC's OPC UA server
In this article, we'll examine a custom gateway solution with Raspberry Pi 4 + Python, but the same logic applies to all platforms.
AWS IoT Core Setup
1. Create Thing (Device)
AWS Console → IoT Core → Manage → Things → Create
{
"thingName": "amazeng-factory-gateway-01",
"thingTypeName": "ModbusGateway",
"attributes": {
"location": "Istanbul-Factory-A",
"firmware": "1.0.0"
}
}
2. Create and Download Certificate
Each IoT device connects to AWS with X.509 certificate:
# Create certificate with AWS CLI
aws iot create-keys-and-certificate \
--set-as-active \
--certificate-pem-outfile gateway-cert.pem \
--public-key-outfile gateway-public.key \
--private-key-outfile gateway-private.key
Important: Store gateway-private.key file securely, cannot be downloaded again!
IoT Gateway Software (Python)
Gateway Application
gateway.py
#!/usr/bin/env python3
import json
import time
import struct
from pymodbus.client.sync import ModbusTcpClient
from awscrt import io, mqtt
from awsiot import mqtt_connection_builder
class ModbusToMQTTGateway:
def __init__(self, config_file):
with open(config_file, 'r') as f:
self.config = json.load(f)
self.modbus_clients = {}
self.mqtt_connection = None
# Create Modbus clients
for device in self.config['modbus_devices']:
if device['type'] == 'tcp':
client = ModbusTcpClient(device['host'], port=device['port'])
client.connect()
self.modbus_clients[device['name']] = {
'client': client,
'config': device
}
# AWS IoT MQTT connection
self._setup_mqtt()
def _setup_mqtt(self):
"""Setup AWS IoT Core MQTT connection"""
aws_config = self.config['aws_iot']
cert_path = aws_config['cert_path']
# Secure connection with TLS
self.mqtt_connection = mqtt_connection_builder.mtls_from_path(
endpoint=aws_config['endpoint'],
port=aws_config['port'],
cert_filepath=f"{cert_path}gateway-cert.pem",
pri_key_filepath=f"{cert_path}gateway-private.key",
ca_filepath=f"{cert_path}AmazonRootCA1.pem",
client_id=aws_config['thing_name'],
clean_session=False,
keep_alive_secs=30
)
# Connect
connect_future = self.mqtt_connection.connect()
connect_future.result()
print(f"✓ Connected to AWS IoT Core: {aws_config['endpoint']}")
def read_modbus_device(self, device_name):
"""Read all registers from a Modbus device"""
device = self.modbus_clients[device_name]
client = device['client']
config = device['config']
data = {
'device_name': device_name,
'timestamp': int(time.time() * 1000),
'values': {}
}
for register in config['registers']:
try:
# Read holding register
result = client.read_holding_registers(
address=register['address'],
count=register['count'],
unit=config['unit_id']
)
if not result.isError():
# Float conversion (IEEE 754)
if register['type'] == 'float' and register['count'] == 2:
raw = (result.registers[0] << 16) | result.registers[1]
value = struct.unpack('f', struct.pack('I', raw))[0]
data['values'][register['name']] = round(value, 3)
except Exception as e:
print(f"✗ Error: {device_name} - {e}")
data['values'][register['name']] = None
return data
def publish_to_aws(self, device_name, data):
"""Publish data to AWS IoT Core"""
topic = f"{self.config['aws_iot']['topic_prefix']}/{device_name}/data"
payload = json.dumps(data)
self.mqtt_connection.publish(
topic=topic,
payload=payload,
qos=mqtt.QoS.AT_LEAST_ONCE
)
print(f"→ Published to {topic}: {payload}")
def run(self):
"""Main loop: Read from Modbus, send to MQTT"""
interval = self.config['polling_interval']
print(f"Gateway started. Polling interval: {interval}s")
try:
while True:
for device_name in self.modbus_clients.keys():
# Read from Modbus
data = self.read_modbus_device(device_name)
# Send to AWS
self.publish_to_aws(device_name, data)
time.sleep(interval)
except KeyboardInterrupt:
print("\nStopping gateway...")
self.shutdown()
if __name__ == "__main__":
gateway = ModbusToMQTTGateway('/etc/iot-gateway/config.json')
gateway.run()
Real-Time Alarm Scenario
Lambda Function for Threshold Control
alarm_handler.py
import json
import boto3
sns = boto3.client('sns')
def lambda_handler(event, context):
# Data from IoT Core
device = event['device_name']
weight = event['values'].get('net_weight', 0)
# Threshold check
if weight > 5000: # Over 5 tons
message = f"ALARM: {device} - Weight limit exceeded: {weight} kg"
sns.publish(
TopicArn='arn:aws:sns:eu-west-1:123456789:factory-alarms',
Subject='Tank Capacity Alarm',
Message=message
)
return {'statusCode': 200, 'body': 'Alarm sent'}
return {'statusCode': 200, 'body': 'Normal'}
Cost Analysis
AWS IoT Core Pricing (eu-west-1)
| Component | Price | Monthly Cost* |
|---|---|---|
| Connectivity | $0.08 / million connection-minutes | $3.45 (2 devices, 24/7) |
| Messaging | $1.00 / million messages | $2.59 (1s polling, 2 devices) |
| Rule Engine | $0.15 / million rules | $0.38 |
| Timestream Write | $0.50 / million writes | $1.30 |
| Timestream Storage | $0.03 / GB-hour | $21.60 (30GB) |
| TOTAL | ~$29/month |
*2 devices, 1 second polling, 30 days retention
Conclusion
Our ZMA and GDT products can easily connect to AWS IoT Core via IoT Gateway thanks to Modbus RTU/TCP support. This enables:
✅ Real-time data monitoring (Grafana/QuickSight)
✅ Long-term data storage (Timestream)
✅ Predictive maintenance (AI/ML models)
✅ Remote alarms and notifications
For more information about our Cloud & IoT Data Collection Solutions, contact us.