說到 IoT (物聯網)很多人第一時間就會想起 Google Home ,Apple HomeKit,Amazon Echo 之類的智能喇叭(音箱),但是他們並不是真的跟 IoT 有關,他們比較像是一個輸入裝置或是一個 gateway 去控制其他裝置而已。
另外比較有技術背景的人可能會想起一些 IoT 的技術,好像說 Bluetooth BLE,Zigbee,2.4Ghz RF 跟 IR 之類的傳輸技術,也有另外一些人會想起 IFTTT、MQTT 之類的通訊協議。可是我們這次不是要說這個,而是
怎樣從零開始自己幹一套 IoT 控制系統出來
嘛,雖然說是從零開始但是總不能從沙開始煉硅晶吧?所以這裡的零界定為:
不使用任何專門為 IoT 設計的產品或軟件來制作這套 IoT 系統。
托比
因為之前我在高中畢業到大一之間的暑假我也曾經研究過一套叫 Home Dynamic 的 IoT 系統,所以這套東西就沿用這個名字叫做 Home Dynamic v2 吧!
選擇硬體平台
如果要開發物聯網系統,最基本的話就是需要有「物聯網」中「物」的部分吧?所以我就先由硬體開始選擇。這裡我有幾個要求
- 便宜,而且容易買到
- 體積要夠小,而且輕便
- 省電(因為要 24/7 的運作)
所以這裡就直接省卻了基於 ARM 處理器的開發板了。剩下的就只有 Arduino 系列用的 ATmega 或是更省電的 ATtiny 或者 ESP 系列的 ESP8266 或 ESP32
選擇連接協議
到了「物聯網」中「聯」的部分,到底要怎樣讓這塊板子連接到其他裝置上?市面上有很多不同的 IoT 連接協議及相關的硬體,常見的包括:
- Zigbee (通常用在 Mesh 網絡上 )
- Bluetooth BLE (通常用在需要超省電的裝置上)
- LoRa (通常用作長距離通訊)
- 2.4Ghz RF (通常用作即時控制,如搖控車)
- WiFi (對,就是你手機筆電在用的那個 WiFi)
這裡的選擇其他也滿簡單的。首先,LoRa 一定用不上,香港的房子連轉身的空間也沒有,能連接數公里距離的 LoRa 一點用途都沒有。 Zigbee 的確是其中一個可以考慮的選擇,但是因為我也想透過現有的 ArozOS 系統控制它,而 Android、Windows 等作業系統都沒有原生支援 Zigbee,所以在這裡也不作考慮。
最後只剩下 3個選擇:BLE,2.4Ghz RF 跟 WiFi ,不過反正大家都是 2.4Ghz,而且因為我的 IoT 裝置都是用來控制電源、或是用來檢測太陽能板等很容易都抓到電源的地方,那當然選擇最容易可以透過不同手提裝置控制的選擇:WiFi
選擇網絡連接方式
說到「物聯網」的「網(絡)」,既然都跑 WiFi 了當然是用 HTTP 啊!雖然也有其他連接方式例如 MQTT 之類的,但是在選擇這個技術之前要考慮到幾點
- 這種技術會不會有 SPOF (Single Point of Failure)
- 這種技術會不會需要額外的伺服器 / 硬體
- 這種技術開發起來方不方便(畢竟是寫給自己用,賺不了錢所以太複雜了反而沒意思)
所以就用最簡單的 GET request 就好了啦。而且這方法也不需要 ESP32 內置的藍牙硬體,所以我們可以直接選擇最便宜的 ESP01 來做我們的控制器,一石二鳥。
可是,我們怎樣從網絡中找到 IoT 裝置?
這是一個好問題,而且之前我在開發 Home Dynamic 系統的時候也遇過類似的問題。但是作為剛剛升大學的我,當時我使用了一個最原始的方法:每個 IP ping 一次
但是在這次的開發計劃中(我把它叫做 Home Dynamic v2),我打算使用 mDNS 來取代這個(爛)IP 掃描方法。畢竟比起一次過把整個區網 ping 一次,例不如讓裝置自己 broadcast 自己的信息好了。剛好 ArozOS 也有內置到 mDNS 掃描器,所以就直接拿來做 IoT 掃描器不就行了嗎?
於是,我把 mDNS 的 transponder 塞到 ESP8266 裡面,然後跟據 ArozOS 的 mDNS transponder 結構寫了個這樣的 metadata 到 transponder 的回應信息裡面。以下為 hdsv2 的電源開關控制器的代碼:
//Inject zeroconf attr into the MDNS respond (For scanning by ArozOS)
void MDNSDynamicServiceTxtCallback(const MDNSResponder::hMDNSService p_hService) {
//Define the domain of the HDSv2 devices
MDNS.addDynamicServiceTxt(p_hService, "domain","hds.arozos.com");
MDNS.addDynamicServiceTxt(p_hService, "protocol","hdsv2");
//Define the OEM written values
MDNS.addDynamicServiceTxt(p_hService, "uuid",getMacAddress());
MDNS.addDynamicServiceTxt(p_hService, "model","Test Unit");
MDNS.addDynamicServiceTxt(p_hService, "vendor","HomeDynamic Project");
MDNS.addDynamicServiceTxt(p_hService, "version_minor","0.00");
MDNS.addDynamicServiceTxt(p_hService, "version_build","0");
}
然後在 ArozOS 啟動 console 模式,並在 terminal 中輸入
scan all
便得到了區網內的 mDNS 回傳
這個時候我們就有辦法找到 IoT 裝置的 IP 地址了。
安全性問題?不會被人入侵嗎?
這也是一個好問題,但是作為開發者,我自己寫的 IoT 裝置是一定不會讓它連到外網的。所以這樣想的話,既然都是把所有東西放在 NAT 路由器後面,而且家裡都是認識的人,應該不用甚麼密碼才能控制裝置的設計吧?如果你還是不放心的話就再加一個 IoT 專用的 NAT WiFi 路由器就好了啦
噢,你問怎樣在家外面控制家裡面的 IoT 裝置?這就要說到我準備要解釋的 IoT Gateway – ArozOS 的部分了。
與 ArozOS 連接及產生控制界面
甚麼是 ArozOS
ArozOS 是一套也是由我開發的雲端桌面系統,可以架設在 Raspberry Pi 上並透過 uPnP 穿透到外網,讓我自己在外面都可以透過網頁桌面存取到我放在 Rpi 上的文件。
這東西也是開源的,可以在這裡找到:
https://github.com/tobychui/arozos
ArozOS 上的 hdsv2 控制器
ArozOS 上面有一個叫 IoT Hub 的綜合控制器,可以讓開發者把自己寫的 IoT 系統與 ArozOS 的 IoT Hub 控制界面連接。它的 Go Struct 看上去像這樣:
type ProtocolHandler interface {
Start() error //Run Startup check. This IoT Protocl Handler will not load if this return any error (e.g. required wireless hardware not found) **TRY NOT TO USE BLOCKING LOGIC HERE**
Scan() ([]*Device, error) //Scan the nearby devices //Return the previous scanned list
Connect(device *Device, authInfo *AuthInfo) error //Connect to the device
Status(device *Device) (map[string]interface{}, error) //Get status of the IoT device
Execute(device *Device, endpoint *Endpoint, payload interface{}) (interface{}, error) //Execute an endpoint for a device
Disconnect(device *Device) error //Disconnect from a device connection
Stats() Stats //Return the properties and status of the Protocol Handler
Icon(device *Device) string //Get the icon of the device, see iot/hub/img/devices for a list of icons
}
只要我們把裡面要求的 function 都寫出來它就會接受這個新模組為新的 IoT 掃描器了。於是我便跟著它的架構寫了一個 hdsv2 的控制器。完整代碼可以在下面找到 (在 hdsv2 資料夾裡面):
https://github.com/tobychui/arozos/tree/master/src/mod/iot
然後順便在 ArozOS 核心中(iot.go)加入一行以登錄這個新的控制器
https://github.com/tobychui/arozos/blob/master/src/iot.go
//Home Dynamic v2
hdsv2Handler := hdsv2.NewProtocolHandler(MDNS)
iotManager.RegisterHandler(hdsv2Handler)
這樣 ArozOS 就能使用這個控制器來掃描跟控制這套新的 IoT 系統了。
ESP8266 上的資訊接點
然後為了讓 ESP8266 可以告訴我寫的控制器這個裝置是幹麼的,到底怎樣才能控制到它,所以我特別設計了幾個 endpoint 並讓伺服器可以要求這些節點以了解系統應該怎樣控制那個裝置。以下為一個很簡單開關的 endpoint respond (在 C++ 裡寫 JSON 真的很麻煩啊)
void handle_endpoints() {
server.send(200, "application/json", "[{\
\"Name\": \"ON\",\
\"RelPath\":\"on\",\
\"Desc\":\"Switch on the device attached to the switch\",\
\"Type\":\"none\",\
\"AllowRead\":false,\
\"AllowWrite\":true\
},{\
\"Name\": \"OFF\",\
\"RelPath\":\"off\",\
\"Desc\":\"Switch off the device attached to the switch\",\
\"Type\":\"none\",\
\"AllowRead\":false,\
\"AllowWrite\":true\
}\
]");
}
由於這部分實在太技術性了所以我就直接把它放到 Github,你們有興趣可以看這裡:
https://github.com/tobychui/Home-Dynamic/tree/master/Home%20Dynamic%20v2
簡單來說就是它會讓掃描器存取它的狀態(status)跟接入點(endpoints),然後讓 ArozOS 前端去產生一個對應的 UI 給使用者控制。
運作原理
而最後這系統的運作原理簡化之後大約是這樣
完成~
系統完成之後大約長這樣子
源碼與架設教學
可以在這裡找到
ArozOS 雲端桌面系統(安裝到 Raspberry Pi 上當 IoT 控制器)
https://github.com/tobychui/arozos
Home Dynamic v2 的源碼及 Arduino IDE 例子