Back to Embedded Systems Hardware Engineering Series

Template 3: Complete IoT Product Build

April 17, 2026 Wasil Zafar 55 min read

Full IoT product development lifecycle: requirements, hardware design, firmware with OTA updates, cloud connectivity (MQTT), enclosure design, regulatory certification (FCC/CE), and production handoff with BOM costing.

Table of Contents

  1. Product Specification
  2. Hardware Design
  3. Firmware & OTA
  4. Cloud Connectivity
  5. Certification & Compliance
  6. Production Costing
  7. IoT Product Planner
  8. Conclusion

Step 1: Product Specification

CategorySpecificationTarget
ProductSmart Environmental Sensor NodeIndoor air quality monitor
MCUESP32-S3 (dual-core, WiFi + BLE)240 MHz, 512 KB SRAM
SensorsBME680 (T/H/P/VOC), SCD41 (CO₂)±1°C, ±3% RH, ±40 ppm
Display1.54″ e-Paper (200 × 200)Ultra-low power, sunlight readable
PowerUSB-C + 18650 LiPo (3000 mAh)6+ months battery on WiFi interval
ConnectivityWiFi 802.11 b/g/n + BLE 5.0MQTT over TLS 1.3
EnclosureInjection-molded ABS, IP4280 × 80 × 30 mm
CertificationsFCC Part 15B, CE (RED), RoHSUnintentional radiator Class B
OTA UpdatesDual-partition A/B schemeEncrypted firmware, rollback
Target BOM Cost1k units< $18 per unit

Step 2: Hardware Design

IoT Sensor Node — System Architecture
flowchart TD
    A["USB-C
5V Input"] --> B["BQ24075
Li-Ion Charger"] B --> C["18650 Cell
3000 mAh"] C --> D["TPS63020
3.3V Buck-Boost"] D --> E["ESP32-S3
WROOM-1"] E -->|I2C| F["BME680
T/H/P/VOC"] E -->|I2C| G["SCD41
CO₂ Sensor"] E -->|SPI| H["1.54" e-Paper
200×200 px"] E -->|WiFi| I["Cloud
MQTT Broker"] E -->|BLE| J["Mobile App
Provisioning"] D --> K["Fuel Gauge
MAX17048"] style E fill:#3B9797,color:#fff
# IoT product BOM cost estimator (1k unit pricing)
# All prices from DigiKey/LCSC at 1000-unit breaks

bom = [
    ("ESP32-S3-WROOM-1 (N16R8)",    "MCU Module",       2.80),
    ("BME680",                        "Env Sensor",       5.20),
    ("SCD41",                         "CO2 Sensor",       4.90),
    ("1.54\" e-Paper (GDEW0154M09)", "Display",          3.50),
    ("BQ24075 (Li-Ion Charger)",     "PMIC",             0.95),
    ("TPS63020 (Buck-Boost)",        "Regulator",        1.20),
    ("MAX17048 (Fuel Gauge)",        "Battery Monitor",   0.85),
    ("USB-C Connector",              "Connector",         0.18),
    ("18650 Battery Holder",         "Mechanical",        0.12),
    ("Antenna (PCB trace)",          "RF",                0.00),
    ("Passives (R, C, L, D) ×35",   "Passives",          0.45),
    ("PCB 4-layer (100×60mm)",       "PCB",               1.20),
    ("Assembly (PCBA)",              "Manufacturing",      2.50),
    ("Enclosure (ABS molded)",       "Mechanical",         1.80),
    ("Packaging + label",            "Packaging",          0.40),
]

print("IoT Sensor Node — BOM Cost Estimate (1,000 units)")
print("=" * 62)
print(f"{'Component':<35} {'Category':<16} {'Cost':>8}")
print("-" * 62)

total = 0
for name, cat, cost in bom:
    total += cost
    print(f"  {name:<33} {cat:<16} ${cost:>6.2f}")

print("-" * 62)
print(f"  {'TOTAL BOM COST':<33} {'':16} ${total:>6.2f}")

margin_target = 0.55  # 55% gross margin
retail_price = total / (1 - margin_target)
print(f"\n  Target Retail (55% margin):       ${retail_price:.2f}")
print(f"  BOM-to-Retail Ratio:              {total/retail_price:.1%}")
print(f"  Under $18 target:                 {'✓ YES' if total < 18 else '✗ NO'}")

Step 3: Firmware & OTA Updates

/* IoT firmware architecture — ESP-IDF with dual-partition OTA
   Partition table: factory(1MB) + ota_0(1MB) + ota_1(1MB) + nvs(24KB) */

#include <stdio.h>
#include <string.h>

/* OTA update state machine */
typedef enum {
    OTA_IDLE,
    OTA_CHECKING,         /* Polling server for new version */
    OTA_DOWNLOADING,      /* Streaming firmware to inactive partition */
    OTA_VERIFYING,        /* SHA-256 + signature check */
    OTA_REBOOTING,        /* Switch boot partition, reboot */
    OTA_ROLLBACK,         /* Revert to previous partition on failure */
    OTA_ERROR
} ota_state_t;

/* Sensor reading structure — packed for MQTT transmission */
typedef struct __attribute__((packed)) {
    uint32_t timestamp;       /* Unix epoch seconds */
    float    temperature;     /* °C from BME680 */
    float    humidity;        /* %RH from BME680 */
    float    pressure;        /* hPa from BME680 */
    float    voc_index;       /* IAQ index 0-500 from BME680 */
    uint16_t co2_ppm;         /* ppm from SCD41 */
    uint8_t  battery_pct;     /* % from MAX17048 */
    uint8_t  wifi_rssi;       /* dBm (signed, stored as uint8) */
} sensor_payload_t;           /* 26 bytes total */

/* Sensor task — reads all sensors, publishes via MQTT
 *
 * Pseudocode flow:
 *   1. Wake from deep sleep (RTC timer)
 *   2. Initialize I2C bus
 *   3. Trigger BME680 forced measurement (wait ~200ms)
 *   4. Read SCD41 single-shot measurement (wait ~5s)
 *   5. Read MAX17048 battery SOC
 *   6. Connect WiFi (use NVS-stored credentials)
 *   7. Publish MQTT message (QoS 1, retain)
 *   8. Check OTA server (every 24 hours)
 *   9. Disconnect WiFi
 *  10. Enter deep sleep (configurable: 5-60 min)
 *
 * Deep sleep current: ~10 µA (ESP32-S3 + all sensors off)
 * Active cycle current: ~160 mA for ~8 seconds
 * Average current at 15-min interval: ~14 µA
 * Battery life (3000 mAh): ~3000/0.014 ≈ 214,000 hours ≈ 24+ years theoretical
 * Real-world with self-discharge: ~12-18 months
 */

Step 4: Cloud Connectivity

# MQTT topic structure and message format for IoT sensor fleet
# Compatible with AWS IoT Core, Azure IoT Hub, HiveMQ

import json

# MQTT topic hierarchy
topics = {
    "telemetry":  "devices/{device_id}/telemetry",
    "status":     "devices/{device_id}/status",
    "command":    "devices/{device_id}/cmd",
    "ota":        "devices/{device_id}/ota",
    "config":     "devices/{device_id}/config",
}

# Example telemetry payload (published every 15 min)
telemetry_msg = {
    "device_id": "sensor-node-001",
    "fw_version": "1.2.0",
    "timestamp": "2026-04-17T10:30:00Z",
    "sensors": {
        "temperature": 23.5,
        "humidity": 45.2,
        "pressure": 1013.25,
        "voc_index": 85,
        "co2_ppm": 620
    },
    "battery": {
        "percent": 78,
        "voltage": 3.82
    },
    "wifi_rssi": -52
}

print("MQTT Topic Structure:")
print("=" * 55)
for purpose, topic in topics.items():
    example = topic.format(device_id="sensor-node-001")
    print(f"  {purpose:<12} → {example}")

print(f"\nTelemetry Payload ({len(json.dumps(telemetry_msg))} bytes JSON):")
print(json.dumps(telemetry_msg, indent=2))

# TLS 1.3 connection parameters
print("\nSecurity Configuration:")
print("  Protocol:    MQTT 3.1.1 over TLS 1.3")
print("  Auth:        X.509 client certificate (per-device)")
print("  CA:          AmazonRootCA1.pem (or equivalent)")
print("  Cipher:      TLS_AES_128_GCM_SHA256")
print("  Keep-alive:  300s (5 min)")

Step 5: Certification & Compliance

CertificationStandardScopeEstimated Cost
FCC Part 15B47 CFR 15 Subpart BUnintentional radiator (Class B)$3,000 – $5,000
FCC Part 15C47 CFR 15 Subpart CIntentional radiator (WiFi/BLE)$8,000 – $15,000
CE (RED)EN 300 328, EN 301 489Radio Equipment Directive (EU)$5,000 – $10,000
RoHSEU 2011/65/EURestricted substances$500 – $1,500
REACHEU 1907/2006Chemical safety$500 – $1,000
UL/IEC 62368-1Safety (AV/ICT)Electrical safety (optional)$10,000 – $20,000
Module Certification Shortcut: Using a pre-certified WiFi/BLE module (ESP32-S3-WROOM-1 has FCC ID: 2AC7Z-ESPS3WROOM1) can eliminate the need for intentional radiator testing (FCC 15C). You only need unintentional radiator testing (15B) for the complete product — saving $8,000–$15,000 in certification costs.

Step 6: Production Costing

# Production cost breakdown — from prototype to 10k units
# Includes NRE, tooling, per-unit COGS, and margin analysis

import numpy as np

# Non-Recurring Engineering (NRE) costs
nre_costs = {
    "PCB Design (schematic + layout)":     5000,
    "Firmware Development":                12000,
    "Cloud Backend Setup":                  4000,
    "Mechanical / Enclosure Design":        3000,
    "Injection Mold Tooling":              8000,
    "FCC Part 15B Testing":                4000,
    "CE (RED) Testing":                    7000,
    "Prototype Runs (3 iterations)":       3000,
    "Safety Testing (UL listing)":         0,     # Optional
}

# Per-unit costs at different volumes
volumes = [100, 1000, 5000, 10000]

# Base BOM at 1k units = $26.05 (from BOM table above)
bom_1k = 26.05

# Volume scaling factors (larger orders = lower per-unit)
scaling = {100: 1.35, 1000: 1.00, 5000: 0.88, 10000: 0.82}

print("IoT Product — Production Cost Analysis")
print("=" * 60)

print("\nNon-Recurring Engineering (NRE):")
print("-" * 45)
nre_total = 0
for item, cost in nre_costs.items():
    if cost > 0:
        nre_total += cost
        print(f"  {item:<40} ${cost:>8,}")
print(f"  {'TOTAL NRE':<40} ${nre_total:>8,}")

print(f"\nPer-Unit Cost at Volume:")
print("-" * 60)
print(f"  {'Volume':<10} {'BOM':>8} {'Assembly':>10} {'Test':>8} "
      f"{'NRE/unit':>10} {'TOTAL':>8}")
print("-" * 60)

for vol in volumes:
    bom = bom_1k * scaling[vol]
    assy = 2.50 * (1.5 if vol == 100 else 1.0 if vol == 1000 else 0.9 if vol == 5000 else 0.85)
    test = 1.00
    nre_per = nre_total / vol
    total = bom + assy + test + nre_per
    print(f"  {vol:<10,} ${bom:>7.2f} ${assy:>9.2f} ${test:>7.2f} "
          f"${nre_per:>9.2f} ${total:>7.2f}")

# Break-even analysis at $49.99 retail
retail = 49.99
cogs_10k = bom_1k * scaling[10000] + 2.50 * 0.85 + 1.00 + nre_total / 10000
gross_margin = (retail - cogs_10k) / retail
print(f"\nRetail Price:     ${retail}")
print(f"COGS at 10k:      ${cogs_10k:.2f}")
print(f"Gross Margin:     {gross_margin:.1%}")
print(f"Break-even units: {int(np.ceil(nre_total / (retail - cogs_10k + nre_total/10000)))}")

IoT Product Planner

IoT Product Planner

Plan your IoT product build with cost estimates and certification requirements. Download as Word, Excel, or PDF.

Draft auto-saved

Conclusion

This template covers the complete IoT product journey from concept to production-ready prototype. The key insight: certification strategy (using pre-certified modules) and production costing (NRE amortization) often determine product viability more than the technical design itself. Plan these from day one.

Series Complete! You’ve now covered all 28 articles in the Embedded Systems Hardware Engineering series — from foundational electronics through advanced topics, eight real-world capstone projects, and three professional templates. Return to the series index to review any topic.

Explore the Full Series

You’ve completed the entire series! Return to the Series Index to revisit any part, capstone project, or template — or start from Part 1: Electronics Foundations for a fresh review.