Fully Integrated Solution: ZMA + HMI + AWS for Weighing Systems

Amazeng Technical Team
15 min read
ZMAHMIAWS IoTIndustrial IoTFull StackCase StudyMilk Tank

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

  1. Real-time Weight Monitoring: Weight of each tank updated every second
  2. Local Display: Touchscreen HMI panel per tank
  3. Central Control: Large central HMI screen (10.1")
  4. Cloud Data: Historical data on AWS for quality reports
  5. Alarms: SMS and email alerts when tank capacity exceeds 90%
  6. 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)