Skip to content

Commit 95aa749

Browse files
committed
Final section
1 parent cc7647e commit 95aa749

10 files changed

Lines changed: 190 additions & 6 deletions

File tree

85.5 KB
Loading
228 KB
Loading
100 KB
Loading
282 KB
Loading
302 KB
Loading
213 KB
Loading
87 KB
Loading

content/learning-paths/iot/azure-iot/_index.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
---
22
title: Creating IoT Solutions in Azure for Arm64-Powered Devices
33

4-
minutes_to_complete: 160
4+
minutes_to_complete: 320
55

66
who_is_this_for: This is an advanced topic for software developers interested in learning how to build a comprehensive IoT solution in Azure that streams, stores, monitors, aggregates, and visualizes data from Arm64-powered IoT devices.
77

@@ -11,7 +11,7 @@ learning_objectives:
1111
- Stream IoT data into Azure services using Azure Stream Analytics.
1212
- Store and persist streamed IoT data in Azure Cosmos DB by configuring a Stream Analytics job.
1313
- Implement data monitoring and alerts by creating an Azure Function that checks sensor data from Cosmos DB and sends notifications when thresholds are exceeded.
14-
- Aggregate sensor readings by developing an Azure Function that periodically calculates average, minimum, and maximum values from data stored in Cosmos DB.
14+
- Aggregate sensor readings by developing an Azure Function that calculates average values from data stored in Cosmos DB.
1515
- Publish aggregated IoT data to a public-facing web portal, by deploying a Static Web App hosted on Azure Blob Storage
1616

1717
prerequisites:

content/learning-paths/iot/azure-iot/aggregation.md

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,8 @@ def get_average_temperature(req: func.HttpRequest) -> func.HttpResponse:
123123
return func.HttpResponse(
124124
json.dumps(response_body),
125125
status_code=200,
126-
mimetype="application/json"
126+
mimetype="application/json",
127+
headers={"Access-Control-Allow-Origin": "*"}
127128
)
128129
```
129130

@@ -135,11 +136,11 @@ Next, it initializes the Cosmos DB client using the provided connection string.
135136

136137
To determine the relevant data points, the function calculates a timestamp representing exactly one minute before the current UTC time. It then constructs and executes a query against Cosmos DB to retrieve temperature readings (temperature values) with a timestamp (_ts) greater than or equal to this calculated value, effectively fetching all recent temperature data from the last minute.
137138

138-
If no recent temperature data is found, the function returns a JSON response stating that no readings are available for that period, along with a 200 OK status.
139+
If no recent temperature data is found, the function returns a JSON response stating that no readings are available for that period, along with a 200 OK status and appropriate CORS headers. The CORS header, set as {"Access-Control-Allow-Origin": "*"}, is essential for allowing cross-origin requests from the portal, ensuring that the client-side application can access the response without any browser security issues.
139140

140-
When data points are available, the function computes the average temperature from the retrieved readings. In case of unexpected errors during calculation, it logs the issue and responds with a 500 Internal Server Error.
141+
When data points are available, the function computes the average temperature from the retrieved readings. In case of unexpected errors during calculation, it logs the issue and responds with a 500 Internal Server Error, while still including the necessary CORS headers so that the portal receives the error response correctly.
141142

142-
Finally, if the average calculation succeeds, the function constructs a JSON response containing the calculated average temperature (rounded to two decimal places) along with a success message. It then sends this response back to the caller with a status code of 200 OK.
143+
Finally, if the average calculation succeeds, the function constructs a JSON response containing the calculated average temperature (rounded to two decimal places) along with a success message. It then sends this response back to the caller with a status code of 200 OK and the configured CORS header {"Access-Control-Allow-Origin": "*"}, which is required to ensure that the portal can successfully retrieve and display the data from the function.
143144

144145
Before running the function, dependencies need to be added and installed. Open the requirements.txt file and include the following lines:
145146

Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
---
2+
# User change
3+
title: "IoT Portal"
4+
5+
weight: 9
6+
7+
layout: "learningpathall"
8+
---
9+
10+
## Objective
11+
We have successfully established the core backend components for our IoT solution. An IoT simulator continuously generates sensor data, streaming it securely to the cloud via Azure IoT Hub. These sensor readings are stored in Cosmos DB, ensuring data persistence and scalability. Additionally, we’ve implemented an Azure Function that can be triggered through HTTP requests to query Cosmos DB and calculate the average temperature from recent sensor data. With these underlying services fully operational, we’re now prepared to build a WWW portal that will visually present real-time temperature information to our end-users.
12+
13+
## Website
14+
Start by creating a new folder named Arm.AzureIoT.Portal, inside which you’ll create three files: index.html, main.js, and styles.css. The first file, index.html will define the structure of the webpage, and contain the HTML markup and links to the JavaScript and CSS files. The second file, main.js will include the logic and interactivity of the webpage. In this project, it will handle fetching temperature data from your Azure Function and updating the displayed content dynamically. The last file, styles.css will contain all the styling information, controlling the visual appearance of your webpage.
15+
16+
### styles.css
17+
Modify your styles.css file by adding the following CSS:
18+
```css
19+
body, html {
20+
margin: 0;
21+
padding: 0;
22+
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
23+
background-color: #121212;
24+
color: #ffffff;
25+
height: 100%;
26+
display: flex;
27+
justify-content: center;
28+
align-items: center;
29+
}
30+
.container {
31+
text-align: center;
32+
padding: 2rem;
33+
border-radius: 8px;
34+
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.5);
35+
background-color: #1e1e1e;
36+
}
37+
h1 {
38+
margin-bottom: 1.5rem;
39+
font-size: 2.5rem;
40+
}
41+
button {
42+
background-color: #1e88e5;
43+
color: #ffffff;
44+
border: none;
45+
padding: 0.75rem 1.5rem;
46+
font-size: 1rem;
47+
border-radius: 4px;
48+
cursor: pointer;
49+
transition: background-color 0.3s ease;
50+
}
51+
button:hover {
52+
background-color: #1565c0;
53+
}
54+
.result {
55+
margin-top: 1.5rem;
56+
font-size: 1.25rem;
57+
}
58+
```
59+
60+
The provided CSS sets a modern, dark-themed appearance for your IoT portal webpage. Here’s a breakdown of its styling:
61+
* body and html. The styles remove default margins and paddings, define a dark background color (#121212), set the text color to white for high contrast, and center content both horizontally and vertically using Flexbox.
62+
* .container - this creates a central container element with padding for spacing, rounded corners (border-radius: 8px) for a softer look, a subtle shadow effect for depth, and a slightly lighter dark background (#1e1e1e) to distinguish the content area from the main page background.
63+
* h1 - this defines the main title style with increased font size (2.5rem) and additional spacing below to clearly separate the title from other content.
64+
* button - styles the interactive “Get Temperature” button, giving it a blue color (#1e88e5), white text for readability, rounded corners for a friendly appearance, and smooth color-transition effects when hovered to improve user experience.
65+
* .result - formats the text area where the temperature reading will appear, adding sufficient margin for clear spacing and slightly larger text size to make the results easily readable.
66+
67+
### main.js
68+
Now, open your main.js file and update it with the following JavaScript code:
69+
```JavaScript
70+
const functionUrl = "<YOUR_FUNCTION_URL_GOES_HERE>";
71+
72+
document.getElementById("getTempBtn").addEventListener("click", async () => {
73+
const resultElement = document.getElementById("result");
74+
resultElement.textContent = "Fetching temperature...";
75+
try {
76+
const response = await fetch(functionUrl);
77+
if (!response.ok) {
78+
throw new Error("Network response was not ok");
79+
}
80+
const data = await response.json();
81+
if (data && data.averageTemperature !== null) {
82+
resultElement.textContent = "Temperature: " + data.averageTemperature + " °C";
83+
} else {
84+
resultElement.textContent = "No temperature data available.";
85+
}
86+
} catch (error) {
87+
console.error("Error fetching temperature:", error);
88+
resultElement.textContent = "Error fetching temperature.";
89+
}
90+
});
91+
```
92+
93+
This JavaScript provides the interactive functionality for the webpage. It connects the portal to the Azure Function previously deployed. Here’s how it works step-by-step. First, replace the placeholder "<YOUR_FUNCTION_URL_GOES_HERE>" with the actual URL of your Azure Function that calculates and returns the average temperature. The code uses event listener for the button. Specifically, it attaches a click event listener to your button (getTempBtn). Each time the button is clicked, it triggers the async JavaScript function that retrieves data.
94+
95+
When the button is clicked, the label (element with id "result") displays a temporary message—“Fetching temperature...”. It is used to inform the user that the request is in progress. The script sends a GET request to your Azure Function URL. If the request succeeds, it parses the JSON response. If the response contains valid temperature data (averageTemperature), it updates the label to show the current temperature. If no data is returned, it notifies the user accordingly.
96+
97+
If any error occurs (e.g., network issues, or a problem in fetching or parsing the data), the script logs the error to the browser console and updates the UI to inform the user (“Error fetching temperature.”).
98+
99+
### index.html
100+
Finally, open the index.html file and replace its content with the following HTML code:
101+
```HTML
102+
<!DOCTYPE html>
103+
<html lang="en">
104+
<head>
105+
<meta charset="UTF-8" />
106+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
107+
<title>IoT Solution</title>
108+
<link rel="stylesheet" href="styles.css" />
109+
</head>
110+
<body>
111+
<div class="container">
112+
<h1>IoT Solution</h1>
113+
<button id="getTempBtn">Get temperature</button>
114+
<div class="result" id="result">Temperature: -- °C</div>
115+
</div>
116+
<script src="main.js"></script>
117+
</body>
118+
</html>
119+
```
120+
121+
This HTML file represents the main structure and entry point of your IoT web portal. It is divided into Head and Body sections: The head body defines basic metadata such as character set (UTF-8) and viewport configuration for responsive design. Then, it sets the title of your webpage to "IoT Solution" and links your CSS stylesheet (styles.css), which defines the appearance of the page.
122+
123+
In the body section we have:
124+
* a centered container (div) with a clear heading (h1) labeled "IoT Solution".
125+
* a button (id="getTempBtn") that users click to trigger the JavaScript logic retrieving temperature data from your Azure Function.
126+
* a placeholder label (div) with the id "result" initially showing "Temperature: -- °C". The JavaScript updates this label dynamically with the actual temperature retrieved from your backend.
127+
128+
Finally, the index.html includes the JavaScript file (main.js) placed at the end of the body to ensure the HTML elements are fully loaded before executing scripts.
129+
130+
## Testing the Implementation:
131+
Make sure you’ve saved all three files (index.html, main.js, and styles.css). Next:
132+
1. Start the IoT Simulator to begin streaming data to the Azure IoT Hub.
133+
2. Open the index.html file locally in your web browser.
134+
3. Click the "Get temperature" button.
135+
136+
You should now see real-time temperature readings displayed:
137+
138+
![img44 alt-text#center](Figures/44.png)
139+
140+
## Deployment to Azure Blob Storage
141+
You will now deploy the web portal you’ve created to Azure Blob Storage, making it accessible online.
142+
143+
### Create and Configure Azure Blob Storage
144+
1. Sign in to the Azure Portal.
145+
2. Create a Storage Account:
146+
* Click “Create a resource”
147+
* Search for “Storage account”
148+
![img45 alt-text#center](Figures/45.png)
149+
* Click “Create”.
150+
![img46 alt-text#center](Figures/46.png)
151+
3. Provide required details:
152+
* Subscription, resource group, storage account name (e.g. armiotstorage).
153+
* For Primary service, choose Azure Blob Storage or Azure Data Lake Storage Gen 2.
154+
* Select Standard performance and Locally-redundant storage (LRS).
155+
![img47 alt-text#center](Figures/47.png)
156+
* Click "Review + create", then "Create".
157+
3. Enable Static Website Hosting:
158+
* Navigate to your newly created storage account.
159+
* Under Data management, click “Static website”.
160+
* Select “Enabled”.
161+
* Set index.html as the index document name.
162+
![img48 alt-text#center](Figures/48.png)
163+
* Click Save.
164+
165+
After saving, Azure provides you with a URL like: https://<storage-account-name>.z22.web.core.windows.net/.
166+
Make sure to save this URL, as it will serve as the public endpoint for your website.
167+
168+
### Upload Files to Azure Blob Storage
169+
You can upload your website files directly using the Azure Portal or via Azure Storage Explorer. Here, we’ll use the Azure Portal:
170+
1. Navigate to your storage account.
171+
2. Under Data storage, select “Containers”.
172+
3. Open the container named ”$web” (created automatically when enabling static websites).
173+
4. Click Upload and select your three website files (index.html, main.js, styles.css), and upload them.
174+
175+
![img49 alt-text#center](Figures/49.png)
176+
177+
### Verify the Deployment
178+
After uploading your files, open a browser and navigate to https://<storage-account-name>.z22.web.core.windows.net/. Your static website should load, allowing you to test the “Get temperature” button (to see temperatures make sure to start the IoT simulator):
179+
180+
![img50 alt-text#center](Figures/50.png)
181+
182+
## Summary
183+
In this learning path, we successfully built a complete, end-to-end prototype of an IoT solution. Our journey began with a simulator streaming realistic telemetry data to Azure through IoT Hub. We leveraged Azure Stream Analytics to process and route this streaming data directly into Cosmos DB, providing scalable and reliable storage. Additionally, we developed two Azure Functions: the first continuously monitors incoming temperature readings, sending email notifications whenever the temperature exceeds a predefined threshold, ensuring proactive alerts. The second Azure Function aggregates recent temperature data from the last minute and provides this information via an HTTP endpoint. Finally, we utilized this aggregation function within our user-friendly web portal, enabling real-time visualization of temperature data, thus completing our robust and interactive IoT solution.

0 commit comments

Comments
 (0)