Skip to content

Repository files navigation

☁️ ExtraNet.GlobalAzureKit

Open-source IoT workshop kit for Global Azure Torino 2026. Stream real-time sensor telemetry from an ESP32-C3 to Azure IoT Hub and visualize it in a live dashboard — all deployed with one Terraform command.

License: Unlicense Terraform Azure Arduino Python AI-accelerated

Engineered by ExtraNet, built in collaboration with GitHub Copilot — code, infrastructure and documentation crafted through human-AI pair programming.


About This Project

This isn't a branded gadget you forget in a drawer.

ExtraNet.GlobalAzureKit is a complete, working IoT pipeline — from a physical sensor on your desk to a real-time dashboard powered by Azure. We built it because we believe the best way to learn cloud isn't reading slides — it's getting your hands on real infrastructure, real telemetry, and real architecture decisions.

The kit reflects how we work every day: Terraform for repeatable deploys, Event Hub for scalable ingestion, Log Analytics for observability, least-privilege auth everywhere. The same patterns you see here are the ones that run in production — scaled up, hardened, and monitored.

GitHub Copilot was our accelerator: we directed the architecture, made the design choices, and Copilot turned them into code at speed. The expertise is ours; the velocity is AI.


What's in the Kit

Component Description
ESP32-C3 Super Mini RISC-V single-core microcontroller, Wi-Fi 4, USB-C, I²C
BMP280 sensor Temperature + atmospheric pressure (I²C, Bosch Sensortec)
BME280 support Temperature + pressure + humidity — pin-compatible drop-in replacement
LED Standard LED (any color) — controlled via Cloud-to-Device commands from the dashboard
220Ω resistor Current-limiting resistor for the LED (GPIO2 → 220Ω → LED → GND)
Arduino firmware Template-based .ino with Azure SDK for Embedded C, SAS auth, MQTT/TLS
configure-sketch.ps1 Auto-generates the firmware from Terraform outputs — zero manual credential copy
Terraform IaC One-command deploy: IoT Hub, Event Hub, Log Analytics, monitoring, alerts
Monitor dashboard Flask + Chart.js + Socket.IO — dark theme, real-time charts, CSV export, LED control
Workshop guides Step-by-step ESP32-C3 wiring, flashing, and troubleshooting (§1–§8)

Architecture

graph TB
    subgraph EDGE["🔌 Edge — ESP32-C3 + Sensors"]
        ESP["ESP32-C3 Super Mini\nRISC-V · Wi-Fi 4 · USB-C"]
        SENSOR["BMP280 / BME280\nTemperature · Pressure · Humidity*"]
        LED["LED + 220Ω Resistor\nGPIO2 · Active HIGH"]
        ESP -->|"I²C — SDA=GPIO8 · SCL=GPIO9"| SENSOR
        ESP -->|"GPIO2"| LED
    end

    subgraph AZURE["☁️ Microsoft Azure"]
        subgraph DM["📡 Device Management"]
            IOT["IoT Hub · F1 Free Tier\nMQTT over TLS 1.2"]
        end
        subgraph SP["📊 Stream Processing"]
            EH["Event Hub · Standard\n2 partitions · 1-day retention"]
            CG["Consumer Group\nmonitor"]
        end
        subgraph OBS["🔍 Observability"]
            LA["Log Analytics\n30-day retention"]
            ALERT["Metric Alert\ndropped telemetry > 10"]
        end
        subgraph IAC["🏗️ Infrastructure as Code"]
            TF["Terraform\nazurerm ~> 4.14"]
            STATE["Remote State\nAzure Storage"]
        end
    end

    subgraph LOCAL["🖥️ Local Dashboard"]
        DASH["Flask · Chart.js · Socket.IO\nReal-time visualization"]
    end

    IOT -->|"C2D · LED commands"| ESP
    ESP ==>|"D2C · MQTT/TLS"| IOT
    IOT -->|"Message Route"| EH
    EH --> CG -->|"AMQP"| DASH
    IOT -.->|"Diagnostics"| LA
    EH -.->|"Diagnostics"| LA
    LA -.-> ALERT
    TF -.->|"manages"| IOT
    TF -.->|"manages"| EH
    TF -.->|"manages"| LA
    TF -->|"state"| STATE

    style AZURE fill:#e8f1fb,stroke:#0078d4,stroke-width:3px
    style EDGE fill:#e8f5e9,stroke:#2e7d32,stroke-width:2px
    style LOCAL fill:#f3e5f5,stroke:#7b1fa2,stroke-width:2px
Loading

* Humidity available only with BME280 sensor

The Data Journey — Edge to Cloud

# Step Component Protocol / Detail
1 Sense BMP280 / BME280 I²C @ 0x76 — temperature, pressure, humidity*
2 Serialize ESP32-C3 firmware {"temperature": 22.5, "pressure": 1013.2, "humidity": 65.3}
3 Transmit ESP32-C3 Wi-Fi MQTT over TLS 1.2 → IoT Hub
4 Authenticate Azure IoT Hub Per-device SAS token (connection string)
5 Route IoT Hub Message Route All D2C messages → Event Hub endpoint
6 Buffer Event Hub Standard, 2 partitions, 1-day retention
7 Consume Monitor dashboard AMQP via monitor consumer group
8 Visualize Browser Chart.js + WebSocket push via Socket.IO
9 Control Dashboard → IoT Hub C2D messages — LED on/off via {"action":"led","seconds":N}
10 Observe Log Analytics Diagnostic logs → KQL queries + metric alerts

Quick Start

Goal: from zero to live dashboard in ~30 minutes.

Platform: the wizards and start.bat target Windows + PowerShell. On Linux/macOS you can still run the manual steps (Option B) — Terraform, Azure CLI and Python are all cross-platform — but the guided experience is Windows-first.

Option A — Interactive Wizard (recommended)

Windows — double-click start.bat (or run .\wizard-gui.ps1 in PowerShell).

A graphical window opens and guides you through every step with buttons, text fields, and a live log:

  1. ✅ Checks prerequisites (Terraform, Azure CLI, Python) with download links
  2. 🔐 Login to Azure (one click)
  3. ☁️ Deploy ~12 Azure resources (one click + confirmation)
  4. 📡 Configure firmware (type WiFi SSID/password, choose sensor, click Generate)
  5. 📊 Launch the monitor dashboard (one click → opens browser)

Every step enables the next. You can close and re-run anytime — it skips what's already done.

Terminal user? Run .\wizard.ps1 instead for a CLI-based wizard with the same steps.

Option B — Manual step-by-step

If you prefer to run each command yourself, expand the section below.

Every command shows what you should see. If the output doesn't match, a troubleshooting tip is right below.

Click to expand the manual procedure

Overview — The 4-Step Journey

Step What you'll do Time
1 Deploy Azure infrastructure (IoT Hub, Event Hub, monitoring) ~5 min
2 Wire ESP32-C3 + BMP280 sensor + LED with resistor and test with Arduino IDE ~15 min
3 Flash the IoT Hub firmware (credentials auto-injected) ~5 min
4 Launch the real-time dashboard and see live data ~3 min

Prerequisites

Install these tools before starting. Click the link to download, then verify with the command:

# Tool Install Verify command
1 Terraform ≥ 1.5 Download terraform -version
2 Azure CLI ≥ 2.50 Download az version
3 Python ≥ 3.10 Download python --version
4 Arduino IDE 2.x Download Open the app
5 Azure subscription Create free account

⚠️ IoT Hub Free (F1) limit: Azure allows only one F1 IoT Hub per subscription. If you already have one (even empty or from an old experiment), the deploy will fail with MaxNumberOfIotHubsExceeded. Fix: delete the existing F1 hub, or set iothub_sku = "S1" in terraform.auto.tfvars (~€21/month pro-rata — pennies for a workshop).

Open PowerShell and verify everything is installed:

terraform -version

✅ You should see Terraform v1.x.x. If it says "not recognized" → install Terraform and restart PowerShell.

az version

✅ You should see "azure-cli": "2.x.x". If it says "not recognized" → install Azure CLI and restart PowerShell.

python --version

✅ You should see Python 3.1x.x. If it says "not recognized" → install Python (check "Add to PATH" during setup!) and restart PowerShell.

Finally, install the Azure IoT extension (safe to re-run if already installed):

az extension add --name azure-iot

Step 1 — Deploy Azure Infrastructure

Open PowerShell in the repository folder (where you cloned/unzipped this project).

1a. Login to Azure

az login

What happens: your default browser opens. Sign in with your Azure account.

When done, the terminal shows your subscription info:

[
  {
    "name": "Your Subscription Name",
    "id": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
    ...
  }
]

❌ Browser doesn't open? Try az login --use-device-code instead — it gives you a code to enter at https://microsoft.com/devicelogin

1b. Navigate to the terraform folder

cd terraform

No output? That's normal — it just changed directory.

1c. Create the Terraform state backend

.\scripts\setup-tfstate-backend.ps1

What you'll see: the script creates an Azure Storage Account (takes ~1 minute):

Creating resource group 'globalazure-tfstate-rg'...
Creating storage account '...'...
Creating container 'tfstate'...
✅ Backend configuration written to backend.tfvars

❌ Error "subscription not found"? Go back to step 1a and make sure az login succeeded.

1d. Create and edit the variables file

Copy-Item terraform.auto.tfvars.example terraform.auto.tfvars

No output? That's correct — the file was copied silently.

Now find your Subscription ID:

az account show --query id -o tsv

What you'll see: a string like xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx. Select it and copy (Ctrl+C).

Open the variables file in Notepad:

notepad terraform.auto.tfvars

Find this line:

subscription_id = "ENTER-YOUR-SUBSCRIPTION-ID-HERE"

Replace the placeholder with your Subscription ID so it looks like:

subscription_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"

Save the file (Ctrl+S) and close Notepad.

1e. Initialize Terraform

terraform init -backend-config=backend.tfvars

What you'll see (takes a few seconds):

Initializing the backend...
...
Terraform has been successfully initialized!

❌ "Error configuring the backend"? Check that backend.tfvars exists (step 1c). ❌ "Backend configuration changed"? Type yes to migrate.

1f. Deploy!

terraform apply

What you'll see: Terraform shows a plan listing ~12 Azure resources. At the bottom:

Do you want to perform these actions?
  Enter a value:

Type yes and press Enter.

Wait 2–3 minutes. When it finishes:

Apply complete! Resources: 12 added, 0 changed, 0 destroyed.

🎉 Azure infrastructure is ready!

❌ Error about IoT Hub region? Open terraform.auto.tfvars again and change iothub_location = "westeurope" to "northeurope".

Full details and troubleshooting: terraform/README.md


Step 2 — Wire and Test the ESP32-C3

Follow the hardware setup guide (§1–§7):

Guide When to use
esp32c3_quickstart.md Happy path — everything works first try
esp32c3_setup_guide.md Troubleshooting — detailed explanations for each step
esp32c3_super_mini_pinout.md Pinout reference — wiring diagram

Done when you see sensor readings in the Serial Monitor:

Temp: 24.35 C   Pressure: 1013.25 hPa

Step 3 — Flash the IoT Hub Firmware

3a. Install Arduino libraries

In Arduino IDE: Sketch → Include Library → Manage Libraries...

Search and install these (one at a time):

Search for Click "Install" on Notes
azure-sdk-for-c azure-sdk-for-c by Microsoft Required
Adafruit BMP280 Adafruit BMP280 Library by Adafruit Install if you have a BMP280
Adafruit BME280 Adafruit BME280 Library by Adafruit Install if you have a BME280

3b. Generate the configured sketch

Go back to PowerShell (you should still be in the terraform folder from Step 1):

cd ../firmware
$pass = Read-Host "WiFi password" -AsSecureString
.\configure-sketch.ps1 -WifiSsid "YOUR_SSID" -WifiPassword $pass -SensorType BME280 -EnableLed

Replace BME280 with BMP280 if you have a BMP280 sensor.

⚠️ Replace YOUR_SSID with your Wi-Fi network name. You'll be prompted for the password securely. The Wi-Fi must be 2.4 GHz — ESP32-C3 doesn't support 5 GHz networks. Add -EnableLed to enable LED control from the dashboard (the kit includes an LED + 220Ω resistor on GPIO2).

What you'll see:

Reading Terraform outputs...
Reading device key from Azure CLI...
Generating sketch for BMP280...
✅ Sketch written to output\GlobalAzureKit\GlobalAzureKit.ino

❌ "terraform: not recognized"? You moved to a different terminal. Open a new PowerShell in the repository folder and cd firmware. ❌ "Cannot find device"? Step 1f (terraform apply) must have completed successfully first.

3c. Upload to ESP32-C3

  1. In Arduino IDE, open firmware\output\GlobalAzureKit\GlobalAzureKit.ino

  2. Check Tools menu settings:

    Setting Value
    Board ESP32C3 Dev Module
    Port COMx (the port you found in §2)
    USB CDC On Boot Enabled ⚠️ critical!
  3. Close the Serial Monitor if it's open (can't upload while it's using the port)

  4. Press Upload (→ button) — wait for Done uploading.

  5. Open Tools → Serial Monitor — set baud rate to 115200

What you'll see:

========================================
 ExtraNet.GlobalAzureKit
 Global Azure Torino 2026
========================================
[INFO] BMP280 sensor detected at 0x76
[INFO] Connecting to WiFi ...
.....
[INFO] WiFi connected, IP address: 192.168.x.x
[INFO] MQTT event MQTT_EVENT_CONNECTED
[INFO] Message published: {"temperature":24.35,"pressure":1013.25,"has_led":true}

If you see MQTT_EVENT_CONNECTED — the device is talking to Azure! 🎉

❌ Serial Monitor blank? → Tools → USB CDC On Boot → Enabled, then re-upload. ❌ "WiFi not connected"? → Check SSID/password. Must be 2.4 GHz (not 5 GHz). ❌ MQTT error? → Check error details in Serial Monitor. Try running configure-sketch.ps1 again.

Full troubleshooting: firmware/README.md


Step 4 — Launch the Monitor Dashboard

4a. Generate the .env configuration file

Go back to PowerShell:

cd ../terraform
terraform output -raw dot_env_content | Out-File -FilePath ../monitor/.env -Encoding UTF8

No output? That's correct — the .env file was created silently.

4b. Set up and start the dashboard

cd ../monitor
python -m venv .venv
.\.venv\Scripts\Activate.ps1
pip install -r requirements.txt

You'll see pip downloading packages — wait until it finishes (takes ~30 seconds).

python app.py

What you'll see:

 * Running on http://127.0.0.1:5000

❌ "EVENTHUB_CONNECTION_STRING not set"? → Step 4a failed. Go back and re-run the terraform output command. ❌ "No module named flask"? → Run .\.venv\Scripts\Activate.ps1 first, then pip install -r requirements.txt.

4c. Open the dashboard

Open your browser and go to: http://localhost:5000

What you'll see:

  • A dark-themed dashboard with the Azure color palette
  • Temperature and pressure charts updating in real-time
  • A device table showing your ESP32-C3 with its latest readings
  • A telemetry log at the bottom with live messages

Done! 🎉

Congratulations — the kit is working end-to-end!

The complete data journey is live:

SensorESP32-C3Wi-FiAzure IoT HubEvent HubDashboard

When you're done, remember to destroy the Azure resources to avoid ongoing costs:

cd terraform
terraform destroy

Supported Sensors

Sensor Temperature Pressure Humidity I²C Address Notes
BMP280 0x76 / 0x77 Included in the kit
BME280 0x76 / 0x77 Drop-in replacement, same pinout

The dashboard auto-detects the sensor type from the telemetry payload:

  • BMP280: {"temperature": 22.5, "pressure": 1013.2, "has_led": true}
  • BME280: {"temperature": 22.5, "pressure": 1013.2, "humidity": 65.3, "has_led": true}

When humidity data arrives, the humidity chart and stats appear automatically.


Dashboard Features

  • Dark theme with Azure-inspired color palette
  • 5 stat cards — active devices, messages, temperature, pressure, humidity
  • Real-time charts — temperature, pressure, and humidity (Chart.js)
  • Device filter pills — click to show/hide individual devices on charts and table
  • Time range buttons — 1m, 5m, 15m, 1h, All
  • CSV export — per device or all devices combined
  • Device table — sensor type (BMP280/BME280), status, readings, message count
  • LED indicator — shows LED status per device (grey/off/glowing) when firmware has LED enabled
  • LED shortcut — one-click 💡 ON button for devices with LED support
  • C2D messaging — send any JSON command to a device via Cloud-to-Device
  • Collapsible telemetry log — color-coded entries with timestamps
  • Auto-detection — humidity chart appears only when BME280 data arrives
  • Live connection indicator — shows WebSocket status

See monitor/README.md for setup and API reference.


Project Structure

.
├── .gitignore                          # Git ignore rules
├── README.md                           # This file
├── start.bat                           # Double-click launcher for the GUI wizard
├── wizard-gui.ps1                      # GUI setup wizard (Windows Forms)
├── wizard.ps1                          # CLI setup wizard (terminal fallback)
├── LICENSE                             # Unlicense (public domain)
├── .editorconfig                       # Editor settings (indent, charset, EOL)
├── .github/
│   ├── copilot-instructions.md         # Copilot rules and conventions
│   └── prompts/
│       └── terraform-deploy.prompt.md  # Guided Azure deploy procedure
├── firmware/
│   ├── README.md                       # Firmware documentation
│   ├── configure-sketch.ps1            # Mustache template → configured sketch
│   ├── GlobalAzureKit/
│   │   └── GlobalAzureKit.mustache     # Mustache template (sensor blocks + credentials)
│   └── output/                         # Generated sketches (gitignored)
│       └── GlobalAzureKit/
│           └── GlobalAzureKit.ino      # Ready-to-flash sketch
├── monitor/
│   ├── app.py                          # Flask server + Event Hub consumer
│   ├── requirements.txt                # Python dependencies
│   ├── requirements-lock.txt            # Pinned dependency versions
│   ├── README.md                       # Monitor documentation
│   └── templates/
│       └── index.html                  # Dashboard (Chart.js + Socket.IO)
├── terraform/
│   ├── main.tf                         # Provider, backend, locals
│   ├── variables.tf                    # Input variables
│   ├── resources.tf                    # IoT Hub, Event Hub, devices
│   ├── monitoring.tf                   # Log Analytics, diagnostics, alerts
│   ├── outputs.tf                      # Connection strings, .env output
│   ├── backend.tfvars.example          # Backend config template
│   ├── terraform.auto.tfvars.example   # Variables template
│   ├── .gitignore                      # Excludes tfstate and sensitive files
│   ├── README.md                       # Terraform documentation
│   └── scripts/
│       └── setup-tfstate-backend.ps1   # Bootstrap storage for tfstate
├── esp32c3_setup_guide.md              # Full guide (§1–§8 + troubleshooting)
├── esp32c3_quickstart.md               # Quick-start guide (happy path)
└── esp32c3_super_mini_pinout.md        # ESP32-C3 pinout + wiring reference

Security

This kit is designed for workshop environments. Credentials are never committed to git:

Asset Protection
Terraform state (.tfstate) Remote Azure Storage backend, gitignored
Variable files (.tfvars) .gitignore — only .example templates in repo
Event Hub connection string .env file, gitignored
Device credentials Mustache template with {{&token}} placeholders; configure-sketch.ps1 injects at runtime
Generated firmware firmware/output/ is gitignored — never committed
Auth rules Least privilege: send-only for devices, listen-only for monitor

Documentation

Document Description
terraform/README.md Terraform infrastructure — deploy, customize, destroy
monitor/README.md Monitor dashboard — setup, API reference, features
firmware/README.md Arduino firmware — libraries, configure-sketch, upload, troubleshoot
esp32c3_setup_guide.md Full ESP32-C3 guide: wiring, flashing, troubleshooting (§1–§8)
esp32c3_quickstart.md Quick-start ESP32-C3 guide (happy path)
esp32c3_super_mini_pinout.md ESP32-C3 Super Mini pinout + sensor wiring

Estimated Azure Costs

Resource Cost
IoT Hub F1 Free (8,000 msgs/day, 500 devices)
Event Hub Standard ~€13/month (1 TU)
Log Analytics ~€2.50/GB ingested
Storage (tfstate) ~€0.02/month
Estimated total ~€13–18/month

For a workshop lasting a few hours, the cost is negligible. Run terraform destroy when done!


Design Philosophy

This kit is designed for hands-on learning — not production use — but every architectural choice mirrors real-world practice:

Decision Why
Event Hub instead of built-in IoT Hub endpoint Decouples ingestion from consumption — the same pattern used when you add Stream Analytics, Azure Functions, or multiple consumers in production
Terraform with remote state Infrastructure as Code from day one — not portal clicks you can't reproduce. Remote state on Azure Storage enables team collaboration
Separate auth rules (send / listen) Least-privilege by default. The device can only send; the dashboard can only read. A compromised consumer can't inject messages
Log Analytics + metric alerts Observability isn't an afterthought — if telemetry drops, you know immediately. This is the same stack that monitors production workloads
Mustache-based firmware template Credentials never touch git. The generation script reads secrets at runtime from Terraform + Azure CLI — zero manual copy-paste
SAS token auth (not X.509) Simpler for a workshop, but the architecture supports certificate-based auth with minimal changes when you move to production

Credits

ExtraNet Srl Architecture, design decisions, and project direction. We build cloud solutions on Azure — this kit is a taste of how we work
GitHub Copilot AI-accelerated development — code, infrastructure, and documentation generated through human-AI collaboration
Global Azure The worldwide community event this kit was created for
Bosch Sensortec BMP280 / BME280 sensor hardware
Espressif Systems ESP32-C3 RISC-V microcontroller

License

This project is released into the public domain under the Unlicense. You can copy, modify, distribute, and use it for any purpose — no attribution required.


Engineered by ExtraNet, AI-accelerated with GitHub Copilot
Global Azure Torino 2026

About

Open-source IoT workshop kit for Global Azure Torino 2026 — ESP32-C3 + Azure IoT Hub + Terraform

Resources

Stars

13 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages