使用 Node-RED 進行可視化編程:輕鬆連接物聯網

已發表: 2022-03-11

通過編程,我們讓機器通過遵循一系列簡單指令來模仿複雜的行為。 使用諸如彙編、C、Python 和 JavaScript 之類的文本編程語言是執行此操作的主要方法之一。 這些編程語言的設計者花費了無數個小時,試圖通過富有表現力的語法、健壯的編程結構和強大的工具鏈,讓編寫程序的體驗盡可能簡單。 然而,所有這些編程語言都有一個共同的特點:文本源代碼。

在文本中編寫程序有效,並且在大多數情況下效果很好。 然而,視覺表達程序的能力通常是可取的。 能夠設計通過更大系統的各個組件的信息流通常是所需要的。 可視化編程工具也對任何剛接觸編程並努力處理變量、指針、信號、範圍等各種概念的人寬容。

使用 Node-RED 通過 API 連接硬件設備

Node-RED 是一種可視化編程工具。 它直觀地顯示關係和功能,並允許用戶編程而無需鍵入語言。 Node-RED 是一個基於瀏覽器的流編輯器,您可以在其中添加或刪除節點並將它們連接在一起以使它們相互通信。

在 Node-RED 中,每個節點都是以下兩種類型之一:注入節點函數節點。 注入節點無需任何輸入即可生成消息並將消息推送到與其連接的下一個節點。 另一方面,功能節點接受輸入並對其執行一些工作。 有大量這些節點可供選擇,Node-RED 使得將硬件設備、API 和在線服務連接在一起比以往任何時候都更容易。

Node-RED 入門

Node-RED 基於 Node.js 構建。 要安裝 Node-RED,您需要同時安裝 Node.js 和 NPM。 使用 NPM,安裝 Node-RED 非常容易:

 npm install -g node-red

Node-RED 的流編輯器是一個基於 Web 瀏覽器的應用程序。 為了能夠使用它,請運行 Node-RED:

 node-red

&hellip 並導航到 http://localhost:1880。

你好世界!

沒有學會說“Hello, world”的初學者編程教程是完整的嗎? 讓我們首先嘗試一下:

  1. 在流編輯器上拖放一個注入節點。 然後雙擊並將有效負載設置為字符串並寫入“Hello world”。
  2. 拖放調試節點,方法與註入節點相同。
  3. 將它們連接在一起。
  4. 單擊右上角的“部署”按鈕。
  5. 單擊注入節點左側的藍色按鈕。

試試吧。 你會看到這樣的東西:

只是 JavaScript

使用 Node-RED,您不僅限於簡單的節點和功能。 由於 Node-RED 是基於 Node.js 構建的,因此它全部由 JavaScript 提供支持。 節點確實是 Node.js 模塊。 它們可以在 http://flows.nodered.org/ 中找到,因此要將它們添加到您的左側面板中,您只需“npm install”即可。 事實上,您可以開發自己的流程並將它們上傳到流程存儲庫。 應用程序可以任意複雜,因為您可以在 Node-RED 提供的代碼編輯器內的函數節點中鍵入 JavaScript。

由於該平台基於 Node.js,因此它利用了相同的事件驅動、非阻塞模型。 因此,基於 Node-RED 構建的應用程序可以在 Raspberry Pi 等低成本硬件上運行,也可以在雲中運行。

Now Let's Go Pro:家庭自動化時代

為了演示 Node-RED 如何融入物聯網領域,讓我們構建一個應用程序來更改智能燈泡的顏色。 並非每個人都可以使用相同的智能照明系統,但沒有什麼可擔心的,因為您可以從官方流存儲庫中找到合適的 Node-RED 模塊。 然而,為了讓事情變得更容易,讓我們去做一些更聰明的事情。

認識網獸。 它是一個開源平台,用於為物聯網設備和設備開發應用程序,而無需擔心無線協議、品牌兼容性等細節,也不必知道如何處理每個特定的 API。 它允許我們使用充當真實設備的虛擬設備! 因此,即使您沒有智能燈泡,也可以使用虛擬燈泡。

我們可以像這樣全局安裝 Netbeast for Node-RED npm 包:

 npm install -g node-red-contrib-netbeast

netbeast-red 節點將代表 Netbeast Dashboard,它將其 API 原語翻譯到您家中的所有智能設備。 幸運的是,它也可以作為模塊使用!

啟動網獸:

 npm install -g netbeast-cli netbeast start

這將使儀表板在端口 8000 上可用,SSL 在 8443 上可用。接下來打開瀏覽器訪問 http://localhost:8000 並導航到 Explore。 在那裡,我們將能夠找到許多應用程序和插件。 查找您的智能燈泡的品牌(Philips Hue、LIFX、WeMo),或者如果您沒有,請嘗試下載燈泡插件。 檢查您的儀表板插件是否包含其中之一!

黃色徽章表示插件正在運行,但找不到任何設備。 單擊燈泡插件以創建虛擬燈泡。 發現的任何其他設備應出現在網絡下。

一切就緒,讓我們重新開始工作。 我們將做一個簡單的流程:

  1. 拖放一個注入節點。
  2. 拖放 Netbeast 節點。
  3. 拖放調試節點。
  4. 將其全部連接起來,如下所示:

現在讓我們向儀表板發送一個 HTTP 請求。 使用 Netbeast API,我們必須通過注入節點發送一個 JSON,其中包含我們想要在燈泡上觸發的值。

按下按鈕為所有智能燈泡注入顏色和電源!

每個主題代表一種不同類型的設備。 所以有關於燈光的主題,也有關於音樂、暖氣和視頻的主題; 以及濕度、存在、溫度傳感器,列表還在繼續。 您可以在他們的文檔中找到要翻譯到各種設備的主題列表及其推薦結構。 這個物聯網引擎不成熟,但功能強大。 開源,允許開發人員重用信息來創建真正連接的場景,從而變得智能。

讓我們更深入

接下來,我們將使用另一個插件(環境噪聲檢測器)創建第二個流,將其用作觸發器,將燈泡的顏色更改為噪聲信號量。 在本教程中,我們將使用虛擬的,因此無需購買新硬件。 讓我們從單擊 Node-RED 編輯器中的“加號”按鈕開始。

再次訪問 Dashboard http://localhost:8000/explore 到 Explore 部分並尋找 Volume-Plugin。 這是一個非常初級的 Web 應用程序,它利用瀏覽器中的getUserMedia()從簡單的 HTML 應用程序中捕獲媒體。 所以它可能只適用於現代瀏覽器!

點擊它打開,就像虛擬燈泡一樣。 它會要求您允許從您的麥克風錄音。 然後它將消息發送到 Netbeast 的 MQTT 代理,該代理將在所有 Dashboard 之間共享,因此我們將能夠訂閱。 為此,我們只需要將netbeast-trigger節點拖放到 node-red 的編輯器中。 然後我們將在觸發器和 Netbeast 節點之間插入一個函數,這樣我們就可以決定它什麼時候聲音太大了。 此外,我們應該使用一些調試節點來檢查一切是否正常。 該方案現在看起來有點像這樣:

現在,讓我們在 tooLoud 函數節點中輸入一些代碼。 是的,我知道我保證你可以編程而不用編寫代碼,但我已經向你展示了! 您可以嘗試組合可用的不同元素或註冊表中的其他節點來完成以下操作。

 var volume = msg.payload.volume node.log(volume) if (volume < 50) { return { topic: 'lights', payload: { power: 1, color: '#00CC00'}} } else if (volume < 75) { return { topic: 'lights', payload: { power: 1, color: '#CCCC00'}} } else { return { topic: 'lights', payload: { power: 1, color: '#FF0000'}} }

這個相當簡單的代碼片段返回具有特定顏色代碼的下一個節點的三個有效負載之一,具體取決於前一個節點報告的音量級別。

現在我們準備好了! 讓我們再次按下 Deploy 按鈕並發出一些聲音。 讓我們看看燈泡如何立即從一種顏色變為另一種顏色!

由於您使用的麥克風和網絡瀏覽器可能不同,請隨意調整功能值和閾值,並使用顏色值來查看它如何改變您的燈泡。

創建自己的插件

這個純 CSS 中的燈泡的靈感來自這個 cssdeck。

您可能已經註意到,之前的虛擬燈泡非常簡陋,因此您可能需要對其進行調整。 甚至更好的是,您可以創建自己的智能家居控制器。 因此,我們將完成為 Netbeast 創建虛擬插件的過程,這將使您能夠為智能設備創建自己的控制器。

您可以使用netbeast-cli包自動生成一些代碼。 通過運行netbeast create myplugin --plugin我們最終會得到一個基本項目,如下所示:

 myplugin ├── README.md ├── index.js ├── package.json └── test.js

前端

現在,讓我們開始用前端模擬燈泡。 設備控制器通常沒有,因此在腳手架命令中還沒有包含公用文件夾。 讓我們在項目中創建一個public目錄,並在其中放置以下 HTML、CSS 和 JS 文件。

索引.html

 <head> <title>Netbeast Bulb Plugin</title> <link rel="stylesheet" href="bulb.css" media="screen" charset="utf-8"> </head> <body> <div class="top-decoration"></div> <div> </div> <div class="bulb-container small"> <div class="bulb light"> <div> <div class="bulb top"></div> <div class="bulb middle-1"></div> <div class="bulb middle-2"></div> <div class="bulb middle-3"></div> <div class="bulb bottom"></div> </div> <div> <div class="screw-top"></div> <div class="screw a"></div> <div class="screw b"></div> <div class="screw a"></div> <div class="screw b"></div> <div class="screw a"></div> <div class="screw b"></div> <div class="screw c"></div> <div class="screw d"></div> </div> </div> </div> <div class="code-container"> <pre><i class="txt-red">beast</i>(<i class="txt-green">'lights'</i>).<i class="txt-blue">set</i>({ <i class="txt-green">color</i>: <i class="txt-green">"<input type="text" class="color" name="color" value="00fea5">"</i>, <i class="txt-green">power</i>: <i class="txt-green">"<input type="text" class="power" name="power" value="on">"</i> })</pre> <button> RUN </button> </div><!-- wrapper --> <!-- declares bulb features and methods --> <script type="text/javascript" src="bulb.js"></script> <!-- real time comms library --> <script type="text/javascript" src="socketio.js"></script> <!-- simulates hardware communication --> <script type="text/javascript" src="hw-api.js"></script> </body>

燈泡.css

 section { float: left; padding: 20px 50px 20px 50px; } .bulb-light { border: 0; background: transparent; margin: 0 auto !important; padding: 0 !important; display: block; z-index: 1; } #bulb { opacity: 1; z-index: 3; display: block;} .bulb.top { border: 0; width: 300px; height: 300px; margin: 0 auto; padding: 0; border-radius: 999px; background: #E7E7E7; } .bulb.middle-1 { margin: -75px auto 0 auto; width: 190px; border-left: 35px solid transparent; border-right: 35px solid transparent; border-top: 55px solid #E7E7E7; } .bulb.middle-2 { margin: -22px auto 0 auto; width: 178px; border-left: 19px solid transparent; border-right: 19px solid transparent; border-top: 50px solid #E7E7E7; } .bulb.middle-3 { margin: -20px auto 0 auto; width: 182px; border-left: 5px solid transparent; border-right: 5px solid transparent; border-top: 30px solid #E7E7E7; } .bulb.bottom { width: 184px; height: 65px; margin: -8px auto 0 auto; padding: 0; border-radius: 0 0 999px 999px; background: #E7E7E7; } #base { position:relative; z-index: 2; } .screw { transform: rotate(-3deg); -ms-transform: rotate(-3deg); -webkit-transform: rotate(-3deg); padding: 0; } .screw-top { margin: -18px auto -4px auto; padding: 0; width: 132px; height: 0; border-left: 15px solid transparent; border-right: 15px solid transparent; border-top: 21px solid #D3D3D3; border-radius: 999px; } .screw.a { background: #DDD; width: 150px; height: 15px; border-radius: 999px; margin: -1px auto 0px; } .screw.b { background: #D9D9D9; width: 135px; height: 15px; margin: -1px auto 0px; } .screw.c { margin: -1px auto 0px; width: 78px; height: 0; border-left: 30px solid transparent; border-right: 30px solid transparent; border-top: 20px solid #DDD; border-radius: 8px; } .screw.d { margin: 0 auto; width: 15px; height: 0; border-left: 30px solid transparent; border-right: 30px solid transparent; border-top: 15px solid #444; } .on #light { -moz-opacity: 1; -khtml-opacity: 1; opacity: 1; } .bulb.top, .bulb.bottom { transition: all 0.5s ease-in-out; } .bulb.middle-1, .bulb.middle-2, .bulb.middle-3 { transition: all 0.5s ease-in-out; }

使用這些 HTML 和 CSS 文件,您應該已經能夠在瀏覽器中看到燈泡形狀。 繼續並打開您的 HTML 文件以實時查看它! 它在工作嗎? 太好了,現在讓我們給它一些功能。

燈泡.js

該文件將通過簡單的單擊、單擊關閉來模擬燈泡行為,並將設置幾個功能,這些功能將用於通過 Netbeast 更改其顏色。

 var color = document.getElementById('color') var power = document.getElementById('power') var bulb = document.getElementById('bulb') var button = document.getElementById('run-btn') var light = document.getElementById('light') button.onclick = function toggleBulbState () { changeBulbParams({ color: color.value, power: power.value }) } function setBulbParams (params) { if (params.power === 'off') { params = { color: 'E7E7E7' } } console.log('set params', params) var bulb_parts = ['.bulb.middle-1', '.bulb.middle-2', '.bulb.middle-3'] document.querySelector('.bulb.top').style.boxShadow = '0px 0px 98px #' + params.color document.querySelector('.bulb.top').style.backgroundColor = params.color document.querySelector('.bulb.bottom').style.backgroundColor = params.color bulb_parts.forEach(function (className) { document.querySelector(className).style.borderTopColor = params.color }) } function changeBulbParams (params) { console.log('change params', params) /* Overwrite html fields if necessary */ color.value = params.color || color.value power.value = params.power || power.value setBulbParams({color: color.value, power: power.value}) }

在此之後,字段和運行按鈕都應該有意義。 您可以開始在全新的虛擬燈泡上嘗試不同的顏色。 然而,我們一路走來的原因是讓它成為我們物聯網生態系統的另一個設備。

hw-api.js

最後一個我們自製的前端JS。 它模擬與服務器的無線連接,就像 WiFi 或藍牙燈泡對其遙控器(如電話、服務器或集線器)所做的那樣。 它是實際插件代碼用來控制它的接口!

 var socket = io.connect() socket.on('connect', function () { console.log('ws:// bulb is online') }) socket.on('disconnect', function () { console.log('ws:// connection with bulb lost') }) socket.on('set', function (params) { changeBulbParams(params) // uses functions from bulb.js! }) socket.on('get', function () { const params = { power: power.value, color: color.value } socket.emit('params', params) })

最後,我們需要從 HTML 中包含 WebSocket 庫,這樣前端就準備好了。 您可以從 https://raw.githubusercontent.com/netbeast/bulb-plugin/master/public/socketio.js 複製源代碼並將其粘貼到名為socketio.js的文件中。 從帶有curlwget的終端,您可以簡單地執行此操作:

 curl https://raw.githubusercontent.com/netbeast/bulb-plugin/master/public/socketio.js > public/socketio.js

我們現在應該有一個看起來像這樣的文件結構:

 myplugin ├── README.md ├── index.js ├── package.json ├── public │ ├── bulb.css │ ├── bulb.js │ ├── hw-api.js │ ├── index.html │ └── socketio.js └── test.js

後端

現在我們要實現與設備的接口並將其註冊到 Netbeast 引擎中。 它將偵聽 websockets 以檢測網絡上已安裝燈泡,然後將POST到 Dashboard API 以便新資源可用。

為此,讓我們看一下我們之前生成的文件:

包.json

此文件包含運行應用程序所需的所有依賴項和信息。 Netbeast 也使用常規的package.json來檢索一些信息,如名稱或類型。 指定這個包是一個插件很重要!

 { "name": "myplugin", "version": "0.0.0", "description": "Netbeast plugin for... <your description>", "main": "index.js", "netbeast": { "bootOnLoad": true, "type": "plugin" }, "dependencies": { "bluebird": "^3.3.5", "body-parser": "^1.15.0", "express": "^4.13.4", "minimist": "^1.2.0", "mocha": "^2.3.2", "morgan": "^1.6.1", "netbeast": "^1.0.6", "socket.io": "^1.4.5", "superagent": "^1.8.3" }, "devDependencies": {}, "scripts": { "test": "node test.js", "start": "node index.js" }, "repository": { "type": "git", "url": "GITHUB_REPOSITORY" }, "keywords": [ "iot", "netbeast", "plugin" ], "author": "YOUR_EMAIL", "license": "GPL 3", "bugs": { "url": "ISSUES_CHANNEL" }, "homepage": "HOMEPAGE" }

index.js

這是從 Netbeast 儀表板調用以啟動插件的代碼! 它需要通過命令行參數接受端口才能知道在哪裡接受傳入的請求。 它將像我們輸入node myplugin.js --port <a free port number>一樣啟動。 請注意開頭的hashbang#!/usr/bin/env node

 #!/usr/bin/env node var io = require('socket.io')() var express = require('express') var bodyParser = require('body-parser') var app = express() // Netbeast apps need to accept the port to be launched by parameters var argv = require('minimist')(process.argv.slice(2)) app.use(express.static('public')) // will serve our app in an HTTP server app.use(bodyParser.json()) // will parse JSON API calls app.use('/api', require('./plugin')(io)) var server = app.listen(argv.port || 31416, function () { console.log('Bulb plugin listening at http://%s:%s', server.address().address, server.address().port) }) // we need websockets to push updates to browser view io.listen(server)

如您所見,我們缺少一個文件來啟動它,該文件實際上實現了socket.io控制器。 沒有什麼花哨!

插件.js

 var express = require('express') var netbeast = require('netbeast') var router = express.Router() var bulbParams // auxiliar variable, nasty way to transmit changes, but works module.exports = function (io) { io = io // Create resource that works on lights topic and listens on /api route netbeast('lights').create({ app: 'myplugin', hook: '/api' }) io.on('connection', function () { console.log('ws:// bulb has connected to plugin') }) io.on('disconnection', function () { console.log('ws:// bulb has disconnected from plugin') }) io.on('connect_failure', function (err) { console.trace(err) }) router.post('/', function (req, res) { io.emit('set', { power: req.body.power, color: req.body.color, }) res.status(200).json(req.body) }) router.get('/', function (req, res) { io.emit('get') var timerReference = setTimeout(function () { if (bulbParams) { res.json(bulbParams) } else { res.status(200).json({ error: 'No bulb available' }) } }, 3000) }) return router }

啟動您的應用程序

現在是時候測試你的應用了。 您可以通過將其打包為tar.gz格式並將其上傳到拖放部分 http://localhost:8000/install 中的儀表板來實現。

 beast package # Compresses your app when ran in myplugin dir

瞧! 您現在可以轉到您的插件並對其進行測試。 轉到網絡部分 (http://localhost:8000/devices) 以查看它正在運行並從那裡更改其顏色。

如果出現問題或者您認為您可能遺漏了某個細節,請嘗試使用node index.js在本地運行它,也許它會比在netbeast start日誌中更容易調試。

發表你的作品

如果您希望您的應用程序顯示在 Netbeast 儀表板的探索部分,您必須在 GitHub 中使用 Netbeast 應用程序或 Netbeast 插件創建一個存儲庫,兩者都包含在描述和README.md中。

為了找到我們使用 GitHub 的搜索 API 的應用程序。 當您向以下位置發出 GET 請求時,我們會看到相同的結果:https://api.github.com/search/repositories?q=netbeast+language:javascript

如果您的應用出現在那裡,您就會知道它會顯示出來!

下一步是什麼?

這兩個項目都是開源的,並且確實涉及社區。 如果您想開始創建自己的 Node-RED 流或節點,請查看他們的官方文檔。 按照那裡描述的步驟操作,您應該能夠立即發布自己的節點或流。

另一方面,如果您想深入了解 Netbeast,您也可以關注他們的文檔或查看 Dashboard 存儲庫。 使用 Netbeast API,您不必再專注於單個設備、品牌或技術,所以試一試吧。 您可以在此處了解更多信息,或加入他們的 Slack 頻道並討論 Node-RED、IoT 或 Node.js。

如果您想在 Raspberry Pi、Beagle Bone 或舊服務器上安裝這些東西,您可以將它們變成一個可破解的智能集線器,無需代碼! 在這兩個站點中都有它們的預製構建。

快樂的黑客。

相關:我們是否正在創建不安全的物聯網 (IoT)?