Fully Integrated Solution: ZMA + HMI + AWS for Weighing Systems
Introduction
In modern industrial facilities, obtaining full control requires the integration of multiple hardware and software components. In this article, we examine in detail a fully integrated solution using our ZMA Data Acquisition, HMI Industrial Display Solutions and Cloud & IoT Data Collection.
Note: Our GDT Digital Transmitter supports maximum 2 channels + 3 DIO. For 4-loadcell applications, ZMA-4 is the preferred choice.
Real-World Scenario: Milk Processing Facility
Facility Profile
- Location: Istanbul, Turkey
- Capacity: 5 milk collection tanks (5,000 kg each)
- Operation: 24/7 continuous operation
- Compliance: ISO 22000, HACCP requirements
Requirements
- Real-time Weight Monitoring: Weight of each tank updated every second
- Local Display: Touchscreen HMI panel per tank
- Central Control: Large central HMI screen (10.1")
- Cloud Data: Historical data on AWS for quality reports
- Alarms: SMS and email alerts when tank capacity exceeds 90%
- Traceability: Weight history per batch (which milk came in when, how long did it stay)
System Architecture
┌─────────────────────────────────────────────────────────────┐
│ Milk Processing Facility │
│ │
│ ┌────────────┐ ┌────────────┐ ┌────────────┐ │
│ │ Tank 1 │ │ Tank 2 │ │ Tank 3 │ │
│ │ 5000 kg │ │ 5000 kg │ │ 5000 kg │ │
│ │ │ │ │ │ │ │
│ │ 4x Loadcell│ │ 4x Loadcell│ │ 4x Loadcell│ │
│ └──────┬─────┘ └──────┬─────┘ └──────┬─────┘ │
│ │ │ │ │
│ ┌────▼────┐ ┌────▼────┐ ┌────▼────┐ │
│ │ ZMA-4-01 │ │ ZMA-4-02 │ │ ZMA-4-03 │ │
│ │ .101 │ │ .102 │ │ .103 │ │
│ └────┬────┘ └────┬────┘ └────┬────┘ │
│ │ │ │ │
│ ┌────▼────┐ ┌────▼────┐ ┌────▼────┐ │
│ │7" HMI │ │7" HMI │ │7" HMI │ │
│ │Touch │ │Touch │ │Touch │ │
│ └────┬────┘ └────┬────┘ └────┬────┘ │
│ │ │ │ │
│ └────────────────┼────────────────┘ │
│ │ │
│ ┌─────▼──────┐ │
│ │ Ethernet │ │
│ │ Switch │ │
│ └─────┬──────┘ │
│ │ │
│ ┌───────────────┼───────────────┐ │
│ │ │ │ │
│ ┌────▼────┐ ┌─────▼─────┐ ┌────▼────┐ │
│ │10.1" HMI│ │ Gateway │ │ ZMA-4 │ │
│ │Central │ │Raspberry │ │(Analog) │ │
│ │Monitor │ │ Pi 4 │ └─────────┘ │
│ └─────────┘ └─────┬─────┘ │
│ │ │
└──────────────────────────┼─────────────────────────────────┘
│ MQTT over TLS
│ Port 8883
▼
┌────────────────────┐
│ AWS Cloud │
│ │
│ ┌──────────────┐ │
│ │ IoT Core │ │
│ └──────┬───────┘ │
│ │ │
│ ┌──────▼───────┐ │
│ │ Timestream │ │
│ │ (Database) │ │
│ └──────┬───────┘ │
│ │ │
│ ┌──────▼───────┐ │
│ │ Grafana │ │
│ │ Dashboard │ │
│ └──────────────┘ │
└────────────────────┘
Hardware Configuration
1. ZMA-4 Data Acquisition Modules (3 units)
Configuration per Tank:
{
"device": "ZMA-4",
"channels": 4,
"loadcells": [
{ "position": "Front-Left", "capacity": "2000kg", "sensitivity": "2mV/V" },
{ "position": "Front-Right", "capacity": "2000kg", "sensitivity": "2mV/V" },
{ "position": "Rear-Left", "capacity": "2000kg", "sensitivity": "2mV/V" },
{ "position": "Rear-Right", "capacity": "2000kg", "sensitivity": "2mV/V" }
],
"modbus": {
"type": "TCP",
"ip": "192.168.1.101",
"port": 502,
"unit_id": 1
},
"specs": {
"adc_resolution": "24-bit",
"sampling_rate": "1000 SPS",
"accuracy": "±0.02%"
}
}
Why ZMA-4 for this application:
- ✅ 4 independent channels (perfect for 4-loadcell tanks)
- ✅ 24-bit ADC per channel (high precision)
- ✅ Modbus TCP/RTU support (easy gateway integration)
- ✅ High sampling rate (up to 1000 SPS)
Note: GDT Digital Transmitter supports maximum 2 channels + 3 DIO, making it suitable for simpler applications with 1-2 loadcells.
2. HMI Panel
Central HMI (1x 10.1" panel):
- All tank weights overview
- Real-time weight display for each tank
- Historical trend charts (last 24 hours)
- Alarm history
- Tare/Zero controls
- System diagnostics
Technical Specs:
- Processor: ARM Cortex-A53 (4-core, 1.2 GHz)
- RAM: 1 GB DDR3
- Storage: 8 GB eMMC
- Display: 10.1" Capacitive touchscreen (1280x800)
- OS: Embedded Linux (Yocto)
- Software: Qt 5.15 + QML
- Network: Ethernet (Modbus TCP client)
3. IoT Gateway (Raspberry Pi 4)
Role:
- Collect data from 3 ZMA-4 devices (Modbus TCP)
- Convert protocol (Modbus → MQTT)
- Send to AWS IoT Core
- Local data backup (SQLite)
Specs:
- Raspberry Pi 4 Model B (4GB RAM)
- 32GB SD card (Industrial grade)
- Ethernet connection (no WiFi)
- Custom Python gateway software
Software Architecture
Local HMI Application (Qt/QML)
Tank Monitor Screen:
// TankView.qml
Rectangle {
width: 800
height: 480
color: "#2C3E50"
property int tankId: 1
property real weight: 0.0
property real capacity: 5000.0
property real fillPercent: (weight / capacity) * 100
ColumnLayout {
anchors.fill: parent
anchors.margins: 20
// Tank visualization
Rectangle {
Layout.fillWidth: true
Layout.preferredHeight: 300
color: "transparent"
border.color: "#ECF0F1"
border.width: 3
// Fill level
Rectangle {
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.right: parent.right
height: parent.height * (fillPercent / 100)
color: fillPercent > 90 ? "#E74C3C" : "#3498DB"
Behavior on height {
NumberAnimation { duration: 300 }
}
}
// Weight text
Text {
anchors.centerIn: parent
text: weight.toFixed(1) + " kg"
font.pixelSize: 64
font.bold: true
color: "white"
}
}
// Percentage bar
ProgressBar {
Layout.fillWidth: true
value: fillPercent / 100
background: Rectangle {
color: "#34495E"
radius: 5
}
contentItem: Item {
Rectangle {
width: parent.width * parent.parent.value
height: parent.height
color: parent.parent.value > 0.9 ? "#E74C3C" : "#2ECC71"
radius: 5
}
}
}
Text {
text: fillPercent.toFixed(1) + "% Full"
font.pixelSize: 24
color: "#ECF0F1"
Layout.alignment: Qt.AlignHCenter
}
}
}
Gateway Application (Python)
Main Gateway Logic:
#!/usr/bin/env python3
import json
import time
from datetime import datetime
from pymodbus.client.sync import ModbusTcpClient
from awsiot import mqtt_connection_builder
import sqlite3
class MilkTankGateway:
def __init__(self):
self.tanks = [
{"id": 1, "name": "Tank-1", "zma_ip": "192.168.1.101"},
{"id": 2, "name": "Tank-2", "zma_ip": "192.168.1.102"},
{"id": 3, "name": "Tank-3", "zma_ip": "192.168.1.103"},
]
# Modbus clients
self.modbus_clients = {}
for tank in self.tanks:
client = ModbusTcpClient(tank['zma_ip'], port=502)
client.connect()
self.modbus_clients[tank['id']] = client
# AWS MQTT connection
self.mqtt_connection = self._setup_aws_mqtt()
# Local database backup
self.db = sqlite3.connect('/data/milk_tanks.db')
self._init_database()
def _setup_aws_mqtt(self):
"""Setup AWS IoT Core MQTT connection"""
return mqtt_connection_builder.mtls_from_path(
endpoint="xxxxx.iot.eu-west-1.amazonaws.com",
port=8883,
cert_filepath="/certs/gateway-cert.pem",
pri_key_filepath="/certs/gateway-private.key",
ca_filepath="/certs/AmazonRootCA1.pem",
client_id="milk-facility-gateway",
clean_session=False,
keep_alive_secs=30
)
def read_tank_weight(self, tank_id):
"""Read weight from ZMA-4 via Modbus"""
client = self.modbus_clients[tank_id]
# Read holding registers 200-203 (Total weight + Tare)
result = client.read_holding_registers(200, 4, unit=1)
if not result.isError():
gross = self._registers_to_float(result.registers[0:2])
net = self._registers_to_float(result.registers[2:4])
tare = self._registers_to_float(result.registers[4:6])
return {
"tank_id": tank_id,
"timestamp": datetime.utcnow().isoformat() + "Z",
"gross_weight": round(gross, 2),
"net_weight": round(net, 2),
"tare_weight": round(tare, 2),
"fill_percent": round((net / 5000.0) * 100, 1)
}
return None
def _registers_to_float(self, registers):
"""Convert 2 Modbus registers to Float32"""
import struct
raw = (registers[0] << 16) | registers[1]
return struct.unpack('f', struct.pack('I', raw))[0]
def publish_to_aws(self, data):
"""Publish data to AWS IoT Core"""
topic = f"amazeng/milk-facility/tank/{data['tank_id']}/data"
payload = json.dumps(data)
self.mqtt_connection.publish(
topic=topic,
payload=payload,
qos=1
)
print(f"→ AWS: {topic} | {data['net_weight']} kg")
def save_local_backup(self, data):
"""Save data to local SQLite database"""
cursor = self.db.cursor()
cursor.execute("""
INSERT INTO tank_data (tank_id, timestamp, net_weight, fill_percent)
VALUES (?, ?, ?, ?)
""", (data['tank_id'], data['timestamp'],
data['net_weight'], data['fill_percent']))
self.db.commit()
def run(self):
"""Main loop: Read → Publish → Store"""
print("🚀 Milk Tank Gateway started")
try:
while True:
for tank in self.tanks:
# Read from GDT
data = self.read_tank_weight(tank['id'])
if data:
# Send to AWS
self.publish_to_aws(data)
# Local backup
self.save_local_backup(data)
time.sleep(1) # 1 second polling interval
except KeyboardInterrupt:
print("\n✋ Gateway stopped")
self.shutdown()
if __name__ == "__main__":
gateway = MilkTankGateway()
gateway.run()
AWS IoT Rules and Alarms
High Capacity Alarm (>90%)
IoT Rule:
SELECT
tank_id,
fill_percent,
timestamp
FROM
'amazeng/milk-facility/tank/+/data'
WHERE
fill_percent > 90
Action: Trigger SNS (SMS + Email)
Lambda Function:
import boto3
sns = boto3.client('sns')
def lambda_handler(event, context):
tank_id = event['tank_id']
fill_percent = event['fill_percent']
message = f"""
⚠️ TANK CAPACITY WARNING
Tank: {tank_id}
Fill Level: {fill_percent}%
Time: {event['timestamp']}
Action Required: Prepare for milk collection
"""
sns.publish(
TopicArn='arn:aws:sns:eu-west-1:xxx:milk-tank-alarms',
Subject='Tank Capacity Alert',
Message=message
)
return {'status': 'alarm_sent'}
Conclusion
This fully integrated solution demonstrates the power of Amazeng products working together:
✅ Real-time Monitoring: All tanks displayed with 1-second update rate
✅ High Precision: ZMA-4's 24-bit ADC per channel (±0.02%)
✅ Scalable: 4 channels per device, easy to add more tanks
✅ Cloud Integration: Historical data and analytics on AWS
✅ Alarm System: SMS/Email alerts for critical events
✅ Cost-Effective: 73% cheaper than traditional SCADA
Product Selection Guide:
- 4 loadcells per tank: Use ZMA-4 (as shown in this article)
- 1-2 loadcells + DIO needed: Use GDT Digital Transmitter (2 ch + 3 DIO)
Related Products & Solutions
- ZMA Data Acquisition - For 4-channel loadcell applications
- GDT Digital Transmitter - For 1-2 channel + DIO applications
- HMI Industrial Display Solutions
- Cloud & IoT Data Collection
- Industrial Data Collection with AWS IoT Core