使用 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)?