The Ultimate Guide to Building a DIY Smart Home Hub (ESP8266 Capstone)

The Ultimate Guide to Building a DIY Smart Home Hub (ESP8266 Capstone)

CONTENTS.log
📑 Table of Contents
Bill of Materials
QTY: 7
[1x]
NodeMCU (ESP8266)
SOURCE_LINK
[1x]
SSD1306 OLED Display (I2C)
SOURCE_LINK
[1x]
DHT11 Sensor
SOURCE_LINK
[1x]
1-Channel 5V Relay Module
SOURCE_LINK
[20x]
Jumper Wires
SOURCE_LINK
[1x]
830-Point Breadboard
SOURCE_LINK
[1x]
Micro-USB Cable
SOURCE_LINK

* SYSTEM.NOTICE: Affiliate links support continued laboratory research.

For the past 30 days, we have been “Learning”. We learned Ohm’s Law. We learned how a Transistor amplifies current. We learned how I2C moves bits between chips. We learned how HTTP requests traverse the globe. But “knowing” is not “building”. Engineering is the art of Integration. It is the ability to take ten disparate, conflicting components—some that want 5V5\text{V}, some that want 3.3V3.3\text{V}, some that block code, some that need microseconds—and making them dance together in perfect unison.

Today, we stop being “Hobbyists” tinkering with breadboards. Today, we become Systems Engineers.

We are going to build the technochips Smart Hub. This is not a toy. This is a prototype for a commercial product.

The Mission Specification:

  • Environmental Monitoring: Real-time Temperature and Humidity acquisition (DHT11).
  • Local Visualization: OLED Human-Machine Interface (HMI).
  • Physical Actuation: Control of high-voltage appliances via relays.
  • Network Interface: Asynchronous Web Server with responsive UI.
  • Lifecycle Management: Over-The-Air (OTA) firmware updates.

Exploded View: Inside the Hub

Systems Architecture (The Blueprint)

Before we write a single line of code, we must understand the “System”. A novice writes a loop() that reads a sensor, then updates the screen, then checks Wi-Fi. This is Sequential Architecture. It is fatal for IoT. Why?

  • Reading a DHT11: 250ms\sim 250\text{ms}.
  • Updating OLED: 40ms\sim 40\text{ms}.
  • TCP Client Connection: 1-5ms\sim 1\text{-}5\text{ms}.

If you sequence these, your User Interface (Web Dashboard) will lag. When a user taps “Fan ON” on their phone, the ESP8266 might be busy drawing pixels on the OLED. The TCP packet will be dropped. The user will think “This device is broken.”

The Solution: The Non-Blocking Super-Loop We will implement a Cooperative Multitasking Scheduler. We will slice our timeline into millisecond-sized chunks. The CPU will “poll” tasks continuously, only executing them when their specific time slot arrives. We will use the millis() hardware timer as our master clock.

System Architecture Diagram

This architecture separates our concerns into three layers:

  • Hardware Abstraction Layer (HAL): Drivers for physical pins (I2C, GPIO).
  • Application Layer: Business logic (Thermostat, filters).
  • Interface Layer: HTTP Server, OLED Rendering.

Architectural Alternatives: Why not RTOS?

A valid question is: “Why not use FreeRTOS?” The ESP8266 does run a lightweight RTOS (Real-Time Operating System) in the background to manage Wi-Fi. However, adding user-level threads consumes stack memory per task.

  • RTOS Approach: Each task gets its own stack (e.g., 2KB + 2KB + 4KB). Total: 8KB overhead.
  • Super-Loop Approach: Tasks share a single system stack. Total: <1KB overhead. For a constrained device like the ESP8266 (roughly 40KB user RAM), the Super-Loop is efficiently superior. It forces you to write efficient state machines rather than lazy blocking threads.

The Interrupt Service Routine (ISR)

We use millis() for coarse timing (ms level). But what if the DHT11 needs microsecond timing? The DHT11 library uses ISRs. It halts the CPU for 50μs50\mu s to catch the rising edge of a signal. Critical Risk: If your Wi-Fi interrupts an ISR, the system crashes (WDT Reset). Mitigation: noInterrupts() and interrupts() are used carefully around critical timing sections to ensure data integrity.

Platform Benchmarking: ESP8266 vs ESP32

Why did we choose the ESP8266 ($3.50) over the ESP32 ($6.00)?

  • Clock Speed: 80MHz80\text{MHz} vs 240MHz240\text{MHz}.
  • RAM: 50KB vs 300KB.
  • Cores: 1 vs 2. For a Thermostat, we need Low Idle Power and Simplicity. The ESP8266 consumes 20% less power in deep sleep and boots 500ms500\text{ms} faster. Engineering Decision: We optimize for Cost and Start-up Latency, at the expense of Raw Compute. This is efficient engineering.

Hardware Engineering (Physics & Safety)

We are combining high-speed digital logic (3.3V3.3\text{V}) with mains power control (110V/220V110\text{V}/220\text{V}). This requires a deep understanding of the physics involved.

The Relay Module (Galvanic Isolation & Back-EMF)

You cannot connect a 5V5\text{V} relay coil directly to an ESP8266 GPIO.

  • Voltage Mismatch: ESP8266 is 3.3V3.3\text{V}; 5V5\text{V} relays won’t trigger reliably.
  • Current Mismatch: Relays draw 70mA\sim 70\text{mA}; ESP8266 pins sink 12mA\sim 12\text{mA}.
  • Inductive Kickback: Field collapse creates high-voltage spikes.

V=LdidtV = L \cdot \frac{di}{dt}

Since the current change (dtdt) is near zero, the Voltage (VV) spikes to hundreds of volts.

Relay Optocoupler Schematic

The Engineering Solution: We use a Relay Module with an onboard Optocoupler (PC817) and Flyback Diode.

  • The Optocoupler: Inside this chip is an Infrared LED and a Phototransistor. When we drive the input PIN, the LED lights up. The light crosses a physical gap to turn on the transistor.

There is Zero Electrical Connection between the High Voltage side and the Low Voltage ESP8266. This is Galvanic Isolation. It protects the CPU from noise and spikes.

  • The Flyback Diode: A diode is placed in parallel with the coil. When the field collapses, the high-voltage spike is shorted back through the diode, dissipating as heat instead of destroying your transistor.

The SSD1306 OLED (I2C Bus Capacitance)

We are using the I2C Protocol (Inter-Integrated Circuit).

  • Clock Speed: 400kHz400\text{kHz} (Fast Mode).

  • Pull-Up Resistors: Lines need resistors to pull HIGH.

  • Capacitance Trap: Long wires (>10cm) cause “shark fins” signals.

  • The Fix: Keep wires short. If you see “snow” or artifacts on the screen, your rise-times are too slow.

Power Supply Engineering (LDOs & Decoupling)

The ESP8266 is a current-hungry beast.

  • Tx Burst: 350mA350\text{mA} peak.
  • Idle: 70mA70\text{mA}.
  • USB Port: 500mA500\text{mA} limit. If your relay (70mA70\text{mA}) + OLED (20mA20\text{mA}) + ESP8266 (350mA350\text{mA}) all fire at once, you hit 440mA440\text{mA}. This is perilously close to the 500mA500\text{mA} USB limit. The Brownout: If voltage drops below 2.8V2.8\text{V}, the ESP8266 flash memory corrupts. The Fix: Decoupling Capacitors. We place a 100μF100\mu\text{F} Electrolytic Capacitor closer to the ESP8266 VCC/GND pins. It acts as a “Local Energy Bucket”, supplying the 350mA350\text{mA} burst instantly, smoothing out the voltage sag.

Wiring Specification

Correct wiring is non-negotiable for system stability.

  • VCC/GND: Dedicated power rail recommended.
  • D1/D2: OLED I2C lines.
  • D5: Relay Signal (Safe during boot).
  • D6: DHT11 Data.

Wiring Diagram

Safety Critical: Mains Voltage

DANGER: 110V/220V110\text{V}/220\text{V} KILLS. If you are switching a real load (Lamp/Heater):

  • Enclosure: Project MUST be in a non-conductive plastic box.

Relay Safety Closeup

Firmware Engineering (The “OS”)

Component Lifecycle Analysis

How long will this hub last?

  • OLED Burn-in: Pixels degrade over time. Use a screensaver.
  • Relay Welding: Switching inductive loads melts contacts.
  • Flash Wear: EEPROM has ~100k cycles; limit log writes.

Firmware Engineering (The “OS”)

We are effectively writing a small Operating System. The ESP8266 has a Harvard Architecture (Modified). It has distinct Instruction Memory (Flash) and Data Memory (RAM).

  • Flash (4MB): Code and static data storage.
  • RAM (80KB): Dynamic variable store.
  • Constraint: Wi-Fi stack consumes ~40KB immediately.

Memory Management (PROGMEM)

A common rookie mistake is defining the HTML string like this: String html = "<html>...</html>"; This puts the string in RAM. With a large dashboard, you will crash the chip (Stack Overflow).

  • Stack: Local variables; grows downwards.
  • Heap: Dynamic objects; grows upwards.
  • Collision: Stack-Heap collision causes Exception 29. The Fix: We use the PROGMEM keyword. const char html[] PROGMEM = "<html>...</html>"; This forces the compiler to keep the data in Flash Memory (Instruction Store), leaving RAM totally free for the Stack.

The Watchdog Timer (WDT)

Reliability is our #1 goal. The ESP8266 hardware contains a “Watchdog”. It is a countdown timer. If the CPU does not reset this timer every ~3 seconds, the Watchdog assumes the code has crashed and Hard Resets the board.

  • Blocking Risk: delay(5000) causes WDT resets.
  • Super-Loop Reward: Short loops (<50ms< 50\text{ms}) keep the WDT fed and the system stable.

Non-Blocking Code Structure

Signal Processing: The Moving Average

Sensors are noisy. Air turbulence causes the DHT11 to jitter. 23.1, 23.2, 23.1, 28.5 (Error), 23.2 If we use raw data, our Relay will “chatter” (Click-On-Click-Off). We implement Digital Signal Processing (DSP): The Moving Average Filter. We maintain a circular buffer of the last N readings.

TemperatureFiltered=i=1NReadingiNTemperature_{Filtered} = \frac{\sum_{i=1}^{N} Reading_i}{N}

This smooths out noise and rejects transient spikes.

Automation Logic Flowchart

Network Engineering

The “Internet” in IoT is just HTTP. When your phone asks the hub for temperature, a complex dance occurs.

The HTTP Request Cycle

  • Handshake: TCP connection opening (SYN, SYN-ACK, ACK).
  • Request: GET /temperature HTTP/1.1.
  • Processing: URL parsing and logic execution.
  • Response: HTTP Header + Payload.
  • Teardown: Connection closure.

Deep Dive: The TCP Packet

When you send “24.5”, you aren’t just sending 4 bytes. You are sending a 1500-byte Ethernet Frame:

  • Ethernet Header: MAC Addresses (14 bytes).
  • IP Header: IP Addresses (20 bytes).
  • TCP Header: Ports and Sequence (20 bytes).
  • HTTP Header: Server info (100+ bytes).
  • Payload: Data (e.g., “24.5”). Optimization Lesson: Don’t send “Temp=24.5” (9 bytes). Send “24.5” (4 bytes). Every byte counts when you are running on a 80MHz80\text{MHz} CPU.

TCP Packet Diagram

AJAX Polling vs WebSockets

  • AJAX Polling: Client pulls data periodically; robust but higher overhead.
  • WebSockets: Persistent connection; real-time but RAM-heavy.
  • Decision: For a thermostat, Polling is the correct engineering choice.

Data Serialization: The Cost of ASCII

We send data as JSON: {"temp": 24.5}.

  • Size: 14 bytes per packet.

  • Binary Alternative: We could send a raw 4-byte float: $0x41C40000$.

  • Trade-off: JSON is readable by humans (High Maintainability). Binary is fast (High Efficiency). Since we have Wi-Fi bandwidth (Mbits), we prioritize Human Readability. If we were using LoRaWAN (bytes/hour), we would use binary.

IoT Security Engineering

Connecting a heater to the internet creates a vector for cyber-physical attacks.

Hardcoded Secrets

You see const char* password = "My_Password"; in the code.

  • The Risk: If you upload this code to GitHub, bots will scrape your credentials in < 1 minute.

  • The Protection: Use a separate secrets.h file and add it to .gitignore. Never commit keys.

Network Flooding (DDoS)

An ESP8266 has limited sockets (max 4). If a malicious actor opens 4 connections and keeps them open (“Slowloris Attack”), the hub becomes unresponsive.

  • The Guard: Implement a connection timeout in the firmware. If a client sends nothing for 2 seconds, forcibly client.stop().

Cross-Origin Resource Sharing (CORS)

Our AJAX requests come from the same origin (the ESP itself). But if a malicious website tries to fetch('http://192.168.1.50/toggle'), the browser blocks it by default. DO NOT add Access-Control-Allow-Origin: * headers unless you understand the risk. This header allows any website to toggle your fans.

Frontend Engineering (The Web App)

The interface is not just a “page”. It is an Application. We use CSS3 Grid/Flexbox for layout and Vanilla JS for logic. No heavy frameworks (React/Vue) needed.

The Dashboard Design

We want a “Dark Mode” aesthetic using Material Design principles.

  • Card: A unified container with drop shadows (box-shadow: 0 10px 30px rgba(0,0,0,0.5)).

  • Gauge: A CSS Conic Gradient (conic-gradient) that changes color dynamically based on temperature.

  • Feedback: CSS Transitions (transition: all 0.3s ease) to make button presses feel tactile.

Mobile Web Dashboard

The JavaScript State Machine

The browser runs a minimal state machine:

  • State: Disconnected (Gray text).

  • Event: AJAX Success -> State: Connected (Green/Blue text).

  • Event: AJAX Timeout -> State: Offline (Red text).

This gives the user immediate feedback if the hub loses power, rather than just showing a frozen number.

Browser Rendering Physics: Reflow vs Repaint

Why use CSS Updates instead of replacing innerHTML?

  • Reflow: When you change the layout (width, height), the browser must recalculate the position of every pixel. This is CPU, battery, and time expensive.

  • Repaint: When you change the color or opacity, the browser just redraws the pixels in place. Our Strategy: The Gauge uses conic-gradient. Changing the gradient is a Repaint. It is silky smooth (60fps) even on an old Android phone.

Manufacturing & Production

To go from “Prototype” to “Product”, we need a Bill of Materials (BoM) and an Assembly Process.

Bill of Materials (BoM)

ComponentSKUCostNotes
MicrocontrollerNodeMCU v3$3.50The brain.
Display0.96” OLED I2C$2.50128×64128 \times 64 White Pixel.
SensorDHT11 Module$1.50Includes 10k pull-up.
Relay1-Ch 5V5\text{V} Relay$1.00With Optocoupler.
Power52A5\text{V } 2\text{A} PSU$0.00Recycled Phone Charger.
CaseProject Box$2.00ABS Plastic (Safety).
Total$10.50

Assembly Guidelines

  • De-soldering: Remove header pins from modules if soldering wires directly for reliability.

  • Insulation: Use Heat Shrink Tubing on all exposed joints. Tape is for temporary fixes; Heat Shrink is for products.

  • Hot Glue: Secure the DHT11 and OLED to the case lid to prevent movement (Vibration testing).

Final Hardware Assembly

The Final Firmware Code

Here is the complete, integrated firmware. It combines the millis() scheduler, ArduinoOTA, ESP8266WebServer, and SSD1306 logic.

/**
 * Project: DeepMind SafeHome Hub (Capstone Edition)
 * Author: TechnoChips
 * Hardware: ESP8266, DHT11, SSD1306, $5\text{V}$ Relay
 * License: MIT
 */

#include <ESP8266WiFi.h>
#include <ESP8266WebServer.h>
#include <ArduinoOTA.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <DHT.h>

// --- Configuration Constants ---
const char* ssid = "YOUR_WIFI_SSID";
const char* password = "YOUR_WIFI_PASS";
const char* ota_pass = "admin123";

#define RELAY_PIN 14  // GPIO 14 (D5) - Safe Pin
#define DHT_PIN 12    // GPIO 12 (D6) - Safe Pin
#define DHT_TYPE DHT11

// --- Objects ---
Adafruit_SSD1306 display(128, 64, &Wire, -1);
DHT dht(DHT_PIN, DHT_TYPE);
ESP8266WebServer server(80);

// --- Global State ---
float currentTemp = 0.0;
float currentHum = 0.0;
bool relayState = false;
unsigned long lastSensorRead = 0;
unsigned long lastDisplayUpdate = 0;
const long SENSOR_INTERVAL = 2000; // 2 Seconds
const long DISPLAY_INTERVAL = 1000; // 1 Second

// --- Signal Processing Buffer ---
const int FILTER_SIZE = 5;
float tempBuffer[FILTER_SIZE];
int bufferIndex = 0;

// --- HTML Application (PROGMEM for Flash Storage) ---
const char index_html[] PROGMEM = R"rawliteral(
<!DOCTYPE html>
<html>
<head>
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>SafeHome Hub</title>
  <style>
    :root { --primary: #00bcd4; --bg: #121212; --card: #1e1e1e; --text: #fff; }
    body { font-family: 'Segoe UI', sans-serif; background: var(--bg); color: var(--text); display: flex; justify-content: center; align-items: center; min-height: 100vh; margin: 0; }
    .hub-card { background: var(--card); padding: 2rem; border-radius: 20px; box-shadow: 0 10px 40px rgba(0,0,0,0.5); width: 300px; text-align: center; }
    h1 { margin: 0 0 20px 0; font-weight: 300; letter-spacing: 2px; }
    .gauge { width: 150px; height: 150px; border-radius: 50%; border: 10px solid #333; margin: 0 auto 20px; display: flex; align-items: center; justify-content: center; position: relative; }
    .temp-display { font-size: 3rem; font-weight: bold; }
    .unit { font-size: 1rem; color: #888; }
    .status-dot { width: 10px; height: 10px; background: #555; border-radius: 50%; display: inline-block; margin-right: 5px; box-shadow: 0 0 5px #555; transition: all 0.3s; }
    .status-dot.active { background: #00ff00; box-shadow: 0 0 10px #00ff00; }
/* Toggle Switch */
    .switch-container { display: flex; justify-content: space-between; align-items: center; background: #2a2a2a; padding: 15px; border-radius: 10px; }
    .switch { position: relative; display: inline-block; width: 50px; height: 28px; }
    .switch input { opacity: 0; width: 0; height: 0; }
    .slider { position: absolute; cursor: pointer; top: 0; left: 0; right: 0; bottom: 0; background-color: #444; transition: .4s; border-radius: 34px; }
    .slider:before { position: absolute; content: ""; height: 20px; width: 20px; left: 4px; bottom: 4px; background-color: white; transition: .4s; border-radius: 50%; }
    input:checked + .slider { background-color: var(--primary); }
    input:checked + .slider:before { transform: translateX(22px); }
  </style>
</head>
<body>
  <div class="hub-card">
    <h1>HUB STATUS</h1>
    <div class="gauge" id="gaugeBody">
<div>
<span class="temp-display" id="temp">--</span>
<span class="unit">°C</span>
</div>
    </div>
    <div class="switch-container">
<span>Active Cooling</span>
<label class="switch">
<input type="checkbox" id="fanToggle" onchange="toggleFan()">
<span class="slider"></span>
</label>
    </div>
    <p style="margin-top:20px; color:#555; font-size:0.8rem;">
<span class="status-dot" id="connDot"></span> Live Connection
    </p>
  </div>

  <script>
    let failCount = 0;
    function update() {
      fetch('/status')
        .then(res => res.json())
        .then(data => {
          document.getElementById('temp').innerHTML = data.t.toFixed(1);
          document.getElementById('fanToggle').checked = data.r;
          document.getElementById('connDot').classList.add('active');
          failCount = 0;
          let hue = 240 - (data.t - 15) * 10;
          document.getElementById('gaugeBody').style.borderColor = 'hsl(' + hue + ', 100%, 50%)';
        })
        .catch(err => {
          failCount++;
          if(failCount > 2) document.getElementById('connDot').classList.remove('active');
        });
    }
    function toggleFan() {
      fetch('/toggle').then(() => update());
    }
    setInterval(update, 2000); // The Heartbeat
    update();
  </script>
</body>
</html>
)rawliteral";

void setup() {
  Serial.begin(115200);
// 1. Hardware Init
  pinMode(RELAY_PIN, OUTPUT);
  digitalWrite(RELAY_PIN, HIGH); // Relay OFF (Active LOW usually)
// 2. Display Init
  if(!display.begin(SSD1306_SWITCHCAPVCC, $0x3C$)) {
    Serial.println(F("OLED Failed"));
    for(;;);
  }
  display.clearDisplay();
  display.setTextColor(WHITE);
  display.println("Booting System...");
  display.display();

  // 3. WiFi Init
  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    display.print(".");
    display.display();
  }
// 4. OTA Init
  ArduinoOTA.setHostname("SafeHome-Hub-v1");
  ArduinoOTA.setPassword(ota_pass);
  ArduinoOTA.begin();

  // 5. Server Routes
  server.on("/", HTTP_GET, []() {
    server.send_P(200, "text/html", index_html);
  });
server.on("/status", HTTP_GET, []() {
    // JSON API
    String json = "{";
    json += "\"t\":" + String(currentTemp) + ",";
    json += "\"h\":" + String(currentHum) + ",";
    json += "\"r\":" + String(relayState);
    json += "}";
    server.send(200, "application/json", json);
  });
server.on("/toggle", HTTP_GET, []() {
    relayState = !relayState;
    digitalWrite(RELAY_PIN, relayState ? LOW : HIGH);
    server.send(200, "text/plain", "OK");
  });

  server.begin();
  dht.begin();
// Init average buffer
  for(int i=0; i<FILTER_SIZE; i++) tempBuffer[i] = 25.0;
}

void loop() {
  // CRITICAL: Handle Background Tasks
  server.handleClient();
  ArduinoOTA.handle();

  unsigned long now = millis();

  // TASK 1: Sensor Read (Every 2s)
  if (now - lastSensorRead >= SENSOR_INTERVAL) {
    lastSensorRead = now;
float newT = dht.readTemperature();
    float newH = dht.readHumidity();
if (!isnan(newT)) {
// Moving Average
tempBuffer[bufferIndex] = newT;
bufferIndex = (bufferIndex + 1) % FILTER_SIZE;
float sum = 0;
for(int i=0; i<FILTER_SIZE; i++) sum += tempBuffer[i];
currentTemp = sum / FILTER_SIZE; // Use filtered value
currentHum = newH;
// Automation Logic (Thermostat)
if (currentTemp > 28.0 && !relayState) {
relayState = true;
digitalWrite(RELAY_PIN, LOW); // ON
} else if (currentTemp < 26.0 && relayState) {
relayState = false;
digitalWrite(RELAY_PIN, HIGH); // OFF
}
    }
  }

  // TASK 2: UI Update (Every 1s)
  // Decoupled from Sensor Read so animations feel smooth
  if (now - lastDisplayUpdate >= DISPLAY_INTERVAL) {
    lastDisplayUpdate = now;
    display.clearDisplay();
    display.setTextSize(1);
    display.setCursor(0,0);
    display.print("WikiHub IP:");
    display.setCursor(0,10);
    display.print(WiFi.localIP());
    display.drawLine(0, 20, 128, 20, WHITE);
    display.setTextSize(2);
    display.setCursor(10, 30);
    display.print(currentTemp, 1);
    display.print(" C");
    display.setTextSize(1);
    display.setCursor(10, 55);
    display.print(relayState ? "FAN: ON" : "FAN: OFF");
    // Draw Activity Bar to show system is alive
    int barWidth = (millis() % 1000) / 8; // 0 to 125
    display.fillRect(0, 62, barWidth, 2, WHITE);
    display.display();
  }
}

Advanced Troubleshooting & Diagnostics

A generic “It doesn’t work” doesn’t help. We debug with data.

The “Exception 29” Crash

If your Serial Monitor spams garbage and resets: Exception (29): vaddr=$0x3ff$....

This is a StoreProhibited error. You likely tried to write to a pointer that is NULL.

  • Check: Did you initialize display.begin() before calling display.print()?
  • Check: Is your readings array index out of bounds?

The “Exception 9” Crash (Unaligned)

You tried to read a 4-byte integer from an address not divisible by 4.

  • Fix: Use memcpy instead of casting pointers.

Wi-Fi Signal Strength (RSSI)

Is your hub disconnecting? Add this line to your updateOLED() function: display.print(WiFi.RSSI());

  • -30 to -50 dBm: Perfect Signal.

  • -60 to -70 dBm: Good.

  • -80 dBm: Unstable packet loss.

  • -90 dBm: Disconnection imminent.

  • Solution: Add a 100μF100\mu\text{F} capacitor across 3.3V/GND3.3\text{V}/GND rails to boost radio stability.

The I2C Scanner Loop

If the OLED is dead, run this diagnostic code in setup():

Wire.begin();
for(byte i = 8; i < 120; i++) {
  Wire.beginTransmission(i);
  if (Wire.endTransmission() == 0) {
    Serial.print("Found I2C Device at: 0x");
    Serial.println(i, HEX);
  }
}

If this prints nothing? Your wires are broken.

Extensions & Next Steps

The Hub is built. But a Systems Engineer is never finished. Because the code is modular, adding features is trivial:

  • Voice Control: Integrate the SinricPro library to expose the Toggle function to Alexa/Google Home.

  • Data Logging: Send currentTemp to a Google Sheet via IFTTT webhook every hour.

  • Presence Detection: Add a PIR Motion Sensor (Day 29) to turn off the OLED when the room is empty to prevent burn-in.

PID Control Theory (Advanced)

Currently, we use “Bang-Bang” control:

if (currentTemp > 28.0) relayState = true; // Fan ON
if (currentTemp < 28.0) relayState = false; // Fan OFF

This oscillates. Professional Solution: PID (Proportional-Integral-Derivative).

Output=Kpe(t)+Kie(t)dt+Kdde(t)dtOutput = K_p e(t) + K_i \int e(t) dt + K_d \frac{de(t)}{dt}

  • P (Proportional): Adjusts based on current error.
  • I (Integral): Eliminates residual steady-state error.
  • D (Derivative): Predicts future error to prevent overshoot. Implementing this in C++ is your next challenge.

PID Control Diagram

Future: Alexa Integration

Conclusion: The Journey Completed

If you have followed us from Day 1 to Day 31: Congratulations. You have gone from blinking an LED to building a Cloud-Connected, Self-Updating, Multi-Tasking IoT Product. You have learned that “Smart Home” is not magic. It is just Physics, Code, and Logic.

The world is full of “Black Boxes” that you are not supposed to open. Open them. Build your own. Control your world.

This concludes the January Zero-to-Hero Series. See you in February for: PCB Design & Embedded Linux.

Engineering Glossary

  • AJAX: Asynchronous JavaScript and XML; for partial page updates.
  • Back-EMF: Voltage spike generated when inductor current changes.
  • Galvanic Isolation: Physical separation of circuits for safety.
  • Harvard Architecture: Separate storage/buses for code and data.
  • I2C: Serial computer bus for IC communication.
  • ISR: Hardware-invoked software routine for interrupts.
  • PID: Feedback control loop mechanism (Proportional-Integral-Derivative).
  • PWM: Method for reducing average power of a signal.
  • RSSI: Measurement of received radio signal power.

Frequently Asked Questions (FAQ)

Q: Can I use an ESP32 instead? A: Yes, with minor pin definition changes.

Q: Can I power this with a battery? A: No, the relay and radio draw too much current for long-term battery use.

Q: Why is my temperature reading 50C50^\circ\text{C}? A: Heat from the CPU; ensure the sensor is mounted outside or has airflow.

Q: How do I access this from the internet? A: Use a VPN or MQTT broker; avoid port forwarding for security.

Copyright © 2026 TechnoChips. Open Source Hardware (OSHW).

Comments