[{"data":1,"prerenderedAt":1423},["ShallowReactive",2],{"blog-en-/en/blog/industrial-data-collection-with-aws-iot-core":3},{"id":4,"title":5,"body":6,"description":1410,"extension":1411,"meta":1412,"navigation":366,"path":1419,"seo":1420,"stem":1421,"__hash__":1422},"en_blog/en/blog/industrial-data-collection-with-aws-iot-core.md","Industrial Data Collection with AWS IoT Core: From ZMA Devices to Cloud",{"type":7,"value":8,"toc":1387},"minimark",[9,14,39,59,63,74,78,104,109,140,151,155,159,162,253,257,264,327,337,341,345,350,1074,1078,1082,1089,1201,1205,1209,1311,1314,1318,1333,1346,1358,1362,1383],[10,11,13],"h2",{"id":12},"introduction","Introduction",[15,16,17,18,22,23,26,27,30,31,34,35,38],"p",{},"Moving industrial data to the cloud is the cornerstone of ",[19,20,21],"strong",{},"Industry 4.0"," and ",[19,24,25],{},"predictive maintenance"," processes. However, data collected from sensors in the field typically communicate using traditional protocols like ",[19,28,29],{},"Modbus RTU"," or ",[19,32,33],{},"Modbus TCP",". An ",[19,36,37],{},"IoT Gateway"," is required to transfer this data to cloud platforms like AWS IoT Core.",[15,40,41,42,22,47,51,52,55,56,58],{},"In this article, we examine in detail how data collected via Modbus from our ",[43,44,46],"a",{"href":45},"/en/products/zma-data-acquisition","ZMA Data Acquisition",[43,48,50],{"href":49},"/en/products/gdt-digital-transmitter","GDT Digital Transmitter"," devices is transferred to ",[19,53,54],{},"AWS IoT Core"," through an ",[19,57,37],{},".",[10,60,62],{"id":61},"architecture-overview","Architecture Overview",[64,65,70],"pre",{"className":66,"code":68,"language":69},[67],"language-text","┌─────────────────────────────────────────────────────────────┐\n│                    Factory / Field                          │\n│                                                             │\n│  ┌─────────┐    Modbus RTU/TCP    ┌──────────────┐        │\n│  │ ZMA-4   │◄────────────────────►│              │        │\n│  │ Device  │                      │  IoT Gateway │        │\n│  └─────────┘                      │  (Linux Box) │        │\n│                                   │              │        │\n│  ┌─────────┐    Modbus RTU/TCP    │  - Modbus    │        │\n│  │ GDT     │◄────────────────────►│    Client    │        │\n│  │ Trans.  │                      │  - MQTT      │        │\n│  └─────────┘                      │    Client    │        │\n│                                   │  - TLS/SSL   │        │\n│                                   └───────┬──────┘        │\n└───────────────────────────────────────────┼───────────────┘\n                                            │\n                                            │ MQTT over TLS\n                                            │ Port 8883\n                                            ▼\n                                   ┌────────────────┐\n                                   │   AWS Cloud    │\n                                   │                │\n                                   │  ┌──────────┐  │\n                                   │  │ IoT Core │  │\n                                   │  └────┬─────┘  │\n                                   │       │        │\n                                   │  ┌────▼─────┐  │\n                                   │  │Timestream│  │\n                                   │  └──────────┘  │\n                                   └────────────────┘\n","text",[71,72,68],"code",{"__ignoreMap":73},"",[10,75,77],{"id":76},"important-note-iot-gateway-requirement","Important Note: IoT Gateway Requirement",[15,79,80,22,83,86,87,89,90,92,93,96,97,99,100,103],{},[19,81,82],{},"ZMA",[19,84,85],{},"GDT"," devices communicate via ",[19,88,29],{}," (RS485) and ",[19,91,33],{}," (Ethernet) protocols. AWS IoT Core uses the ",[19,94,95],{},"MQTT"," protocol. An ",[19,98,37],{}," device is needed to perform ",[19,101,102],{},"protocol conversion"," between these two worlds.",[105,106,108],"h3",{"id":107},"gateway-options","Gateway Options",[110,111,112,119,125,134],"ol",{},[113,114,115,118],"li",{},[19,116,117],{},"Raspberry Pi / Jetson Nano",": Linux-based, custom script with Python/Node.js",[113,120,121,124],{},[19,122,123],{},"Industrial IoT Gateways",": Ready solutions like Moxa, Advantech, Siemens",[113,126,127,133],{},[19,128,129],{},[43,130,132],{"href":131},"/en/solutions/hmi-industrial-display-solutions","Our HMI Panel",": Can function as both HMI and gateway on Embedded Linux",[113,135,136,139],{},[19,137,138],{},"PLC + OPC UA",": Cloud connection via PLC's OPC UA server",[15,141,142,143,146,147,150],{},"In this article, we'll examine a custom gateway solution with ",[19,144,145],{},"Raspberry Pi 4"," + ",[19,148,149],{},"Python",", but the same logic applies to all platforms.",[10,152,154],{"id":153},"aws-iot-core-setup","AWS IoT Core Setup",[105,156,158],{"id":157},"_1-create-thing-device","1. Create Thing (Device)",[15,160,161],{},"AWS Console → IoT Core → Manage → Things → Create",[64,163,167],{"className":164,"code":165,"language":166,"meta":73,"style":73},"language-json shiki shiki-themes github-light github-dark","{\n  \"thingName\": \"amazeng-factory-gateway-01\",\n  \"thingTypeName\": \"ModbusGateway\",\n  \"attributes\": {\n    \"location\": \"Istanbul-Factory-A\",\n    \"firmware\": \"1.0.0\"\n  }\n}\n","json",[71,168,169,178,195,208,217,230,241,247],{"__ignoreMap":73},[170,171,174],"span",{"class":172,"line":173},"line",1,[170,175,177],{"class":176},"sVt8B","{\n",[170,179,181,185,188,192],{"class":172,"line":180},2,[170,182,184],{"class":183},"sj4cs","  \"thingName\"",[170,186,187],{"class":176},": ",[170,189,191],{"class":190},"sZZnC","\"amazeng-factory-gateway-01\"",[170,193,194],{"class":176},",\n",[170,196,198,201,203,206],{"class":172,"line":197},3,[170,199,200],{"class":183},"  \"thingTypeName\"",[170,202,187],{"class":176},[170,204,205],{"class":190},"\"ModbusGateway\"",[170,207,194],{"class":176},[170,209,211,214],{"class":172,"line":210},4,[170,212,213],{"class":183},"  \"attributes\"",[170,215,216],{"class":176},": {\n",[170,218,220,223,225,228],{"class":172,"line":219},5,[170,221,222],{"class":183},"    \"location\"",[170,224,187],{"class":176},[170,226,227],{"class":190},"\"Istanbul-Factory-A\"",[170,229,194],{"class":176},[170,231,233,236,238],{"class":172,"line":232},6,[170,234,235],{"class":183},"    \"firmware\"",[170,237,187],{"class":176},[170,239,240],{"class":190},"\"1.0.0\"\n",[170,242,244],{"class":172,"line":243},7,[170,245,246],{"class":176},"  }\n",[170,248,250],{"class":172,"line":249},8,[170,251,252],{"class":176},"}\n",[105,254,256],{"id":255},"_2-create-and-download-certificate","2. Create and Download Certificate",[15,258,259,260,263],{},"Each IoT device connects to AWS with ",[19,261,262],{},"X.509 certificate",":",[64,265,269],{"className":266,"code":267,"language":268,"meta":73,"style":73},"language-bash shiki shiki-themes github-light github-dark","# Create certificate with AWS CLI\naws iot create-keys-and-certificate \\\n  --set-as-active \\\n  --certificate-pem-outfile gateway-cert.pem \\\n  --public-key-outfile gateway-public.key \\\n  --private-key-outfile gateway-private.key\n","bash",[71,270,271,277,292,299,309,319],{"__ignoreMap":73},[170,272,273],{"class":172,"line":173},[170,274,276],{"class":275},"sJ8bj","# Create certificate with AWS CLI\n",[170,278,279,283,286,289],{"class":172,"line":180},[170,280,282],{"class":281},"sScJk","aws",[170,284,285],{"class":190}," iot",[170,287,288],{"class":190}," create-keys-and-certificate",[170,290,291],{"class":183}," \\\n",[170,293,294,297],{"class":172,"line":197},[170,295,296],{"class":183},"  --set-as-active",[170,298,291],{"class":183},[170,300,301,304,307],{"class":172,"line":210},[170,302,303],{"class":183},"  --certificate-pem-outfile",[170,305,306],{"class":190}," gateway-cert.pem",[170,308,291],{"class":183},[170,310,311,314,317],{"class":172,"line":219},[170,312,313],{"class":183},"  --public-key-outfile",[170,315,316],{"class":190}," gateway-public.key",[170,318,291],{"class":183},[170,320,321,324],{"class":172,"line":232},[170,322,323],{"class":183},"  --private-key-outfile",[170,325,326],{"class":190}," gateway-private.key\n",[15,328,329,332,333,336],{},[19,330,331],{},"Important",": Store ",[71,334,335],{},"gateway-private.key"," file securely, cannot be downloaded again!",[10,338,340],{"id":339},"iot-gateway-software-python","IoT Gateway Software (Python)",[105,342,344],{"id":343},"gateway-application","Gateway Application",[15,346,347],{},[19,348,349],{},"gateway.py",[64,351,355],{"className":352,"code":353,"language":354,"meta":73,"style":73},"language-python shiki shiki-themes github-light github-dark","#!/usr/bin/env python3\n\nimport json\nimport time\nimport struct\nfrom pymodbus.client.sync import ModbusTcpClient\nfrom awscrt import io, mqtt\nfrom awsiot import mqtt_connection_builder\n\nclass ModbusToMQTTGateway:\n    def __init__(self, config_file):\n        with open(config_file, 'r') as f:\n            self.config = json.load(f)\n\n        self.modbus_clients = {}\n        self.mqtt_connection = None\n\n        # Create Modbus clients\n        for device in self.config['modbus_devices']:\n            if device['type'] == 'tcp':\n                client = ModbusTcpClient(device['host'], port=device['port'])\n\n            client.connect()\n            self.modbus_clients[device['name']] = {\n                'client': client,\n                'config': device\n            }\n\n        # AWS IoT MQTT connection\n        self._setup_mqtt()\n\n    def _setup_mqtt(self):\n        \"\"\"Setup AWS IoT Core MQTT connection\"\"\"\n        aws_config = self.config['aws_iot']\n        cert_path = aws_config['cert_path']\n\n        # Secure connection with TLS\n        self.mqtt_connection = mqtt_connection_builder.mtls_from_path(\n            endpoint=aws_config['endpoint'],\n            port=aws_config['port'],\n            cert_filepath=f\"{cert_path}gateway-cert.pem\",\n            pri_key_filepath=f\"{cert_path}gateway-private.key\",\n            ca_filepath=f\"{cert_path}AmazonRootCA1.pem\",\n            client_id=aws_config['thing_name'],\n            clean_session=False,\n            keep_alive_secs=30\n        )\n\n        # Connect\n        connect_future = self.mqtt_connection.connect()\n        connect_future.result()\n        print(f\"✓ Connected to AWS IoT Core: {aws_config['endpoint']}\")\n\n    def read_modbus_device(self, device_name):\n        \"\"\"Read all registers from a Modbus device\"\"\"\n        device = self.modbus_clients[device_name]\n        client = device['client']\n        config = device['config']\n\n        data = {\n            'device_name': device_name,\n            'timestamp': int(time.time() * 1000),\n            'values': {}\n        }\n\n        for register in config['registers']:\n            try:\n                # Read holding register\n                result = client.read_holding_registers(\n                    address=register['address'],\n                    count=register['count'],\n                    unit=config['unit_id']\n                )\n\n                if not result.isError():\n                    # Float conversion (IEEE 754)\n                    if register['type'] == 'float' and register['count'] == 2:\n                        raw = (result.registers[0] \u003C\u003C 16) | result.registers[1]\n                        value = struct.unpack('f', struct.pack('I', raw))[0]\n\n                    data['values'][register['name']] = round(value, 3)\n\n            except Exception as e:\n                print(f\"✗ Error: {device_name} - {e}\")\n                data['values'][register['name']] = None\n\n        return data\n\n    def publish_to_aws(self, device_name, data):\n        \"\"\"Publish data to AWS IoT Core\"\"\"\n        topic = f\"{self.config['aws_iot']['topic_prefix']}/{device_name}/data\"\n        payload = json.dumps(data)\n\n        self.mqtt_connection.publish(\n            topic=topic,\n            payload=payload,\n            qos=mqtt.QoS.AT_LEAST_ONCE\n        )\n\n        print(f\"→ Published to {topic}: {payload}\")\n\n    def run(self):\n        \"\"\"Main loop: Read from Modbus, send to MQTT\"\"\"\n        interval = self.config['polling_interval']\n\n        print(f\"Gateway started. Polling interval: {interval}s\")\n\n        try:\n            while True:\n                for device_name in self.modbus_clients.keys():\n                    # Read from Modbus\n                    data = self.read_modbus_device(device_name)\n\n                    # Send to AWS\n                    self.publish_to_aws(device_name, data)\n\n                time.sleep(interval)\n\n        except KeyboardInterrupt:\n            print(\"\\nStopping gateway...\")\n            self.shutdown()\n\nif __name__ == \"__main__\":\n    gateway = ModbusToMQTTGateway('/etc/iot-gateway/config.json')\n    gateway.run()\n","python",[71,356,357,362,368,373,378,383,388,393,398,403,409,415,421,427,432,438,444,449,455,461,467,473,478,484,490,496,502,508,513,519,525,530,536,542,548,554,559,565,571,577,583,589,595,601,607,613,619,625,630,636,642,648,654,659,665,671,677,683,689,694,700,706,712,718,724,729,735,741,747,753,759,765,771,777,782,788,794,800,806,812,817,823,828,834,840,846,851,857,862,868,874,880,886,891,897,903,909,915,920,925,931,936,942,948,954,959,965,970,976,982,988,994,1000,1005,1011,1017,1022,1028,1033,1039,1045,1051,1056,1062,1068],{"__ignoreMap":73},[170,358,359],{"class":172,"line":173},[170,360,361],{},"#!/usr/bin/env python3\n",[170,363,364],{"class":172,"line":180},[170,365,367],{"emptyLinePlaceholder":366},true,"\n",[170,369,370],{"class":172,"line":197},[170,371,372],{},"import json\n",[170,374,375],{"class":172,"line":210},[170,376,377],{},"import time\n",[170,379,380],{"class":172,"line":219},[170,381,382],{},"import struct\n",[170,384,385],{"class":172,"line":232},[170,386,387],{},"from pymodbus.client.sync import ModbusTcpClient\n",[170,389,390],{"class":172,"line":243},[170,391,392],{},"from awscrt import io, mqtt\n",[170,394,395],{"class":172,"line":249},[170,396,397],{},"from awsiot import mqtt_connection_builder\n",[170,399,401],{"class":172,"line":400},9,[170,402,367],{"emptyLinePlaceholder":366},[170,404,406],{"class":172,"line":405},10,[170,407,408],{},"class ModbusToMQTTGateway:\n",[170,410,412],{"class":172,"line":411},11,[170,413,414],{},"    def __init__(self, config_file):\n",[170,416,418],{"class":172,"line":417},12,[170,419,420],{},"        with open(config_file, 'r') as f:\n",[170,422,424],{"class":172,"line":423},13,[170,425,426],{},"            self.config = json.load(f)\n",[170,428,430],{"class":172,"line":429},14,[170,431,367],{"emptyLinePlaceholder":366},[170,433,435],{"class":172,"line":434},15,[170,436,437],{},"        self.modbus_clients = {}\n",[170,439,441],{"class":172,"line":440},16,[170,442,443],{},"        self.mqtt_connection = None\n",[170,445,447],{"class":172,"line":446},17,[170,448,367],{"emptyLinePlaceholder":366},[170,450,452],{"class":172,"line":451},18,[170,453,454],{},"        # Create Modbus clients\n",[170,456,458],{"class":172,"line":457},19,[170,459,460],{},"        for device in self.config['modbus_devices']:\n",[170,462,464],{"class":172,"line":463},20,[170,465,466],{},"            if device['type'] == 'tcp':\n",[170,468,470],{"class":172,"line":469},21,[170,471,472],{},"                client = ModbusTcpClient(device['host'], port=device['port'])\n",[170,474,476],{"class":172,"line":475},22,[170,477,367],{"emptyLinePlaceholder":366},[170,479,481],{"class":172,"line":480},23,[170,482,483],{},"            client.connect()\n",[170,485,487],{"class":172,"line":486},24,[170,488,489],{},"            self.modbus_clients[device['name']] = {\n",[170,491,493],{"class":172,"line":492},25,[170,494,495],{},"                'client': client,\n",[170,497,499],{"class":172,"line":498},26,[170,500,501],{},"                'config': device\n",[170,503,505],{"class":172,"line":504},27,[170,506,507],{},"            }\n",[170,509,511],{"class":172,"line":510},28,[170,512,367],{"emptyLinePlaceholder":366},[170,514,516],{"class":172,"line":515},29,[170,517,518],{},"        # AWS IoT MQTT connection\n",[170,520,522],{"class":172,"line":521},30,[170,523,524],{},"        self._setup_mqtt()\n",[170,526,528],{"class":172,"line":527},31,[170,529,367],{"emptyLinePlaceholder":366},[170,531,533],{"class":172,"line":532},32,[170,534,535],{},"    def _setup_mqtt(self):\n",[170,537,539],{"class":172,"line":538},33,[170,540,541],{},"        \"\"\"Setup AWS IoT Core MQTT connection\"\"\"\n",[170,543,545],{"class":172,"line":544},34,[170,546,547],{},"        aws_config = self.config['aws_iot']\n",[170,549,551],{"class":172,"line":550},35,[170,552,553],{},"        cert_path = aws_config['cert_path']\n",[170,555,557],{"class":172,"line":556},36,[170,558,367],{"emptyLinePlaceholder":366},[170,560,562],{"class":172,"line":561},37,[170,563,564],{},"        # Secure connection with TLS\n",[170,566,568],{"class":172,"line":567},38,[170,569,570],{},"        self.mqtt_connection = mqtt_connection_builder.mtls_from_path(\n",[170,572,574],{"class":172,"line":573},39,[170,575,576],{},"            endpoint=aws_config['endpoint'],\n",[170,578,580],{"class":172,"line":579},40,[170,581,582],{},"            port=aws_config['port'],\n",[170,584,586],{"class":172,"line":585},41,[170,587,588],{},"            cert_filepath=f\"{cert_path}gateway-cert.pem\",\n",[170,590,592],{"class":172,"line":591},42,[170,593,594],{},"            pri_key_filepath=f\"{cert_path}gateway-private.key\",\n",[170,596,598],{"class":172,"line":597},43,[170,599,600],{},"            ca_filepath=f\"{cert_path}AmazonRootCA1.pem\",\n",[170,602,604],{"class":172,"line":603},44,[170,605,606],{},"            client_id=aws_config['thing_name'],\n",[170,608,610],{"class":172,"line":609},45,[170,611,612],{},"            clean_session=False,\n",[170,614,616],{"class":172,"line":615},46,[170,617,618],{},"            keep_alive_secs=30\n",[170,620,622],{"class":172,"line":621},47,[170,623,624],{},"        )\n",[170,626,628],{"class":172,"line":627},48,[170,629,367],{"emptyLinePlaceholder":366},[170,631,633],{"class":172,"line":632},49,[170,634,635],{},"        # Connect\n",[170,637,639],{"class":172,"line":638},50,[170,640,641],{},"        connect_future = self.mqtt_connection.connect()\n",[170,643,645],{"class":172,"line":644},51,[170,646,647],{},"        connect_future.result()\n",[170,649,651],{"class":172,"line":650},52,[170,652,653],{},"        print(f\"✓ Connected to AWS IoT Core: {aws_config['endpoint']}\")\n",[170,655,657],{"class":172,"line":656},53,[170,658,367],{"emptyLinePlaceholder":366},[170,660,662],{"class":172,"line":661},54,[170,663,664],{},"    def read_modbus_device(self, device_name):\n",[170,666,668],{"class":172,"line":667},55,[170,669,670],{},"        \"\"\"Read all registers from a Modbus device\"\"\"\n",[170,672,674],{"class":172,"line":673},56,[170,675,676],{},"        device = self.modbus_clients[device_name]\n",[170,678,680],{"class":172,"line":679},57,[170,681,682],{},"        client = device['client']\n",[170,684,686],{"class":172,"line":685},58,[170,687,688],{},"        config = device['config']\n",[170,690,692],{"class":172,"line":691},59,[170,693,367],{"emptyLinePlaceholder":366},[170,695,697],{"class":172,"line":696},60,[170,698,699],{},"        data = {\n",[170,701,703],{"class":172,"line":702},61,[170,704,705],{},"            'device_name': device_name,\n",[170,707,709],{"class":172,"line":708},62,[170,710,711],{},"            'timestamp': int(time.time() * 1000),\n",[170,713,715],{"class":172,"line":714},63,[170,716,717],{},"            'values': {}\n",[170,719,721],{"class":172,"line":720},64,[170,722,723],{},"        }\n",[170,725,727],{"class":172,"line":726},65,[170,728,367],{"emptyLinePlaceholder":366},[170,730,732],{"class":172,"line":731},66,[170,733,734],{},"        for register in config['registers']:\n",[170,736,738],{"class":172,"line":737},67,[170,739,740],{},"            try:\n",[170,742,744],{"class":172,"line":743},68,[170,745,746],{},"                # Read holding register\n",[170,748,750],{"class":172,"line":749},69,[170,751,752],{},"                result = client.read_holding_registers(\n",[170,754,756],{"class":172,"line":755},70,[170,757,758],{},"                    address=register['address'],\n",[170,760,762],{"class":172,"line":761},71,[170,763,764],{},"                    count=register['count'],\n",[170,766,768],{"class":172,"line":767},72,[170,769,770],{},"                    unit=config['unit_id']\n",[170,772,774],{"class":172,"line":773},73,[170,775,776],{},"                )\n",[170,778,780],{"class":172,"line":779},74,[170,781,367],{"emptyLinePlaceholder":366},[170,783,785],{"class":172,"line":784},75,[170,786,787],{},"                if not result.isError():\n",[170,789,791],{"class":172,"line":790},76,[170,792,793],{},"                    # Float conversion (IEEE 754)\n",[170,795,797],{"class":172,"line":796},77,[170,798,799],{},"                    if register['type'] == 'float' and register['count'] == 2:\n",[170,801,803],{"class":172,"line":802},78,[170,804,805],{},"                        raw = (result.registers[0] \u003C\u003C 16) | result.registers[1]\n",[170,807,809],{"class":172,"line":808},79,[170,810,811],{},"                        value = struct.unpack('f', struct.pack('I', raw))[0]\n",[170,813,815],{"class":172,"line":814},80,[170,816,367],{"emptyLinePlaceholder":366},[170,818,820],{"class":172,"line":819},81,[170,821,822],{},"                    data['values'][register['name']] = round(value, 3)\n",[170,824,826],{"class":172,"line":825},82,[170,827,367],{"emptyLinePlaceholder":366},[170,829,831],{"class":172,"line":830},83,[170,832,833],{},"            except Exception as e:\n",[170,835,837],{"class":172,"line":836},84,[170,838,839],{},"                print(f\"✗ Error: {device_name} - {e}\")\n",[170,841,843],{"class":172,"line":842},85,[170,844,845],{},"                data['values'][register['name']] = None\n",[170,847,849],{"class":172,"line":848},86,[170,850,367],{"emptyLinePlaceholder":366},[170,852,854],{"class":172,"line":853},87,[170,855,856],{},"        return data\n",[170,858,860],{"class":172,"line":859},88,[170,861,367],{"emptyLinePlaceholder":366},[170,863,865],{"class":172,"line":864},89,[170,866,867],{},"    def publish_to_aws(self, device_name, data):\n",[170,869,871],{"class":172,"line":870},90,[170,872,873],{},"        \"\"\"Publish data to AWS IoT Core\"\"\"\n",[170,875,877],{"class":172,"line":876},91,[170,878,879],{},"        topic = f\"{self.config['aws_iot']['topic_prefix']}/{device_name}/data\"\n",[170,881,883],{"class":172,"line":882},92,[170,884,885],{},"        payload = json.dumps(data)\n",[170,887,889],{"class":172,"line":888},93,[170,890,367],{"emptyLinePlaceholder":366},[170,892,894],{"class":172,"line":893},94,[170,895,896],{},"        self.mqtt_connection.publish(\n",[170,898,900],{"class":172,"line":899},95,[170,901,902],{},"            topic=topic,\n",[170,904,906],{"class":172,"line":905},96,[170,907,908],{},"            payload=payload,\n",[170,910,912],{"class":172,"line":911},97,[170,913,914],{},"            qos=mqtt.QoS.AT_LEAST_ONCE\n",[170,916,918],{"class":172,"line":917},98,[170,919,624],{},[170,921,923],{"class":172,"line":922},99,[170,924,367],{"emptyLinePlaceholder":366},[170,926,928],{"class":172,"line":927},100,[170,929,930],{},"        print(f\"→ Published to {topic}: {payload}\")\n",[170,932,934],{"class":172,"line":933},101,[170,935,367],{"emptyLinePlaceholder":366},[170,937,939],{"class":172,"line":938},102,[170,940,941],{},"    def run(self):\n",[170,943,945],{"class":172,"line":944},103,[170,946,947],{},"        \"\"\"Main loop: Read from Modbus, send to MQTT\"\"\"\n",[170,949,951],{"class":172,"line":950},104,[170,952,953],{},"        interval = self.config['polling_interval']\n",[170,955,957],{"class":172,"line":956},105,[170,958,367],{"emptyLinePlaceholder":366},[170,960,962],{"class":172,"line":961},106,[170,963,964],{},"        print(f\"Gateway started. Polling interval: {interval}s\")\n",[170,966,968],{"class":172,"line":967},107,[170,969,367],{"emptyLinePlaceholder":366},[170,971,973],{"class":172,"line":972},108,[170,974,975],{},"        try:\n",[170,977,979],{"class":172,"line":978},109,[170,980,981],{},"            while True:\n",[170,983,985],{"class":172,"line":984},110,[170,986,987],{},"                for device_name in self.modbus_clients.keys():\n",[170,989,991],{"class":172,"line":990},111,[170,992,993],{},"                    # Read from Modbus\n",[170,995,997],{"class":172,"line":996},112,[170,998,999],{},"                    data = self.read_modbus_device(device_name)\n",[170,1001,1003],{"class":172,"line":1002},113,[170,1004,367],{"emptyLinePlaceholder":366},[170,1006,1008],{"class":172,"line":1007},114,[170,1009,1010],{},"                    # Send to AWS\n",[170,1012,1014],{"class":172,"line":1013},115,[170,1015,1016],{},"                    self.publish_to_aws(device_name, data)\n",[170,1018,1020],{"class":172,"line":1019},116,[170,1021,367],{"emptyLinePlaceholder":366},[170,1023,1025],{"class":172,"line":1024},117,[170,1026,1027],{},"                time.sleep(interval)\n",[170,1029,1031],{"class":172,"line":1030},118,[170,1032,367],{"emptyLinePlaceholder":366},[170,1034,1036],{"class":172,"line":1035},119,[170,1037,1038],{},"        except KeyboardInterrupt:\n",[170,1040,1042],{"class":172,"line":1041},120,[170,1043,1044],{},"            print(\"\\nStopping gateway...\")\n",[170,1046,1048],{"class":172,"line":1047},121,[170,1049,1050],{},"            self.shutdown()\n",[170,1052,1054],{"class":172,"line":1053},122,[170,1055,367],{"emptyLinePlaceholder":366},[170,1057,1059],{"class":172,"line":1058},123,[170,1060,1061],{},"if __name__ == \"__main__\":\n",[170,1063,1065],{"class":172,"line":1064},124,[170,1066,1067],{},"    gateway = ModbusToMQTTGateway('/etc/iot-gateway/config.json')\n",[170,1069,1071],{"class":172,"line":1070},125,[170,1072,1073],{},"    gateway.run()\n",[10,1075,1077],{"id":1076},"real-time-alarm-scenario","Real-Time Alarm Scenario",[105,1079,1081],{"id":1080},"lambda-function-for-threshold-control","Lambda Function for Threshold Control",[15,1083,1084],{},[19,1085,1086],{},[71,1087,1088],{},"alarm_handler.py",[64,1090,1092],{"className":352,"code":1091,"language":354,"meta":73,"style":73},"import json\nimport boto3\n\nsns = boto3.client('sns')\n\ndef lambda_handler(event, context):\n    # Data from IoT Core\n    device = event['device_name']\n    weight = event['values'].get('net_weight', 0)\n\n    # Threshold check\n    if weight > 5000:  # Over 5 tons\n        message = f\"ALARM: {device} - Weight limit exceeded: {weight} kg\"\n\n        sns.publish(\n            TopicArn='arn:aws:sns:eu-west-1:123456789:factory-alarms',\n            Subject='Tank Capacity Alarm',\n            Message=message\n        )\n\n        return {'statusCode': 200, 'body': 'Alarm sent'}\n\n    return {'statusCode': 200, 'body': 'Normal'}\n",[71,1093,1094,1098,1103,1107,1112,1116,1121,1126,1131,1136,1140,1145,1150,1155,1159,1164,1169,1174,1179,1183,1187,1192,1196],{"__ignoreMap":73},[170,1095,1096],{"class":172,"line":173},[170,1097,372],{},[170,1099,1100],{"class":172,"line":180},[170,1101,1102],{},"import boto3\n",[170,1104,1105],{"class":172,"line":197},[170,1106,367],{"emptyLinePlaceholder":366},[170,1108,1109],{"class":172,"line":210},[170,1110,1111],{},"sns = boto3.client('sns')\n",[170,1113,1114],{"class":172,"line":219},[170,1115,367],{"emptyLinePlaceholder":366},[170,1117,1118],{"class":172,"line":232},[170,1119,1120],{},"def lambda_handler(event, context):\n",[170,1122,1123],{"class":172,"line":243},[170,1124,1125],{},"    # Data from IoT Core\n",[170,1127,1128],{"class":172,"line":249},[170,1129,1130],{},"    device = event['device_name']\n",[170,1132,1133],{"class":172,"line":400},[170,1134,1135],{},"    weight = event['values'].get('net_weight', 0)\n",[170,1137,1138],{"class":172,"line":405},[170,1139,367],{"emptyLinePlaceholder":366},[170,1141,1142],{"class":172,"line":411},[170,1143,1144],{},"    # Threshold check\n",[170,1146,1147],{"class":172,"line":417},[170,1148,1149],{},"    if weight > 5000:  # Over 5 tons\n",[170,1151,1152],{"class":172,"line":423},[170,1153,1154],{},"        message = f\"ALARM: {device} - Weight limit exceeded: {weight} kg\"\n",[170,1156,1157],{"class":172,"line":429},[170,1158,367],{"emptyLinePlaceholder":366},[170,1160,1161],{"class":172,"line":434},[170,1162,1163],{},"        sns.publish(\n",[170,1165,1166],{"class":172,"line":440},[170,1167,1168],{},"            TopicArn='arn:aws:sns:eu-west-1:123456789:factory-alarms',\n",[170,1170,1171],{"class":172,"line":446},[170,1172,1173],{},"            Subject='Tank Capacity Alarm',\n",[170,1175,1176],{"class":172,"line":451},[170,1177,1178],{},"            Message=message\n",[170,1180,1181],{"class":172,"line":457},[170,1182,624],{},[170,1184,1185],{"class":172,"line":463},[170,1186,367],{"emptyLinePlaceholder":366},[170,1188,1189],{"class":172,"line":469},[170,1190,1191],{},"        return {'statusCode': 200, 'body': 'Alarm sent'}\n",[170,1193,1194],{"class":172,"line":475},[170,1195,367],{"emptyLinePlaceholder":366},[170,1197,1198],{"class":172,"line":480},[170,1199,1200],{},"    return {'statusCode': 200, 'body': 'Normal'}\n",[10,1202,1204],{"id":1203},"cost-analysis","Cost Analysis",[105,1206,1208],{"id":1207},"aws-iot-core-pricing-eu-west-1","AWS IoT Core Pricing (eu-west-1)",[1210,1211,1212,1228],"table",{},[1213,1214,1215],"thead",{},[1216,1217,1218,1222,1225],"tr",{},[1219,1220,1221],"th",{},"Component",[1219,1223,1224],{},"Price",[1219,1226,1227],{},"Monthly Cost*",[1229,1230,1231,1245,1258,1271,1284,1297],"tbody",{},[1216,1232,1233,1239,1242],{},[1234,1235,1236],"td",{},[19,1237,1238],{},"Connectivity",[1234,1240,1241],{},"$0.08 / million connection-minutes",[1234,1243,1244],{},"$3.45 (2 devices, 24/7)",[1216,1246,1247,1252,1255],{},[1234,1248,1249],{},[19,1250,1251],{},"Messaging",[1234,1253,1254],{},"$1.00 / million messages",[1234,1256,1257],{},"$2.59 (1s polling, 2 devices)",[1216,1259,1260,1265,1268],{},[1234,1261,1262],{},[19,1263,1264],{},"Rule Engine",[1234,1266,1267],{},"$0.15 / million rules",[1234,1269,1270],{},"$0.38",[1216,1272,1273,1278,1281],{},[1234,1274,1275],{},[19,1276,1277],{},"Timestream Write",[1234,1279,1280],{},"$0.50 / million writes",[1234,1282,1283],{},"$1.30",[1216,1285,1286,1291,1294],{},[1234,1287,1288],{},[19,1289,1290],{},"Timestream Storage",[1234,1292,1293],{},"$0.03 / GB-hour",[1234,1295,1296],{},"$21.60 (30GB)",[1216,1298,1299,1304,1306],{},[1234,1300,1301],{},[19,1302,1303],{},"TOTAL",[1234,1305],{},[1234,1307,1308],{},[19,1309,1310],{},"~$29/month",[15,1312,1313],{},"*2 devices, 1 second polling, 30 days retention",[10,1315,1317],{"id":1316},"conclusion","Conclusion",[15,1319,1320,1321,22,1323,1325,1326,1328,1329,1332],{},"Our ",[43,1322,82],{"href":45},[43,1324,85],{"href":49}," products can easily connect to AWS IoT Core via ",[19,1327,37],{}," thanks to ",[19,1330,1331],{},"Modbus RTU/TCP"," support. This enables:",[15,1334,1335,1336,1339,1340,1342,1343,1345],{},"✅ Real-time data monitoring (Grafana/QuickSight)",[1337,1338],"br",{},"\n✅ Long-term data storage (Timestream)",[1337,1341],{},"\n✅ Predictive maintenance (AI/ML models)",[1337,1344],{},"\n✅ Remote alarms and notifications",[15,1347,1348,1349,1353,1354,58],{},"For more information about our ",[43,1350,1352],{"href":1351},"/en/solutions/cloud-iot-data-collection","Cloud & IoT Data Collection Solutions",", ",[43,1355,1357],{"href":1356},"/en/contact","contact us",[105,1359,1361],{"id":1360},"related-solutions","Related Solutions",[1363,1364,1365,1370,1374,1378],"ul",{},[113,1366,1367],{},[43,1368,1369],{"href":1351},"Cloud & IoT Data Collection",[113,1371,1372],{},[43,1373,46],{"href":45},[113,1375,1376],{},[43,1377,50],{"href":49},[113,1379,1380],{},[43,1381,1382],{"href":131},"HMI Industrial Display Solutions",[1384,1385,1386],"style",{},"html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}",{"title":73,"searchDepth":180,"depth":180,"links":1388},[1389,1390,1391,1394,1398,1401,1404,1407],{"id":12,"depth":180,"text":13},{"id":61,"depth":180,"text":62},{"id":76,"depth":180,"text":77,"children":1392},[1393],{"id":107,"depth":197,"text":108},{"id":153,"depth":180,"text":154,"children":1395},[1396,1397],{"id":157,"depth":197,"text":158},{"id":255,"depth":197,"text":256},{"id":339,"depth":180,"text":340,"children":1399},[1400],{"id":343,"depth":197,"text":344},{"id":1076,"depth":180,"text":1077,"children":1402},[1403],{"id":1080,"depth":197,"text":1081},{"id":1203,"depth":180,"text":1204,"children":1405},[1406],{"id":1207,"depth":197,"text":1208},{"id":1316,"depth":180,"text":1317,"children":1408},[1409],{"id":1360,"depth":197,"text":1361},"Transfer of data collected via Modbus from ZMA and GDT devices to AWS IoT Core cloud platform via IoT gateway using MQTT protocol.","md",{"date":1413,"author":1414,"readTime":417,"tags":1415},"2025-12-29","Amazeng Technical Team",[54,95,82,85,1416,1417,1418],"Modbus Gateway","Cloud IoT","Industrial IoT","/en/blog/industrial-data-collection-with-aws-iot-core",{"title":5,"description":1410},"en/blog/industrial-data-collection-with-aws-iot-core","SqqbdRvhQPk0TAF2BuutIGnJB2ehA_8Mioj6rykY5Po",1778229658718]