從零開始自幹房間自動化燈光系統(Part 1)
說到我房間裡的 IoT 控制系統,從 2021 年起我就一直在用一套自己開發的 IoT 協議名為 Home Dynamic。這套系統的原理和詳情可在我另一篇文章( 從零開始的 IoT 系統設計 )裡找到,但是簡單來說它就是一個很簡單的 HTTP RESTful request 模型下的迷你 IoT 系統,可以用於開關電燈和透過 Tasmota firmware 來控制幾個 Sonoff 的 WiFi Smart Switch 而已。 到最近,因為我香港的房間要整修,所以我就把房間裡有點 hacky 的系統升級到新的自動化系統。Home Dynamic v2 (簡稱 HDSv2)雖然在設計的時候已經有考慮到很多不同的使用場景和可能出現的問題(如 off grid control,伺服器 / relay 的 SPOF 等等),但是其控制 latency 總是讓我覺得有點煩惱。即使後來 ArozOS 改用 Golang 重寫,從按按鈕到電燈真的亮起的時間大概也只是在 2 - 3 秒內,這個速度跟我去按一個物理開關還是有很明顯的差別。所以在新裝潢的房間裡,我採用了一個更新版本的 HDS 協議來開發我的新自動化系統,現在我把它暫稱為 HDSv3。 控制器硬體部分 那說到 IoT 跟自動化,在提軟體之前一定要先講講硬體。畢竟只要硬體夠強,軟體再爛也是能跑得很順暢(望向 python) 。這次為了未來的擴展性和標準化,這次我選用了半工業級的硬體。下圖中是我用的繼電器模組,是一片市售的 4 路 WiFi 繼電器模組。它是使用 ESP12E 作為主控制器,跟先前的 HDSv2 一樣都是基於相同的 MCU。因此大部分 HDSv2 的源碼能夠直接移植到 v3 然後下載到這片板子上。雖然這片東西可以使用 110 - 220V 輸入,但是因為我原先整套系統都是用 24V 的,因此這部分的機能就白白被我浪費掉了( 那當然,這片東西不能就這樣放在我的桌面上。因此我買了一個 DIN Rail 用的外殼,然後把整個組好的安裝到 DIN Rail 上。 現時的照明控制系統 那你可能會好奇,既然是控制燈光,那一個簡單的繼電器模組就可以做到了。旁邊那兩個盒子又是甚麼? 如果你仔細看一下那個繼電器模組的照片,你就會發現那個模組真的單純在做切換而已,它本身並沒有供電能力。所以為了讓接上去的 LED 燈條有電力供應,就必須透過另一個裝置為 Relay 的 NO (Normally Open)端供電。這裡中間的模組就是作為一個 power distribution hub 的用法來提供 4 個相同於 Vin 的輸出和 3 個可調節的輸出。 大概原理 而最左邊的是 LED strip 的限流模組,大概就是因為 24V 的燈條用 24V 供電的話會太光,所以那個小黑色模組裡面是一個簡單的 XL6009 自動升降壓模組來把電壓調整到大約 19.2V (我喜歡的亮度) 原本是長這樣的,後來想了想還是把它放到 DIN Rail 上比較好 軟體與控制部分 講到軟體部分,就不得不提一下 IoT 技術了。說真的,雖然我知道很多不同的 IoT 通訊協議,從 MQTT(DIY 很多人用,基於 TCP/IP)、 Zigbee (IKEA 那套)到工業用的 RS485 / RS232 之類的,我還是比較喜歡用 2.4Ghz WiFi。雖然距離短、用電量高,但是本來我的裝置就是用來控制電源的(aka 長期會有電源供應),加上香港地方實在太少,我想盡量省下一個放 hub 的空間 + 本身房間已經有 WiFi 覆蓋,所以自然地 WiFi 是最好的選擇,所以我還是選擇了基於 TCP/IP 的協議。 考慮到設備的相容性、不用額外安裝程式便能控制等優點,在眾多基於 TCP/IP 的協議下我選擇了 RESTFUL。一來是所有手機都有網頁瀏覽器,即使伺服器炸掉只要有一台手機能連接到同一網絡便可以透過 p2p 的方式控制 IoT 裝置。另一個好處是 RESTFUL API 實在太通用了,從 ESPHome 到 Home Assitant,最好支援的裝置類型依然是基於 RESTFUL API 的裝置,再加上怎樣看 HTTP request 這東西在未來 10 年應該都會還存在(畢竟是 Web 技術的基礎),選它一定沒錯( 而為了解決…
WebStick – 基於 ESP8266 的網頁伺服器棒棒
自從我看了 ESP8266 的 SD Web Server Youtube 教學之後,我一直都很想要自己做一個來玩玩看。你想想看,有一根可以隨身攜帶(?)、足夠便宜到可以送人而且能夠把虛擬的東西(網站)實體化,不是一件很有趣的事情嗎? 可是之前一直都沒有時間研究這個東西,其中一個原因是以前的焊接技術還沒足夠讓我可以直的完成這個 project,另外就是因為那個時候我對燒錄器設計還是沒有甚麼概念,導致到當我要做到跟一片 Wemos D1 mini 同等效能的 ESP8266 的時候,完全沒辦法好好的設計和焊接成成品出來。 開坑 早在 2018 年,我就已經買來了一片 Wemos D1 mini 跟超級難買的 SD shield 來當小型網頁伺服器。當時的我對網頁編程和嵌入式開發還是非常的入門,結果做出來的東西就是長這樣 Wemos D1 + SD Shield + 小容量 SD 卡 雖然是能用,而且我也把它用 USB 供電吊在我的桌面旁邊當作實驗品開發了一段時間,但是由於用的是別人的 code 加上當時我並不是太熟悉 Arduino C++ (和當時還沒有 ChatGPT),導致最後開發出來的東西不但不好看而且很難用 設計嘗試,失敗收場 之後一年我試著把這個設計弄成一片真正的電路板。由於當時我還沒有開發燒錄器的能力(特別是要想辦燒焊那兩個超小的 BC817 transistor),因此我的想法是先把 ESP8266 焊到板子上,再透過現成的燒錄器對它進行開發(見下面那一排燒錄用排針),然而後來我弄著弄著就覺得不對勁,而且這種每次都要插燒錄器才能寫東西的系統感覺開發起來就很麻煩,應該不會有人想用,所以最後還是沒拿來生產。 第一代的 ESP8266 網頁伺服器,最後沒做生產真是一個明智的決定 5 年之後,我終於做出了 WebStick 原型機 WebStick 這東西其實在我腦海中已經卡住很多年了。但是一直就軟硬體技術原因無法開坑。直到最近開始學會了 drag soldering 跟找到了一款可以讓我進行超精密焊接(但是缺點是一次性)的烙鐵頭後,我終於把這個封塵多年的 project 重新抓出來做。 首先:燒錄器 網上有很多不同的設計,有自動的也有半自動(燒錄的時候要按 FLASH 按鈕)。半自動的能省下兩顆 transistor 跟兩顆電阻,但是由於編程的時候每次都要按 FLASH 和 RESET 有夠麻煩,所以我在設計的時候便選擇了自動燒錄器。以下是我參考的設計圖: 燒錄器設計圖 但是這設計圖差了點東西,就是圖裡沒有標注到 GPIO15 應該要 PULL LOW。在設計電路板的時候記得把 GPIO15 經電阻(如 10k)接地,這樣 SPI CS 才能正常使用。 為甚麼我會知道? 這真是個他媽的好問題(被坑) 這顆電阻雖然不加也可以,但是會無法使用 SPI 另外這兩顆 BC817 的 routing 也弄得我懷疑人生,不過後來還是在板上 route 好了。 我都不知道自己當晚是怎樣發神經想到可以這樣 route 的,嘛總之能動就好了。 其次:SD 卡 ESP8266 有內置的 SPI 針腳可以用來讀取 SD 卡。很多人以為 SD 卡模組上面有特別 IC 去把 SD 卡轉換成 ESP8266 可以讀取的信號,然而實際上的讀取方法聽上去有點奇妙,就是直接把 SD 卡當成 SPI Slave Device 來讀取。 那你會好奇,如果 SD 卡可以直接接 ESP8266,那外面在賣的 SD 卡模組上面那顆 IC 是幹甚麼用的?這是個好問題,那是防呆用的(不對) 你看,不是所有 MCU 都是在用 3.3V Logic Level 的說。例如說 Arduino UNO 在用的 Atmega328 就是支援 3.3v - 5v 輸入。SD 卡只能用 3.3V 讀取,過高的通訊電壓會把它弄壞,因此很多模組為了相容更多的開發板,只好加上一顆用來轉換信號線電壓的 IC,通稱 LLC (Logic Level Converter ,大陸好像叫 「電平轉換器」)。但是由於我在用的 ESP12E 只支援 3.3V,所以剛好與 SD 卡需要的電壓一樣,所以就不用轉直接接 MCU 就好啦。 不說不知道,如果你走去翻 Wemos D1 SD shield 的設計電路圖,它基本上就只有一個 SD 卡插槽,跟一顆差不多看不見大小的電容來做 ripple filter。真的有夠簡單欸 把兩個東西整合理起 當兩個電路都有了,剩下的部分就是把電路給整合到同一片板子上。我本來是打算用名片大小的設計,這樣做不但能有一種「這是送你的見面禮」感覺,而且也有更多的空間來寫使用教學,有點像國外嵌入式工程師大神做的能跑 Linux 的名片一樣 國外工程師做的,能跑 Linux…
CH552G 怎樣直接輸入中文到 Windows 上的應用程式?
CH552G 作為一款便宜好用的 USB HID 模擬晶片,我當然是想辦法讓他做到各種神奇功能的東西。而其中一個我很有興趣做的就是能直接輸出中文的鍵盤。 可是這樣問題又來了。到底要怎樣才能讓 CH552G 直接輸出中文呢?作為只有 8bit 的 E8051 處理器,根本沒辦法輸出中文不是嗎? 黑魔法:ALT + BIG5 碼 如果有一定年紀的 Windows 使用者,有玩過一些很早期的網絡遊戲的話應該都知道,早期的中文輸入法對各種遊戲和應用程式的支援很差,有時候即使是輸入法能抓到被遊戲 handle 的鍵盤輸入,也不一定能夠輸出字元到遊戲內的輸入框。這個時候就出現了一個神奇的輸入方法,就是在鍵盤上長按 ALT 然後在 keypad 上輸入該中文字對應的 big-5 碼,之後放開 ALT,中文字就出來了!? 然後我又花了幾天把所有 utf-8 跟 big-5 交雜的中文字轉成其對應的輸入用 DEC 碼 當然,在 Windows 10 之後,有不少應用程式(例如 wordpad)都開始轉用萬國碼 UTF-8,但是只要能夠背下兩邊的碼表的話理論上就能夠不透過任何中文輸入法輸入中文了喔? 那麼,我們讓 CH552G 代替我們按鍵盤不就行了嗎? 理論上是這樣沒錯,但是這裡又有一個問題了,就是 Keyboard_press 沒辦法按數字鍵盤啊??? 假設我們要輸入 「你」 好了。它對應的 big-5 和 utf-8 碼分別為 20320 ( 4F60 HEX) 跟 42817 ( A741 HEX),可是當你想著:這很簡單啊,只要 Keyboard_press(KEY_LEFT_CTRL); Keyboard_write("2"); Keyboard_write("0"); Keyboard_write("3"); Keyboard_write("2"); Keyboard_write("0"); Keyboard_release(KEY_LEFT_CTRL); 的時候,它卻輸出了 "20320" 到電腦上。 嗯?為甚麼會這樣? 原來是因為在 USB HID Keyboard 的定義裡面,數字鍵盤的數字跟你英文字上面那排的 hex code 是不一樣的原因。這個時候為了要轉換這堆數字,我寫了這樣的一個轉換 function,把數字的 char 轉換成 keypad 的 hex code (為了找這個我快花了 3 個晚上,累死) char PressNumericAsKeypad(char in){ int delayTime = 1; switch (in) { case '0': Keyboard_press('\352'); delay(delayTime); Keyboard_release('\352'); break; case '1': Keyboard_press('\341'); delay(delayTime); Keyboard_release('\341'); break; case '2': Keyboard_press('\342'); delay(delayTime); Keyboard_release('\342'); break; case '3': Keyboard_press('\343'); delay(delayTime); Keyboard_release('\343'); break; case '4': Keyboard_press('\344'); delay(delayTime); Keyboard_release('\344'); break; case '5': Keyboard_press('\345'); delay(delayTime); Keyboard_release('\345'); break; case '6': Keyboard_press('\346'); delay(delayTime); Keyboard_release('\346'); break; case '7': Keyboard_press('\347'); delay(delayTime); Keyboard_release('\347'); break; case '8': Keyboard_press('\350'); delay(delayTime); Keyboard_release('\350'); break; case '9': Keyboard_press('\351'); delay(delayTime); Keyboard_release('\351'); break; default: return in; break; } } 這樣我們就順利的把數字 char 換成 keypad 輸出的 hex code 了。之後就是再寫一個 wrapper function 把整個輸入中文字的流程更容易開發: //Pass…
ESP8266 讀取 SD 卡太慢?要試試全速 SPI 嗎?
我看到日本技術宅的 Blog ,覺得奇怪這裡為甚麼他可以在 SD.begin 後面設定一個指定的速度(? https://www.mgo-tec.com/blog-entry-esp8266-wroom-spi-speed-up.html 於是我跑去翻 ESP8266 Arduino Core 的源碼,原來 ESP8266 比起原生的 Arduino core 在 SD.begin function call 多了一個可選擇指定的 uint32_t 參數,預設是 SPI 一半速度(4 Mhz),但是如果你的 SD 卡夠快(例如說現在大部分 A1 Class 10 的卡)都可以上到 8Mhz (SPI Full Speed) 或以上(這裡日本部落格用的是 40Mhz,為了資料安全好孩子不要隨便超頻你的 SD 卡) https://github.com/esp8266/Arduino/blob/313b3c07ecccbe6fee24aa9fa447c4aed16ca499/libraries/SD/src/SD.h#L35 嘛不過 ESP8266 的 WiFi 速度極限也就 4Mbps,如果要用來做網頁伺服器的話 SPI 速度設定到全速(8Mhz)已經足夠盡用它的網絡速度了。 備注:如果要設定速度的話可以用 ESP8266 SD library 內預設的常數 uint32_t const SPI_FULL_SPEED = 8000000; uint32_t const SPI_HALF_SPEED = 4000000; uint32_t const SPI_QUARTER_SPEED = 2000000;
Arduino 用在產品上太貴?有聽過 CH552G 嗎?
話說一個月前我在想到底有甚麼方法可以替 Raspberry Pi 增加一個 Analog Read 的功能,看了網上很多的教學和文章,大致上的方法就只有 用 ADC 透過 SPI 傳到 Raspberry Pi 的 GPIO用 USB 接一片 Arduino UNO 再透過 Serial 傳送讀取的資料 可是 ADC 需要接到 Raspberry Pi 的 3.3V GPIO,用來量 5V 的 Analog 電壓的話總覺得有點危險,要安全的話要加一片 LLC (Logic Level Converter,5V 轉 3.3V 的),需要的空間又會變更多,而且生產上來也不會特別便宜。 另外第二個方法比較安全(畢竟 USB 的 5V 是直接來自電供的,如果有短路電供會先斷開作保護),但是一片 Arduino (ATmega328)價格一點都不便宜,即使用較便宜的 ATtiny 44 也需要快 10 - 12 港幣一片,如果真的要量產起來一點都不比 ADC + LLC 便宜。 然後剛好我在研究 CH340 的各種版本時,在那間公司的網站不但發現到有一種 CH340 有內置晶振(不用外接,省掉焊接和零件的成本)而且還發現了一種新的 MCU 沒錯,這就是今天的主題:CH552G 於是我便隨便買來了一片開發板,來到之後長這樣,真的好小,跟我前女友的現任男友 ㄐㄐ 差不多大小。 CH552G 的好處在於以下幾點 內置 USB HID 控制器,不需要外接 USB to Serial 轉接晶片(如 CH340)5V Logic Level,可測電壓為 0 - 5v (對應 0 - 255,共 8bit 準確度)超級的便宜,大概 5.5 港幣就能買到一片(開發板也只是約 15 港幣一片) 使用 Arduino IDE 對它進行編程 https://github.com/DeqingSun/ch55xduino 上面是這次使用的 Arduino board definition。跟其他的第三方板子一樣,先把它給的 json 檔加入你的 Arduino 設定裡,再在 broad manager 找到 ch5xx 的板子把它加進去。 https://raw.githubusercontent.com/DeqingSun/ch55xduino/ch55xduino/package_ch55xduino_mcs51_index.json 對於全新的開發板,第一件事情就是先安裝 bootloader。詳情教學可以看 Github,但是對於 Windows 用戶在跟著 Github 教學前需要先為 CH552G 安裝 driver,不然在 Windows 內會顯示為不明裝置。其步驟為下: 下載 Zadig ,https://zadig.akeo.ie/按著在沒插電的開發板上的 PROG 按鈕(或「下載」按鈕)把開發板透過 USB 線連到電腦(注:不能鬆開按鈕)等待開發板在「裝置管理員」以「不明裝置」顯示之後才可鬆開打開 Zadig,點選 Unknown Device 1,選擇 Install Driver第 3 - 5 分鐘(可能更慢,看電腦規格)完成之後把開發板拔出,再重新插回去電腦裝置管理員會顯示為 COMX (我的話就是 COM7) 完成後的 Zadig 界面 安裝完 driver 的開發板,COM7 燒錄 Bootloader 在開發板寫程式的時候 bootloader 會順便送進去,所以這裡我們先要選好正確的板子(我這片是 CH552) Pin 的編號就是以開發板上寫的 x10 為準,例如說要控制 P3.0,pin number 則輸入 30,P3.2 則是 32 如此類推。因為我要順便測試 analogRead 跟 USB Serial,所以我把 blink 改成這樣 int val = 0;…
Sinilink XY-WF5V 灌 Home Dynamic v2 IoT 控制器
最近因為一些原因拿到了兩塊 Sinilink 的 XY-WF5V WiFi 繼電器模組。由於它的內置 firmware WiFi 有鎖,加上它的 App 看上去就覺得很爛,所以我決寫把它的 firmware 刷掉重新寫一個 HDSv2 的 firmware 進去,讓我的雲端 IoT 控制器能夠控制到它。 首先,怎樣在非開發板上灌程式? 這個時候你就需要 USB 編程器了(大陸叫下載器,通常我叫它 ISP),你可以用不同的方式來灌程式進去,可是我這裡用的是最便宜的 ESP01 用編程器,樣子大約長這樣 然後我們就可以直接把線從那個 2 x 8 2.54 母頭裡拉出來,直接焊接在 ESP8266 上面,線路如下 接線圖 完成之後大約長這樣 逆向工程 之後就是把板子的線路重新畫出來。由於這是一塊兩層板,所以沒甚麼大問題。之後再把找到的 GPIO 跟據需要重新寫進 HDSv2 On/Off 的 example sketch 裡面, 可是,我們怎知道應該使用哪一塊開發板的配置呢? 這個我們可以選擇使用 Wemos D1 R1 作為基礎,重新把 ESP8266 上對應的針腳在 Wemos D1 上找到對應的 Digital Pin 號碼即可。完成之後 Arduino sketch 裡面的開關 pin 被改成如下: int signalOutputPin = D14; //The pin to activate the mosfet of relay int buttonPin = D6; int refPin = D8; 如果找不到 Pull up / down 電阻怎辦? 另外,如果你在板子上找到沒有 pull up / pull down 的按鈕,很有可能它是使用軟體方式做的 internal pull up / down 電阻。在更改 HDSv2 的 example 時只要把 INPUT pin 的設定由 INPUT 改成 INPUT_PULLUP 即可,例子如下: pinMode(buttonPin, INPUT_PULLUP); 相關可以使用程式 pull up / down 的針腳可以參考下表(請對應 Generic 8266 的 GPIO 號碼,不要使用最左欄的 pin 號碼) 完成圖 完成之後在 ArozOS 內重新掃描一下 IoT 裝置,應該就會有新的 Sinilink WiFi Relay 在列表中出現了。
從零開始的 IoT 系統設計
說到 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…