[{"data":1,"prerenderedAt":2003},["ShallowReactive",2],{"blog-en-/en/blog/fully-integrated-solution-zma-gdt-hmi-aws":3},{"id":4,"title":5,"body":6,"description":1986,"extension":1987,"meta":1988,"navigation":645,"path":1999,"seo":2000,"stem":2001,"__hash__":2002},"en_blog/en/blog/fully-integrated-solution-zma-gdt-hmi-aws.md","Fully Integrated Solution: ZMA + HMI + AWS for Weighing Systems",{"type":7,"value":8,"toc":1963},"minimark",[9,14,43,54,58,63,91,95,134,138,149,153,157,162,473,478,492,497,501,506,526,531,554,558,563,577,582,596,600,604,609,1023,1027,1032,1667,1671,1675,1680,1727,1733,1738,1861,1865,1868,1907,1912,1929,1933,1959],[10,11,13],"h2",{"id":12},"introduction","Introduction",[15,16,17,18,22,23,26,27,32,33,37,38,42],"p",{},"In modern industrial facilities, obtaining ",[19,20,21],"strong",{},"full control"," requires the integration of multiple hardware and software components. In this article, we examine in detail a ",[19,24,25],{},"fully integrated solution"," using our ",[28,29,31],"a",{"href":30},"/en/products/zma-data-acquisition","ZMA Data Acquisition",", ",[28,34,36],{"href":35},"/en/solutions/hmi-industrial-display-solutions","HMI Industrial Display Solutions"," and ",[28,39,41],{"href":40},"/en/solutions/cloud-iot-data-collection","Cloud & IoT Data Collection",".",[15,44,45,48,49,53],{},[19,46,47],{},"Note:"," Our ",[28,50,52],{"href":51},"/en/products/gdt-digital-transmitter","GDT Digital Transmitter"," supports maximum 2 channels + 3 DIO. For 4-loadcell applications, ZMA-4 is the preferred choice.",[10,55,57],{"id":56},"real-world-scenario-milk-processing-facility","Real-World Scenario: Milk Processing Facility",[59,60,62],"h3",{"id":61},"facility-profile","Facility Profile",[64,65,66,73,79,85],"ul",{},[67,68,69,72],"li",{},[19,70,71],{},"Location:"," Istanbul, Turkey",[67,74,75,78],{},[19,76,77],{},"Capacity:"," 5 milk collection tanks (5,000 kg each)",[67,80,81,84],{},[19,82,83],{},"Operation:"," 24/7 continuous operation",[67,86,87,90],{},[19,88,89],{},"Compliance:"," ISO 22000, HACCP requirements",[59,92,94],{"id":93},"requirements","Requirements",[96,97,98,104,110,116,122,128],"ol",{},[67,99,100,103],{},[19,101,102],{},"Real-time Weight Monitoring:"," Weight of each tank updated every second",[67,105,106,109],{},[19,107,108],{},"Local Display:"," Touchscreen HMI panel per tank",[67,111,112,115],{},[19,113,114],{},"Central Control:"," Large central HMI screen (10.1\")",[67,117,118,121],{},[19,119,120],{},"Cloud Data:"," Historical data on AWS for quality reports",[67,123,124,127],{},[19,125,126],{},"Alarms:"," SMS and email alerts when tank capacity exceeds 90%",[67,129,130,133],{},[19,131,132],{},"Traceability:"," Weight history per batch (which milk came in when, how long did it stay)",[10,135,137],{"id":136},"system-architecture","System Architecture",[139,140,145],"pre",{"className":141,"code":143,"language":144},[142],"language-text","┌─────────────────────────────────────────────────────────────┐\n│                   Milk Processing Facility                  │\n│                                                             │\n│  ┌────────────┐  ┌────────────┐  ┌────────────┐           │\n│  │  Tank 1    │  │  Tank 2    │  │  Tank 3    │           │\n│  │  5000 kg   │  │  5000 kg   │  │  5000 kg   │           │\n│  │            │  │            │  │            │           │\n│  │ 4x Loadcell│  │ 4x Loadcell│  │ 4x Loadcell│           │\n│  └──────┬─────┘  └──────┬─────┘  └──────┬─────┘           │\n│         │                │                │                │\n│    ┌────▼────┐      ┌────▼────┐      ┌────▼────┐          │\n│    │ ZMA-4-01 │      │ ZMA-4-02 │      │ ZMA-4-03 │          │\n│    │  .101    │      │  .102    │      │  .103    │          │\n│    └────┬────┘      └────┬────┘      └────┬────┘          │\n│         │                │                │                │\n│    ┌────▼────┐      ┌────▼────┐      ┌────▼────┐          │\n│    │7\" HMI   │      │7\" HMI   │      │7\" HMI   │          │\n│    │Touch    │      │Touch    │      │Touch    │          │\n│    └────┬────┘      └────┬────┘      └────┬────┘          │\n│         │                │                │                │\n│         └────────────────┼────────────────┘                │\n│                          │                                 │\n│                    ┌─────▼──────┐                          │\n│                    │  Ethernet  │                          │\n│                    │  Switch    │                          │\n│                    └─────┬──────┘                          │\n│                          │                                 │\n│          ┌───────────────┼───────────────┐                │\n│          │               │               │                │\n│     ┌────▼────┐    ┌─────▼─────┐   ┌────▼────┐           │\n│     │10.1\" HMI│    │  Gateway  │   │  ZMA-4  │           │\n│     │Central  │    │Raspberry  │   │(Analog) │           │\n│     │Monitor  │    │   Pi 4    │   └─────────┘           │\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                  │  │ (Database)   │  │\n                  │  └──────┬───────┘  │\n                  │         │          │\n                  │  ┌──────▼───────┐  │\n                  │  │  Grafana     │  │\n                  │  │  Dashboard   │  │\n                  │  └──────────────┘  │\n                  └────────────────────┘\n","text",[146,147,143],"code",{"__ignoreMap":148},"",[10,150,152],{"id":151},"hardware-configuration","Hardware Configuration",[59,154,156],{"id":155},"_1-zma-4-data-acquisition-modules-3-units","1. ZMA-4 Data Acquisition Modules (3 units)",[15,158,159],{},[19,160,161],{},"Configuration per Tank:",[139,163,167],{"className":164,"code":165,"language":166,"meta":148,"style":148},"language-json shiki shiki-themes github-light github-dark","{\n  \"device\": \"ZMA-4\",\n  \"channels\": 4,\n  \"loadcells\": [\n    { \"position\": \"Front-Left\", \"capacity\": \"2000kg\", \"sensitivity\": \"2mV/V\" },\n    { \"position\": \"Front-Right\", \"capacity\": \"2000kg\", \"sensitivity\": \"2mV/V\" },\n    { \"position\": \"Rear-Left\", \"capacity\": \"2000kg\", \"sensitivity\": \"2mV/V\" },\n    { \"position\": \"Rear-Right\", \"capacity\": \"2000kg\", \"sensitivity\": \"2mV/V\" }\n  ],\n  \"modbus\": {\n    \"type\": \"TCP\",\n    \"ip\": \"192.168.1.101\",\n    \"port\": 502,\n    \"unit_id\": 1\n  },\n  \"specs\": {\n    \"adc_resolution\": \"24-bit\",\n    \"sampling_rate\": \"1000 SPS\",\n    \"accuracy\": \"±0.02%\"\n  }\n}\n","json",[146,168,169,178,195,208,217,254,284,314,345,351,360,373,386,399,410,416,424,437,450,461,467],{"__ignoreMap":148},[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","  \"device\"",[170,186,187],{"class":176},": ",[170,189,191],{"class":190},"sZZnC","\"ZMA-4\"",[170,193,194],{"class":176},",\n",[170,196,198,201,203,206],{"class":172,"line":197},3,[170,199,200],{"class":183},"  \"channels\"",[170,202,187],{"class":176},[170,204,205],{"class":183},"4",[170,207,194],{"class":176},[170,209,211,214],{"class":172,"line":210},4,[170,212,213],{"class":183},"  \"loadcells\"",[170,215,216],{"class":176},": [\n",[170,218,220,223,226,228,231,233,236,238,241,243,246,248,251],{"class":172,"line":219},5,[170,221,222],{"class":176},"    { ",[170,224,225],{"class":183},"\"position\"",[170,227,187],{"class":176},[170,229,230],{"class":190},"\"Front-Left\"",[170,232,32],{"class":176},[170,234,235],{"class":183},"\"capacity\"",[170,237,187],{"class":176},[170,239,240],{"class":190},"\"2000kg\"",[170,242,32],{"class":176},[170,244,245],{"class":183},"\"sensitivity\"",[170,247,187],{"class":176},[170,249,250],{"class":190},"\"2mV/V\"",[170,252,253],{"class":176}," },\n",[170,255,257,259,261,263,266,268,270,272,274,276,278,280,282],{"class":172,"line":256},6,[170,258,222],{"class":176},[170,260,225],{"class":183},[170,262,187],{"class":176},[170,264,265],{"class":190},"\"Front-Right\"",[170,267,32],{"class":176},[170,269,235],{"class":183},[170,271,187],{"class":176},[170,273,240],{"class":190},[170,275,32],{"class":176},[170,277,245],{"class":183},[170,279,187],{"class":176},[170,281,250],{"class":190},[170,283,253],{"class":176},[170,285,287,289,291,293,296,298,300,302,304,306,308,310,312],{"class":172,"line":286},7,[170,288,222],{"class":176},[170,290,225],{"class":183},[170,292,187],{"class":176},[170,294,295],{"class":190},"\"Rear-Left\"",[170,297,32],{"class":176},[170,299,235],{"class":183},[170,301,187],{"class":176},[170,303,240],{"class":190},[170,305,32],{"class":176},[170,307,245],{"class":183},[170,309,187],{"class":176},[170,311,250],{"class":190},[170,313,253],{"class":176},[170,315,317,319,321,323,326,328,330,332,334,336,338,340,342],{"class":172,"line":316},8,[170,318,222],{"class":176},[170,320,225],{"class":183},[170,322,187],{"class":176},[170,324,325],{"class":190},"\"Rear-Right\"",[170,327,32],{"class":176},[170,329,235],{"class":183},[170,331,187],{"class":176},[170,333,240],{"class":190},[170,335,32],{"class":176},[170,337,245],{"class":183},[170,339,187],{"class":176},[170,341,250],{"class":190},[170,343,344],{"class":176}," }\n",[170,346,348],{"class":172,"line":347},9,[170,349,350],{"class":176},"  ],\n",[170,352,354,357],{"class":172,"line":353},10,[170,355,356],{"class":183},"  \"modbus\"",[170,358,359],{"class":176},": {\n",[170,361,363,366,368,371],{"class":172,"line":362},11,[170,364,365],{"class":183},"    \"type\"",[170,367,187],{"class":176},[170,369,370],{"class":190},"\"TCP\"",[170,372,194],{"class":176},[170,374,376,379,381,384],{"class":172,"line":375},12,[170,377,378],{"class":183},"    \"ip\"",[170,380,187],{"class":176},[170,382,383],{"class":190},"\"192.168.1.101\"",[170,385,194],{"class":176},[170,387,389,392,394,397],{"class":172,"line":388},13,[170,390,391],{"class":183},"    \"port\"",[170,393,187],{"class":176},[170,395,396],{"class":183},"502",[170,398,194],{"class":176},[170,400,402,405,407],{"class":172,"line":401},14,[170,403,404],{"class":183},"    \"unit_id\"",[170,406,187],{"class":176},[170,408,409],{"class":183},"1\n",[170,411,413],{"class":172,"line":412},15,[170,414,415],{"class":176},"  },\n",[170,417,419,422],{"class":172,"line":418},16,[170,420,421],{"class":183},"  \"specs\"",[170,423,359],{"class":176},[170,425,427,430,432,435],{"class":172,"line":426},17,[170,428,429],{"class":183},"    \"adc_resolution\"",[170,431,187],{"class":176},[170,433,434],{"class":190},"\"24-bit\"",[170,436,194],{"class":176},[170,438,440,443,445,448],{"class":172,"line":439},18,[170,441,442],{"class":183},"    \"sampling_rate\"",[170,444,187],{"class":176},[170,446,447],{"class":190},"\"1000 SPS\"",[170,449,194],{"class":176},[170,451,453,456,458],{"class":172,"line":452},19,[170,454,455],{"class":183},"    \"accuracy\"",[170,457,187],{"class":176},[170,459,460],{"class":190},"\"±0.02%\"\n",[170,462,464],{"class":172,"line":463},20,[170,465,466],{"class":176},"  }\n",[170,468,470],{"class":172,"line":469},21,[170,471,472],{"class":176},"}\n",[15,474,475],{},[19,476,477],{},"Why ZMA-4 for this application:",[64,479,480,483,486,489],{},[67,481,482],{},"✅ 4 independent channels (perfect for 4-loadcell tanks)",[67,484,485],{},"✅ 24-bit ADC per channel (high precision)",[67,487,488],{},"✅ Modbus TCP/RTU support (easy gateway integration)",[67,490,491],{},"✅ High sampling rate (up to 1000 SPS)",[15,493,494,496],{},[19,495,47],{}," GDT Digital Transmitter supports maximum 2 channels + 3 DIO, making it suitable for simpler applications with 1-2 loadcells.",[59,498,500],{"id":499},"_2-hmi-panel","2. HMI Panel",[15,502,503],{},[19,504,505],{},"Central HMI (1x 10.1\" panel):",[64,507,508,511,514,517,520,523],{},[67,509,510],{},"All tank weights overview",[67,512,513],{},"Real-time weight display for each tank",[67,515,516],{},"Historical trend charts (last 24 hours)",[67,518,519],{},"Alarm history",[67,521,522],{},"Tare/Zero controls",[67,524,525],{},"System diagnostics",[15,527,528],{},[19,529,530],{},"Technical Specs:",[64,532,533,536,539,542,545,548,551],{},[67,534,535],{},"Processor: ARM Cortex-A53 (4-core, 1.2 GHz)",[67,537,538],{},"RAM: 1 GB DDR3",[67,540,541],{},"Storage: 8 GB eMMC",[67,543,544],{},"Display: 10.1\" Capacitive touchscreen (1280x800)",[67,546,547],{},"OS: Embedded Linux (Yocto)",[67,549,550],{},"Software: Qt 5.15 + QML",[67,552,553],{},"Network: Ethernet (Modbus TCP client)",[59,555,557],{"id":556},"_3-iot-gateway-raspberry-pi-4","3. IoT Gateway (Raspberry Pi 4)",[15,559,560],{},[19,561,562],{},"Role:",[64,564,565,568,571,574],{},[67,566,567],{},"Collect data from 3 ZMA-4 devices (Modbus TCP)",[67,569,570],{},"Convert protocol (Modbus → MQTT)",[67,572,573],{},"Send to AWS IoT Core",[67,575,576],{},"Local data backup (SQLite)",[15,578,579],{},[19,580,581],{},"Specs:",[64,583,584,587,590,593],{},[67,585,586],{},"Raspberry Pi 4 Model B (4GB RAM)",[67,588,589],{},"32GB SD card (Industrial grade)",[67,591,592],{},"Ethernet connection (no WiFi)",[67,594,595],{},"Custom Python gateway software",[10,597,599],{"id":598},"software-architecture","Software Architecture",[59,601,603],{"id":602},"local-hmi-application-qtqml","Local HMI Application (Qt/QML)",[15,605,606],{},[19,607,608],{},"Tank Monitor Screen:",[139,610,614],{"className":611,"code":612,"language":613,"meta":148,"style":148},"language-qml shiki shiki-themes github-light github-dark","// TankView.qml\nRectangle {\n    width: 800\n    height: 480\n    color: \"#2C3E50\"\n\n    property int tankId: 1\n    property real weight: 0.0\n    property real capacity: 5000.0\n    property real fillPercent: (weight / capacity) * 100\n\n    ColumnLayout {\n        anchors.fill: parent\n        anchors.margins: 20\n\n        // Tank visualization\n        Rectangle {\n            Layout.fillWidth: true\n            Layout.preferredHeight: 300\n            color: \"transparent\"\n            border.color: \"#ECF0F1\"\n            border.width: 3\n\n            // Fill level\n            Rectangle {\n                anchors.bottom: parent.bottom\n                anchors.left: parent.left\n                anchors.right: parent.right\n                height: parent.height * (fillPercent / 100)\n                color: fillPercent > 90 ? \"#E74C3C\" : \"#3498DB\"\n\n                Behavior on height {\n                    NumberAnimation { duration: 300 }\n                }\n            }\n\n            // Weight text\n            Text {\n                anchors.centerIn: parent\n                text: weight.toFixed(1) + \" kg\"\n                font.pixelSize: 64\n                font.bold: true\n                color: \"white\"\n            }\n        }\n\n        // Percentage bar\n        ProgressBar {\n            Layout.fillWidth: true\n            value: fillPercent / 100\n\n            background: Rectangle {\n                color: \"#34495E\"\n                radius: 5\n            }\n\n            contentItem: Item {\n                Rectangle {\n                    width: parent.width * parent.parent.value\n                    height: parent.height\n                    color: parent.parent.value > 0.9 ? \"#E74C3C\" : \"#2ECC71\"\n                    radius: 5\n                }\n            }\n        }\n\n        Text {\n            text: fillPercent.toFixed(1) + \"% Full\"\n            font.pixelSize: 24\n            color: \"#ECF0F1\"\n            Layout.alignment: Qt.AlignHCenter\n        }\n    }\n}\n","qml",[146,615,616,621,626,631,636,641,647,652,657,662,667,671,676,681,686,690,695,700,705,710,715,720,726,731,737,743,749,755,761,767,773,778,784,790,796,802,807,813,819,825,831,837,843,849,854,860,865,871,877,882,888,893,899,905,911,916,921,927,933,939,945,951,957,962,967,972,977,983,989,995,1001,1007,1012,1018],{"__ignoreMap":148},[170,617,618],{"class":172,"line":173},[170,619,620],{},"// TankView.qml\n",[170,622,623],{"class":172,"line":180},[170,624,625],{},"Rectangle {\n",[170,627,628],{"class":172,"line":197},[170,629,630],{},"    width: 800\n",[170,632,633],{"class":172,"line":210},[170,634,635],{},"    height: 480\n",[170,637,638],{"class":172,"line":219},[170,639,640],{},"    color: \"#2C3E50\"\n",[170,642,643],{"class":172,"line":256},[170,644,646],{"emptyLinePlaceholder":645},true,"\n",[170,648,649],{"class":172,"line":286},[170,650,651],{},"    property int tankId: 1\n",[170,653,654],{"class":172,"line":316},[170,655,656],{},"    property real weight: 0.0\n",[170,658,659],{"class":172,"line":347},[170,660,661],{},"    property real capacity: 5000.0\n",[170,663,664],{"class":172,"line":353},[170,665,666],{},"    property real fillPercent: (weight / capacity) * 100\n",[170,668,669],{"class":172,"line":362},[170,670,646],{"emptyLinePlaceholder":645},[170,672,673],{"class":172,"line":375},[170,674,675],{},"    ColumnLayout {\n",[170,677,678],{"class":172,"line":388},[170,679,680],{},"        anchors.fill: parent\n",[170,682,683],{"class":172,"line":401},[170,684,685],{},"        anchors.margins: 20\n",[170,687,688],{"class":172,"line":412},[170,689,646],{"emptyLinePlaceholder":645},[170,691,692],{"class":172,"line":418},[170,693,694],{},"        // Tank visualization\n",[170,696,697],{"class":172,"line":426},[170,698,699],{},"        Rectangle {\n",[170,701,702],{"class":172,"line":439},[170,703,704],{},"            Layout.fillWidth: true\n",[170,706,707],{"class":172,"line":452},[170,708,709],{},"            Layout.preferredHeight: 300\n",[170,711,712],{"class":172,"line":463},[170,713,714],{},"            color: \"transparent\"\n",[170,716,717],{"class":172,"line":469},[170,718,719],{},"            border.color: \"#ECF0F1\"\n",[170,721,723],{"class":172,"line":722},22,[170,724,725],{},"            border.width: 3\n",[170,727,729],{"class":172,"line":728},23,[170,730,646],{"emptyLinePlaceholder":645},[170,732,734],{"class":172,"line":733},24,[170,735,736],{},"            // Fill level\n",[170,738,740],{"class":172,"line":739},25,[170,741,742],{},"            Rectangle {\n",[170,744,746],{"class":172,"line":745},26,[170,747,748],{},"                anchors.bottom: parent.bottom\n",[170,750,752],{"class":172,"line":751},27,[170,753,754],{},"                anchors.left: parent.left\n",[170,756,758],{"class":172,"line":757},28,[170,759,760],{},"                anchors.right: parent.right\n",[170,762,764],{"class":172,"line":763},29,[170,765,766],{},"                height: parent.height * (fillPercent / 100)\n",[170,768,770],{"class":172,"line":769},30,[170,771,772],{},"                color: fillPercent > 90 ? \"#E74C3C\" : \"#3498DB\"\n",[170,774,776],{"class":172,"line":775},31,[170,777,646],{"emptyLinePlaceholder":645},[170,779,781],{"class":172,"line":780},32,[170,782,783],{},"                Behavior on height {\n",[170,785,787],{"class":172,"line":786},33,[170,788,789],{},"                    NumberAnimation { duration: 300 }\n",[170,791,793],{"class":172,"line":792},34,[170,794,795],{},"                }\n",[170,797,799],{"class":172,"line":798},35,[170,800,801],{},"            }\n",[170,803,805],{"class":172,"line":804},36,[170,806,646],{"emptyLinePlaceholder":645},[170,808,810],{"class":172,"line":809},37,[170,811,812],{},"            // Weight text\n",[170,814,816],{"class":172,"line":815},38,[170,817,818],{},"            Text {\n",[170,820,822],{"class":172,"line":821},39,[170,823,824],{},"                anchors.centerIn: parent\n",[170,826,828],{"class":172,"line":827},40,[170,829,830],{},"                text: weight.toFixed(1) + \" kg\"\n",[170,832,834],{"class":172,"line":833},41,[170,835,836],{},"                font.pixelSize: 64\n",[170,838,840],{"class":172,"line":839},42,[170,841,842],{},"                font.bold: true\n",[170,844,846],{"class":172,"line":845},43,[170,847,848],{},"                color: \"white\"\n",[170,850,852],{"class":172,"line":851},44,[170,853,801],{},[170,855,857],{"class":172,"line":856},45,[170,858,859],{},"        }\n",[170,861,863],{"class":172,"line":862},46,[170,864,646],{"emptyLinePlaceholder":645},[170,866,868],{"class":172,"line":867},47,[170,869,870],{},"        // Percentage bar\n",[170,872,874],{"class":172,"line":873},48,[170,875,876],{},"        ProgressBar {\n",[170,878,880],{"class":172,"line":879},49,[170,881,704],{},[170,883,885],{"class":172,"line":884},50,[170,886,887],{},"            value: fillPercent / 100\n",[170,889,891],{"class":172,"line":890},51,[170,892,646],{"emptyLinePlaceholder":645},[170,894,896],{"class":172,"line":895},52,[170,897,898],{},"            background: Rectangle {\n",[170,900,902],{"class":172,"line":901},53,[170,903,904],{},"                color: \"#34495E\"\n",[170,906,908],{"class":172,"line":907},54,[170,909,910],{},"                radius: 5\n",[170,912,914],{"class":172,"line":913},55,[170,915,801],{},[170,917,919],{"class":172,"line":918},56,[170,920,646],{"emptyLinePlaceholder":645},[170,922,924],{"class":172,"line":923},57,[170,925,926],{},"            contentItem: Item {\n",[170,928,930],{"class":172,"line":929},58,[170,931,932],{},"                Rectangle {\n",[170,934,936],{"class":172,"line":935},59,[170,937,938],{},"                    width: parent.width * parent.parent.value\n",[170,940,942],{"class":172,"line":941},60,[170,943,944],{},"                    height: parent.height\n",[170,946,948],{"class":172,"line":947},61,[170,949,950],{},"                    color: parent.parent.value > 0.9 ? \"#E74C3C\" : \"#2ECC71\"\n",[170,952,954],{"class":172,"line":953},62,[170,955,956],{},"                    radius: 5\n",[170,958,960],{"class":172,"line":959},63,[170,961,795],{},[170,963,965],{"class":172,"line":964},64,[170,966,801],{},[170,968,970],{"class":172,"line":969},65,[170,971,859],{},[170,973,975],{"class":172,"line":974},66,[170,976,646],{"emptyLinePlaceholder":645},[170,978,980],{"class":172,"line":979},67,[170,981,982],{},"        Text {\n",[170,984,986],{"class":172,"line":985},68,[170,987,988],{},"            text: fillPercent.toFixed(1) + \"% Full\"\n",[170,990,992],{"class":172,"line":991},69,[170,993,994],{},"            font.pixelSize: 24\n",[170,996,998],{"class":172,"line":997},70,[170,999,1000],{},"            color: \"#ECF0F1\"\n",[170,1002,1004],{"class":172,"line":1003},71,[170,1005,1006],{},"            Layout.alignment: Qt.AlignHCenter\n",[170,1008,1010],{"class":172,"line":1009},72,[170,1011,859],{},[170,1013,1015],{"class":172,"line":1014},73,[170,1016,1017],{},"    }\n",[170,1019,1021],{"class":172,"line":1020},74,[170,1022,472],{},[59,1024,1026],{"id":1025},"gateway-application-python","Gateway Application (Python)",[15,1028,1029],{},[19,1030,1031],{},"Main Gateway Logic:",[139,1033,1037],{"className":1034,"code":1035,"language":1036,"meta":148,"style":148},"language-python shiki shiki-themes github-light github-dark","#!/usr/bin/env python3\nimport json\nimport time\nfrom datetime import datetime\nfrom pymodbus.client.sync import ModbusTcpClient\nfrom awsiot import mqtt_connection_builder\nimport sqlite3\n\nclass MilkTankGateway:\n    def __init__(self):\n        self.tanks = [\n            {\"id\": 1, \"name\": \"Tank-1\", \"zma_ip\": \"192.168.1.101\"},\n            {\"id\": 2, \"name\": \"Tank-2\", \"zma_ip\": \"192.168.1.102\"},\n            {\"id\": 3, \"name\": \"Tank-3\", \"zma_ip\": \"192.168.1.103\"},\n        ]\n\n        # Modbus clients\n        self.modbus_clients = {}\n        for tank in self.tanks:\n            client = ModbusTcpClient(tank['zma_ip'], port=502)\n            client.connect()\n            self.modbus_clients[tank['id']] = client\n\n        # AWS MQTT connection\n        self.mqtt_connection = self._setup_aws_mqtt()\n\n        # Local database backup\n        self.db = sqlite3.connect('/data/milk_tanks.db')\n        self._init_database()\n\n    def _setup_aws_mqtt(self):\n        \"\"\"Setup AWS IoT Core MQTT connection\"\"\"\n        return mqtt_connection_builder.mtls_from_path(\n            endpoint=\"xxxxx.iot.eu-west-1.amazonaws.com\",\n            port=8883,\n            cert_filepath=\"/certs/gateway-cert.pem\",\n            pri_key_filepath=\"/certs/gateway-private.key\",\n            ca_filepath=\"/certs/AmazonRootCA1.pem\",\n            client_id=\"milk-facility-gateway\",\n            clean_session=False,\n            keep_alive_secs=30\n        )\n\n    def read_tank_weight(self, tank_id):\n        \"\"\"Read weight from ZMA-4 via Modbus\"\"\"\n        client = self.modbus_clients[tank_id]\n\n        # Read holding registers 200-203 (Total weight + Tare)\n        result = client.read_holding_registers(200, 4, unit=1)\n\n        if not result.isError():\n            gross = self._registers_to_float(result.registers[0:2])\n            net = self._registers_to_float(result.registers[2:4])\n            tare = self._registers_to_float(result.registers[4:6])\n\n            return {\n                \"tank_id\": tank_id,\n                \"timestamp\": datetime.utcnow().isoformat() + \"Z\",\n                \"gross_weight\": round(gross, 2),\n                \"net_weight\": round(net, 2),\n                \"tare_weight\": round(tare, 2),\n                \"fill_percent\": round((net / 5000.0) * 100, 1)\n            }\n\n        return None\n\n    def _registers_to_float(self, registers):\n        \"\"\"Convert 2 Modbus registers to Float32\"\"\"\n        import struct\n        raw = (registers[0] \u003C\u003C 16) | registers[1]\n        return struct.unpack('f', struct.pack('I', raw))[0]\n\n    def publish_to_aws(self, data):\n        \"\"\"Publish data to AWS IoT Core\"\"\"\n        topic = f\"amazeng/milk-facility/tank/{data['tank_id']}/data\"\n        payload = json.dumps(data)\n\n        self.mqtt_connection.publish(\n            topic=topic,\n            payload=payload,\n            qos=1\n        )\n\n        print(f\"→ AWS: {topic} | {data['net_weight']} kg\")\n\n    def save_local_backup(self, data):\n        \"\"\"Save data to local SQLite database\"\"\"\n        cursor = self.db.cursor()\n        cursor.execute(\"\"\"\n            INSERT INTO tank_data (tank_id, timestamp, net_weight, fill_percent)\n            VALUES (?, ?, ?, ?)\n        \"\"\", (data['tank_id'], data['timestamp'],\n              data['net_weight'], data['fill_percent']))\n        self.db.commit()\n\n    def run(self):\n        \"\"\"Main loop: Read → Publish → Store\"\"\"\n        print(\"🚀 Milk Tank Gateway started\")\n\n        try:\n            while True:\n                for tank in self.tanks:\n                    # Read from GDT\n                    data = self.read_tank_weight(tank['id'])\n\n                    if data:\n                        # Send to AWS\n                        self.publish_to_aws(data)\n\n                        # Local backup\n                        self.save_local_backup(data)\n\n                time.sleep(1)  # 1 second polling interval\n\n        except KeyboardInterrupt:\n            print(\"\\n✋ Gateway stopped\")\n            self.shutdown()\n\nif __name__ == \"__main__\":\n    gateway = MilkTankGateway()\n    gateway.run()\n","python",[146,1038,1039,1044,1049,1054,1059,1064,1069,1074,1078,1083,1088,1093,1098,1103,1108,1113,1117,1122,1127,1132,1137,1142,1147,1151,1156,1161,1165,1170,1175,1180,1184,1189,1194,1199,1204,1209,1214,1219,1224,1229,1234,1239,1244,1248,1253,1258,1263,1267,1272,1277,1281,1286,1291,1296,1301,1305,1310,1315,1320,1325,1330,1335,1340,1344,1348,1353,1357,1362,1367,1372,1377,1382,1386,1391,1396,1402,1408,1413,1419,1425,1431,1437,1442,1447,1453,1458,1464,1470,1476,1482,1488,1494,1500,1506,1512,1517,1523,1529,1535,1540,1546,1552,1558,1564,1570,1575,1581,1587,1593,1598,1604,1610,1615,1621,1626,1632,1638,1644,1649,1655,1661],{"__ignoreMap":148},[170,1040,1041],{"class":172,"line":173},[170,1042,1043],{},"#!/usr/bin/env python3\n",[170,1045,1046],{"class":172,"line":180},[170,1047,1048],{},"import json\n",[170,1050,1051],{"class":172,"line":197},[170,1052,1053],{},"import time\n",[170,1055,1056],{"class":172,"line":210},[170,1057,1058],{},"from datetime import datetime\n",[170,1060,1061],{"class":172,"line":219},[170,1062,1063],{},"from pymodbus.client.sync import ModbusTcpClient\n",[170,1065,1066],{"class":172,"line":256},[170,1067,1068],{},"from awsiot import mqtt_connection_builder\n",[170,1070,1071],{"class":172,"line":286},[170,1072,1073],{},"import sqlite3\n",[170,1075,1076],{"class":172,"line":316},[170,1077,646],{"emptyLinePlaceholder":645},[170,1079,1080],{"class":172,"line":347},[170,1081,1082],{},"class MilkTankGateway:\n",[170,1084,1085],{"class":172,"line":353},[170,1086,1087],{},"    def __init__(self):\n",[170,1089,1090],{"class":172,"line":362},[170,1091,1092],{},"        self.tanks = [\n",[170,1094,1095],{"class":172,"line":375},[170,1096,1097],{},"            {\"id\": 1, \"name\": \"Tank-1\", \"zma_ip\": \"192.168.1.101\"},\n",[170,1099,1100],{"class":172,"line":388},[170,1101,1102],{},"            {\"id\": 2, \"name\": \"Tank-2\", \"zma_ip\": \"192.168.1.102\"},\n",[170,1104,1105],{"class":172,"line":401},[170,1106,1107],{},"            {\"id\": 3, \"name\": \"Tank-3\", \"zma_ip\": \"192.168.1.103\"},\n",[170,1109,1110],{"class":172,"line":412},[170,1111,1112],{},"        ]\n",[170,1114,1115],{"class":172,"line":418},[170,1116,646],{"emptyLinePlaceholder":645},[170,1118,1119],{"class":172,"line":426},[170,1120,1121],{},"        # Modbus clients\n",[170,1123,1124],{"class":172,"line":439},[170,1125,1126],{},"        self.modbus_clients = {}\n",[170,1128,1129],{"class":172,"line":452},[170,1130,1131],{},"        for tank in self.tanks:\n",[170,1133,1134],{"class":172,"line":463},[170,1135,1136],{},"            client = ModbusTcpClient(tank['zma_ip'], port=502)\n",[170,1138,1139],{"class":172,"line":469},[170,1140,1141],{},"            client.connect()\n",[170,1143,1144],{"class":172,"line":722},[170,1145,1146],{},"            self.modbus_clients[tank['id']] = client\n",[170,1148,1149],{"class":172,"line":728},[170,1150,646],{"emptyLinePlaceholder":645},[170,1152,1153],{"class":172,"line":733},[170,1154,1155],{},"        # AWS MQTT connection\n",[170,1157,1158],{"class":172,"line":739},[170,1159,1160],{},"        self.mqtt_connection = self._setup_aws_mqtt()\n",[170,1162,1163],{"class":172,"line":745},[170,1164,646],{"emptyLinePlaceholder":645},[170,1166,1167],{"class":172,"line":751},[170,1168,1169],{},"        # Local database backup\n",[170,1171,1172],{"class":172,"line":757},[170,1173,1174],{},"        self.db = sqlite3.connect('/data/milk_tanks.db')\n",[170,1176,1177],{"class":172,"line":763},[170,1178,1179],{},"        self._init_database()\n",[170,1181,1182],{"class":172,"line":769},[170,1183,646],{"emptyLinePlaceholder":645},[170,1185,1186],{"class":172,"line":775},[170,1187,1188],{},"    def _setup_aws_mqtt(self):\n",[170,1190,1191],{"class":172,"line":780},[170,1192,1193],{},"        \"\"\"Setup AWS IoT Core MQTT connection\"\"\"\n",[170,1195,1196],{"class":172,"line":786},[170,1197,1198],{},"        return mqtt_connection_builder.mtls_from_path(\n",[170,1200,1201],{"class":172,"line":792},[170,1202,1203],{},"            endpoint=\"xxxxx.iot.eu-west-1.amazonaws.com\",\n",[170,1205,1206],{"class":172,"line":798},[170,1207,1208],{},"            port=8883,\n",[170,1210,1211],{"class":172,"line":804},[170,1212,1213],{},"            cert_filepath=\"/certs/gateway-cert.pem\",\n",[170,1215,1216],{"class":172,"line":809},[170,1217,1218],{},"            pri_key_filepath=\"/certs/gateway-private.key\",\n",[170,1220,1221],{"class":172,"line":815},[170,1222,1223],{},"            ca_filepath=\"/certs/AmazonRootCA1.pem\",\n",[170,1225,1226],{"class":172,"line":821},[170,1227,1228],{},"            client_id=\"milk-facility-gateway\",\n",[170,1230,1231],{"class":172,"line":827},[170,1232,1233],{},"            clean_session=False,\n",[170,1235,1236],{"class":172,"line":833},[170,1237,1238],{},"            keep_alive_secs=30\n",[170,1240,1241],{"class":172,"line":839},[170,1242,1243],{},"        )\n",[170,1245,1246],{"class":172,"line":845},[170,1247,646],{"emptyLinePlaceholder":645},[170,1249,1250],{"class":172,"line":851},[170,1251,1252],{},"    def read_tank_weight(self, tank_id):\n",[170,1254,1255],{"class":172,"line":856},[170,1256,1257],{},"        \"\"\"Read weight from ZMA-4 via Modbus\"\"\"\n",[170,1259,1260],{"class":172,"line":862},[170,1261,1262],{},"        client = self.modbus_clients[tank_id]\n",[170,1264,1265],{"class":172,"line":867},[170,1266,646],{"emptyLinePlaceholder":645},[170,1268,1269],{"class":172,"line":873},[170,1270,1271],{},"        # Read holding registers 200-203 (Total weight + Tare)\n",[170,1273,1274],{"class":172,"line":879},[170,1275,1276],{},"        result = client.read_holding_registers(200, 4, unit=1)\n",[170,1278,1279],{"class":172,"line":884},[170,1280,646],{"emptyLinePlaceholder":645},[170,1282,1283],{"class":172,"line":890},[170,1284,1285],{},"        if not result.isError():\n",[170,1287,1288],{"class":172,"line":895},[170,1289,1290],{},"            gross = self._registers_to_float(result.registers[0:2])\n",[170,1292,1293],{"class":172,"line":901},[170,1294,1295],{},"            net = self._registers_to_float(result.registers[2:4])\n",[170,1297,1298],{"class":172,"line":907},[170,1299,1300],{},"            tare = self._registers_to_float(result.registers[4:6])\n",[170,1302,1303],{"class":172,"line":913},[170,1304,646],{"emptyLinePlaceholder":645},[170,1306,1307],{"class":172,"line":918},[170,1308,1309],{},"            return {\n",[170,1311,1312],{"class":172,"line":923},[170,1313,1314],{},"                \"tank_id\": tank_id,\n",[170,1316,1317],{"class":172,"line":929},[170,1318,1319],{},"                \"timestamp\": datetime.utcnow().isoformat() + \"Z\",\n",[170,1321,1322],{"class":172,"line":935},[170,1323,1324],{},"                \"gross_weight\": round(gross, 2),\n",[170,1326,1327],{"class":172,"line":941},[170,1328,1329],{},"                \"net_weight\": round(net, 2),\n",[170,1331,1332],{"class":172,"line":947},[170,1333,1334],{},"                \"tare_weight\": round(tare, 2),\n",[170,1336,1337],{"class":172,"line":953},[170,1338,1339],{},"                \"fill_percent\": round((net / 5000.0) * 100, 1)\n",[170,1341,1342],{"class":172,"line":959},[170,1343,801],{},[170,1345,1346],{"class":172,"line":964},[170,1347,646],{"emptyLinePlaceholder":645},[170,1349,1350],{"class":172,"line":969},[170,1351,1352],{},"        return None\n",[170,1354,1355],{"class":172,"line":974},[170,1356,646],{"emptyLinePlaceholder":645},[170,1358,1359],{"class":172,"line":979},[170,1360,1361],{},"    def _registers_to_float(self, registers):\n",[170,1363,1364],{"class":172,"line":985},[170,1365,1366],{},"        \"\"\"Convert 2 Modbus registers to Float32\"\"\"\n",[170,1368,1369],{"class":172,"line":991},[170,1370,1371],{},"        import struct\n",[170,1373,1374],{"class":172,"line":997},[170,1375,1376],{},"        raw = (registers[0] \u003C\u003C 16) | registers[1]\n",[170,1378,1379],{"class":172,"line":1003},[170,1380,1381],{},"        return struct.unpack('f', struct.pack('I', raw))[0]\n",[170,1383,1384],{"class":172,"line":1009},[170,1385,646],{"emptyLinePlaceholder":645},[170,1387,1388],{"class":172,"line":1014},[170,1389,1390],{},"    def publish_to_aws(self, data):\n",[170,1392,1393],{"class":172,"line":1020},[170,1394,1395],{},"        \"\"\"Publish data to AWS IoT Core\"\"\"\n",[170,1397,1399],{"class":172,"line":1398},75,[170,1400,1401],{},"        topic = f\"amazeng/milk-facility/tank/{data['tank_id']}/data\"\n",[170,1403,1405],{"class":172,"line":1404},76,[170,1406,1407],{},"        payload = json.dumps(data)\n",[170,1409,1411],{"class":172,"line":1410},77,[170,1412,646],{"emptyLinePlaceholder":645},[170,1414,1416],{"class":172,"line":1415},78,[170,1417,1418],{},"        self.mqtt_connection.publish(\n",[170,1420,1422],{"class":172,"line":1421},79,[170,1423,1424],{},"            topic=topic,\n",[170,1426,1428],{"class":172,"line":1427},80,[170,1429,1430],{},"            payload=payload,\n",[170,1432,1434],{"class":172,"line":1433},81,[170,1435,1436],{},"            qos=1\n",[170,1438,1440],{"class":172,"line":1439},82,[170,1441,1243],{},[170,1443,1445],{"class":172,"line":1444},83,[170,1446,646],{"emptyLinePlaceholder":645},[170,1448,1450],{"class":172,"line":1449},84,[170,1451,1452],{},"        print(f\"→ AWS: {topic} | {data['net_weight']} kg\")\n",[170,1454,1456],{"class":172,"line":1455},85,[170,1457,646],{"emptyLinePlaceholder":645},[170,1459,1461],{"class":172,"line":1460},86,[170,1462,1463],{},"    def save_local_backup(self, data):\n",[170,1465,1467],{"class":172,"line":1466},87,[170,1468,1469],{},"        \"\"\"Save data to local SQLite database\"\"\"\n",[170,1471,1473],{"class":172,"line":1472},88,[170,1474,1475],{},"        cursor = self.db.cursor()\n",[170,1477,1479],{"class":172,"line":1478},89,[170,1480,1481],{},"        cursor.execute(\"\"\"\n",[170,1483,1485],{"class":172,"line":1484},90,[170,1486,1487],{},"            INSERT INTO tank_data (tank_id, timestamp, net_weight, fill_percent)\n",[170,1489,1491],{"class":172,"line":1490},91,[170,1492,1493],{},"            VALUES (?, ?, ?, ?)\n",[170,1495,1497],{"class":172,"line":1496},92,[170,1498,1499],{},"        \"\"\", (data['tank_id'], data['timestamp'],\n",[170,1501,1503],{"class":172,"line":1502},93,[170,1504,1505],{},"              data['net_weight'], data['fill_percent']))\n",[170,1507,1509],{"class":172,"line":1508},94,[170,1510,1511],{},"        self.db.commit()\n",[170,1513,1515],{"class":172,"line":1514},95,[170,1516,646],{"emptyLinePlaceholder":645},[170,1518,1520],{"class":172,"line":1519},96,[170,1521,1522],{},"    def run(self):\n",[170,1524,1526],{"class":172,"line":1525},97,[170,1527,1528],{},"        \"\"\"Main loop: Read → Publish → Store\"\"\"\n",[170,1530,1532],{"class":172,"line":1531},98,[170,1533,1534],{},"        print(\"🚀 Milk Tank Gateway started\")\n",[170,1536,1538],{"class":172,"line":1537},99,[170,1539,646],{"emptyLinePlaceholder":645},[170,1541,1543],{"class":172,"line":1542},100,[170,1544,1545],{},"        try:\n",[170,1547,1549],{"class":172,"line":1548},101,[170,1550,1551],{},"            while True:\n",[170,1553,1555],{"class":172,"line":1554},102,[170,1556,1557],{},"                for tank in self.tanks:\n",[170,1559,1561],{"class":172,"line":1560},103,[170,1562,1563],{},"                    # Read from GDT\n",[170,1565,1567],{"class":172,"line":1566},104,[170,1568,1569],{},"                    data = self.read_tank_weight(tank['id'])\n",[170,1571,1573],{"class":172,"line":1572},105,[170,1574,646],{"emptyLinePlaceholder":645},[170,1576,1578],{"class":172,"line":1577},106,[170,1579,1580],{},"                    if data:\n",[170,1582,1584],{"class":172,"line":1583},107,[170,1585,1586],{},"                        # Send to AWS\n",[170,1588,1590],{"class":172,"line":1589},108,[170,1591,1592],{},"                        self.publish_to_aws(data)\n",[170,1594,1596],{"class":172,"line":1595},109,[170,1597,646],{"emptyLinePlaceholder":645},[170,1599,1601],{"class":172,"line":1600},110,[170,1602,1603],{},"                        # Local backup\n",[170,1605,1607],{"class":172,"line":1606},111,[170,1608,1609],{},"                        self.save_local_backup(data)\n",[170,1611,1613],{"class":172,"line":1612},112,[170,1614,646],{"emptyLinePlaceholder":645},[170,1616,1618],{"class":172,"line":1617},113,[170,1619,1620],{},"                time.sleep(1)  # 1 second polling interval\n",[170,1622,1624],{"class":172,"line":1623},114,[170,1625,646],{"emptyLinePlaceholder":645},[170,1627,1629],{"class":172,"line":1628},115,[170,1630,1631],{},"        except KeyboardInterrupt:\n",[170,1633,1635],{"class":172,"line":1634},116,[170,1636,1637],{},"            print(\"\\n✋ Gateway stopped\")\n",[170,1639,1641],{"class":172,"line":1640},117,[170,1642,1643],{},"            self.shutdown()\n",[170,1645,1647],{"class":172,"line":1646},118,[170,1648,646],{"emptyLinePlaceholder":645},[170,1650,1652],{"class":172,"line":1651},119,[170,1653,1654],{},"if __name__ == \"__main__\":\n",[170,1656,1658],{"class":172,"line":1657},120,[170,1659,1660],{},"    gateway = MilkTankGateway()\n",[170,1662,1664],{"class":172,"line":1663},121,[170,1665,1666],{},"    gateway.run()\n",[10,1668,1670],{"id":1669},"aws-iot-rules-and-alarms","AWS IoT Rules and Alarms",[59,1672,1674],{"id":1673},"high-capacity-alarm-90","High Capacity Alarm (>90%)",[15,1676,1677],{},[19,1678,1679],{},"IoT Rule:",[139,1681,1685],{"className":1682,"code":1683,"language":1684,"meta":148,"style":148},"language-sql shiki shiki-themes github-light github-dark","SELECT\n  tank_id,\n  fill_percent,\n  timestamp\nFROM\n  'amazeng/milk-facility/tank/+/data'\nWHERE\n  fill_percent > 90\n","sql",[146,1686,1687,1692,1697,1702,1707,1712,1717,1722],{"__ignoreMap":148},[170,1688,1689],{"class":172,"line":173},[170,1690,1691],{},"SELECT\n",[170,1693,1694],{"class":172,"line":180},[170,1695,1696],{},"  tank_id,\n",[170,1698,1699],{"class":172,"line":197},[170,1700,1701],{},"  fill_percent,\n",[170,1703,1704],{"class":172,"line":210},[170,1705,1706],{},"  timestamp\n",[170,1708,1709],{"class":172,"line":219},[170,1710,1711],{},"FROM\n",[170,1713,1714],{"class":172,"line":256},[170,1715,1716],{},"  'amazeng/milk-facility/tank/+/data'\n",[170,1718,1719],{"class":172,"line":286},[170,1720,1721],{},"WHERE\n",[170,1723,1724],{"class":172,"line":316},[170,1725,1726],{},"  fill_percent > 90\n",[15,1728,1729,1732],{},[19,1730,1731],{},"Action:"," Trigger SNS (SMS + Email)",[15,1734,1735],{},[19,1736,1737],{},"Lambda Function:",[139,1739,1741],{"className":1034,"code":1740,"language":1036,"meta":148,"style":148},"import boto3\n\nsns = boto3.client('sns')\n\ndef lambda_handler(event, context):\n    tank_id = event['tank_id']\n    fill_percent = event['fill_percent']\n\n    message = f\"\"\"\n    ⚠️ TANK CAPACITY WARNING\n\n    Tank: {tank_id}\n    Fill Level: {fill_percent}%\n    Time: {event['timestamp']}\n\n    Action Required: Prepare for milk collection\n    \"\"\"\n\n    sns.publish(\n        TopicArn='arn:aws:sns:eu-west-1:xxx:milk-tank-alarms',\n        Subject='Tank Capacity Alert',\n        Message=message\n    )\n\n    return {'status': 'alarm_sent'}\n",[146,1742,1743,1748,1752,1757,1761,1766,1771,1776,1780,1785,1790,1794,1799,1804,1809,1813,1818,1823,1827,1832,1837,1842,1847,1852,1856],{"__ignoreMap":148},[170,1744,1745],{"class":172,"line":173},[170,1746,1747],{},"import boto3\n",[170,1749,1750],{"class":172,"line":180},[170,1751,646],{"emptyLinePlaceholder":645},[170,1753,1754],{"class":172,"line":197},[170,1755,1756],{},"sns = boto3.client('sns')\n",[170,1758,1759],{"class":172,"line":210},[170,1760,646],{"emptyLinePlaceholder":645},[170,1762,1763],{"class":172,"line":219},[170,1764,1765],{},"def lambda_handler(event, context):\n",[170,1767,1768],{"class":172,"line":256},[170,1769,1770],{},"    tank_id = event['tank_id']\n",[170,1772,1773],{"class":172,"line":286},[170,1774,1775],{},"    fill_percent = event['fill_percent']\n",[170,1777,1778],{"class":172,"line":316},[170,1779,646],{"emptyLinePlaceholder":645},[170,1781,1782],{"class":172,"line":347},[170,1783,1784],{},"    message = f\"\"\"\n",[170,1786,1787],{"class":172,"line":353},[170,1788,1789],{},"    ⚠️ TANK CAPACITY WARNING\n",[170,1791,1792],{"class":172,"line":362},[170,1793,646],{"emptyLinePlaceholder":645},[170,1795,1796],{"class":172,"line":375},[170,1797,1798],{},"    Tank: {tank_id}\n",[170,1800,1801],{"class":172,"line":388},[170,1802,1803],{},"    Fill Level: {fill_percent}%\n",[170,1805,1806],{"class":172,"line":401},[170,1807,1808],{},"    Time: {event['timestamp']}\n",[170,1810,1811],{"class":172,"line":412},[170,1812,646],{"emptyLinePlaceholder":645},[170,1814,1815],{"class":172,"line":418},[170,1816,1817],{},"    Action Required: Prepare for milk collection\n",[170,1819,1820],{"class":172,"line":426},[170,1821,1822],{},"    \"\"\"\n",[170,1824,1825],{"class":172,"line":439},[170,1826,646],{"emptyLinePlaceholder":645},[170,1828,1829],{"class":172,"line":452},[170,1830,1831],{},"    sns.publish(\n",[170,1833,1834],{"class":172,"line":463},[170,1835,1836],{},"        TopicArn='arn:aws:sns:eu-west-1:xxx:milk-tank-alarms',\n",[170,1838,1839],{"class":172,"line":469},[170,1840,1841],{},"        Subject='Tank Capacity Alert',\n",[170,1843,1844],{"class":172,"line":722},[170,1845,1846],{},"        Message=message\n",[170,1848,1849],{"class":172,"line":728},[170,1850,1851],{},"    )\n",[170,1853,1854],{"class":172,"line":733},[170,1855,646],{"emptyLinePlaceholder":645},[170,1857,1858],{"class":172,"line":739},[170,1859,1860],{},"    return {'status': 'alarm_sent'}\n",[10,1862,1864],{"id":1863},"conclusion","Conclusion",[15,1866,1867],{},"This fully integrated solution demonstrates the power of Amazeng products working together:",[15,1869,1870,1871,1874,1875,1878,1879,1882,1883,1878,1885,1888,1889,1878,1891,1894,1895,1878,1897,1900,1901,1878,1903,1906],{},"✅ ",[19,1872,1873],{},"Real-time Monitoring:"," All tanks displayed with 1-second update rate",[1876,1877],"br",{},"\n✅ ",[19,1880,1881],{},"High Precision:"," ZMA-4's 24-bit ADC per channel (±0.02%)",[1876,1884],{},[19,1886,1887],{},"Scalable:"," 4 channels per device, easy to add more tanks",[1876,1890],{},[19,1892,1893],{},"Cloud Integration:"," Historical data and analytics on AWS",[1876,1896],{},[19,1898,1899],{},"Alarm System:"," SMS/Email alerts for critical events",[1876,1902],{},[19,1904,1905],{},"Cost-Effective:"," 73% cheaper than traditional SCADA",[15,1908,1909],{},[19,1910,1911],{},"Product Selection Guide:",[64,1913,1914,1920],{},[67,1915,1916,1919],{},[19,1917,1918],{},"4 loadcells per tank:"," Use ZMA-4 (as shown in this article)",[67,1921,1922,1925,1926,1928],{},[19,1923,1924],{},"1-2 loadcells + DIO needed:"," Use ",[28,1927,52],{"href":51}," (2 ch + 3 DIO)",[59,1930,1932],{"id":1931},"related-products-solutions","Related Products & Solutions",[64,1934,1935,1940,1945,1949,1953],{},[67,1936,1937,1939],{},[28,1938,31],{"href":30}," - For 4-channel loadcell applications",[67,1941,1942,1944],{},[28,1943,52],{"href":51}," - For 1-2 channel + DIO applications",[67,1946,1947],{},[28,1948,36],{"href":35},[67,1950,1951],{},[28,1952,41],{"href":40},[67,1954,1955],{},[28,1956,1958],{"href":1957},"/en/blog/industrial-data-collection-with-aws-iot-core","Industrial Data Collection with AWS IoT Core",[1960,1961,1962],"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);}",{"title":148,"searchDepth":180,"depth":180,"links":1964},[1965,1966,1970,1971,1976,1980,1983],{"id":12,"depth":180,"text":13},{"id":56,"depth":180,"text":57,"children":1967},[1968,1969],{"id":61,"depth":197,"text":62},{"id":93,"depth":197,"text":94},{"id":136,"depth":180,"text":137},{"id":151,"depth":180,"text":152,"children":1972},[1973,1974,1975],{"id":155,"depth":197,"text":156},{"id":499,"depth":197,"text":500},{"id":556,"depth":197,"text":557},{"id":598,"depth":180,"text":599,"children":1977},[1978,1979],{"id":602,"depth":197,"text":603},{"id":1025,"depth":197,"text":1026},{"id":1669,"depth":180,"text":1670,"children":1981},[1982],{"id":1673,"depth":197,"text":1674},{"id":1863,"depth":180,"text":1864,"children":1984},[1985],{"id":1931,"depth":197,"text":1932},"Real-world case study: Complete system architecture for 5-tank milk processing facility using ZMA, HMI and AWS IoT.","md",{"date":1989,"author":1990,"readTime":412,"featured":645,"tags":1991},"2025-12-29","Amazeng Technical Team",[1992,1993,1994,1995,1996,1997,1998],"ZMA","HMI","AWS IoT","Industrial IoT","Full Stack","Case Study","Milk Tank","/en/blog/fully-integrated-solution-zma-gdt-hmi-aws",{"title":5,"description":1986},"en/blog/fully-integrated-solution-zma-gdt-hmi-aws","oNAPJHhhUx_xIlmzIw5fzEJW-Z1YVZcwb0dz09jUNCY",1778229658251]