<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Fabio Beoni</title>
    <description>The latest articles on DEV Community by Fabio Beoni (@fabio_beoni).</description>
    <link>https://dev.to/fabio_beoni</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1987625%2Fc5929f5d-8693-466c-b4b0-ce01c80fcd7f.jpeg</url>
      <title>DEV Community: Fabio Beoni</title>
      <link>https://dev.to/fabio_beoni</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/fabio_beoni"/>
    <language>en</language>
    <item>
      <title>Part 3 - Providing workers guidance with Amazon Bedrock Knowledge-base and Gen-AI</title>
      <dc:creator>Fabio Beoni</dc:creator>
      <pubDate>Thu, 10 Oct 2024 14:29:21 +0000</pubDate>
      <link>https://dev.to/fabio_beoni/part-3-providing-workers-guidance-with-amazon-bedrock-knowledge-base-and-gen-ai-mn6</link>
      <guid>https://dev.to/fabio_beoni/part-3-providing-workers-guidance-with-amazon-bedrock-knowledge-base-and-gen-ai-mn6</guid>
      <description>&lt;p&gt;This article is part of the series &lt;a href="https://dev.to/fabio_beoni/generative-ai-driven-chatbot-for-the-factory-plant-21j6"&gt;"Generative-AI-driven Chatbot for the Factory Plant"&lt;/a&gt; implementing a P.O.C. about an environmental pollution monitoring (PM2.5/PM10) to safeguard workers in a production plant.&lt;/p&gt;

&lt;h2&gt;
  
  
  Disclaimer
&lt;/h2&gt;

&lt;p&gt;The information provided in this document is for informational purposes only and is subject to change. While efforts have been made to ensure the accuracy and reliability of the content, no guarantees are made regarding the completeness, reliability, or suitability of the information.&lt;br&gt;
The author shall not be liable for any losses, injuries, or damages arising from the use or reliance on the information contained herein.&lt;/p&gt;

&lt;p&gt;The AWS resources provisioned following this article can generate costs. Make sure to delete all the resources created at the end of the exercise.&lt;/p&gt;
&lt;h2&gt;
  
  
  Overview
&lt;/h2&gt;

&lt;p&gt;The main purpose of this series of articles is to test out Amazon Bedrock Studio (no-code editor for Gen-AI driven apps), at the time I am writing the Studio is still in preview and actually after a quick try I encountered a number of bugs that prevent me to complete this P.O.C.&lt;/p&gt;

&lt;p&gt;To get around of these issues, I decided to go ahead with P.O.C. by implementing my self the missing part, keeping a low-code approach and favoring configuration of Bedrock components over imperative programming as much as possible. I will then come back to Studio as soon as it reaches a stable release.&lt;/p&gt;

&lt;p&gt;For those of you who had a read to &lt;a href="https://dev.to/fabio_beoni/part-2-deploy-a-chat-app-bedrock-agent-and-actions-to-ask-questions-about-live-data-1i9m"&gt;Part 2 - Deploy a Chat App, Bedrock Agent and Actions to Ask Questions about Live Data&lt;/a&gt; this new article completes the implementation of the features related to the Knowledge-base by:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Configuring a Knowledge-base where a business process manager (or knowledge manager) can upload relevant documents, to make them available to the Amazon Bedrock AI Agent to answer workers questions&lt;/li&gt;
&lt;li&gt;Implementing the missing feature of document upload from the chat app&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;
  
  
  Preview of the App
&lt;/h3&gt;

&lt;p&gt;The screenshots below display a typical interaction, this time the Amazon Bedrock &lt;strong&gt;AI Agent not only answers question about live pollution data&lt;/strong&gt; (second screen, third screen top question), but &lt;strong&gt;also offers guidance&lt;/strong&gt; to the worker extracting &lt;strong&gt;relevant information from the documents uploaded&lt;/strong&gt; into the Knowledge-base (third screen, last question/answer).&lt;/p&gt;

&lt;p&gt;When you have the app running, you can upload some test documents into the knowledge-base by clicking the attachment icon. A sample document is available for you to try under &lt;code&gt;/_docs/knowledge-base/sop-reducing-particulate-matter-pm.md&lt;/code&gt;.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
  &lt;tr&gt;
    &lt;td&gt;
      &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F80s7wnoj3xuwik8itr9r.png" width="601" height="1203"&gt;
    &lt;/td&gt;
    &lt;td&gt;
      &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqt3c1w5jdis96nd3keha.png" width="662" height="1372"&gt;
    &lt;/td&gt;
    &lt;td&gt;
        &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjqrrve788wx9xawqo91r.png" width="662" height="1335"&gt;
    &lt;/td&gt;
  &lt;/tr&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;h3&gt;
  
  
  Architecture Recap
&lt;/h3&gt;

&lt;p&gt;Click to zoom.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/g704a42aovqypyu5mch6.png" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg704a42aovqypyu5mch6.png" alt="architecture-diagram" width="800" height="540"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  AWS Services / Bedrock Components
&lt;/h3&gt;
&lt;h5&gt;
  
  
  S3 Bucket
&lt;/h5&gt;

&lt;p&gt;Hosting the storage of the knowledge-base documents uploaded from process manager users.&lt;/p&gt;
&lt;h5&gt;
  
  
  Bedrock Knowledge-base
&lt;/h5&gt;

&lt;p&gt;Reading documents from the S3 Bucket describe above, it uses the LLM Titan Embeddings to generate new embeddings from the documents, stores the embeddings into the vector database AWS OpenSearch.&lt;/p&gt;

&lt;p&gt;Currently, the Bedrock Knowledge-base supports file up to 50MB, you can upload PDF, DOCX, CSV, TXT, MD file types. When configuring the knowledge-base component is mandatory to describe the kind of contents will be hosted.&lt;/p&gt;

&lt;p&gt;Such description guides the Bedrock Agent and LLM to understand when to use knowledge-base to answer questions. You can even configure more than one if needed.&lt;/p&gt;

&lt;p&gt;Here a sample included from the configuration of the P.O.C.:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# you can find this content into the repo: cloud/ai-module/prompt_templates/kb_instructions.txt&lt;/span&gt;

&lt;span class="s2"&gt;"Knowledge base to get information on how to handle excessive pollution in the factory plant, how to
perform maintenance on machines causing it, provide answers about SOP and safety procedures for workers."&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ref.: &lt;a href="https://docs.aws.amazon.com/bedrock/latest/userguide/kb-how-it-works.html" rel="noopener noreferrer"&gt;https://docs.aws.amazon.com/bedrock/latest/userguide/kb-how-it-works.html&lt;/a&gt;&lt;/p&gt;

&lt;h5&gt;
  
  
  LLM Amazon Titan Embeddings
&lt;/h5&gt;

&lt;p&gt;The Large Language Model provided by AWS to read text documents and generate vector representations (embeddings) to be processed when answering user questions.&lt;/p&gt;

&lt;p&gt;Ref.: &lt;a href="https://aws.amazon.com/bedrock/titan" rel="noopener noreferrer"&gt;https://aws.amazon.com/bedrock/titan&lt;/a&gt;&lt;/p&gt;

&lt;h5&gt;
  
  
  Bedrock Agent
&lt;/h5&gt;

&lt;p&gt;Logical coordinator of LLM, Actions and Knowledge-base.&lt;/p&gt;

&lt;p&gt;Here you can read the full agent instructions, with explicit instructions to write answers based on live pollution data and knowledge-base documents:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Specific instructions to push the LLM to select content from the knowledge-base.&lt;/span&gt;
&lt;span class="c"&gt;# (cloud/ai-module/prompt_templates/agent-instructions.txt)&lt;/span&gt;
&lt;span class="s2"&gt;"""
As part of the tools you have access to answer user questions, you have a knowledge-base tool listing relevant business documents like S.O.P., guides and other procures about maintenance of machines and safety equipments. When answering questions from the user about those topics above, make sure to prepare the answer according to the content of those documents more than information from internet-based public knowledge.
"""&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# All other LLM instructions to configure the Agent as a technical assistant&lt;/span&gt;
&lt;span class="c"&gt;# (cloud/ai-module/prompt_templates/agent-instructions.txt)&lt;/span&gt;
&lt;span class="s2"&gt;"""
Your primary role is to act as an intelligent interface between the factory's pollution monitoring system and its human operators. You should monitor the real-time PM2.5 and PM10 data collected by the factory's sensors and proactively engage with workers to ensure their safety and the optimal performance of the production equipment.

When pollution levels exceed safe thresholds, you should automatically alert the relevant personnel and provide clear, concise instructions on the appropriate steps to take.

Beyond emergency response, you should also serve as an on-demand technical advisor. You should be able to answer questions, offer recommendations, and provide detailed explanations on topics such as:

- Interpreting pollution data and understanding its implications,
- Implementing best practices for dust control and air quality management,
- Performing preventive maintenance on pollution monitoring equipment,
- Troubleshooting issues with the pollution control systems,
- Ensuring compliance with environmental regulations and safety protocols,
- To provide this information, you should be able to make API requests to the pollution data endpoint, using the plant_name as a parameter in the path. The API response will provide the necessary data points (PM10, PM25, data_time) for you to generate informative responses to the users' questions.

Seamlessly integrate with the factory's existing systems and data sources to become a trusted partner in the cement production process. Empower workers to make informed decisions, optimize equipment performance, and maintain a safe and sustainable work environment through your natural, human-like interactions.

If you have multiple pollution measures, select the most recent one.

If you don't get any pollution value when invoking the API to get PM values by plant name, it means that there are no recent values because the monitoring is not active or there was a problem in reading data from the monitoring system. Never ask the user to provide the pollution data, he doesn't know that, while the user knows the name of the plant. Also in such case be short in the answer, just recall the fact that there are not fresh data as stated above.

Format the timestamp in a human-readable date/time, also specify the time zone. When presenting the PM values display them in a list format. Don't suggest actions or remediation's if not requested, but always say if the measured values are under safe limits or not. Also say what are the safe limits, between brackets.

Always ask the user the plant name if you haven't that to invoke the API.

Don't use the word 'timestamp', use a less technical one. Avoid to display the timestamp value in numeric format.

Always end your answer saying that you can provide information about actions to take and security or maintenance measures.

Split paragraph with new lines, so the answer is more readable.
"""&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ref.: &lt;a href="https://docs.aws.amazon.com/bedrock/latest/userguide/agents-how.html" rel="noopener noreferrer"&gt;https://docs.aws.amazon.com/bedrock/latest/userguide/agents-how.html&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Build &amp;amp; Deploy
&lt;/h2&gt;

&lt;p&gt;Most of the deployment commands are similar to &lt;a href="https://dev.to/fabio_beoni/part-2-deploy-a-chat-app-bedrock-agent-and-actions-to-ask-questions-about-live-data-1i9m#build-and-deploy"&gt;Part 2&lt;/a&gt;, here you can find the updated version including the build of the lambda function to upload documents to the knowledge-base.&lt;/p&gt;

&lt;h4&gt;
  
  
  Repository (version 3)
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://bitbucket.org/fabiobeoni/bedrock-studio-chatbot/src/v3/" rel="noopener noreferrer"&gt;https://bitbucket.org/fabiobeoni/bedrock-studio-chatbot/src/v3/&lt;/a&gt; (note &lt;strong&gt;v3&lt;/strong&gt;)&lt;/p&gt;

&lt;h4&gt;
  
  
  Setup / Requirements
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;In &lt;a href="https://us-east-1.console.aws.amazon.com/bedrock/home#models" rel="noopener noreferrer"&gt;AWS Console / Amazon Bedrock / Base Models&lt;/a&gt; :
you have to request access to the model &lt;code&gt;anthropic.claude-3-sonnet-20240229-v1:0&lt;/code&gt;. You can use a different one, in that case
remember to override the default variable &lt;code&gt;llm_id&lt;/code&gt; in &lt;code&gt;./variables.tf&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;AWS CLI&lt;/li&gt;
&lt;li&gt;Terraform&lt;/li&gt;
&lt;li&gt;NodeJS&lt;/li&gt;
&lt;li&gt;OpenSSL&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Build Lambda Functions
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&amp;gt;&amp;gt; Build Lambda Functions"&lt;/span&gt;

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&amp;gt;&amp;gt; AI module"&lt;/span&gt;
&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; &lt;span class="s2"&gt;"cloud/ai-module/functions"&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; /bin/bash package.sh query-pm-values-fnc&lt;span class="o"&gt;)&lt;/span&gt;

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&amp;gt;&amp;gt; Chat App module"&lt;/span&gt;
&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; &lt;span class="s2"&gt;"cloud/chat-app-module/functions"&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; /bin/bash package.sh chat-write-user-message-fnc&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; &lt;span class="s2"&gt;"cloud/chat-app-module/functions"&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; /bin/bash package.sh chat-read-user-messages-fnc&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; &lt;span class="s2"&gt;"cloud/chat-app-module/functions"&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; /bin/bash package.sh upload-kb-document-fnc&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; &lt;span class="s2"&gt;"cloud/chat-app-module/functions"&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; /bin/bash package.sh ask-bot-fnc&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Create and Register the Device Certificate
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'&amp;gt;&amp;gt; Provisioning self signed certificate'&lt;/span&gt;

&lt;span class="c"&gt;# set a variable to host the name&lt;/span&gt;
&lt;span class="nv"&gt;thing_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"gateway_pm_monitor"&lt;/span&gt;

&lt;span class="c"&gt;# create certificate and key pairs, &lt;/span&gt;
&lt;span class="c"&gt;# assign the $thing_name as subject &lt;/span&gt;
openssl req &lt;span class="nt"&gt;-x509&lt;/span&gt;  &lt;span class="nt"&gt;-newkey&lt;/span&gt; rsa:2048 &lt;span class="nt"&gt;-sha256&lt;/span&gt;  &lt;span class="nt"&gt;-days&lt;/span&gt;  365  &lt;span class="nt"&gt;-nodes&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-keyout&lt;/span&gt; edge/pm-monitor-service/gateway.private_key.pem &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-out&lt;/span&gt; edge/pm-monitor-service/gateway.certificate.pem &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-subj&lt;/span&gt; &lt;span class="s2"&gt;"/CN=&lt;/span&gt;&lt;span class="nv"&gt;$thing_name&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

 &lt;span class="c"&gt;#download AWS CA&lt;/span&gt;
 curl &lt;span class="nt"&gt;-O&lt;/span&gt; https://www.amazontrust.com/repository/AmazonRootCA1.pem
 &lt;span class="nb"&gt;mv &lt;/span&gt;AmazonRootCA1.pem edge/pm-monitor-service
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Deploy Cloud Resources to AWS
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&amp;gt;&amp;gt; Deploy cloud infra"&lt;/span&gt;
terraform init
terraform plan &lt;span class="nt"&gt;-out&lt;/span&gt; plan
terraform apply plan

&lt;span class="c"&gt;# save cloud endpoints and IDs to the edge/chat-app to instruct the chat app&lt;/span&gt;
terraform output &lt;span class="nt"&gt;-json&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; edge/chat-app/src/config.json

&lt;span class="c"&gt;# save iot endpoint and cognito client ID to the edge/pm-monitor-service to instruct the edge device&lt;/span&gt;
terraform output &lt;span class="nt"&gt;-json&lt;/span&gt;  | jq &lt;span class="s1"&gt;'{cognito_client_id: .cognito_client_id, iot_endpoint_address: .iot_endpoint_address}'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; edge/pm-monitor-service/cloud_config.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Build Chat App
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&amp;gt;&amp;gt; Build chat app"&lt;/span&gt;
&lt;span class="nv"&gt;HOSTING_URL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;terraform output &lt;span class="nt"&gt;-raw&lt;/span&gt; hosting_url&lt;span class="si"&gt;)&lt;/span&gt;
npm &lt;span class="nt"&gt;--prefix&lt;/span&gt; edge/chat-app/ run setup
npm &lt;span class="nt"&gt;--prefix&lt;/span&gt; edge/chat-app/ run build
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Deploy Chat App to Surge.sh CDN or Run in localhost:3000
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&amp;gt;&amp;gt; Deploying the chat app to the CDN: &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;HOSTING_URL&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

&lt;span class="c"&gt;# Note: the first time Surge asks you to provide an email/pw to register an account &lt;/span&gt;
&lt;span class="nv"&gt;HOSTING_URL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;HOSTING_URL&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; npm &lt;span class="nt"&gt;--prefix&lt;/span&gt; edge/chat-app/ run deploy

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&amp;gt;&amp;gt; You can now open the app to: &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;HOSTING_URL&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; and login with user/pw provided to Terraform/Cognito"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; &lt;strong&gt;for free accounts sometimes Surge.sh websites do not respond&lt;/strong&gt;, in that case you can &lt;strong&gt;test the chat-app in &lt;a href="http://localhost:3000" rel="noopener noreferrer"&gt;http://localhost:3000&lt;/a&gt;&lt;/strong&gt;, by:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# terminal window 1&lt;/span&gt;
&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;edge/chat-app &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; npm run dev&lt;span class="o"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# terminal window 2&lt;/span&gt;
&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;edge/chat-app &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; npm run serve&lt;span class="o"&gt;)&lt;/span&gt;

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&amp;gt;&amp;gt; finally open browser to http://localhost:3000 and login with user/pw provided to Terraform/Cognito"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As stated in the terminal, you can now open the app in the browser to &lt;code&gt;${HOSTING_URL}&lt;/code&gt; and login in Cognito with email/pw provided to Terraform.&lt;/p&gt;

&lt;h4&gt;
  
  
  Run PM Monitor Service (emulated locally)
&lt;/h4&gt;

&lt;p&gt;Some sample pm values are available already into the DynamoDB, just start this service if you want to test the overall process with live data from IoT Core.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&amp;gt;&amp;gt; Running the pm monitor service emulating the edge device"&lt;/span&gt;

npm &lt;span class="nt"&gt;--prefix&lt;/span&gt; edge/pm-monitor-service/ &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--omit&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;dev
npm &lt;span class="nt"&gt;--prefix&lt;/span&gt; edge/pm-monitor-service/ run start
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Clean Up Cloud Resources
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;terraform destroy &lt;span class="nt"&gt;-auto-approve&lt;/span&gt;

surge list
surge teardown &amp;lt;output_from_list&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Previous
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dev.to/fabio_beoni/generative-ai-driven-chatbot-for-the-factory-plant-21j6"&gt;Introduction - Generative-AI-driven Chatbot for the Factory Plant&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/fabio_beoni/part-1-gathering-pm-values-from-the-sensor-pushing-and-saving-them-to-the-cloud-storage-tch-2l99"&gt;Part 1 - Gathering pollution data from the edge, pushing it to AWS IoT Core and DynamoDB&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/fabio_beoni/part-2-deploy-a-chat-app-bedrock-agent-and-actions-to-ask-questions-about-live-data-1i9m"&gt;Part 2 - Deploy a Chat App, Bedrock Agent and Actions to Ask Questions about Live Data&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
    </item>
    <item>
      <title>Part 2 - Deploy a Chat App, Bedrock Agent and Actions to Ask Questions about Live Data</title>
      <dc:creator>Fabio Beoni</dc:creator>
      <pubDate>Sun, 29 Sep 2024 17:05:13 +0000</pubDate>
      <link>https://dev.to/fabio_beoni/part-2-deploy-a-chat-app-bedrock-agent-and-actions-to-ask-questions-about-live-data-1i9m</link>
      <guid>https://dev.to/fabio_beoni/part-2-deploy-a-chat-app-bedrock-agent-and-actions-to-ask-questions-about-live-data-1i9m</guid>
      <description>&lt;p&gt;This article is part of the series &lt;a href="https://dev.to/fabio_beoni/generative-ai-driven-chatbot-for-the-factory-plant-21j6"&gt;"Generative-AI-driven Chatbot for the Factory Plant"&lt;/a&gt; implementing a P.O.C. about an environmental pollution monitoring (PM2.5/PM10) to safeguard workers in a production plant.&lt;/p&gt;

&lt;h2&gt;
  
  
  Disclaimer
&lt;/h2&gt;

&lt;p&gt;The information provided in this document is for informational purposes only and is subject to change. While efforts have been made to ensure the accuracy and reliability of the content, no guarantees are made regarding the completeness, reliability, or suitability of the information.&lt;br&gt;
The author shall not be liable for any losses, injuries, or damages arising from the use or reliance on the information contained herein.&lt;/p&gt;

&lt;p&gt;The AWS resources provisioned following this article can generate costs. Make sure to delete all the resources created at the end of the exercise.&lt;/p&gt;
&lt;h2&gt;
  
  
  Overview
&lt;/h2&gt;

&lt;p&gt;The main purpose of this series of articles is to test out Amazon Bedrock Studio (no-code editor for Gen-AI driven apps), at the time I am writing the Studio is still in preview and actually after a quick try I encountered a number of bugs that prevent me to complete this P.O.C.&lt;/p&gt;

&lt;p&gt;To get around of these issues, I decided to go ahead with P.O.C. by implementing my self the missing part, keeping a low-code approach and favoring configuration of Bedrock components over imperative programming as much as possible. I will then come back to Studio as soon as it reaches a stable release.&lt;/p&gt;

&lt;p&gt;For those of you who had a read to "&lt;a href="https://dev.to/fabio_beoni/part-1-gathering-pm-values-from-the-sensor-pushing-and-saving-them-to-the-cloud-storage-tch-2l99"&gt;Part 1 - Gathering pollution data from the edge, pushing them to AWS IoT Core and DynamoDB&lt;/a&gt;", this new article adopts Terraform to provision all the resources, including those you have created before (IoT Core, Certificates, IoT Rules, DynamoDB Table, etc...).&lt;/p&gt;

&lt;p&gt;The tutorial also runs a simulation of the IoT edge device (RaspberryPI+Nova Laser Sensor), so you can start from scratch without any physical hardware.&lt;/p&gt;
&lt;h3&gt;
  
  
  Preview of the App
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg8zrkkwrguhnt5yuyvg9.png" width="300" height="593"&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv4l76dgy9tc7bo36eaaf.png" width="300" height="593"&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;h3&gt;
  
  
  Bedrock Components
&lt;/h3&gt;
&lt;h5&gt;
  
  
  LLM Claude 3 Sonnet
&lt;/h5&gt;

&lt;p&gt;Gen-AI layer carrying out:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;user question understanding, semantic meaning, intent recognition and parameters extraction (if any),&lt;/li&gt;
&lt;li&gt;understanding of sensor data domain as well as data specific pollution values and metadata,&lt;/li&gt;
&lt;li&gt;generation a contextual answer including a selection of most relevant data, according to the business rules provided to the LLM instructions.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Ref.: &lt;a href="https://docs.aws.amazon.com/bedrock/latest/userguide/model-ids.html" rel="noopener noreferrer"&gt;https://docs.aws.amazon.com/bedrock/latest/userguide/model-ids.html&lt;/a&gt;&lt;/p&gt;
&lt;h5&gt;
  
  
  Bedrock Actions Group
&lt;/h5&gt;

&lt;p&gt;Layer providing to the LLM a text description of "actions" available to the Agent to carry out work, by invoking lambda functions. In this use-case the Agent can invoke the action &lt;code&gt;query-pm-values&lt;/code&gt; to get near real-time pollution data from the sensor.&lt;/p&gt;

&lt;p&gt;Ref.: &lt;a href="https://docs.aws.amazon.com/bedrock/latest/userguide/agents-action-create.html" rel="noopener noreferrer"&gt;https://docs.aws.amazon.com/bedrock/latest/userguide/agents-action-create.html&lt;/a&gt;&lt;/p&gt;
&lt;h5&gt;
  
  
  Lambda Functions
&lt;/h5&gt;

&lt;p&gt;Business logic layer implementing read/write of user/agent chat messages, as well as reading of pollution data.&lt;/p&gt;
&lt;h5&gt;
  
  
  Bedrock Knowledge-base
&lt;/h5&gt;

&lt;p&gt;Technical and procedural knowledge layer to further instruct workers with company's specific know-how. Included later, on the next article.&lt;br&gt;
Ref.: &lt;a href="https://docs.aws.amazon.com/bedrock/latest/userguide/kb-how-it-works.html" rel="noopener noreferrer"&gt;https://docs.aws.amazon.com/bedrock/latest/userguide/kb-how-it-works.html&lt;/a&gt;&lt;/p&gt;
&lt;h5&gt;
  
  
  Bedrock Agent
&lt;/h5&gt;

&lt;p&gt;Logical coordinator of LLM, Actions and Knowledge-base. It is the "glue" making Bedrock a smart tool to build Gen-AI services.&lt;br&gt;
Ref.: &lt;a href="https://docs.aws.amazon.com/bedrock/latest/userguide/agents-how.html" rel="noopener noreferrer"&gt;https://docs.aws.amazon.com/bedrock/latest/userguide/agents-how.html&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;An overview of how Agent/LLM/Actions work together:&lt;/p&gt;

&lt;p&gt;Ref.: &lt;a href="https://docs.aws.amazon.com/images/bedrock/latest/userguide/images/agents/agents-runtime.png" rel="noopener noreferrer"&gt;https://docs.aws.amazon.com/images/bedrock/latest/userguide/images/agents/agents-runtime.png&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Source image: AWS Documentation&lt;/p&gt;
&lt;h3&gt;
  
  
  Other Cloud Resources Included
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;AWS&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Cognito (IDP, user authentication, login UI)&lt;/li&gt;
&lt;li&gt;API Gateway (http interface layer chat-app/services)&lt;/li&gt;
&lt;li&gt;DynamoDB (db data storage of chat messages and pollution data)&lt;/li&gt;
&lt;li&gt;OpenSearch Serverless (knowledge-base documents, from the next article)&lt;/li&gt;
&lt;li&gt;IoT Core components (to interact and gather data from the edge device)&lt;/li&gt;
&lt;li&gt;IAM roles/policies&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Surge.sh&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;CDN domain (to host the chat-app UI)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;
  
  
  Architecture Recap
&lt;/h3&gt;

&lt;p&gt;Click to zoom.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/g704a42aovqypyu5mch6.png" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg704a42aovqypyu5mch6.png" alt="architecture-diagram" width="800" height="540"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; this diagram shows the components of the Knowledge-base module that is still coming soon.&lt;/p&gt;
&lt;h2&gt;
  
  
  Project Structure
&lt;/h2&gt;

&lt;p&gt;Source code repo: &lt;a href="https://bitbucket.org/fabiobeoni/bedrock-studio-chatbot/src/v2/" rel="noopener noreferrer"&gt;https://bitbucket.org/fabiobeoni/bedrock-studio-chatbot/src/v2/&lt;/a&gt; (note &lt;strong&gt;v2&lt;/strong&gt;)&lt;/p&gt;
&lt;h4&gt;
  
  
  Modules
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;/cloud&lt;/strong&gt; (Terraform/Bash/JS):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;/ai-module&lt;/strong&gt;: Amazon Bedrock components&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;/chat-app-module&lt;/strong&gt;: AWS resources serving the mobile chat app ui&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;/pm-monitor-module&lt;/strong&gt;: AWS IoT Core serving the edge device&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;/edge&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;/chat-app&lt;/strong&gt; (TS/Ionic/Vue3)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;/pm-monitor-service&lt;/strong&gt; (NodeJS) Pollution reader service. This module designed to run on a RaspberryPI with Nova PM Laser sensor attached, by default runs locally emulating the IoT device (&lt;code&gt;./device_config.json  mockSensor=true&lt;/code&gt;).&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Build &amp;amp; Deploy
&lt;/h2&gt;
&lt;h4&gt;
  
  
  Repository (version 2)
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://bitbucket.org/fabiobeoni/bedrock-studio-chatbot/src/v2/" rel="noopener noreferrer"&gt;https://bitbucket.org/fabiobeoni/bedrock-studio-chatbot/src/v2/&lt;/a&gt; (note &lt;strong&gt;v2&lt;/strong&gt;)&lt;/p&gt;
&lt;h4&gt;
  
  
  Setup / Requirements
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;In &lt;a href="https://us-east-1.console.aws.amazon.com/bedrock/home#models" rel="noopener noreferrer"&gt;AWS Console / Amazon Bedrock / Base Models&lt;/a&gt; :
you have to request access to the model &lt;code&gt;anthropic.claude-3-sonnet-20240229-v1:0&lt;/code&gt;. You can use a different one, in that case
remember to override the default variable &lt;code&gt;llm_id&lt;/code&gt; in &lt;code&gt;./variables.tf&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;AWS CLI&lt;/li&gt;
&lt;li&gt;Terraform&lt;/li&gt;
&lt;li&gt;NodeJS&lt;/li&gt;
&lt;li&gt;OpenSSL&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;
  
  
  Build Lambda Functions
&lt;/h4&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&amp;gt;&amp;gt; Build Lambda Functions"&lt;/span&gt;

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&amp;gt;&amp;gt; AI module"&lt;/span&gt;
&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; &lt;span class="s2"&gt;"cloud/ai-module/functions"&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; /bin/bash package.sh query-pm-values-fnc&lt;span class="o"&gt;)&lt;/span&gt;

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&amp;gt;&amp;gt; Chat App module"&lt;/span&gt;
&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; &lt;span class="s2"&gt;"cloud/chat-app-module/functions"&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; /bin/bash package.sh chat-write-user-message-fnc&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; &lt;span class="s2"&gt;"cloud/chat-app-module/functions"&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; /bin/bash package.sh chat-read-user-messages-fnc&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; &lt;span class="s2"&gt;"cloud/chat-app-module/functions"&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; /bin/bash package.sh ask-bot&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h4&gt;
  
  
  Create and Register the Device Certificate
&lt;/h4&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'&amp;gt;&amp;gt; Provisioning self signed certificate'&lt;/span&gt;

&lt;span class="c"&gt;# set a variable to host the name&lt;/span&gt;
&lt;span class="nv"&gt;thing_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"gateway_pm_monitor"&lt;/span&gt;

&lt;span class="c"&gt;# create certificate and key pairs, &lt;/span&gt;
&lt;span class="c"&gt;# assign the $thing_name as subject &lt;/span&gt;
openssl req &lt;span class="nt"&gt;-x509&lt;/span&gt;  &lt;span class="nt"&gt;-newkey&lt;/span&gt; rsa:2048 &lt;span class="nt"&gt;-sha256&lt;/span&gt;  &lt;span class="nt"&gt;-days&lt;/span&gt;  365  &lt;span class="nt"&gt;-nodes&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-keyout&lt;/span&gt; edge/pm-monitor-service/gateway.private_key.pem &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-out&lt;/span&gt; edge/pm-monitor-service/gateway.certificate.pem &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-subj&lt;/span&gt; &lt;span class="s2"&gt;"/CN=&lt;/span&gt;&lt;span class="nv"&gt;$thing_name&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

 &lt;span class="c"&gt;#download AWS CA&lt;/span&gt;
 curl &lt;span class="nt"&gt;-O&lt;/span&gt; https://www.amazontrust.com/repository/AmazonRootCA1.pem
 &lt;span class="nb"&gt;mv &lt;/span&gt;AmazonRootCA1.pem edge/pm-monitor-service
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h4&gt;
  
  
  Deploy Cloud Resources to AWS
&lt;/h4&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&amp;gt;&amp;gt; Deploy cloud infra"&lt;/span&gt;
terraform init
terraform apply

&lt;span class="c"&gt;# save cloud endpoints and IDs to the edge/chat-app to instruct the chat app&lt;/span&gt;
terraform output &lt;span class="nt"&gt;-json&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; edge/chat-app/src/config.json

&lt;span class="c"&gt;# save iot endpoint and cognito client ID to the edge/pm-monitor-service to instruct the edge device&lt;/span&gt;
terraform output &lt;span class="nt"&gt;-json&lt;/span&gt;  | jq &lt;span class="s1"&gt;'{cognito_client_id: .cognito_client_id, iot_endpoint_address: .iot_endpoint_address}'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; edge/pm-monitor-service/cloud_config.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h4&gt;
  
  
  Build Chat App
&lt;/h4&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&amp;gt;&amp;gt; Build chat app"&lt;/span&gt;
&lt;span class="nv"&gt;HOSTING_URL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;terraform output &lt;span class="nt"&gt;-raw&lt;/span&gt; hosting_url&lt;span class="si"&gt;)&lt;/span&gt;
npm &lt;span class="nt"&gt;--prefix&lt;/span&gt; edge/chat-app/ run setup
npm &lt;span class="nt"&gt;--prefix&lt;/span&gt; edge/chat-app/ run build
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h4&gt;
  
  
  Deploy Chat App to Surge.sh CDN or Run in localhost
&lt;/h4&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&amp;gt;&amp;gt; Deploying the chat app to the CDN: &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;HOSTING_URL&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

&lt;span class="c"&gt;# Note: the first time Surge asks you to provide an email/pw to register an account &lt;/span&gt;
&lt;span class="nv"&gt;HOSTING_URL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;HOSTING_URL&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; npm &lt;span class="nt"&gt;--prefix&lt;/span&gt; edge/chat-app/ run deploy

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&amp;gt;&amp;gt; You can now open the app to: &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;HOSTING_URL&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; and login with user/pw provided to Terraform/Cognito"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; &lt;strong&gt;for free accounts sometimes Surge.sh websites do not respond&lt;/strong&gt;, in that case you can &lt;strong&gt;test the chat-app in &lt;a href="http://localhost:3000" rel="noopener noreferrer"&gt;http://localhost:3000&lt;/a&gt;&lt;/strong&gt;, by:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# terminal window 1&lt;/span&gt;
&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;edge/chat-app &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; npm run dev&lt;span class="o"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# terminal window 2&lt;/span&gt;
&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;edge/chat-app &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; npm run serve&lt;span class="o"&gt;)&lt;/span&gt;

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&amp;gt;&amp;gt; finally open browser to http://localhost:3000 and login with user/pw provided to Terraform/Cognito"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Run PM Monitor Service (emulated locally)
&lt;/h4&gt;

&lt;p&gt;Some sample pm values are available already into the DynamoDB, just start this service if you want to test the overall process with live data from IoT Core.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&amp;gt;&amp;gt; Running the pm monitor service emulating the edge device"&lt;/span&gt;

npm &lt;span class="nt"&gt;--prefix&lt;/span&gt; edge/pm-monitor-service/ &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--omit&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;dev
npm &lt;span class="nt"&gt;--prefix&lt;/span&gt; edge/pm-monitor-service/ run start
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Clean Up Cloud Resources
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;terraform destroy &lt;span class="nt"&gt;-auto-approve&lt;/span&gt;

surge list
surge teardown &amp;lt;output_from_list&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Final Notes
&lt;/h3&gt;

&lt;h5&gt;
  
  
  Steps Functions over Lambda
&lt;/h5&gt;

&lt;p&gt;Bedrock Agent's Action wants a Lambda target. To embrace a full no-code approach with configuration over programming you might want to consider using lambda to proxy all the action executions requests to AWS Step Functions. In that way you can define you business logic declaratively with YAML/JSON, or even draw them with the visual editor provided by Step Functions. Keep in mind that you will deal with sync execution and timeouts, because the Bedrock Agent does require Lambda to return a result.&lt;/p&gt;

&lt;h5&gt;
  
  
  Sensor Input Validation
&lt;/h5&gt;

&lt;p&gt;The P.O.C. does not implement any validation logic for data coming from the IoT edge device. For the purpose of a demo/P.O.C. that's not a problem, but keep in mind if you use this repo to start a new project.&lt;/p&gt;

&lt;h2&gt;
  
  
  Next
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dev.to/fabio_beoni/part-3-providing-workers-guidance-with-amazon-bedrock-knowledge-base-and-gen-ai-mn6"&gt;Part 3 - Providing workers guidance with Amazon Bedrock Knowledge-base and Gen-AI&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Previous
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dev.to/fabio_beoni/generative-ai-driven-chatbot-for-the-factory-plant-21j6"&gt;Introduction - Generative-AI-driven Chatbot for the Factory Plant&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/fabio_beoni/part-1-gathering-pm-values-from-the-sensor-pushing-and-saving-them-to-the-cloud-storage-tch-2l99"&gt;Part 1 - Gathering pollution data from the edge, pushing it to AWS IoT Core and DynamoDB&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
    </item>
    <item>
      <title>Part 1 - Gathering pollution data from the edge, pushing it to AWS IoT Core and DynamoDB</title>
      <dc:creator>Fabio Beoni</dc:creator>
      <pubDate>Mon, 02 Sep 2024 15:36:06 +0000</pubDate>
      <link>https://dev.to/fabio_beoni/part-1-gathering-pm-values-from-the-sensor-pushing-and-saving-them-to-the-cloud-storage-tch-2l99</link>
      <guid>https://dev.to/fabio_beoni/part-1-gathering-pm-values-from-the-sensor-pushing-and-saving-them-to-the-cloud-storage-tch-2l99</guid>
      <description>&lt;p&gt;This article is part of the series &lt;a href="https://dev.to/fabio_beoni/generative-ai-driven-chatbot-for-the-factory-plant-21j6"&gt;"Generative-AI-driven Chatbot for the Factory Plant"&lt;/a&gt; implementing a P.O.C. about an environmental pollution monitoring (PM2.5/PM10) to safeguard workers in a production plant.&lt;/p&gt;

&lt;p&gt;This a techie article, if you are interested only on the high level use case aspects you can skip it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Disclaimer
&lt;/h2&gt;

&lt;p&gt;The information provided in this document is for informational purposes only and is subject to change. While efforts have been made to ensure the accuracy and reliability of the content, no guarantees are made regarding the completeness, reliability, or suitability of the information.&lt;br&gt;
The author shall not be liable for any losses, injuries, or damages arising from the use or reliance on the information contained herein.&lt;/p&gt;

&lt;p&gt;The AWS resources provisioned following this article can generate costs. Make sure to delete all the resources created at the end of the exercise.&lt;/p&gt;
&lt;h2&gt;
  
  
  Overview
&lt;/h2&gt;

&lt;p&gt;In this section you focus on the IoT part configuring the gateway to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;directly read data from the PM sensor&lt;/li&gt;
&lt;li&gt;authenticate to AWS IoT Core&lt;/li&gt;
&lt;li&gt;periodically push PM values to AWS IoT Core&lt;/li&gt;
&lt;li&gt;verify availability of the data&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/g704a42aovqypyu5mch6.png" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg704a42aovqypyu5mch6.png" alt="architecture-diagram" width="800" height="540"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  1. Setup certificates and IoT cloud resources
&lt;/h2&gt;

&lt;p&gt;The following steps assume you have:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html#getting-started-install-instructions" rel="noopener noreferrer"&gt;AWS CLI installed and configured&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/openssl/openssl/blob/master/INSTALL.md#installing-openssl" rel="noopener noreferrer"&gt;OpenSSL installed&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://nodejs.org/en/download/package-manager" rel="noopener noreferrer"&gt;NodeJS (v18 or v20) installed&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://developer.hashicorp.com/terraform/install" rel="noopener noreferrer"&gt;Optional - Terraform (&amp;gt;= v.1.9.5)&lt;/a&gt; if you prefer to work with IaC approach instead of running AWS CLI commands&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;
  
  
  1.1 Prepare the working location
&lt;/h3&gt;

&lt;p&gt;Open the Terminal to start with the following commands:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# set required env variables for AWS CLI&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;AWS_DEFAULT_REGION&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&amp;lt;your-region&amp;gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;AWS_ACCOUNT_ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&amp;lt;your-account-id&amp;gt;

&lt;span class="c"&gt;# move to user home directory&lt;/span&gt;
&lt;span class="nb"&gt;cd&lt;/span&gt; ~/

&lt;span class="c"&gt;# copy source code of the project to you directory&lt;/span&gt;
git clone git@bitbucket.org:fabiobeoni/bedrock-studio-chatbot.git
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note: &lt;strong&gt;If you don't use Git&lt;/strong&gt;, &lt;strong&gt;download&lt;/strong&gt; the code from the same web address, and unpack it:&lt;br&gt;
&lt;br&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbitbucket.org%2Ffabiobeoni%2Fbedrock-studio-chatbot%2Fraw%2F6f968249667a1295b58da8e773f389bcc5ec8f23%2Fdocs%2Fimg%2Fpart-1%2Fcode-download.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbitbucket.org%2Ffabiobeoni%2Fbedrock-studio-chatbot%2Fraw%2F6f968249667a1295b58da8e773f389bcc5ec8f23%2Fdocs%2Fimg%2Fpart-1%2Fcode-download.png" width="437" height="405"&gt;&lt;/a&gt;&lt;br&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# move into the project directory&lt;/span&gt;
&lt;span class="nb"&gt;cd &lt;/span&gt;bedrock-studio-chatbot/pm-monitor-service

&lt;span class="c"&gt;# install nodejs dependencies&lt;/span&gt;
npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--production&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  1.2 Create authentication certificate &amp;amp; key
&lt;/h3&gt;

&lt;p&gt;Make the gateway device controller to connect to AWS IoT Core by authenticating it with &lt;strong&gt;certificate&lt;/strong&gt; and &lt;strong&gt;private key&lt;/strong&gt;. &lt;br&gt;
You also want to associate the certificate to a virtual representation of the gateway known as &lt;strong&gt;IoT Things&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Give a name to the gateway device:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# set a variable to host the name&lt;/span&gt;
&lt;span class="nv"&gt;thing_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"gateway_pm_monitor"&lt;/span&gt;

&lt;span class="c"&gt;# create certificate and key pairs, &lt;/span&gt;
&lt;span class="c"&gt;# assign the $thing_name as subject &lt;/span&gt;
openssl req &lt;span class="nt"&gt;-x509&lt;/span&gt;  &lt;span class="nt"&gt;-newkey&lt;/span&gt; rsa:2048 &lt;span class="nt"&gt;-sha256&lt;/span&gt;  &lt;span class="nt"&gt;-days&lt;/span&gt;  365  &lt;span class="nt"&gt;-nodes&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-keyout&lt;/span&gt; gateway.private_key.pem &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-out&lt;/span&gt; gateway.certificate.pem &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-subj&lt;/span&gt; &lt;span class="s2"&gt;"/CN=&lt;/span&gt;&lt;span class="nv"&gt;$thing_name&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Download the Amazon Root CA1 certificate:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-O&lt;/span&gt; https://www.amazontrust.com/repository/AmazonRootCA1.pem
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Perform a &lt;code&gt;ls -l&lt;/code&gt; in the local folder, to make sure &lt;strong&gt;it includes&lt;/strong&gt; the following files:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;AmazonRootCA1.pem
gateway.certificate.pem
gateway.private_key.pem
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  1.3 Provision IoT resources
&lt;/h3&gt;

&lt;p&gt;From now on, you can &lt;strong&gt;go ahead in two ways&lt;/strong&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;provisioning&lt;/strong&gt; the cloud resources &lt;strong&gt;by running AWS CLI commands&lt;/strong&gt; (easy to follow step-by-step, requires manual deletion of the resources when done), or&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;provisioning by IaC&lt;/strong&gt; approach using the &lt;a href="https://bitbucket.org/fabiobeoni/bedrock-studio-chatbot/src/master/main.tf" rel="noopener noreferrer"&gt;Terraform template&lt;/a&gt; (much faster and preconfigured, requires basic knowledge of Terraform - File included in the repo you just downloaded)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The &lt;strong&gt;article follows the provisioning by the AWS CLI&lt;/strong&gt;, to make it easier to understand the process step-by-step.&lt;/p&gt;

&lt;p&gt;From the terminal, &lt;strong&gt;provision a IoT  Thing&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws iot create-thing &lt;span class="nt"&gt;--thing-name&lt;/span&gt; &lt;span class="nv"&gt;$thing_name&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then &lt;strong&gt;register the certificate into AWS IoT Core&lt;/strong&gt;, to enable the device authentication:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# register certificate&lt;/span&gt;
&lt;span class="nv"&gt;certificatedata&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;aws iot register-certificate-without-ca &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--certificate-pem&lt;/span&gt; file://gateway.certificate.pem &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--status&lt;/span&gt; ACTIVE&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# keep track of the certificate Arn &lt;/span&gt;
&lt;span class="c"&gt;# and certificate ID in variables&lt;/span&gt;
&lt;span class="nv"&gt;certificate_arn&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nv"&gt;$certificatedata&lt;/span&gt; | jq &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="s1"&gt;'.certificateArn'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;  
&lt;span class="nv"&gt;certificate_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nv"&gt;$certificatedata&lt;/span&gt; | jq &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="s1"&gt;'.certificateId'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At this point you can find the certificate in the AWS Web Console to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# open the following address with a browser, make sure to replace&lt;/span&gt;
&lt;span class="c"&gt;# &amp;lt;ASW_DEFAULT_REGION&amp;gt; with your region&lt;/span&gt;
https://&amp;lt;ASW_DEFAULT_REGION&amp;gt;.console.aws.amazon.com/iot/home/#/certificatehub
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Get&lt;/strong&gt; the AWS IoT Core &lt;strong&gt;endpoint URL&lt;/strong&gt; in your region, to push data from device to it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# get the iot endpoint and save on a variable&lt;/span&gt;
&lt;span class="nv"&gt;endpoint_address&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;aws iot describe-endpoint &lt;span class="nt"&gt;--endpoint-type&lt;/span&gt; iot:Data-ATS &lt;span class="nt"&gt;--query&lt;/span&gt;  &lt;span class="s1"&gt;'endpointAddress'&lt;/span&gt; &lt;span class="nt"&gt;--output&lt;/span&gt; text&lt;span class="si"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Create a client ID&lt;/strong&gt;, a unique identifier for the gateway client:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# create a client ID&lt;/span&gt;
&lt;span class="nv"&gt;client_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;uuidgen&lt;span class="si"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As always, in AWS everything must be authorized to perform a given task. The gateway device must connect and publish message data, so you want to &lt;strong&gt;provision a Policy&lt;/strong&gt; to request the &lt;strong&gt;following permissions&lt;/strong&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;iot:Connect&lt;/li&gt;
&lt;li&gt;iot:Publish
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# define the name and the "topic" you push data to &lt;/span&gt;
&lt;span class="nv"&gt;policy_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;thing_name&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;_iot_policy"&lt;/span&gt;
&lt;span class="nv"&gt;monitor_topic&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"pm_monitor"&lt;/span&gt;
&lt;span class="nv"&gt;topic_arn&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"arn:aws:iot:&lt;/span&gt;&lt;span class="nv"&gt;$AWS_DEFAULT_REGION&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="nv"&gt;$AWS_ACCOUNT_ID&lt;/span&gt;&lt;span class="s2"&gt;:*"&lt;/span&gt;

&lt;span class="c"&gt;# actually create the policy with required permissions&lt;/span&gt;
aws iot create-policy &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--policy-name&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$policy_name&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--policy-document&lt;/span&gt; &lt;span class="s1"&gt;'{
  "Version": "2012-10-17",
  "Statement": [
    {"Effect": "Allow", "Action": ["iot:Connect"], "Resource": ["*"]},
    {"Effect": "Allow", "Action": ["iot:Publish"], "Resource": ["'&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$topic_arn&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s1"&gt;'"]}
  ]
}'&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now you &lt;strong&gt;attach the policy to the certificate&lt;/strong&gt;, so that the gateway device authenticating with the certificate inherits the permissions listed in the policy, then &lt;strong&gt;attach the certificate to the thing&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws iot attach-policy &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--policy-name&lt;/span&gt; &lt;span class="nv"&gt;$policy_name&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--target&lt;/span&gt; &lt;span class="nv"&gt;$certificate_arn&lt;/span&gt;

aws iot attach-thing-principal &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--principal&lt;/span&gt; &lt;span class="nv"&gt;$certificate_arn&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--thing-name&lt;/span&gt; &lt;span class="nv"&gt;$thing_name&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  1.4 Provision database resources
&lt;/h3&gt;

&lt;p&gt;Time for storing data. Right now the messages don't get persisted in any database yet. To &lt;strong&gt;save all incoming data&lt;/strong&gt; from the gateway device you &lt;strong&gt;create a DynamoDB table&lt;/strong&gt; (nosql database), and an &lt;strong&gt;IoT Rule&lt;/strong&gt; (forwarding rule) &lt;strong&gt;to forward all incoming device data&lt;/strong&gt; to it.&lt;/p&gt;

&lt;p&gt;The items stored in the table have this JSON shape:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;of&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;company's&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;plant&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;where&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;gateway&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;device&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;and&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;sensor&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;reside&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;and&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;measure&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;(dynamodb&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;partition&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;key)&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"plant_name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"plant-abc"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; 

    &lt;/span&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;timestamp&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;of&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;measure&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;when&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;it&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;happened&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;(dynamodb&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;sort&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;key)&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"date_time"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1633036800&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;

    &lt;/span&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;PM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;2.5&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;value&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"pm25"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;12.5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;

    &lt;/span&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;PM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;value&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"pm10"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;20.3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;

    &lt;/span&gt;&lt;span class="err"&gt;//metadata&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;about&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;machine&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;generating&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;PM&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"machine"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"machine-one"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"type-1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"code"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"mot1"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create the &lt;strong&gt;DynamoDB table&lt;/strong&gt; with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;dbtable_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;pm_values

aws dynamodb create-table &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--table-name&lt;/span&gt; &lt;span class="nv"&gt;$dbtable_name&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--attribute-definitions&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
        &lt;span class="nv"&gt;AttributeName&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;plant_name,AttributeType&lt;span class="o"&gt;=&lt;/span&gt;S &lt;span class="se"&gt;\&lt;/span&gt;
        &lt;span class="nv"&gt;AttributeName&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;date_time,AttributeType&lt;span class="o"&gt;=&lt;/span&gt;N &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--key-schema&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
        &lt;span class="nv"&gt;AttributeName&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;plant_name,KeyType&lt;span class="o"&gt;=&lt;/span&gt;HASH &lt;span class="se"&gt;\&lt;/span&gt;
        &lt;span class="nv"&gt;AttributeName&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;date_time,KeyType&lt;span class="o"&gt;=&lt;/span&gt;RANGE &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--provisioned-throughput&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
        &lt;span class="nv"&gt;ReadCapacityUnits&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;5,WriteCapacityUnits&lt;span class="o"&gt;=&lt;/span&gt;5
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You may want to feed the table with some synthetic data to test it out, before getting data from the gateway and IoT Core. If so, use the following script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# current timestamp&lt;/span&gt;
&lt;span class="nv"&gt;current_time&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt; +%s&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# insert 20 items in a time range with different pm2.5 and pm10 values, they all refer to "plant-abc"&lt;/span&gt;
&lt;span class="k"&gt;for &lt;/span&gt;i &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;0..19&lt;span class="o"&gt;}&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="c"&gt;# Calculate the date_time for each item (1 hour apart)&lt;/span&gt;
    &lt;span class="nv"&gt;date_time&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$((&lt;/span&gt;current_time &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;i &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="m"&gt;3600&lt;/span&gt;&lt;span class="k"&gt;))&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;  &lt;span class="c"&gt;# 3600 seconds in an hour&lt;/span&gt;
    &lt;span class="nv"&gt;pm25&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$((&lt;/span&gt;RANDOM &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="m"&gt;61&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="m"&gt;10&lt;/span&gt;&lt;span class="k"&gt;))&lt;/span&gt;  &lt;span class="c"&gt;# Random value between 10 and 70&lt;/span&gt;
    &lt;span class="nv"&gt;pm10&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$((&lt;/span&gt;RANDOM &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="m"&gt;61&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="m"&gt;10&lt;/span&gt;&lt;span class="k"&gt;))&lt;/span&gt;  &lt;span class="c"&gt;# Random value between 10 and 70&lt;/span&gt;

    aws dynamodb put-item &lt;span class="se"&gt;\&lt;/span&gt;
        &lt;span class="nt"&gt;--table-name&lt;/span&gt; &lt;span class="nv"&gt;$dbtable_name&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
        &lt;span class="nt"&gt;--item&lt;/span&gt; &lt;span class="s1"&gt;'{
            "plant_name": {"S": "plant-abc"},
            "date_time": {"N": "'&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$date_time&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s1"&gt;'"},
            "pm25": {"N": "'&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$pm25&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s1"&gt;'"},
            "pm10": {"N": "'&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$pm10&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s1"&gt;'"},
            "machine": {
                "M": {
                    "name": {"S": "machine-one"},
                    "type": {"S": "type-1"},
                    "code": {"S": "mot1"}
                }
            }
        }'&lt;/span&gt;

&lt;span class="k"&gt;done&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run a query to read the data you just pushed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# test the data inserted by querying dynamodb&lt;/span&gt;
aws dynamodb query &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--table-name&lt;/span&gt; &lt;span class="nv"&gt;$dbtable_name&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--key-condition-expression&lt;/span&gt; &lt;span class="s2"&gt;"plant_name = :plant_name"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--expression-attribute-values&lt;/span&gt; &lt;span class="s1"&gt;'{":plant_name": {"S": "plant-abc"}}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  1.5 Provision the IoT Rule to store IoT data into the database
&lt;/h3&gt;

&lt;p&gt;First, create &lt;strong&gt;role and policies&lt;/strong&gt; to &lt;strong&gt;allow the data forwarding&lt;/strong&gt; from IoT Core to the DynamoDB:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;role_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;pm_monitor_iot_push_to_dynamo_role

&lt;span class="c"&gt;# Trust policy – specifies the trusted accounts (iot.amazonaws.com) that are allowed to assume the role.&lt;/span&gt;
aws iam create-role &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--role-name&lt;/span&gt; &lt;span class="nv"&gt;$role_name&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--assume-role-policy-document&lt;/span&gt; &lt;span class="s1"&gt;'{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Service": "iot.amazonaws.com"
      },
      "Action": "sts:AssumeRole"
    }
  ]
}'&lt;/span&gt;

&lt;span class="c"&gt;# Permissions policy – grants the user of the role the required permissions to put items into the table.&lt;/span&gt;
&lt;span class="nv"&gt;policy_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;pm_monitor_iot_push_to_dynamo_policy

aws iam put-role-policy &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--role-name&lt;/span&gt; &lt;span class="nv"&gt;$role_name&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--policy-name&lt;/span&gt; &lt;span class="nv"&gt;$policy_name&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--policy-document&lt;/span&gt; &lt;span class="s2"&gt;"{
        &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;Version&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;2012-10-17&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;,
        &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;Statement&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: [
            {
                &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;Effect&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;Allow&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;,
                &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;Action&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;dynamodb:PutItem&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;,
                &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;Resource&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;arn:aws:dynamodb:&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;AWS_DEFAULT_REGION&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;AWS_ACCOUNT_ID&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;:table/&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;dbtable_name&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;
            }
        ]
    }"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create an &lt;strong&gt;IoT Rule&lt;/strong&gt; to direct messages from the topic to the table.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws iot create-topic-rule &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--region&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;AWS_DEFAULT_REGION&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--rule-name&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;monitor_topic&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;_to_dynamodb"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--topic-rule-payload&lt;/span&gt; &lt;span class="s2"&gt;"{
      &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;sql&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;SELECT * FROM '&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;monitor_topic&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;'&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;,
      &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;description&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;Rule to send messages from &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;monitor_topic&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; topic to dynamodb &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;dbtable_name&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; table&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;,
      &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;actions&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: [
          {
              &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;dynamoDBv2&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: {
                  &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;putItem&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: {  &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;tableName&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:  &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;dbtable_name&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;  },
                  &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;roleArn&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;arn:aws:iam::&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;AWS_ACCOUNT_ID&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;:role/&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;role_name&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;
              }
          }
      ],
      &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;ruleDisabled&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: false,
      &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;awsIotSqlVersion&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;2016-03-23&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;
    }"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  1.6 Test the provisioned resources
&lt;/h3&gt;

&lt;p&gt;To test the provisioned IoT resources, &lt;strong&gt;push a message&lt;/strong&gt; to AWS IoT Core. To see messages coming from the test, &lt;strong&gt;subrscribe to the topic&lt;/strong&gt; where the message is pushed to. This time you go ahead using the AWS Web Console.&lt;/p&gt;

&lt;p&gt;First open the web browser to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;https://&amp;lt;AWS_DEFAULT_REGION&amp;gt;.console.aws.amazon.com/iot/home/#/test
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Second, in the web console create a &lt;strong&gt;subscription to the "pm_monitor"&lt;/strong&gt; topic:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://bitbucket.org/fabiobeoni/bedrock-studio-chatbot/raw/6f968249667a1295b58da8e773f389bcc5ec8f23/docs/img/part-1/aws-console-iot-subscrption.png" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbitbucket.org%2Ffabiobeoni%2Fbedrock-studio-chatbot%2Fraw%2F6f968249667a1295b58da8e773f389bcc5ec8f23%2Fdocs%2Fimg%2Fpart-1%2Faws-console-iot-subscrption.png" width="800" height="583"&gt;&lt;/a&gt;&lt;br&gt;&lt;/p&gt;

&lt;p&gt;Now &lt;strong&gt;push a message&lt;/strong&gt; to IoT Core:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# install the mqtt client&lt;/span&gt;
curl &lt;span class="nt"&gt;-L&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; mqtt-cli-4.31.0.deb https://github.com/hivemq/mqtt-cli/releases/download/v4.31.0/mqtt-cli-4.31.0.deb
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install&lt;/span&gt; ./mqtt-cli-4.31.0.deb

&lt;span class="c"&gt;# push a message to the topic "pm_monitor"&lt;/span&gt;
mqtt pub &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-h&lt;/span&gt; &lt;span class="nv"&gt;$endpoint_address&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-p&lt;/span&gt; 8883 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--cafile&lt;/span&gt; AmazonRootCA1.pem &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--cert&lt;/span&gt; gateway.certificate.pem &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--key&lt;/span&gt; gateway.private_key.pem &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-t&lt;/span&gt; &lt;span class="nv"&gt;$monitor_topic&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"{ &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;plant_name&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;plant-abc&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;, &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;date_time&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: 1633036800, &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;pm25&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: 12.5, &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;pm10&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: 20.3, &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;machine&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: { &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;name&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; : &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;machine-one&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;, &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;type&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; : &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;type-1&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;, &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;code&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; : &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;mot1&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; } }"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;-V&lt;/span&gt; 5 &lt;span class="nt"&gt;-q&lt;/span&gt; 0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The message appears in the web console (bottom of the page):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;https://&amp;lt;AWS_DEFAULT_REGION&amp;gt;.console.aws.amazon.com/iot/home/#/test
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Check that the message is also stored in the DynamoDB table:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# test the data inserted by querying dynamodb&lt;/span&gt;
aws dynamodb query &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--table-name&lt;/span&gt; &lt;span class="nv"&gt;$dbtable_name&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--key-condition-expression&lt;/span&gt; &lt;span class="s2"&gt;"plant_name = :plant_name"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--expression-attribute-values&lt;/span&gt; &lt;span class="s1"&gt;'{":plant_name": {"S": "plant-abc"}}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or navigate the web console, then select the table:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;https://&amp;lt;AWS_DEFAULT_REGION&amp;gt;.console.aws.amazon.com/dynamodbv2/home/#tables
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  2. Setup the gateway device and sensor
&lt;/h2&gt;

&lt;h3&gt;
  
  
  2.1 The "hard" part ;)
&lt;/h3&gt;

&lt;p&gt;Start by connecting the Nova PM sensor...&lt;/p&gt;

&lt;p&gt;&lt;br&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbitbucket.org%2Ffabiobeoni%2Fbedrock-studio-chatbot%2Fraw%2F6f968249667a1295b58da8e773f389bcc5ec8f23%2Fdocs%2Fimg%2Fpart-1%2Fnova.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbitbucket.org%2Ffabiobeoni%2Fbedrock-studio-chatbot%2Fraw%2F6f968249667a1295b58da8e773f389bcc5ec8f23%2Fdocs%2Fimg%2Fpart-1%2Fnova.jpg" width="400" height="400"&gt;&lt;/a&gt;&lt;br&gt;&lt;/p&gt;

&lt;p&gt;...to the RaspberryPi4 gateway device via USB:&lt;br&gt;&lt;br&gt;
&lt;br&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbitbucket.org%2Ffabiobeoni%2Fbedrock-studio-chatbot%2Fraw%2F6f968249667a1295b58da8e773f389bcc5ec8f23%2Fdocs%2Fimg%2Fpart-1%2FPi4B.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbitbucket.org%2Ffabiobeoni%2Fbedrock-studio-chatbot%2Fraw%2F6f968249667a1295b58da8e773f389bcc5ec8f23%2Fdocs%2Fimg%2Fpart-1%2FPi4B.jpg" width="500" height="399"&gt;&lt;/a&gt;&lt;br&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  2.2 Device configuration
&lt;/h3&gt;
&lt;h4&gt;
  
  
  2.2.1 Installing configuration, user and service software module
&lt;/h4&gt;

&lt;p&gt;Create a &lt;strong&gt;JSON configuration file&lt;/strong&gt; (to configure the IoT client instance later on):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;device_config&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;
{
  "host": "&lt;/span&gt;&lt;span class="nv"&gt;$endpoint_address&lt;/span&gt;&lt;span class="sh"&gt;",
  "port":8883,
  "clientId": "&lt;/span&gt;&lt;span class="nv"&gt;$client_id&lt;/span&gt;&lt;span class="sh"&gt;",
  "thingName": "&lt;/span&gt;&lt;span class="nv"&gt;$thing_name&lt;/span&gt;&lt;span class="sh"&gt;",
  "cert": "./gateway.certificate.pem",
  "key": "./gateway.private_key.pem",
  "ca": "./AmazonRootCA1.pem",
  "topic":"&lt;/span&gt;&lt;span class="nv"&gt;$monitor_topic&lt;/span&gt;&lt;span class="sh"&gt;",
  "mockSensor":false,
  "sensorPath":"/dev/ttyUSB0",
  "pushFrequency":3000,
  "pmMultiplier": 10
}
&lt;/span&gt;&lt;span class="no"&gt;EOF
&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# save the configuration to a file&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nv"&gt;$device_config&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; device_config.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Perform a &lt;code&gt;cat device_config.json&lt;/code&gt; and cross-check that all fields have values.&lt;/p&gt;

&lt;p&gt;From the terminal, &lt;strong&gt;connect to the Raspberry gateway device&lt;/strong&gt; (assuming the gateway device already connected to your network) to copy the &lt;strong&gt;service&lt;/strong&gt; and the &lt;strong&gt;certificate files&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; ~/bedrock-studio-chatbot
scp &lt;span class="nt"&gt;-r&lt;/span&gt; pm-monitor-service pi@raspberrypi.local:~/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Connect to the Raspberry:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ssh pi@raspberrypi.local
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Make the &lt;strong&gt;service user, config location&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# create the service user "pmmonitor" and provide a password&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;adduser pmmonitor 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Move the files and &lt;strong&gt;assign the permissions&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# move the project file to the user home&lt;/span&gt;
&lt;span class="nb"&gt;sudo mv&lt;/span&gt; ~/pm-monitor-service /home/pmmonitor

&lt;span class="c"&gt;# assign permissions&lt;/span&gt;
&lt;span class="nb"&gt;sudo chown &lt;/span&gt;pmmonitor /home/pmmonitor/pm-monitor-service &lt;span class="nt"&gt;-R&lt;/span&gt;

&lt;span class="c"&gt;# assing permissions to read the input data from the sensor&lt;/span&gt;
&lt;span class="nb"&gt;sudo chown &lt;/span&gt;pmmonitor /dev/ttyUSB0 &lt;span class="c"&gt;# or any USB port you have set in the device_config.json ("sensorPath":"/dev/ttyUSB0")&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  2.2.2 Installing NodeJS runtime
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# install nodejs 18&lt;/span&gt;
curl &lt;span class="nt"&gt;-fsSL&lt;/span&gt; https://deb.nodesource.com/setup_18.x | &lt;span class="nb"&gt;sudo&lt;/span&gt; &lt;span class="nt"&gt;-E&lt;/span&gt; bash -
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; nodejs

&lt;span class="c"&gt;# login as the service user&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;su pmmonitor

&lt;span class="nb"&gt;cd&lt;/span&gt; ~/pm-monitor-service
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2.2 Run the pm-monitor-service
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# start the service&lt;/span&gt;
nodejs main.mjs
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Going back to web console, access the DynamoDB table "pm_values" (link), click on button "Scan" to see the data.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;https://&amp;lt;AWS_DEFAULT_REGION&amp;gt;.console.aws.amazon.com/dynamodbv2/home/#table?name&lt;span class="o"&gt;=&lt;/span&gt;pm_values
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;br&gt;&lt;a href="https://bitbucket.org/fabiobeoni/bedrock-studio-chatbot/raw/6f968249667a1295b58da8e773f389bcc5ec8f23/docs/img/part-1/dynamo-db-table.png" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbitbucket.org%2Ffabiobeoni%2Fbedrock-studio-chatbot%2Fraw%2F6f968249667a1295b58da8e773f389bcc5ec8f23%2Fdocs%2Fimg%2Fpart-1%2Fdynamo-db-table.png" width="800" height="268"&gt;&lt;/a&gt;&lt;br&gt;&lt;/p&gt;

&lt;p&gt;You can stop the service at any time by:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ctrl+c
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; If you want the service to run automatically at boot time, you may want to create an &lt;code&gt;.service&lt;/code&gt; file for "systemd". &lt;/p&gt;

&lt;h2&gt;
  
  
  Next
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://dev.to/fabio_beoni/part-2-deploy-a-chat-app-bedrock-agent-and-actions-to-ask-questions-about-live-data-1i9m"&gt;Part 2 - Deploy a Chat App, Bedrock Agent and Actions to Ask Questions about Live Data&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Previous
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://dev.to/fabio_beoni/generative-ai-driven-chatbot-for-the-factory-plant-21j6"&gt;Introduction&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Generative AI-driven Chatbot for the Factory Plant</title>
      <dc:creator>Fabio Beoni</dc:creator>
      <pubDate>Fri, 30 Aug 2024 16:27:48 +0000</pubDate>
      <link>https://dev.to/fabio_beoni/generative-ai-driven-chatbot-for-the-factory-plant-21j6</link>
      <guid>https://dev.to/fabio_beoni/generative-ai-driven-chatbot-for-the-factory-plant-21j6</guid>
      <description>&lt;p&gt;This is the first article of a short series presenting a proof-of-concept (P.O.C) about the adoption of the  &lt;strong&gt;generative AI technology to implement a digital agent to talk to an environmental pollution (PM2.5/PM10) monitoring system&lt;/strong&gt;. Serving a fictitious cements factory, the main goal of the digital agent is &lt;strong&gt;safeguarding workers and machines&lt;/strong&gt;, &lt;strong&gt;providing technical knowledge&lt;/strong&gt; to adopt safe behaviors and guide maintenance actions.   &lt;/p&gt;

&lt;p&gt;The recent &lt;strong&gt;raising of AI&lt;/strong&gt; and &lt;a href="https://en.wikipedia.org/wiki/Large_language_model" rel="noopener noreferrer"&gt;large language models&lt;/a&gt; like Chat-GPT &lt;strong&gt;enables dialog interaction between people and machines&lt;/strong&gt; or systems, &lt;strong&gt;without learning new tools&lt;/strong&gt; or software. &lt;/p&gt;

&lt;p&gt;I had the opportunity to work on a similar system, for a real project in a different business domain, using technologies other than generative AI. Recently &lt;strong&gt;&lt;a href="https://aws.amazon.com/bedrock/studio/" rel="noopener noreferrer"&gt;AWS released Amazon Bedrock Studio&lt;/a&gt;&lt;/strong&gt;, a &lt;strong&gt;low-code tool to create gen-AI apps&lt;/strong&gt; using top-class large language models from main market providers like Anthropic and Meta. I decided to roll up one's sleeves in give it try.&lt;/p&gt;

&lt;h3&gt;
  
  
  Business Context
&lt;/h3&gt;

&lt;p&gt;In several businesses, &lt;strong&gt;many kinds of processes to produce goods do generate particulate matter (PM) pollution&lt;/strong&gt;, including PM 2.5 and PM 10. These particles are &lt;strong&gt;small enough to be inhaled&lt;/strong&gt; and can cause &lt;strong&gt;serious health problems&lt;/strong&gt; to workers, as well as have negative impacts on machinery and equipment. Fine particles can &lt;strong&gt;accumulate on surfaces and in moving parts&lt;/strong&gt;, leading to wear and tear, reduced efficiency, and increased maintenance costs. PM can &lt;strong&gt;cause corrosion and damage&lt;/strong&gt; to electronic components, which can lead to &lt;strong&gt;equipment failure and costly repairs&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;In a cement factory where normally &lt;strong&gt;raw materials such as limestone, clay, and sand are processed and transformed&lt;/strong&gt; into cement through a series of steps that include &lt;strong&gt;crushing, grinding, mixing&lt;/strong&gt;, and heating in kilns, &lt;strong&gt;PM pollution must be monitored and reduced&lt;/strong&gt; through the use of various air pollution control devices and techniques, such as electrostatic precipitators, fabric filters, and many more.&lt;/p&gt;

&lt;h3&gt;
  
  
  Business Problems
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Current lack of real-time air quality monitoring poses a risk worker health and machines reliability,&lt;/li&gt;
&lt;li&gt;Workers do not have easy access to procedures and guidelines, getting difficult and inefficient to take actions against a degrading air quality,
&lt;/li&gt;
&lt;li&gt;Missing of a track history of pollution data does not enable analysis and data-driven decision-making,&lt;/li&gt;
&lt;li&gt;Failure to comply with relevant legislation and regulations can result in fines, penalties, and reputational damage.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  P.O.C. Solution - High Level View
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;S.O.P., guides, relevant documents&lt;/strong&gt; - to feed the generative AI model and chatbot agent with the company's know-how and procedures,&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Gen-AI chatbot agent&lt;/strong&gt; - to answer workers' questions about environment air quality status, get guidelines and procedures,&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cloud IoT digital platform &amp;amp; storage&lt;/strong&gt; - to manage the PM sensors data flow and storage,&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;IoT gateway and service&lt;/strong&gt; - to get pollution data from the sensors, push them to the IoT digital platform,&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;PM 2.5/10 sensors&lt;/strong&gt; - distributed across the production plant, to gather pollution values from the working environment in near real-time.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://bitbucket.org/fabiobeoni/bedrock-studio-chatbot/raw/6f968249667a1295b58da8e773f389bcc5ec8f23/docs/img/intro/high-level-solution-diagram.png" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Foecvyrqrn1zyh71fcgua.png" alt="Solution Diagram - Business View" width="800" height="710"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  The Invisible Super-Power of the Gen-AI
&lt;/h3&gt;

&lt;p&gt;We all know how good it is to create new text, images/videos, charts. Following this use-case &lt;strong&gt;you can discover how powerful it becomes in combination with other actors like the IoT platform&lt;/strong&gt;, or any other source of business data.&lt;/p&gt;

&lt;p&gt;Normally, when you want a chatbot to interact with some source of data you must define logical flows (and code it). Coordinate the interactions between the user and software components (REST APIs or so), that's a lot of IT work.&lt;/p&gt;

&lt;p&gt;This is how a &lt;strong&gt;standard project without gen-AI&lt;/strong&gt; would handle the chatbot configuration, as well as the interaction between it and all sort of  data needed to answer user's questions:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
  &lt;tr&gt;
    &lt;th&gt;Professionals&lt;/th&gt;
    &lt;th&gt;Tasks to Carry Out&lt;/th&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;Analyst + UX Expert&lt;/td&gt;
    &lt;td&gt;
      &lt;ul&gt;
        &lt;li&gt;define all possible user/chatbot interactions (questions, answers, paths, side-cases, errors),&lt;/li&gt;
        &lt;li&gt;draw logical flows according to the use-cases,&lt;/li&gt;
        &lt;li&gt;define what kind of data the chatbot needs,&lt;/li&gt;
        &lt;li&gt;define all examples needed to train the chatbot engine (many),&lt;/li&gt;
        &lt;li&gt;define all tests to run against the chatbot.&lt;/li&gt;
      &lt;/ul&gt;
    &lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;Architect&lt;/td&gt;
    &lt;td&gt;
      &lt;ul&gt;
        &lt;li&gt;define how to implement the integrations between the chatbot and the sources of data (can be internal REST APIs, databases, third-party tools and so on).&lt;/li&gt;
      &lt;/ul&gt;
    &lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;Developer&lt;/td&gt;
    &lt;td&gt;
      &lt;ul&gt;
        &lt;li&gt;configures the project environment,&lt;/li&gt;
        &lt;li&gt;configures the chatbot flows and examples,&lt;/li&gt;
        &lt;li&gt;implement the integrations to the data sources,&lt;/li&gt;
        &lt;li&gt;runs the tests.&lt;/li&gt;
      &lt;/ul&gt;
    &lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;Developer + Analyst + UX Expert&lt;/td&gt;
    &lt;td&gt;
      &lt;ul&gt;
        &lt;li&gt;re-do the process for every test failing, or &lt;/li&gt;
        &lt;li&gt;for every new side-case emerging during the production use by the end-user.&lt;/li&gt;
      &lt;/ul&gt;
    &lt;/td&gt;
  &lt;/tr&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;While &lt;strong&gt;adopting gen-AI, and specifically Amazon Bedrock Studio&lt;/strong&gt; used in this P.O.C., you rely on the gen-AI semantic and reasoning capabilities to &lt;strong&gt;automatically figure-out&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;chatbot answers&lt;/li&gt;
&lt;li&gt;logical flows&lt;/li&gt;
&lt;li&gt;test cases&lt;/li&gt;
&lt;li&gt;data needed to answer&lt;/li&gt;
&lt;li&gt;what external data sources are mandatory to answer, automatically selecting them from a known list (!)&lt;/li&gt;
&lt;li&gt;logical flows to call the external data sources and get data (!)&lt;/li&gt;
&lt;li&gt;automatic engagement of the end-user when some mandatory information was not provided in original question (!)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So that the IT professionals and tasks change as follows:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
  &lt;tr&gt;
    &lt;th&gt;Professionals&lt;/th&gt;
    &lt;th&gt;Tasks to Carry Out&lt;/th&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;Analyst&lt;/td&gt;
    &lt;td&gt;
      &lt;ul&gt;
        &lt;li&gt;define a very few examples of questions, and some sample answers (optional),&lt;/li&gt;
        &lt;li&gt;define a very few tests.&lt;/li&gt;
      &lt;/ul&gt;
    &lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;IT Product Specialist&lt;/td&gt;
    &lt;td&gt;
      &lt;ul&gt;
        &lt;li&gt;configures the project environment,&lt;/li&gt;
        &lt;li&gt;list and describe available sources of data to the gen-AI,&lt;/li&gt;
        &lt;li&gt;list examples and tests to the get-AI,&lt;/li&gt;
        &lt;li&gt;asks the get-AI (different LLM) to generate some additional &lt;a href="https://en.wikipedia.org/wiki/Synthetic_data" rel="noopener noreferrer"&gt;synthetic examples&lt;/a&gt; and related tests&lt;/li&gt;
        &lt;li&gt;runs the tests.&lt;/li&gt;
      &lt;/ul&gt;
    &lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;Analyst + IT Product Specialist&lt;/td&gt;
    &lt;td&gt;
      &lt;ul&gt;
        &lt;li&gt;refine gen-AI examples for every new side-case emerging during the production use by the end-user.&lt;/li&gt;
      &lt;/ul&gt;
    &lt;/td&gt;
  &lt;/tr&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;As a result, you gain:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a much cleaner configuration process, &lt;/li&gt;
&lt;li&gt;easier work for IT people to carry out,&lt;/li&gt;
&lt;li&gt;a much smaller risk of unanswered questions (happy users),&lt;/li&gt;
&lt;li&gt;increased time to market.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  P.O.C. Solution - Technologies &amp;amp; Architecture
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Amazon Bedrock Studio&lt;/strong&gt; (low code AI app builder):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;LLMs: Amazon Titan Embeddings, Anthropic Claude&lt;/li&gt;
&lt;li&gt;Chatbot Agent &amp;amp; Web chat app&lt;/li&gt;
&lt;li&gt;Knowledge-base: AWS OpenSearch Serverless&lt;/li&gt;
&lt;li&gt;PM Querying function: AWS Lambda&lt;/li&gt;
&lt;li&gt;AA.: AWS Identity Center &amp;amp; Policies&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;strong&gt;AWS IoT Core&lt;/strong&gt; (IoT platform):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Gateway twin: AWS IoT Thing&lt;/li&gt;
&lt;li&gt;Message routing: AWS IoT Rule&lt;/li&gt;
&lt;li&gt;Data storage: AWS DynamoDB&lt;/li&gt;
&lt;li&gt;AA.: Certificates &amp;amp; Policies&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;strong&gt;Raspberry Pi4&lt;/strong&gt; (gateway):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Data reader service: NodeJS runtime, Nova sensor driver, mqtt client&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;&lt;p&gt;&lt;strong&gt;Nova PM sensor SDS011&lt;/strong&gt; (pollution sensor)&lt;/p&gt;&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/g704a42aovqypyu5mch6.png" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg704a42aovqypyu5mch6.png" alt="architecture-diagram" width="800" height="540"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Next
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://dev.to/fabio_beoni/part-1-gathering-pm-values-from-the-sensor-pushing-and-saving-them-to-the-cloud-storage-tch-2l99"&gt;Part 1 - Gathering pollution data from the edge, pushing it to AWS IoT Core and DynamoDB&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://dev.to/fabio_beoni/part-2-deploy-a-chat-app-bedrock-agent-and-actions-to-ask-questions-about-live-data-1i9m"&gt;Part 2 - Deploy a Chat App, Bedrock Agent and Actions to Ask Questions about Live Data&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://dev.to/fabio_beoni/part-3-providing-workers-guidance-with-amazon-bedrock-knowledge-base-and-gen-ai-mn6"&gt;Part 3 - Providing workers guidance with Amazon Bedrock Knowledge-base and Gen-AI&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Coming Soon:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Part 4 - Working with Bedrock Studio (visual app builder)&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>awsbedrock</category>
      <category>serverless</category>
      <category>industry</category>
      <category>machinelearning</category>
    </item>
  </channel>
</rss>
