基于reTerminal DM与Node-RED的BACnet TCP边缘网关实战
2026/8/2 11:39:52 网站建设 项目流程

1. 项目缘起:当工业边缘计算遇上楼宇自动化

最近在折腾一个楼宇自动化(Building Automation)的现场项目,客户有一批新部署的工业边缘计算设备,型号是reTerminal DM,核心需求是想用它来替代传统昂贵的专用网关,实现暖通空调(HVAC)系统中多个BACnet设备的集中数据采集、协议转换和边缘逻辑处理。这个需求听起来很典型,但实操起来,从硬件选型到软件栈的搭建,每一步都有不少讲究。

reTerminal DM是Seeed Studio推出的一款基于树莓派CM4计算模块的工业级边缘终端,自带7英寸多点触控屏、丰富的工业接口(如RS-485、CAN、数字I/O)和宽温宽压设计,天生就是为现场恶劣环境下的数据交互和轻量级计算准备的。而BACnet TCP,作为楼宇自控领域事实上的标准通信协议,负责将空调机组、VAV变风量末端、传感器等设备的状态和数据“说”出来。

问题的核心在于:如何让这台“全能型”的工业边缘硬件,高效、可靠地“听懂”并“处理”BACnet设备的数据,甚至还能根据数据做出一些简单的自动化决策?这就是Node-RED登场的时候了。Node-RED以其低代码、流式编程和强大的社区节点生态,成为了连接硬件(reTerminal DM)、协议(BACnet TCP)和业务逻辑(自动化规则)的完美粘合剂。这个组合,本质上是在构建一个高度定制化、成本可控且具备边缘智能的楼宇控制网关。

2. 硬件基石:reTerminal DM的选型考量与开箱配置

选择reTerminal DM而不是普通的树莓派或者工控机,是基于几个非常实际的现场因素。首先,工业环境对稳定性的要求极高,普通的消费级硬件在持续运行、电压波动或温度变化时容易出问题。reTerminal DM支持9-36V的宽压直流输入,并且能在-20°C到70°C的温度范围内工作,这直接解决了现场供电不稳和机房温度可能过高或过低的问题。

其次,其自带的接口几乎是为工业物联网(IIoT)量身定做。一个关键的接口是RS-485,这是许多现场仪表、传感器(包括部分老式BACnet MS/TP设备)的物理层标准。虽然我们这次主要用BACnet TCP(走以太网),但保留RS-485接口为未来可能的协议扩展或对接其他设备留下了余地。另外,它的CAN总线接口在汽车和某些工业控制场景也很有用。自带屏幕则是一个巨大的调试和监控优势,你可以在现场直接查看设备状态、日志和简单的数据可视化,无需额外连接显示器。

拿到设备后,第一步是准备系统。我推荐直接从Seeed Studio官网下载为其定制的树莓派OS镜像(基于Raspberry Pi OS Bullseye)。这个镜像已经包含了必要的内核驱动和优化,比如屏幕触摸校准、GPIO库等。使用Raspberry Pi Imager工具将镜像写入microSD卡,插入reTerminal DM,上电启动。

注意:首次启动时,建议先连接键盘和鼠标进行基础配置,如设置Wi-Fi/有线网络、地区、时区,并务必立即修改默认密码。对于工业设备,安全是第一位,弱密码是绝对的红线。

系统初始化完成后,通过SSH远程登录是更高效的操作方式。你需要知道设备的IP地址,可以在屏幕上查看,或者通过路由器后台查找。使用ssh pi@<reTerminal-IP>命令连接,密码就是你刚才设置的。

接下来是基础环境加固与优化。第一件事是更新系统:sudo apt update && sudo apt upgrade -y。然后,考虑到这个设备将7x24小时运行并可能处理敏感的控制指令,我强烈建议做两件事:

  1. 禁用不必要的服务:例如蓝牙(sudo systemctl disable bluetooth)和音频(如果不用),减少资源占用和潜在攻击面。
  2. 配置静态IP:对于工业网络中的网关设备,动态IP(DHCP)可能带来不确定性。编辑/etc/dhcpcd.conf文件,为有线网卡(通常是eth0)配置固定的IP地址、网关和DNS。
# 示例:在 /etc/dhcpcd.conf 末尾添加 interface eth0 static ip_address=192.168.1.100/24 static routers=192.168.1.1 static domain_name_servers=8.8.8.8

完成这些,你的reTerminal DM就有了一个稳定、安全的基础运行环境,可以开始安装我们的核心软件了。

3. 软件栈搭建:Node-RED与BACnet节点的部署详解

软件部分的核心是Node-RED。在树莓派OS上安装它非常简单,官方提供了一键脚本。但根据我的经验,直接使用脚本有时会安装较旧的版本。我更喜欢用Node.js的包管理器npm来安装,这样可以更好地控制版本,也便于后续管理。

首先,安装Node.js。树莓派OS仓库里的Node.js版本可能较旧。我们可以从NodeSource获取更新的LTS版本(如18.x)。执行以下命令:

curl -fsSL https://deb.nodesource.com/setup_18.x | sudo -E bash - sudo apt install -y nodejs

安装完成后,用node -vnpm -v检查版本。接下来,全局安装Node-RED:

sudo npm install -g --unsafe-perm node-red

--unsafe-perm参数在树莓派这类环境下通常是必需的,以避免权限问题。

安装完成后,我们需要让Node-RED以服务的形式在后台运行,并设置开机自启。这样即使设备重启,我们的流(Flow)也能自动恢复。使用以下命令创建系统服务:

sudo npm install -g node-red-admin sudo node-red-admin init

运行init命令后,它会引导你创建一个node-red.service文件。通常直接接受默认配置即可。然后启用并启动服务:

sudo systemctl enable node-red.service sudo systemctl start node-red.service

现在,打开浏览器,访问http://<reTerminal-IP>:1880,你应该能看到Node-RED的图形化编辑界面了。

接下来是最关键的一步:安装BACnet节点。Node-RED的强大在于其节点库,我们需要一个能够与BACnet TCP设备通信的节点。社区里最成熟、维护最活跃的是node-red-contrib-bacnet不要在Node-RED的“管理面板”->“节点管理”里搜索安装,对于这种依赖本地编译(需要bacnet-stack库)的节点,通过命令行安装更可靠。

首先,安装BACnet协议栈的编译依赖:

sudo apt install -y build-essential libtool automake

然后,在Node-RED的用户目录(通常是~/.node-red)下安装这个节点:

cd ~/.node-red npm install node-red-contrib-bacnet

这个过程可能会花费几分钟,因为它需要从源码编译bacnet-stack。安装成功后,重启Node-RED服务以加载新节点:

sudo systemctl restart node-red.service

刷新Node-RED编辑器页面,在左侧节点面板的“网络”分类下,你应该能看到新的“bacnet”节点了,主要包括bacnet in(读取)、bacnet out(写入)和bacnet whois(设备发现)。

4. BACnet TCP网络配置与设备发现实战

在开始编程之前,必须确保reTerminal DM与BACnet设备处于同一个二层网络(即同一个子网),并且网络通畅。BACnet/IP(包括TCP和UDP)依赖于广播和单播。将reTerminal DM的IP(例如192.168.1.100)和BACnet设备的IP(例如192.168.1.201)设置在同一个网段。

在Node-RED中处理BACnet TCP,我们需要理解几个核心概念:

  • BBMD(BACnet Broadcast Management Device):如果网络中存在路由器,广播包无法跨网段传播,这时可能需要配置BBMD。对于大多数单子网的小型楼宇系统,可以暂时不考虑。
  • 设备实例号(Device Instance):BACnet网络中每个设备的唯一标识,是一个数字。你需要知道目标设备的这个号码。
  • 对象标识符(Object Identifier):设备内的具体数据点,如一个模拟量输入(AI)、模拟量输出(AO)或二进制输入(BI)。它由对象类型(如analogInput)和对象实例号(如0)组成。

最实用的起点是使用“bacnet whois”节点进行设备发现。拖一个inject节点(用于手动触发),一个bacnet whois节点和一个debug节点到工作区,并连接它们。

双击bacnet whois节点进行配置:

  • Name:可以命名为“发现BACnet设备”。
  • Interface:选择你的网络接口,通常是eth0wlan0
  • Low Limit / High Limit:这是要搜索的设备实例号范围。如果你不知道,可以设置为04194303(BACnet标准允许的最大值),但大范围扫描会慢一些。如果知道大概范围,可以缩小以加快速度。

点击inject节点的按钮,触发“whois”广播。如果网络中有BACnet设备,它们会回复“I am”报文。查看debug选项卡(右侧边栏),你应该能看到返回的消息。消息体通常包含设备的IP地址、设备实例号、设备名称等信息。把这个信息记录下来,这是后续通信的基础。

提示:如果debug没有输出,首先检查防火墙。树莓派OS默认的ufw防火墙可能是开启的,需要放行BACnet端口(通常是47808)。执行sudo ufw allow 47808。其次,确认目标BACnet设备是否允许外部读写,有些设备需要启用“BACnet/IP”服务或配置访问列表。

5. Node-RED流设计:数据读取、处理与写入的完整链路

发现设备后,我们就可以构建数据流了。一个完整的边缘网关数据流通常包含几个环节:周期性读取->数据清洗/转换->逻辑判断->存储或转发->(必要时)控制写入

5.1 构建周期性数据读取流

假设我们要从IP为192.168.1.201、设备实例号为12345的设备中,读取一个温度传感器的值,这个传感器是设备内的第一个模拟量输入(analogInput:0)。

  1. 配置读取节点:拖入一个inject节点,设置为按时间间隔触发,比如每30秒一次。将其连接到一个bacnet in节点。
  2. 配置bacnet in节点
    • Name: “读取室温传感器”。
    • Device Address: 填写192.168.1.201:47808(IP:端口)。
    • Device ID:12345
    • Object Type: 选择analogInput
    • Object Instance:0
    • Property ID: 选择presentValue(当前值)。
  3. 处理返回值:将bacnet in节点连接到一个function节点。bacnet in节点输出的msg.payload通常是一个包含value等属性的对象。我们可以在function节点里提取并做单位转换(例如,原始值可能是十分之一摄氏度,需要除以10)。
    // function 节点代码示例 var rawValue = msg.payload.value; // 假设原始值是整数,单位为0.1°C var temperature = rawValue / 10; msg.payload = { temperature: temperature, unit: "°C", timestamp: new Date().toISOString(), deviceId: 12345, objectId: "analogInput:0" }; return msg;
  4. 输出与调试:将function节点连接到debug节点,可以在调试窗口看到格式化后的数据。同时,可以连接一个dashboard库的chartgauge节点,在UI上实时显示。

5.2 实现简单的边缘逻辑与控制

Node-RED的精髓在于逻辑编排。假设我们想实现:当温度高于26°C时,自动打开一个连接到AO(模拟量输出)点的电动水阀(开度设为50%);当温度低于22°C时,关闭水阀(开度设为0)。

  1. 逻辑判断:在刚才的function节点后,连接一个switch节点。配置两个规则:
    • 规则1:msg.payload.temperature > 26, 输出到端口1。
    • 规则2:msg.payload.temperature < 22, 输出到端口2。
  2. 构造控制指令:为switch节点的两个输出端口各连接一个function节点。
    • 端口1对应的function节点,构造“打开”指令:
      msg.payload = { address: '192.168.1.201:47808', deviceId: 12345, type: 'analogOutput', instance: 0, // 假设控制的是第一个模拟量输出 property: 'presentValue', value: 50 // 开度50% }; return msg;
    • 端口2对应的function节点,构造“关闭”指令(将value设为0)。
  3. 执行写入:将两个function节点连接到一个bacnet out节点。配置bacnet out节点,其“Device Address”和“Device ID”可以留空,因为指令对象msg.payload里已经包含了这些信息。这样,当温度条件满足时,控制指令就会自动发出。
  4. 加入防抖与状态保持:为了避免在临界值附近频繁开关,可以在switch节点前加入一个delay节点,设置为“限速”模式,例如每5分钟只允许通过一条消息。同时,可以使用context存储阀门当前状态,只在状态需要改变时才发送bacnet out指令,减少不必要的网络通信和设备负载。

5.3 数据持久化与远程上报

本地数据可以存储到reTerminal DM的SD卡或外接USB存储中。可以使用node-red-contrib-sqlite节点将数据写入SQLite数据库,轻量且高效。同时,通过mqtt out节点将处理后的数据上报到云端MQTT Broker(如EMQX、AWS IoT Core),实现云端监控与分析。

一个典型的流结尾可能是这样的:function(处理数据) ->split(克隆消息) -> 一路到sqlite(本地存储),另一路到mqtt out(上报云端)。

6. 稳定性加固与现场调试经验谈

将原型流部署到7x24小时运行的工业现场,还需要做大量的加固工作。

第一,处理网络中断与设备离线。BACnet设备或网络可能会偶尔不稳定。bacnet in节点在请求超时或失败时,msg.payload可能会包含错误信息。我们需要用switch节点检查msg.error是否存在,并将其路由到一个告警处理分支(例如,发送邮件通知、在本地UI显示红色警报、或将异常状态记录到特定日志文件)。

第二,流的管理与版本控制。Node-RED的流是以JSON格式存储的。我强烈建议使用Git对~/.node-red目录进行版本控制。每次对流做出重大修改前,进行一次提交。这样当某次修改导致系统异常时,可以快速回滚到上一个稳定版本。同时,利用Node-RED的“项目”功能,将相关的流、子流和函数模块化组织,提高可维护性。

第三,资源监控与日志。reTerminal DM的资源(CPU、内存、存储)是有限的。安装node-red-contrib-system节点,可以创建一个监控面板,实时显示设备资源使用情况。另外,不要仅仅依赖Node-RED编辑器的调试输出。配置将重要的操作日志(如设备控制记录、异常错误)写入到/var/log/node-red.log(需要配置settings.js文件中的logging部分)或独立的日志文件中,便于后期排查问题。

第四,现场调试技巧。当流不工作时,一个高效的排查顺序是:

  1. 物理层:网线是否插好?LED灯是否闪烁?
  2. 网络层:从reTerminal DM能ping通BACnet设备吗?sudo tcpdump -i eth0 port 47808能否抓到BACnet报文?
  3. Node-RED层debug节点是否有任何输出?检查每个节点的状态(节点下方的小蓝点是否常亮?出现红点则表示错误)。查看Node-RED的启动日志:sudo journalctl -u node-red -f
  4. BACnet层:使用开源的BACnet扫描工具(如bacpypes库的示例程序)在reTerminal DM上直接测试,可以排除Node-RED节点配置的问题。

这次将reTerminal DM、Node-RED和BACnet TCP组合应用于楼宇自动化边缘网关的实践,证明了用开源软硬件构建可靠工业解决方案的可行性。它的优势在于极高的定制灵活性和可控的成本,但挑战也在于需要开发者对硬件、网络、协议和应用逻辑都有一定的掌控力。对于具体的项目,关键在于前期的充分测试,以及为现场可能出现的各种异常设计健壮的处理机制。这个组合不仅限于楼宇自动化,任何需要将传统工业协议(如Modbus、OPC UA)与现代IT系统(云平台、数据库)桥接的场景,都可以借鉴这个架构思路。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询