← 回所有專案

個人專題 ‧ 網路規劃與硬體整合 · 2018

智慧家電控制系統

以家中數據機的固定 IP 為入口,透過 Port Forwarding 連進自己規劃的 /24 內網;每個區域各放一組 Arduino + Wi-Fi 模組控制繼電器,用 App Inventor 做的手機 App 就能從外網開關客廳、房間的電器與電腦,並加上一套可定時、也能遠端調整的自動澆花系統。

ArduinoESP8266繼電器模組DS3231 RTCMIT App InventorPort ForwardingNATIP 子網路規劃IoT
智慧家電控制系統

專案概述

這個專題想解決的是很實際的小麻煩:出門後才想到房間燈沒關、想在回家前先把電腦 開機、出遠門幾天家裡的盆栽沒人澆水。市面上的智慧家電要整套換掉,於是我決定自 己把現有的家電接上網路。

整個系統分成三個部分:

  • 網路:利用家裡數據機的固定 IP,透過 Port Forwarding 把外網請求 導進我自己切出來的 /24 內網,讓手機在任何地方都能連回家
  • 控制器:每個區域(客廳、我的房間、電腦、澆花)各放一組 Arduino + ESP8266 Wi-Fi 模組,用繼電器控制電源開關
  • App:用 MIT App Inventor 做手機 App,一個畫面就能看到並切換所有設備 的狀態

這個專題讓我第一次把網路規劃和嵌入式控制放在同一個系統裡思考,做完之 後也發現自己對「網路」這一塊特別有興趣。

網路架構:固定 IP + Port Forwarding + /24 內網

要讓手機從外面控制家裡的設備,第一個問題是「外面怎麼找得到家裡的 Arduino?」 家用網路的設備都躲在 NAT 後面、只有私有 IP,外網是看不到的。我的做法是:

網路拓樸圖:手機從外網連到數據機的固定公網 IP,依 Port Forwarding 規則轉送到 192.168.10.0/24 內網中各區域的控制器

  • 固定 IP 當入口:家裡的數據機申請了固定 IP,App 只要記住一個不會變的位 址,不需要額外架 DDNS
  • 切出專用的 /24 網段:在內網規劃一個 192.168.10.0/24 給這套系統使用, Gateway 為 .1,控制器依區域固定分配 IP(.11 客廳、.12 房間、.13 電腦、.14 澆花)。IP 固定是 Port Forwarding 能運作的前提——如果控制器靠 DHCP 拿到浮動 IP,轉送規則隨時可能指錯設備
  • 一個外部 Port 對應一台控制器:數據機上設定 :8081 → .11:80、 :8082 → .12:80… 以此類推。外部用不同 Port 區分設備,內部每台控制器都只要 開一個標準的 80 Port,程式可以完全共用

IP 規劃的好處在後續擴充也看得出來:/24 有 254 個可用位址,新增設備只要挑一個 沒用過的 IP、在數據機加一條轉送規則,App 多一個按鈕即可,不會動到既有設備。

控制器:Arduino + ESP8266 + 繼電器

每個區域的控制器硬體組成都一樣:Arduino 負責邏輯、ESP8266 負責 Wi-Fi 連線與 HTTP、繼電器模組負責實際切換 110V 電源,讓低電壓的控制電路和家電的 高電壓完全隔開。

控制流程圖:App 發出 HTTP GET 到公網 IP 的指定 Port,數據機 NAT 轉送到內網的 ESP8266,Arduino 解析路徑後切換繼電器,並把目前狀態回傳給 App 更新畫面

控制介面刻意設計得非常簡單:一個動作就是一個 URL 路徑,回傳值就是目前狀 態的純文字,App 端不需要解析任何複雜格式。

路徑動作回傳
/on繼電器吸合,設備通電living:ON
/off繼電器斷開,設備斷電living:OFF
/status只查詢不切換目前狀態
cpp
#include <SoftwareSerial.h>
#include <WiFiEsp.h>

const int RELAY_PIN = 7;
SoftwareSerial esp(2, 3);            // RX, TX:與 ESP8266 溝通
IPAddress ip(192, 168, 10, 11);      // 每台控制器燒錄時改成自己的固定 IP
WiFiEspServer server(80);
bool isOn = false;

void setup() {
  pinMode(RELAY_PIN, OUTPUT);
  esp.begin(9600);
  WiFi.init(&esp);
  WiFi.config(ip);
  WiFi.begin("HomeIoT", "********");
  server.begin();
}

void loop() {
  WiFiEspClient client = server.available();
  if (!client) return;

  String request = client.readStringUntil('\r');
  if (request.indexOf("GET /on") >= 0)  isOn = true;
  if (request.indexOf("GET /off") >= 0) isOn = false;
  digitalWrite(RELAY_PIN, isOn ? HIGH : LOW);

  client.print("HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\n\r\n");
  client.print(isOn ? "living:ON" : "living:OFF");
  client.stop();
}

三種設備在接線上的差異:

  • 客廳、我的房間:繼電器串接在電燈與插座的電源線上,/on、/off 直接對 應通電與斷電
  • 電腦:電腦不能直接斷電,所以繼電器改成並聯在主機板的電源按鈕接腳 上。收到 /on 時讓繼電器吸合約 0.5 秒再放開,模擬「按一下電源鍵」,讓電腦走 正常的開機流程

自動澆花系統

澆花控制器在同一套架構上多了一個 DS3231 RTC 時鐘模組。Arduino 本身沒有可 靠的時鐘,一斷電時間就歸零;RTC 模組有自己的電池,停電後再復電仍然知道現在幾 點,排程不會亂掉。

自動澆花流程圖:控制器每秒讀取 RTC 時間,到了設定時間就開啟水泵、澆 N 秒後自動關閉;手機 App 可遠端調整澆花時間、澆水秒數或立即手動澆一次

  • 定時澆花:控制器每秒讀一次 RTC,到了設定的時間(例如每天 07:00)就讓繼電 器吸合、啟動小型水泵,澆滿設定秒數後自動關閉
  • 遠端調整:App 可以透過 /schedule?h=7&m=0 修改澆花時間、 /duration?s=15 修改澆水秒數,出門在外也能依天氣調整
  • 手動澆一次:/water 立即澆一次,適合天氣特別熱的時候額外補水
  • 安全上限:不論收到什麼參數,單次澆水時間都有上限,避免參數打錯或網路重 送請求時,水泵一直開著把陽台淹掉

手機 App:App Inventor

App 用 MIT App Inventor 開發,整個畫面就是一排依區域分組的按鈕:客廳、我的 房間、電腦、澆花。

  • 送出指令:每個按鈕對應一組 URL,點擊時用 Web 元件把 Url 設為 http://<固定IP>:<Port>/<動作> 再呼叫 Web.Get
  • 同步狀態:Web.GotText 收到控制器回傳的文字後,依 ON / OFF 把按鈕 改成對應的顏色與文字;App 開啟時則對每台控制器打一次 /status,讓畫面顯示的 永遠是設備的實際狀態,而不是上次按了什麼
  • 連線失敗提示:控制器沒有回應時(斷電、斷線)跳出提示,而不是讓使用者以為 已經關掉了

成果與收穫

  • 完成可以實際在家使用的系統:人在外面就能用手機關掉忘記關的燈、提早把電腦開 機,盆栽也能按時自動澆水
  • 第一次從頭規劃一個網段:IP 位址規劃、固定 IP 分配、NAT 與 Port Forwarding 這些原本只在課本上的觀念,在這裡都要自己設定、自己除錯。設定錯 一條轉送規則、或控制器的 IP 被 DHCP 換掉,App 就連不到設備,這些坑踩過一次 就記住了,也讓我更確定畢業後想往網路工程的方向走
  • 目前最大的問題是安全性:控制器直接把 HTTP 服務開在公網上,沒有加密也沒有身 分驗證,只要知道 IP 和 Port 就能控制家電。下一步想研究的是讓設備不直接對外 開 Port,例如手機先 VPN 回家再控制,或改用 MQTT 讓設備主動連到外面的 Broker, 再加上加密與帳號驗證

使用技術

  • 網路:固定 IP、NAT / Port Forwarding、/24 子網路規劃、靜態 IP 分配
  • 硬體:Arduino、ESP8266 Wi-Fi 模組、繼電器模組、DS3231 RTC 時鐘模組、小 型水泵
  • 韌體:Arduino C/C++(WiFiEsp HTTP Server、繼電器控制、RTC 排程)
  • App:MIT App Inventor(Web、Button、Notifier 元件)、Android