Skip to content

Latest commit

 

History

History

firmware

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 
 
 
 
 

README.md

⚡ Firmware — ESP32-C3 → Azure IoT Hub

Arduino firmware for streaming sensor telemetry to Azure IoT Hub via MQTT/TLS. Credentials are injected automatically from Terraform — no manual copy-paste.


How It Works

The firmware is a Mustache template rendered by a PowerShell script that reads credentials directly from Terraform outputs and Azure CLI:

firmware/GlobalAzureKit/GlobalAzureKit.mustache  ← Mustache template (in git)
        │
        │  configure-sketch.ps1
        │  reads: terraform output + az iot hub device-identity
        ▼
firmware/output/GlobalAzureKit/GlobalAzureKit.ino   ← ready to flash (gitignored)

No credentials are ever committed to git. The generated sketch lives in firmware/output/ which is gitignored.


Telemetry Format

Sensor JSON payload
BMP280 {"temperature": 24.35, "pressure": 1013.25, "has_led": true}
BME280 {"temperature": 24.35, "pressure": 1013.25, "humidity": 52.10, "has_led": true}

Temperature in °C, pressure in hPa, humidity in %. The has_led field indicates whether the firmware was compiled with LED control enabled (-EnableLed). The monitor dashboard auto-detects the sensor type from the payload.

Kit default: LED is enabled. The kit includes a standard LED + 220Ω resistor wired to GPIO2 (active HIGH). See LED Wiring for the circuit diagram.


Prerequisites

Before starting, make sure you have:

  1. Completed §1–§7 of the hardware setup guide — board, sensor, and Arduino IDE working
  2. Azure infrastructure deployed — run terraform apply (terraform/README.md)
  3. Azure CLI with azure-iot extension installed (az extension add --name azure-iot)

Step 1 — Install Arduino Libraries

Open Arduino IDE → Sketch → Include Library → Manage Libraries... and install:

Library Author Required for
azure-sdk-for-c Microsoft Azure IoT Hub MQTT + SAS auth
Adafruit BMP280 Adafruit BMP280 sensor (install all dependencies)
Adafruit BME280 Adafruit BME280 sensor (only if you have one)

Step 2 — Generate the Configured Sketch

From the firmware/ directory, run:

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

⚠️ Replace YOUR_SSID with your actual Wi-Fi network name. The Wi-Fi must be 2.4 GHz — ESP32-C3 doesn't support 5 GHz networks.

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 need to run this from the firmware/ directory, and terraform apply must have completed first. ❌ "Cannot find device"? Make sure terraform apply ran successfully in Step 1.

The script automatically:

  1. Reads IoT Hub hostname and device ID from Terraform outputs
  2. Retrieves the device key from Azure CLI
  3. Renders the Mustache template with real values (via Poshstache)
  4. Selects the correct sensor code (BMP280, BME280, or auto-detect)
  5. Writes the ready-to-flash sketch to firmware/output/GlobalAzureKit/

Parameters

Parameter Required Default Values
-WifiSsid Yes — Your WiFi network name
-WifiPassword Yes — Your WiFi password
-SensorType No auto BMP280, BME280, auto
-EnableLed No (off) Switch — enables LED control via C2D commands (GPIO2)
-DeviceIndex No 0 Device number (0, 1, 2...)

Tip: use -SensorType auto if you want the firmware to detect both BMP280 and BME280 at startup. Add -EnableLed to include Cloud-to-Device LED control (GPIO2). The telemetry will include "has_led": true and the dashboard will show LED controls and state.

LED Wiring

The kit includes a standard LED and a 220Ω resistor. Wire them before flashing with -EnableLed:

ESP32-C3 Component Notes
GPIO2 220Ω resistor → LED anode (+) Long leg
GND LED cathode (−) Short leg

See esp32c3_super_mini_pinout.md for the full wiring diagram.


Step 3 — Upload and Monitor

  1. Open firmware/output/GlobalAzureKit/GlobalAzureKit.ino in Arduino IDE

  2. Verify board settings in Tools menu:

    Setting Value
    Board ESP32C3 Dev Module
    Port COMx (your port from §2)
    USB CDC On Boot Enabled
  3. Close Serial Monitor if open (can't use the port simultaneously)

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

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

Expected Output

========================================
 ExtraNet.GlobalAzureKit
 Global Azure Torino 2026
========================================

2026/06/14 10:30:00 [INFO] BMP280 sensor detected at 0x76
2026/06/14 10:30:00 [INFO] Connecting to WiFi SSID MyNetwork
.....
2026/06/14 10:30:02 [INFO] WiFi connected, IP address: 192.168.1.42
2026/06/14 10:30:02 [INFO] Setting time using SNTP
.......
2026/06/14 10:30:05 [INFO] Time initialized!
2026/06/14 10:30:05 [INFO] Client ID: globalazure-kit-device-0
2026/06/14 10:30:05 [INFO] Username: globalazure-kit-iothub.azure-devices.net/...
2026/06/14 10:30:05 [INFO] MQTT client started
2026/06/14 10:30:06 [INFO] MQTT event MQTT_EVENT_CONNECTED
2026/06/14 10:30:06 [INFO] Subscribed for cloud-to-device messages; id:1
2026/06/14 10:30:06 [INFO] Sending telemetry ...
2026/06/14 10:30:06 [INFO] Message published: {"temperature":24.35,"pressure":1013.25,"has_led":true}
2026/06/14 10:30:06 [INFO] MQTT event MQTT_EVENT_PUBLISHED

If you see MQTT_EVENT_CONNECTED and Message published — the device is sending telemetry to Azure IoT Hub!


Step 4 — Verify on Azure

Check that messages are arriving at IoT Hub:

# Get your hub and device names from Terraform
cd ../terraform
$hubName = terraform output -raw iothub_name
$deviceId = terraform output -raw project_prefix
$deviceId = "${deviceId}-device-0"

az iot hub monitor-events --hub-name $hubName --device-id $deviceId

If you used default settings, the values are globalazure-kit-iothub and globalazure-kit-device-0.

Note: this command requires Azure CLI ≥ 2.67.0 and the azure-iot extension.

You should see JSON payloads arriving in real-time. Press Ctrl+C to stop.


Telemetry Interval

Default: 10 seconds. Configurable in the template:

#define TELEMETRY_FREQUENCY_MILLISECS 10000  // 10 seconds

IoT Hub F1 free tier allows 8,000 messages/day (~1 every 11 seconds). For a workshop of a few hours, 10 seconds is safe.


Template System

The template .ino in firmware/GlobalAzureKit/ uses two mechanisms:

Token Placeholders

#define IOT_CONFIG_WIFI_SSID    "{{WIFI_SSID}}"
#define IOT_CONFIG_IOTHUB_FQDN  "{{IOTHUB_FQDN}}"

Replaced with real values by configure-sketch.ps1.

Conditional Sensor Blocks

// {{#BMP}}
#include <Adafruit_BMP280.h>
// {{/BMP}}
// {{#BME}}
#include <Adafruit_BME280.h>
// {{/BME}}
Tag Kept when -SensorType is
BMP BMP280 or auto
BME BME280 or auto
AUTO auto only
ONLY_BMP BMP280 only
ONLY_BME BME280 only
LED -EnableLed switch is set

TLS Certificate Note

Azure IoT Hub uses Microsoft PKI (issuer: MSFT RS256 CA-1, root: Microsoft RSA Root Certificate Authority 2017).

The azure_ca.h header shipped with azure-sdk-for-c (last updated Feb 2023) contains only:

  • Baltimore CyberTrust Root — expired October 2025
  • DigiCert Global Root G2 — valid, but not the primary root for Azure IoT Hub

It does not include Microsoft RSA Root Certificate Authority 2017, so using azure_ca.h directly causes TLS handshake failures.

The firmware bypasses azure_ca.h entirely and embeds both required roots directly in the sketch:

  • Microsoft RSA Root Certificate Authority 2017 — primary, expires 2042
  • DigiCert Global Root G2 — fallback, expires 2038

No action needed — the certificates are already embedded in the generated sketch.


Troubleshooting

Problem Solution
Serial Monitor empty Tools → USB CDC On Boot → Enabled, then re-upload
"No BMP280/BME280 found" Check wiring: SDA→GPIO8, SCL→GPIO9, VCC→3.3V. Try 0x77 instead of 0x76
WiFi won't connect Verify SSID/password. ESP32-C3 supports 2.4 GHz only (not 5 GHz)
MQTT_EVENT_ERROR (TLS) Check Serial Monitor for esp_tls_last_esp_err codes. Ensure NTP time synced
MQTT_EVENT_ERROR (refused) Verify device exists: az iot hub device-identity show --hub-name ... --device-id ...
"Failed generating SAS token" Check IOT_CONFIG_DEVICE_KEY — must be a valid base64 key
Upload fails Close Serial Monitor before uploading. Verify board/port in Tools
configure-sketch.ps1 fails Run from firmware/ directory. Ensure terraform apply was run first

File Structure

firmware/
├── README.md                       # This file
├── 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

Next Step

The firmware is sending data — now start the dashboard to visualize it!

First, generate the .env file (if you haven't already):

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

Then start the dashboard:

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

Open http://localhost:5000 and see your sensor data in real-time charts.

Full setup and API reference: monitor/README.md


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