做工业边缘计算这两年,Node-RED 里三菱 PLC 的采集方案基本只有一个选择——node-red-contrib-mcprotocol。用过的都知道,它不是不能用,是用起来太拧巴。

我忍了一年,列了 7 个忍无可忍的痛点,最终决定自己造一个。现在它已经跑在 3 个现场项目里,npm 上也发了稳定版。这篇文章不是广告,是一个工程师的踩坑记录。

一、痛点 1:散点、跨类型、混寄存器,只能一个一个配

mcprotocol 一个节点可以读一段连续地址(比如 D100 开始的 100 个 INT16),这个没问题。但真实工控场景根本不是"100 个连续 INT16"这么理想。

更多时候是这样的:

- D100(温度,INT16,斜率 0.1)
- D200(压力,FLOAT32,占两个寄存器)
- D500(状态,UINT16)
- X0(开关 BOOL)
- M5(运行状态 BOOL)

散点、跨地址、混寄存器类型。mcprotocol 遇到这些只能拆成好几个节点,后面再挂一堆 function 拼 Buffer、算斜率。

我的做法:一个节点里放表格。需要采集的点位一行一行填,同类型自动聚类合并——D 寄存器归一组发一条批量读指令,X 归一组发另一条。十几个散点不是十几个 TCP 连接,是两三个。

表格里还支持批量生成:起始 100,数量 100,点一下自动填充。中间个别特殊的(比如 D150 是 FLOAT32)单独改就行。

二、痛点 2:FLOAT32 要自己拼 Buffer

PLC 里 D200 存的是 FLOAT32(占 D200 和 D201 两个寄存器)。mcprotocol 读回来是两个 INT16,你得在后面挂一个 function 节点:

var buf = Buffer.alloc(4);
buf.writeInt16LE(msg.payload[0], 0);
buf.writeInt16LE(msg.payload[1], 2);
msg.payload = buf.readFloatLE(0);
return msg;

这还只是 FLOAT32。UINT16 读到负数要自己 +65536,INT32 要拼高低字,斜率换算还要再挂一个 function。

我的做法:表格里下拉选数据类型——INT16 / UINT16 / INT32 / UINT32 / FLOAT32 / BOOL。节点自动解码,你拿到手的 engValue 就是最终值。

三、痛点 3:斜率换算再挂一个 function

温度存在 PLC 里是 2530,实际是 253.0℃。mcprotocol 给你 2530,你自己除 10。

我的做法:表格里填斜率 0.1,偏移 0。输出直接是 engValue: 253.0。

四、痛点 4:出错只会说 "timeout"

PLC 返回 0xC052(地址越界)、0xC059(批量超限)、0xC051(软元件不支持),mcprotocol 统统给你转成一句 "timeout"。

你盯着日志查了半天,最后发现是表里填错了一个地址。更惨的是,你根本不知道 PLC 到底拒绝的是哪个请求。

这也是早期项目条件下无奈的选择——那时候能连上 PLC 就不错了。但今天工业现场对稳定性要求更高,不能再忍了。

我的做法:内置 19 个 MC 错误码映射。出错时直接告诉你:

[PLC 0xC052] Address out of range (D100)
[PLC 0xC059] Points out of range
[PLC 0xC051] Device not supported

五、痛点 5:serialNo 是摆设

4E 帧的 serialNo 字段设计来就是做请求-响应匹配的。mcprotocol 直接写死一个值不变,PLC 响应回来根本不知道是哪个请求的。

我的做法:每发一帧 serialNo 自增 1,响应回来校验。不匹配直接丢弃重试。

六、痛点 6:没有模拟模式

出差路上、在家写流程、CI 自动化测试,没有 PLC 怎么办?mcprotocol 直接 "Not connected"。

我的做法:Node-RED settings.js 里加一行 mcSimulationMode: true,不连 PLC 也能跑,随机生成仿真数据。

七、痛点 7:依赖冲突

mcprotocol 本身虽然零依赖,但它和某些版本的 Node-RED 共存时会有连接池 bug。你查了半天,最后发现是插件内部 connection pool 状态崩了。

我的做法:整个节点只用了 Node.js 自带的 net 和 Buffer。没有第三方依赖,没有连接池黑盒,TCP 连接我自己管。

硬碰硬对比:

mcprotocol:
- 散点跨类型采集:拖多个节点 + 挂 function
- FLOAT32:自己写 function 拼接
- 原始值→工程值:自己写 function 换算
- 出错提示:"timeout"
- serialNo:固定不变
- 模拟测试:不支持
- 外部依赖:有

我的节点:
- 散点跨类型采集:拖 1 个节点,填表
- FLOAT32:表格选 FLOAT32,自动解码
- 原始值→工程值:填斜率 0.1,自动变换
- 出错提示:[PLC 0xC052] Address out of range
- serialNo:每帧自增 + 响应校验
- 模拟测试:一行配置
- 外部依赖:0

不只是独立节点

这个 MC 节点不是孤立存在的。它和 node-red-contrib-edgelink-pg 是一对:

mitsubishi-read → edgelink-pg-store → PostgreSQL/TimescaleDB
    ↑ PLC 数据采集         ↑ 批量写入(带缓冲、重试、断线保护)

MC 读节点输出的数据格式,PG 写节点直接认识,零配置入库。deviceId 自动做分表、regType 自动写 register_type 列、engValue 直接写 eng_value 列。

PG 节点也不是普通的 SQL 执行器——它内置了批量缓冲(100 条一次 INSERT)、失败重试队列、PG 假死超时 kill、内存双上限防 OOM。这俩加起来就是一条完整的 PLC→数据库链路,中间不需要写一行代码。

一条完整的数据流长什么样:

左侧一个 mitsubishi-read 节点,表格里填了 5 行点位(D100 温度、D200 压力 FLOAT32、X0 开关……)

右边连一个 edgelink-pg-store,配置了数据库连接(127.0.0.1:5432)

Debug 面板直接输出:
{
  "温度": { "engValue": 253.0, "regType": "D", "quality": 0 },
  "压力": { "engValue": 1.25, "regType": "D", "quality": 0 },
  "开关": { "engValue": 1, "regType": "X", "quality": 0 }
}

拖出来、填表、部署,数据直接进数据库。不需要写 function,不需要拼 Buffer。

安装

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

或者直接在 Node-RED Palette Manager 里搜 mitsubishi 一键安装。

GitHub:https://github.com/Qq225083/node-red-contrib-mitsubishi

局限(诚实说)

当前版本只支持 3E 和 4E 帧:

- Q 系列、L 系列、iQ-R、iQ-F(FX5U):支持
- FX3U + 以太网模块(FX3U-ENET):支持
- 纯串口的 FX3U(需要 1E 帧):不支持
- A 系列(需要 1E 帧):不支持

不带以太网模块的老 FX3U 暂时用不了。1E 帧已经在路上了。

最后

造这个节点的初衷很简单:我是一个工业现场工程师,我不想把时间花在写 function 节点、拼 Buffer、猜 timeout 上。我想拖一个节点、填表、部署,然后去喝杯咖啡。

现在可以了。

如果你也在用 Node-RED 做工业采集,欢迎试用,有问题直接提 Issue。

更新记录:
- 2026.06.30:v1.0.3 发布,修复 4E 帧序列号校验、TCP 响应完整性检查
- 2026.06.27:v1.0.0 发布,初始版本

Logo

欢迎加入 MCP 技术社区!与志同道合者携手前行,一同解锁 MCP 技术的无限可能!

更多推荐