Ampelmännchen是東德的一個流行符號,每個街角的行人交通燈上都有它的身影。甚至有一家總部位於柏林的零售連鎖店也受到了他們的設計啟發。
我在跳蚤市場發現了一組二手 Ampelmann 訊號,我想透過手機控制它們。如果您也想這樣做,請繼續閱讀!
健康警告:本項目使用220V市電。我不是電工。請您自行承擔遵循這些說明的風險。
我想建造一些具有柏林美學的東西,並且讓來我家的遊客與之互動會很有趣。不幸的是,今年我們的遊客數量嚴重下降,但仍然希望 2021 年能有良好的迴響…?
本機網路上的每個裝置都有自己的私人 IP 位址(例如192.168.1.20
)。某些路由器(例如 FritzBox)還允許您按本機主機名稱進行瀏覽,因此也可以透過mydevice.fritz.box
存取192.168.1.20
。對於交通燈,設備主機名稱是traffic-light
,因此我們可以透過http://traffic-light.fritz.box
存取它。
Web 應用程式是一個非常簡單的響應式單頁應用程式。它顯示:
程式碼位於/webapp目錄下。不需要外部依賴項,因為它只依賴標準瀏覽器功能,例如 CSS 轉換、WebSockets 和 XHR。您可以在此處預覽正在運行的應用程序,儘管它不會控制任何內容,因為您當然不在我的區域網路上。如果您從本地網路(例如http://traffic-light.fritz.box
存取它,它將完全正常運作。
載入頁面後,應用程式發出 GET 請求以尋找/api/status
處的當前狀態,然後在連接埠81
上開啟與伺服器的 WebSocket 連線。後續狀態更新將始終透過 WebSocket 進行,以保持多個客戶端同步。每次 websocket 事件到達時,我們都會將變更應用於單一全域state
物件。不久之後, updateScreen()
方法將這些變更套用到 DOM。
啟動時,我們也會偵測使用者是否使用行動裝置或桌面設備,以處理觸控事件或點擊事件。我們實際上使用touchend
touchstart
向伺服器發送命令,因為這在 iPhone X 上執行更可靠。
最後,我們希望盡可能減少伺服器的負載。請記住,ESP8266 運作在80MHz處理器上,RAM 僅約50kB 。它不是一個強大的設備。因此,當瀏覽器處於非活動狀態時,我們會中斷 websocket 的連線。當選項卡或瀏覽器重新開啟時,我們再次檢查狀態並重新連線 WebSocket。
ESP8266 正忙於處理 API 請求和計時程式碼,因此它沒有必要的資源來服務 Web 應用程式本身。此外,如果我每次想要應用更新時都需要物理連接到硬件,那麼對網絡應用程式進行外觀更改也很困難。
webapp的index.html遵循單頁應用原則,一切都應該由Javascript渲染,使得HTML內容本身非常小。像550 位元組小。其他所有內容均由客戶端瀏覽器加載,無需進一步調用伺服器。因此,該 Web 應用程式實際上完全託管在 GitHub Pages(一個免費的靜態網站託管工具)上。點擊/index.html
實際上向 GitHub 頁面發出代理請求,並將結果傳回客戶端瀏覽器。
現在我們可以更改 web 應用程式中的任何內容,並且伺服器不受影響。偉大的!嗯,差不多…
此 Web 應用程式的大部分程式碼都在 CSS 和 JS 檔案中,而不是在index.html
本身。瀏覽器會在重新要求載入的檔案之前將其快取一段不確定的時間。如果index.html沒有改變,但我們部署了新的JS版本,我們的客戶如何知道他們需要載入新的JS版本?
當我們將程式碼的任何新版本推送到 git master
分支時,會執行一個 GitHub Action,該操作會執行到 GitHub Pages 的部署,其中頁面實際上是向公眾提供的。這裡的技巧是在index.html
中將後綴?version=latest
添加到我們自己的 CSS 和 JS 文件的末尾。在將內容複製到gh-pages
分支之前,該操作使用命令sed
將「 latest
」替換為變數$GITHUB_SHA
的值,這實際上是master
分支上的最後一次提交 ID。 (例如像b43200422c4f5da6dd70676456737e5af46cb825
這樣的值)。
然後,下次客戶端訪問 web 應用程式時,瀏覽器將在?version=
之後看到一個新的不同值,並請求新的、更新的 JS 或 CSS 文件,該文件尚未快取。
請參閱traffic-light-controller.ino
中的setup(void)
方法和 Arduino 程式碼部分,以了解其實際運作原理。
我決定同時使用 REST 和 WebSocket。 REST 主要由客戶端用來控制伺服器。 WebSocket 用於向客戶端廣播狀態資訊。有很多像 Postman 這樣的工具可以讓你輕鬆嘗試 REST API,所以我發現這更方便。
HTTP API:請參閱此處的 Swagger 文件。
WebSocket API: WebSocket 連線發送 JSON Blob,Web 應用程式使用這些 Blob 來更新其內部狀態。 Websocket 事件可以包含一個或多個要更新的欄位。包含環境資訊的範例可能如下所示:
{
"redTemperature" : 21.6 ,
"greenTemperature" : 22.7 ,
"greenHumidity" : 55 ,
"redHumidity" : 59
}
目前沒有資料透過 websocket 從客戶端發送到伺服器,儘管這是可能的。
arduino 程式碼全部位於一個文件中,其中包含解釋性註釋。
它從一組引腳位置定義、庫導入以及 HTTP 內容類型和回應代碼值等硬編碼值開始。接下來是一組可以在運行時更改的變量,所有變量都帶有下劃線前綴。這裡也初始化了一些對象,包括 Web 伺服器、Web 套接字伺服器、WiFi 用戶端和溫度感測器。 「系統時鐘」由_currentMillis
字段維護。
啟動後, setup(void)
方法運行。完成一些引腳設定後,它會為 REST 端點建立必要的映射,並啟動伺服器偵聽客戶端請求。 loop(void)
方法負責其他一切。每個週期它都會處理任何待處理的網路請求,更新節奏週期,並在必要時讀取感測器。如果我們處於聚會模式,它將設定當前的閃光/脈衝狀態。
節奏(用於派對模式)被硬編碼為在RHYTHM_PATTERN
字段中播放序列,但理論上它可以在運行時更改為其他任何內容。每次呼叫rhythm()
方法時,我們都會使用目前的_bpm
和_currentMillis
值來計算出我們應該在模式中處於什麼位置。這存儲在_rhythmStep
字段中。
在節奏模式期間,有時兩個繼電器實際上都關閉。但由於這些燈是白熾燈泡,它們不會立即啟動或停止發光。看起來燈泡需要大約 1.7 秒才能完全打開或關閉。因此,透過在模式中添加一段時間,使兩者都關閉,我們最終會在燈泡升溫和冷卻時得到溫和的脈衝模式。
在partyFlash()
方法中,我們取得目前應該顯示的模式項目(或兩者都會關閉),並使用適當的參數呼叫lightSwitch(...)
。 lightSwitch(...)
依序呼叫sendToWebSocketClients(...)
以便所有連線的客戶端都更新為新狀態。
如果使用者只需單擊其中一盞燈即可將其打開或關閉,則過程類似,但作為 REST 請求進行處理。呼叫handleX
方法之一,該方法驗證請求,然後呼叫lightSwitch(...)
。
我們會不頻繁地檢查兩個機櫃的溫度,並透過 WebSocket 將其傳送給所有用戶端。目前,這僅用於提供信息,但可用於在溫度超過某個安全限制時禁用燈光。
感謝 @mrcosta 幫忙審閱本文。