<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>kyoungminp00 님의 블로그</title>
    <link>https://kyoungminp00.tistory.com/</link>
    <description>kyoungminp00 님의 블로그 입니다.</description>
    <language>ko</language>
    <pubDate>Sun, 5 Apr 2026 22:53:19 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>kyoungminp00</managingEditor>
    <item>
      <title>OT-IT convergence: IoT 프로젝트 (2)</title>
      <link>https://kyoungminp00.tistory.com/3</link>
      <description>&lt;h1&gt;Building a Smart Factory IoT Platform: AWS + Terraform + OPC UA + AI&lt;/h1&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Introduction&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;This is Part 2 of my IoT pipeline series. In Part 1, I covered the hardware setup &amp;mdash; wiring the DHT22 sensor to a Raspberry Pi 5 and confirming sensor readings.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;In this post I'll cover connecting the Pi to AWS, storing data in DynamoDB, adding AI-powered anomaly detection with Lambda, provisioning infrastructure with Terraform, and finally adding an OPC UA industrial protocol layer.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Part 1: Connecting to AWS IoT Core&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;I created an IoT &lt;b&gt;Thing&lt;/b&gt; (rpi-dht22-seoul-001) in the AWS Console and attached a least-privilege IoT Policy. AWS generates X.509 certificates during this process (a device certificate, private key, and Root CA), which I transferred to the Pi using scp.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;The endpoint address is retrieved via CLI:&lt;/p&gt;
&lt;pre class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;aws iot describe-endpoint --endpoint-type iot:Data-ATS --region ap-northeast-2&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;The main script (sensor_to_aws.py) uses the AWS IoT SDK's mtls_from_path() to establish an MQTT connection over TLS port 8883, authenticated with the X.509 certificates. Every 60 seconds it reads the sensor and publishes a JSON message to sensors/dht22/data.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;I verified live data in the &lt;b&gt;MQTT test client&lt;/b&gt; in the AWS Console.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Part 2: Storing Data in DynamoDB&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;I set up an IoT Rules Engine rule to automatically write every incoming message to a DynamoDB table (dht22-sensor-data, partition key: device_id, sort key: timestamp).&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Troubleshooting: DynamoDB vs DynamoDBv2&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;The first attempt used the &quot;DynamoDB&quot; action type, which bundled all fields into a single payload column. Switching to &lt;b&gt;DynamoDBv2&lt;/b&gt; fixed this immediately, writing each field as a clean separate column:&lt;/p&gt;
&lt;pre class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;device_id            | timestamp   | humidity | temperature
rpi-dht22-seoul-001  | 1772548058  | 32.1     | 22.8&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;This was the only significant issue encountered in the entire cloud setup.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Part 3: Infrastructure as Code with Terraform&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;All AWS resources were then codified into Terraform &amp;mdash; IoT Core, DynamoDB, IAM roles, Lambda, and EventBridge &amp;mdash; split across five .tf files.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Since some resources already existed from manual setup, I imported them first:&lt;/p&gt;
&lt;pre class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;terraform import aws_dynamodb_table.sensor_data dht22-sensor-data
terraform import aws_iot_thing.dht22 rpi-dht22-seoul-001&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;The full infrastructure can now be reproduced in any AWS region with:&lt;/p&gt;
&lt;pre class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;terraform init
terraform apply&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;I also replaced root account usage with a dedicated IAM user (terraform-admin) following least-privilege principles.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Part 4: AI Anomaly Detection with Lambda&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;An AWS Lambda function runs every 5 minutes via EventBridge, performing &lt;b&gt;Z-score statistical analysis&lt;/b&gt; on the last 24 hours of sensor data:&lt;/p&gt;
&lt;pre class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;Z = (current value - mean) / standard deviation&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Readings with a Z-score above 2 are flagged as anomalies and stored in a separate anomaly-alerts DynamoDB table. This simulates the kind of predictive maintenance logic used in real factory monitoring systems &amp;mdash; detecting abnormal equipment behaviour before it becomes a failure.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Test result from the Lambda console:&lt;/p&gt;
&lt;pre class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;{
  &quot;severity&quot;: &quot;NORMAL&quot;,
  &quot;temp_z_score&quot;: 0.3,
  &quot;hum_z_score&quot;: 0.84,
  &quot;anomalies&quot;: []
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Part 5: OPC UA Industrial Protocol Layer&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;To make the project genuinely industrial-grade, I added an &lt;b&gt;OPC UA&lt;/b&gt; layer. OPC UA is the standard communication protocol used in smart factories &amp;mdash; it structures data in a factory hierarchy following ISA-95:&lt;/p&gt;
&lt;pre class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;Factory_Seoul &amp;rarr; ProductionLine_A1 &amp;rarr; Equipment_EnvMonitor_01 &amp;rarr; Sensors&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Two scripts run on the Pi:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;opcua_server.py: &lt;/b&gt;reads the DHT22 and exposes data as a structured OPC UA object tree with equipment metadata and status logic (RUNNING / WARNING / ALARM)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;opcua_client.py: &lt;/b&gt;navigates the hierarchy, reads sensor values, and forwards them to AWS IoT Core via MQTT with an enriched industrial payload including equipment_id, location, and protocol: OPC-UA&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;This is the same edge gateway pattern used in real manufacturing environments for connecting field devices to cloud infrastructure.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Final Architecture&lt;/h2&gt;
&lt;pre class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;DHT22 Sensor
     &amp;darr;
OPC UA Server (ISA-95 hierarchy)
     &amp;darr; opc.tcp
OPC UA Client (protocol translation)
     &amp;darr; MQTT/TLS + X.509
AWS IoT Core &amp;rarr; DynamoDB
     &amp;darr; EventBridge (every 5 min)
Lambda (Z-score anomaly detection)
     &amp;darr;
anomaly-alerts DynamoDB table

All infrastructure &amp;rarr; Terraform &amp;rarr; GitHub&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Key Takeaways&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Use &lt;b&gt;DynamoDBv2&lt;/b&gt; (not DynamoDB) in IoT Rules Engine for clean separate columns&lt;/li&gt;
&lt;li&gt;Always run terraform plan before terraform apply to preview changes safely&lt;/li&gt;
&lt;li&gt;OPC UA with ISA-95 hierarchy transforms a basic sensor project into an industrial-grade platform&lt;/li&gt;
&lt;li&gt;Z-score anomaly detection requires no ML framework: simple statistics are effective for time-series sensor data&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Full source code:&lt;/b&gt; &lt;a href=&quot;https://github.com/h3566652/Edge-Cloud-Gateway-Project&quot;&gt;github.com/h3566652/Edge-Cloud-Gateway-Project&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Kyoungmin Park | &lt;a href=&quot;https://www.linkedin.com/in/kyoung-min-park-7801a3260/&quot;&gt;LinkedIn&lt;/a&gt;&lt;/p&gt;</description>
      <category>AWS</category>
      <category>dynamodb</category>
      <category>Iot</category>
      <category>IOT 프로젝트</category>
      <category>lambda</category>
      <category>OPC UA</category>
      <category>Smart Factory</category>
      <category>terraform</category>
      <category>스마트팩토리</category>
      <category>클라우드</category>
      <author>kyoungminp00</author>
      <guid isPermaLink="true">https://kyoungminp00.tistory.com/3</guid>
      <comments>https://kyoungminp00.tistory.com/3#entry3comment</comments>
      <pubDate>Fri, 13 Mar 2026 00:30:31 +0900</pubDate>
    </item>
    <item>
      <title>OT-IT convergence: IoT 프로젝트 (1)</title>
      <link>https://kyoungminp00.tistory.com/2</link>
      <description>&lt;h1&gt;Building a Real-Time IoT Pipeline: [Raspberry Pi + DHT22] + [AWS IoT Core + DynamoDB]&lt;/h1&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Introduction&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;This post documents my end-to-end journey building a real-time IoT data pipeline from scratch.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;From my prior post, I stated that I will start a side project with a topic related to OT-IT convergence, and utilizing cloud features.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;I bought the hardware (Raspberry Pi), bought sensors for collecting live data(DHT22), and permanently stored the data in AWS cloud. If you're interested in IoT, cloud engineering, or just want a hands-on AWS project for your portfolio, this is a great starting point for gaining experience and learning new skills.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Sharing my current result: a Raspberry Pi 5 reads temperature and humidity from a DHT22 sensor every 60 seconds, sends the data to AWS IoT Core over a secure MQTT/TLS connection, and stores it in DynamoDB automatically. The automation was done by Python scripts.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Part 1: Buying the Hardware&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;The first step was simple. I ordered from &quot;Device Mart (디바이스마트, &lt;a href=&quot;https://www.devicemart.co.kr/main/index&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.devicemart.co.kr/main/index&lt;/a&gt;)&quot;:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Raspberry Pi 5&lt;/b&gt; (8GB)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;DHT22 temperature &amp;amp; humidity sensor (AM2302)&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;What I didn't anticipate was that these two components alone weren't enough. When the parts arrived, I quickly realized that I needed additional electronics to actually wire everything together.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;What I ended up buying additionally:&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Breadboard:&lt;/b&gt;&amp;nbsp;for prototyping without soldering&lt;/li&gt;
&lt;li&gt;&lt;b&gt;10k&amp;Omega; resistor: &lt;/b&gt;required as a pull-up resistor for the DHT22 data line&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Male-to-Female (M-F) jumper wires:&lt;/b&gt;&amp;nbsp;to connect the Pi's GPIO pins to the breadboard&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Female-to-Female (F-F) jumper wires:&amp;nbsp;&lt;/b&gt;just in case of wire length issues&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;These were all easily bought from Coupang(쿠팡) and arrived quickly.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;I think what I made is a common beginner mistake. One should always check what supporting components a sensor needs before ordering. However, the decision and arrival of the electronics were fast, for which I can conclude the response solution was quick and adequate.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Part 2: Wiring the DHT22 to the Raspberry Pi&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;The DHT22 I bought was the &lt;b&gt;bare 4-pin version&lt;/b&gt; (not the breakout board), which meant I needed the 10k&amp;Omega; pull-up resistor. The resistor ensures the data line properly returns to a HIGH state between readings. Without it, the readings become unreliable or fail entirely.&lt;br /&gt;&lt;br /&gt;When using the 3-pin DHT22 sensor with the breakout board, it already has the resistor built in which doesn't require any additional resistors just like this step.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Here's the wiring I used, referencing the Raspberry Pi GPIO datasheet:&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;IMG_2489 2.jpg&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;3395&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/MqE9k/dJMcahp2T44/I66evF3hA3qWPk6LUR9beK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/MqE9k/dJMcahp2T44/I66evF3hA3qWPk6LUR9beK/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/MqE9k/dJMcahp2T44/I66evF3hA3qWPk6LUR9beK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FMqE9k%2FdJMcahp2T44%2FI66evF3hA3qWPk6LUR9beK%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;471&quot; height=&quot;529&quot; data-filename=&quot;IMG_2489 2.jpg&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;3395&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Raspberry Pi 5 --- DHT22&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;3.3V --- Pin 1 (VCC)&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;3.3V --[10k ohm] -- Pin 2 (DATA)&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;GPI04 --- Pin 2 (DATA)&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;---- Pin 3 (NC - not connected)&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;GND --- Pin 4 (GND - ground)&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;IMG_3083.jpg&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/drY0AO/dJMcafsc7Z4/HcDGkuPhkhs1qHBqwS5KpK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/drY0AO/dJMcafsc7Z4/HcDGkuPhkhs1qHBqwS5KpK/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/drY0AO/dJMcafsc7Z4/HcDGkuPhkhs1qHBqwS5KpK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdrY0AO%2FdJMcafsc7Z4%2FHcDGkuPhkhs1qHBqwS5KpK%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;463&quot; height=&quot;617&quot; data-filename=&quot;IMG_3083.jpg&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Part 3: Connecting to the Raspberry Pi via SSH&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;[I received assistance from Claude for coding, downloading adequate patches/extensions and writing python scripts]&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Rather than connecting a monitor and keyboard to the Pi(for which I have to buy additionally), I accessed it remotely from my Mac using SSH.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;First, I used the Raspberry Pi Imager software for configuring the rPi, downloaded the OS to a micro SD card, and then installed the SD card into the rPi5 for a start.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Screenshot 2026-03-06 at 4.22.47 PM.png&quot; data-origin-width=&quot;1354&quot; data-origin-height=&quot;960&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bEKuCT/dJMb996B46U/uCkCfwDevy37uKpPEXxYg1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bEKuCT/dJMb996B46U/uCkCfwDevy37uKpPEXxYg1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bEKuCT/dJMb996B46U/uCkCfwDevy37uKpPEXxYg1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbEKuCT%2FdJMb996B46U%2FuCkCfwDevy37uKpPEXxYg1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1354&quot; height=&quot;960&quot; data-filename=&quot;Screenshot 2026-03-06 at 4.22.47 PM.png&quot; data-origin-width=&quot;1354&quot; data-origin-height=&quot;960&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Next, I found the Pi's hostname(which I set up), then connected from my Mac terminal:&lt;/p&gt;
&lt;pre class=&quot;autoit&quot;&gt;&lt;code&gt;ssh kpark30525@kmpark.local
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Once connected, the terminal prompt changed to confirm I was inside the Pi:&lt;/p&gt;
&lt;pre class=&quot;elixir&quot;&gt;&lt;code&gt;kpark30525@kmpark:~ $
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;From here, all commands run on the Pi remotely.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Part 4: Setting Up the Python Virtual Environment&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Before writing any code, I created a &lt;b&gt;Python virtual environment(venv)&lt;/b&gt;&amp;nbsp;inside the project folder:&lt;/p&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;mkdir ~/iot-project
cd ~/iot-project
python3 -m venv venv
source venv/bin/activate
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Why use a virtual environment?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;The Raspberry Pi runs Raspberry Pi OS, which has its own system Python with pre-installed packages. If new packages are installed directly, there is a risk of version conflicts that can break OS-level tools.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;A virtual environment creates an isolated Python installation just for the project. Packages installed inside it don't affect anything else on the system. It also makes your project reproducible; anyone can clone your repo and recreate the exact same environment.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Once activated, the terminal prompt shows (venv) to confirm it's active:&lt;/p&gt;
&lt;pre class=&quot;elixir&quot;&gt;&lt;code&gt;(venv) kpark30525@kmpark:~/iot-project $
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Important:&lt;/b&gt; The venv resets every time you SSH into the Pi. Always run source ~/iot-project/venv/bin/activate first before working on the project.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Installing the required libraries:&lt;/h3&gt;
&lt;pre class=&quot;cmake&quot;&gt;&lt;code&gt;pip install adafruit-circuitpython-dht
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Part 5: Writing and Testing the Sensor Script&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;I created a simple Python script test_sensor.py to verify the sensor was wired correctly and returning valid readings:&lt;/p&gt;
&lt;pre class=&quot;xl&quot;&gt;&lt;code&gt;import board
import adafruit_dht
import time

dhtDevice = adafruit_dht.DHT22(board.D4)

while True:
    temperature = dhtDevice.temperature
    humidity = dhtDevice.humidity
    print(f&quot;Temperature: {temperature:.1f}&amp;deg;C  Humidity: {humidity:.1f}%&quot;)
    time.sleep(2)
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Running it:&lt;/p&gt;
&lt;pre class=&quot;vim&quot;&gt;&lt;code&gt;python3 test_sensor.py
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Output:&lt;/p&gt;
&lt;pre class=&quot;yaml&quot;&gt;&lt;code&gt;Temperature: 22.6&amp;deg;C  Humidity: 32.5%
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;The sensor was working. ✅&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Part 6: Setting Up AWS IoT Core&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;With the sensor confirmed working, the next step was connecting it to AWS. I used &lt;b&gt;AWS IoT Core&lt;/b&gt; which is AWS's service for connecting IoT devices to the cloud.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Creating an IoT Thing&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;In the AWS Console (region: ap-northeast-2 / Seoul), I navigated to:&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;IoT Core &amp;rarr; Manage &amp;rarr; All devices &amp;rarr; Things &amp;rarr; Create things&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Thing name: rpi-dht22-seoul-001&lt;/li&gt;
&lt;li&gt;Certificate: Auto-generated&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Creating an IoT Policy&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;The policy defines what the device is allowed to do. I used a least-privilege policy, which is only granting exactly what's needed:&lt;/p&gt;
&lt;pre class=&quot;json&quot;&gt;&lt;code&gt;{
  &quot;Version&quot;: &quot;2012-10-17&quot;,
  &quot;Statement&quot;: [
    {
      &quot;Effect&quot;: &quot;Allow&quot;,
      &quot;Action&quot;: &quot;iot:Connect&quot;,
      &quot;Resource&quot;: &quot;arn:aws:iot:ap-northeast-2:*:client/rpi-dht22-*&quot;
    },
    {
      &quot;Effect&quot;: &quot;Allow&quot;,
      &quot;Action&quot;: [&quot;iot:Publish&quot;, &quot;iot:Receive&quot;],
      &quot;Resource&quot;: &quot;arn:aws:iot:ap-northeast-2:*:topic/sensors/dht22/*&quot;
    },
    {
      &quot;Effect&quot;: &quot;Allow&quot;,
      &quot;Action&quot;: &quot;iot:Subscribe&quot;,
      &quot;Resource&quot;: &quot;arn:aws:iot:ap-northeast-2:*:topicfilter/sensors/dht22/*&quot;
    }
  ]
}
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Part 7: Downloading the X.509 Certificates&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AWS IoT Core uses &lt;b&gt;mutual TLS authentication&lt;/b&gt; &amp;mdash; both the device and AWS verify each other's identity using X.509 certificates. During Thing creation, AWS generated and offered four files to download:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;xxxx-certificate.pem.crt &amp;mdash; Device certificate (identity)&lt;/li&gt;
&lt;li&gt;xxxx-private.pem.key &amp;mdash; Private key (never share this)&lt;/li&gt;
&lt;li&gt;xxxx-public.pem.key &amp;mdash; Public key&lt;/li&gt;
&lt;li&gt;AmazonRootCA1.pem &amp;mdash; Root CA (verifies AWS's identity)&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Critical:&lt;/b&gt; The certificates are only shown once at creation time, so the user should download all of them before moving on.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;I transferred them to the Pi's certs/ folder from my Mac:&lt;/p&gt;
&lt;pre class=&quot;groovy&quot;&gt;&lt;code&gt;scp ~/Downloads/*.pem.crt kpark30525@kmpark.local:/home/kpark30525/iot-project/certs/
scp ~/Downloads/*.pem.key kpark30525@kmpark.local:/home/kpark30525/iot-project/certs/
scp ~/Downloads/AmazonRootCA1.pem kpark30525@kmpark.local:/home/kpark30525/iot-project/certs/
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Part 8: Getting the AWS Endpoint&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;The endpoint is the AWS IoT Core address your device connects to. I couldn't find it in the Settings UI (AWS had recently moved it), so I retrieved it via the AWS CLI instead:&lt;/p&gt;
&lt;pre class=&quot;stata&quot;&gt;&lt;code&gt;aws iot describe-endpoint --endpoint-type iot:Data-ATS --region ap-northeast-2
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Output:&lt;/p&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;a1x71u4jsfn1px-ats.iot.ap-northeast-2.amazonaws.com
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Part 9: Writing the Main Script&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;With all the pieces in place, I wrote &quot;sensor_to_aws.py&quot; to read from the DHT22 and publish to AWS IoT Core every 60 seconds:&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;import time
import json
import board
import adafruit_dht
from awscrt import mqtt
from awsiot import mqtt_connection_builder

ENDPOINT = &quot;a1x71u4jsfn1px-ats.iot.ap-northeast-2.amazonaws.com&quot;
CLIENT_ID = &quot;rpi-dht22-seoul-001&quot;
TOPIC     = &quot;sensors/dht22/data&quot;

CERT_PATH = &quot;/home/kpark30525/iot-project/certs/xxxx-certificate.pem.crt&quot;
KEY_PATH  = &quot;/home/kpark30525/iot-project/certs/xxxx-private.pem.key&quot;
CA_PATH   = &quot;/home/kpark30525/iot-project/certs/AmazonRootCA1.pem&quot;

dhtDevice = adafruit_dht.DHT22(board.D4)

def on_connection_success(connection, callback_data):
    print(&quot;Connected to AWS IoT Core!&quot;)

def on_connection_failure(connection, callback_data):
    print(f&quot;Connection failed: {callback_data.error}&quot;)

mqtt_connection = mqtt_connection_builder.mtls_from_path(
    endpoint=ENDPOINT,
    cert_filepath=CERT_PATH,
    pri_key_filepath=KEY_PATH,
    ca_filepath=CA_PATH,
    client_id=CLIENT_ID,
    clean_session=False,
    keep_alive_secs=30,
    on_connection_success=on_connection_success,
    on_connection_failure=on_connection_failure
)

print(&quot;Connecting to AWS IoT Core...&quot;)
connect_future = mqtt_connection.connect()
connect_future.result()

try:
    while True:
        try:
            temperature = dhtDevice.temperature
            humidity    = dhtDevice.humidity
            message = {
                &quot;device_id&quot;:   CLIENT_ID,
                &quot;timestamp&quot;:   int(time.time()),
                &quot;temperature&quot;: round(temperature, 2),
                &quot;humidity&quot;:    round(humidity, 2)
            }
            mqtt_connection.publish(
                topic=TOPIC,
                payload=json.dumps(message),
                qos=mqtt.QoS.AT_LEAST_ONCE
            )
            print(f&quot;Published: {message}&quot;)
        except RuntimeError as e:
            print(f&quot;Sensor error: {e}&quot;)
        time.sleep(60)

except KeyboardInterrupt:
    print(&quot;Stopping...&quot;)
finally:
    mqtt_connection.disconnect()
    dhtDevice.exit()
    print(&quot;Disconnected.&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;How MQTT/TLS works&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;The connection uses &lt;b&gt;MQTT over TLS on port 8883&lt;/b&gt;. I didn't have to configure the port manually, but the &quot;mtls_from_path()&quot; function from the AWS IoT SDK handles it automatically using the certificates. What enables the secure connection is the combination of:&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;X.509 certificates&lt;/b&gt; &amp;mdash; prove the Pi's identity to AWS&lt;/li&gt;
&lt;li&gt;&lt;b&gt;IoT Policy&lt;/b&gt; &amp;mdash; grant the device permission to connect and publish&lt;/li&gt;
&lt;li&gt;&lt;b&gt;mtls_from_path()&lt;/b&gt; &amp;mdash; tells the SDK to use mutual TLS authentication&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Testing it&lt;/h3&gt;
&lt;pre class=&quot;vim&quot;&gt;&lt;code&gt;python3 scripts/sensor_to_aws.py
&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;lasso&quot;&gt;&lt;code&gt;Connecting to AWS IoT Core...
Connected to AWS IoT Core!
Published: {'device_id': 'rpi-dht22-seoul-001', 'timestamp': 1772545137, 'temperature': 22.6, 'humidity': 32.5}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;I verified the data was arriving in AWS using the &lt;b&gt;MQTT test client&lt;/b&gt; (IoT Core &amp;rarr; Test &amp;rarr; MQTT test client), subscribing to sensors/dht22/data. Messages appeared in real time. ✅&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Part 10: Storing Data in DynamoDB&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Seeing data in the MQTT test client is great, but it disappears when the browser is closed. The next step was &lt;b&gt;permanently storing every reading&lt;/b&gt; using DynamoDB.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Creating the DynamoDB Table&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Table name: dht22-sensor-data&lt;/li&gt;
&lt;li&gt;Partition key: device_id (String)&lt;/li&gt;
&lt;li&gt;Sort key: timestamp (Number)&lt;/li&gt;
&lt;li&gt;Capacity mode: &lt;b&gt;On-demand&lt;/b&gt; (free tier friendly)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Setting Up the IoT Rules Engine&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;The Rules Engine acts as the bridge between IoT Core and DynamoDB. Every time the Pi publishes a message, the rule automatically triggers and writes it to the database.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;I navigated to &lt;b&gt;IoT Core &amp;rarr; Message routing &amp;rarr; Rules &amp;rarr; Create rule&lt;/b&gt;:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Rule name: dht22_to_dynamodb&lt;/li&gt;
&lt;li&gt;SQL statement:&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;SELECT device_id, timestamp, temperature, humidity FROM 'sensors/dht22/data'
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Troubleshooting: DynamoDB vs DynamoDBv2&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;This is where I hit my first real issue. After setting up the rule using the &lt;b&gt;&quot;DynamoDB&quot;&lt;/b&gt; action type, data was being saved, but everything was bundled into a single payload column instead of clean separate columns:&lt;/p&gt;
&lt;pre class=&quot;1c&quot;&gt;&lt;code&gt;device_id          | timestamp  | payload
rpi-dht22-seoul-001 | 1772547541 | {&quot;temperature&quot;: {&quot;N&quot;: &quot;22.7&quot;}, &quot;humidity&quot;: {&quot;N&quot;: &quot;32.3&quot;}, ...}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;I tried clearing the &quot;Write message data to this column&quot; field in the DynamoDB action settings, but the payload column kept appearing.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;After thought process, I found the problem was the action type.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;DynamoDB&lt;/b&gt; (v1) &amp;mdash; always bundles the message into a payload column&lt;/li&gt;
&lt;li&gt;&lt;b&gt;DynamoDBv2&lt;/b&gt; &amp;mdash; writes each JSON field as a separate, clean column ✅&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;I removed the original action, added a new &lt;b&gt;DynamoDBv2&lt;/b&gt; action instead, and re-tested. The result was exactly what I wanted:&lt;/p&gt;
&lt;pre class=&quot;coq&quot;&gt;&lt;code&gt;device_id            | timestamp   | humidity | temperature
rpi-dht22-seoul-001  | 1772548058  | 32.1     | 22.8
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;The data is much more clear and structured.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Screenshot 2026-03-06 at 4.38.24 PM.png&quot; data-origin-width=&quot;2874&quot; data-origin-height=&quot;1276&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kLphj/dJMcabcgzTf/L2r0IhyyBzQ4NFXcntC2RK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kLphj/dJMcabcgzTf/L2r0IhyyBzQ4NFXcntC2RK/img.png&quot; data-alt=&quot;DynamoDB from AWS&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kLphj/dJMcabcgzTf/L2r0IhyyBzQ4NFXcntC2RK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkLphj%2FdJMcabcgzTf%2FL2r0IhyyBzQ4NFXcntC2RK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2874&quot; height=&quot;1276&quot; data-filename=&quot;Screenshot 2026-03-06 at 4.38.24 PM.png&quot; data-origin-width=&quot;2874&quot; data-origin-height=&quot;1276&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;DynamoDB from AWS&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Final Architecture&lt;/h2&gt;
&lt;pre class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;DHT22 Sensor (GPIO4)
        &amp;darr;
Raspberry Pi 5 &amp;mdash; Python script (sensor_to_aws.py)
        &amp;darr; MQTT over TLS / port 8883 / X.509 auth
AWS IoT Core (Thing: rpi-dht22-seoul-001)
        &amp;darr; Rules Engine (DynamoDBv2 action)
DynamoDB (dht22-sensor-data)
├── device_id
├── timestamp
├── temperature
└── humidity&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;What's Next&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;The solid pipeline structure is made, so the next step is adding an &lt;b&gt;AI/anomaly detection layer &lt;/b&gt;for flagging unusual temperature or humidity readings automatically using AWS Lambda.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Moreover, I will deploy Terraform and utilize IaC. Also, I will use OPC UA and Modbus protocols, using rPi5 as an edge device.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;This project is part of my IoT + Cloud portfolio. Full source code available on GitHub.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/h3566652&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://github.com/h3566652&lt;/a&gt;&lt;/p&gt;</description>
      <category>[Project] On premise-Edge-Cloud Bridge</category>
      <category>AWS</category>
      <category>Cloud</category>
      <category>DHT22</category>
      <category>dynamodb</category>
      <category>Iot</category>
      <category>Mqtt</category>
      <category>python</category>
      <category>Raspberry Pi</category>
      <category>사이드 프로젝트</category>
      <author>kyoungminp00</author>
      <guid isPermaLink="true">https://kyoungminp00.tistory.com/2</guid>
      <comments>https://kyoungminp00.tistory.com/2#entry2comment</comments>
      <pubDate>Fri, 6 Mar 2026 16:43:11 +0900</pubDate>
    </item>
    <item>
      <title>[IoT 프로젝트] #0 OT to IT convergence</title>
      <link>https://kyoungminp00.tistory.com/1</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;5,1,1,1,0&quot;&gt;자동제어 엔지니어가 클라우드 연결점에 도전하는 이유&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저는 현재 건물 자동제어 시스템 업계에 종사하고 있는 엔지니어입니다. 대학교 시절 당시 전기전자공학과 컴퓨터 공학을 공부하며 자연스럽게 전자 디바이스 관련된 업무에 관심을 갖게 되었고 결론적으로는 지멘스 Siemens 사의 여러가지 자동제어 제품군을 다룰 수 있는 업무를 담당하게 되었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;512&quot; data-origin-height=&quot;512&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/owoKH/dJMcacPnptf/AKmXGj1ry0CVuMy9yGvs11/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/owoKH/dJMcacPnptf/AKmXGj1ry0CVuMy9yGvs11/img.webp&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/owoKH/dJMcacPnptf/AKmXGj1ry0CVuMy9yGvs11/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FowoKH%2FdJMcacPnptf%2FAKmXGj1ry0CVuMy9yGvs11%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;172&quot; height=&quot;172&quot; data-origin-width=&quot;512&quot; data-origin-height=&quot;512&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 하는 업무는 IEC61131-3 언어를 활용한 PLC programming, Modbus 와 BACnet 등 자동제어 프로토콜들을 활용한 네트워크 관리 및 트러블 슈팅, BAS (Building Automation System)에 활용되는 여러 컨트롤러들의 I/O 포인트들을 configure하고 네트워크 어드레스 맵핑, 스케줄 관리 등 관련된 업무입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;800&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/LCN4W/dJMcafFkWcE/Mkj4czAv6PxvaKc0vXIKX1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/LCN4W/dJMcafFkWcE/Mkj4czAv6PxvaKc0vXIKX1/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/LCN4W/dJMcafFkWcE/Mkj4czAv6PxvaKc0vXIKX1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FLCN4W%2FdJMcafFkWcE%2FMkj4czAv6PxvaKc0vXIKX1%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;162&quot; height=&quot;162&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;800&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;학창시절 때부터 디지털 전환 digital transformation 에 대해서 관심이 많았습니다. IoT 엣지 디바이스들을 이용해서 데이터를 빠르고 효율적으로 처리하며 어디서든 접속할 수 있도록 접근성이 좋으면서도 보안에 취약하지 않은 시스템이 개발되면, IT 계열 뿐만 아닌 현재 존재하는 모든 분야의 매니지먼트 형태의 기술 패러다임 자체가 바뀔 것이라고 생각합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;거창한 아이디어와 상상력만 가지고 기술 자체에 대한 자세한 지식은 없던 상태에서 AWS Solution Architect-Associate 자격증 취득을 위해 공부하며 클라우드라는 개념을 제대로 접했고, 클라우드 생태계에 대해 자세히 알아가며 점점 매료되기 시작했습니다. 공부를 하면서 기본적인 AWS Services에 관련된 실습을 AWS Free tier로 진행했습니다. 솔루션 아키텍트라는 분야 특성상 Engineering 기술력 자체에만 집중하는 것보다 비용 관련된 cost optimization도 고려하며 아키텍쳐를 구성하는 것이 굉장히 흥미로웠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;512&quot; data-origin-height=&quot;512&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/nkIeI/dJMcahb4m6M/qs6rAep3CGnklKYbBdkJ9K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/nkIeI/dJMcahb4m6M/qs6rAep3CGnklKYbBdkJ9K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nkIeI/dJMcahb4m6M/qs6rAep3CGnklKYbBdkJ9K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FnkIeI%2FdJMcahb4m6M%2Fqs6rAep3CGnklKYbBdkJ9K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;178&quot; height=&quot;178&quot; data-origin-width=&quot;512&quot; data-origin-height=&quot;512&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;On premise -&amp;gt; Cloud&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 건물 자동제어 관련 업무는 장점과 단점이 명확하다고 생각합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본적으로 여러 센서들 및 하윗단 장비들의 기본적인 데이터를 불러모아 &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;Modbus 프로토콜 등 전용 프로토콜을 사용하여 데이터를 수집하고 자동제어 시스템을 관리하는 형태입니다. 현장에 알맞는 장비 선택 등 기초부터 탄탄하게 잘 짜여지면 안정적이지만, 기술 진입장벽이 높고 폐쇠적이며 확장 가능성이 적어서 해당 프로젝트를 담당했던 전문가 몇 명 이외에는 유지 보수도 곤란하여 현장 방문이 잦아지고 건물 인프라 확장 시 기존 시스템을 확장 시킬 수 있는 능력이 현저히 부족합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;이에 보완하고자 클라우드 서버로의 연결점을 만들고 데이터를 마이그레이션 migration 하며 '디지털 전환 (digital transformation)을 달성하면 원격으로도 손쉽게 실시간 데이터를 접하며 다양한 스케줄링 및 알람 시스템을 관리할 수 있으며, 기술적 결함이 생길때도 실시간 원격으로 처리하며 업무의 효율성 및 가동범위가 굉장히 넓어질 수 있다는 장점이 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;클라우드 마이그레이션을 하면 생길 수 있는 단점은 더욱 digitally open 되며 보안에 취약해질 수도 있다는 점이 있습니다. 그러나 이는 클라우드가 가진 가장 큰 장점이자 단점이라고 생각하는데, 이 단점을 커버할 수 있는 클라우드 보안 Cloud Security 관련된 내용도 추가적으로 다룰 예정입니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;512&quot; data-origin-height=&quot;512&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bnwghD/dJMcadgprMC/MLxmC1ynmG5Am0KGvjdrF0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bnwghD/dJMcadgprMC/MLxmC1ynmG5Am0KGvjdrF0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bnwghD/dJMcadgprMC/MLxmC1ynmG5Am0KGvjdrF0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbnwghD%2FdJMcadgprMC%2FMLxmC1ynmG5Am0KGvjdrF0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;198&quot; height=&quot;198&quot; data-origin-width=&quot;512&quot; data-origin-height=&quot;512&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;우선 AWS SAA 자격증을 취득하며 공부했던 AWS 관련, 그리고 기본적인 클라우드 인프라와 엣지 컴퓨팅 관련된 학습을 진행하면서 관련 프로젝트를 하나 진행할 생각입니다. 가장 직관적으로 현재 하는 업무에서 연결점을 지어서 주제를 정했는데, 실시간 온습도를 센서를 통해 데이터를 엣지 디바이스로 보낸 이후, 클라우드까지 연결 지어서 정리된 데이터를 모니터링을 하거나 원격으로 조정할 수 있는 프로젝트를 진행할 예정입니다.&lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 프로젝트를 통해서 엣지 디바이스와 클라우드에 관련된 기본적인 지식과 직접 다뤄보는 실무 경험, 그리고 이를 바탕으로 다양한 방면으로 IoT와 클라우드를 활용하는 방향으로 프로젝트를 확장할 생각입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>[Project] On premise-Edge-Cloud Bridge</category>
      <category>IT 프로젝트</category>
      <category>건물 자동제어</category>
      <category>사이드 프로젝트</category>
      <category>엣지 디바이스</category>
      <category>엣지 컴퓨팅</category>
      <category>자동제어</category>
      <category>클라우드</category>
      <category>클라우드 엔지니어링</category>
      <category>클라우드 인프라</category>
      <category>클라우드 컴퓨팅</category>
      <author>kyoungminp00</author>
      <guid isPermaLink="true">https://kyoungminp00.tistory.com/1</guid>
      <comments>https://kyoungminp00.tistory.com/1#entry1comment</comments>
      <pubDate>Wed, 14 Jan 2026 14:48:17 +0900</pubDate>
    </item>
  </channel>
</rss>