如何将Node-Red作为ModbusTCP从站

一、内容简介

本篇内容主要介绍如何分别用Modbus-Server和Modbus-Flex-Server节点来部署ModbusTCP服务器,并通过buffer-maker节点按一定格式写入数据,如float、double等,然后用Modbus Poll软件来测试写入的数据是否正确。

buffer-maker是node-red-contrib-buffer-parser插件的一个节点,在文章Node-Red怎么解析Modbus数据中有详细的安装说明。

Modbus-Server和Modbus-Flex-Server是node-red-contrib-modbus插件的节点,在文章Node-Red与ModbusTCP设备通信——读数据中也有安装说明。

ModbusPoll软件可以模拟Modbus主站,一般用于测试Modbus从站设备,如没有安装,请先安装该软件,可以按文章末尾提供的方式获取,也可以从官网下载。

Download

二、环境搭建

1.ModbusTCP服务器配置

从左侧面板中拖入一个inject(时间戳)节点、一个Modbus-Server节点、一个debug节点到工作区,并依次连接。

双击Modbus-Server节点,记录下监听端口10502,或者将其修改为其它想要监听的端口。

部署流程后,点击inject(时间戳)节点触发流,调试面板显示如下:

第一条是Modbus-Server第一个输出点输出的数据,表示保持寄存器(holding register)缓存,后面依次为线圈(coils register)缓存、输入寄存器(input register)缓存、离散输入寄存器(discrete inputs register)缓存,缓存中一个元素表示一个字节。

2.ModbusPoll配置

打开软件,点击Connection菜单,在弹出的对话框输入IP“ 127.0.0.1”,填写上面记录的端口“10502”点击“OK”,连接到ModbusTCP服务器。

软件打开会默认新建10个保持寄存器(holding register),需要再新建10个线圈(coils register),点击菜单File->New,默认创建10个保持寄存器,再点击菜单Setup->Read/Write Definition,修改Function为“01 Read Coils (0x)”,点击OK确认。

  • Modbus-Server

在Node-Red中通过buffer-maker节点向Modbus-Server写入数据,分别以Bool,Signed、Unsigned、Long、Float、Double等编码格式写入,并通过ModbusPoll软件来测试是否写入正确,以保持寄存器(holding register)和线圈(coils register)为例进行说明,其它寄存器写入方法类似。

1.保持寄存器(holding register)
  1. 按类型Signed写入

在Modbus-Server节点与inject(时间戳)节点之间插入一个buffer-maker节点和一个function节点,并将其连接。

双击inject(时间戳)节点,将msg.payload的类型修改为“{}JSON”,值为修改“[33,444,5555]”,点击完成按钮确定。

双击buffer-maker节点将Name修改为“Signed”,item1的Type修改为“int16(be)”,Length修改为“-1”,其余保持不变,点击完成按钮确定。

双击function节点,将名称改为“holding”,输入如下内容:

msg.payload = {
'value': msg.payload,
'register': 'holding',
'address': 0,
'disableMsgOutput': 0
};
return msg;

点击完成按钮确定,然后部署流程。

点击inject(时间戳)节触发流进行测试,调试面板显示如下:

缓存显示的是字节流,看起来比较混乱,再来看ModbusPoll中的保持寄存器(holding register):

默认按Signed类型显示,显示正确。

注意:Modbus-Server节点在Node-Red重新部署之后ModbusPoll需要手动断开并重新连接,否则数据不会刷新。

  1. 按类型Unsigned写入

在Node-Red工作区复制inject([33,444,5555])和buffer-maker(Signed)节点,并将其连接到function(holding)节点。

修改inject([33,444,5555])节点msg.payload的值为[888,32768],点击完成按钮确定。

将buffer-maker(Signed)节点的Name修改为“Unsigned”,item1的Type修改为“uint16(be)”,点击完成按钮确定。

部署流程。

触发流进行测试,ModbusPoll显示为负数:

这是由于类型不匹配而导致,鼠标拖动选中所有寄存器,点击菜单Display->Unsigned,将显示类型设置为Unsigned:

数据显示正确。

3.按类型Long写入

在Node-Red工作区复制inject([888,32768])和buffer-maker(Unsigned)节点,并将其连接到function(holding)节点。

将buffer-maker(Unsigned)节点的Name修改为“LongABCD”,item1的Type修改为“int32(be)”,点击完成按钮确定。

部署流程之后触发该流,

在ModbusPoll中将显示类型切换到LongABCD,结果显示正确。

在Node-Red工作区将节点inject([888,32768])和buffer-maker(LongABCD)复制三遍,分别依次连接到function(holding)节点,将复制的第一个buffer-maker(LongABCD)节点的名称修改为“LongCDAB”,item1的Type改为“int32(le)”,Bype swap选择“16”,点击完成按钮确认。

再将复制的第二个buffer-maker(LongABCD)节点的名称修改为“LongBADC”,Bype swap选择“16”,点击完成按钮确认。

最后将复制的第三个buffer-maker(LongABCD)节点的名称修改为“LongDCBA”,item1的Type改为“int32(le)”,确认修改之后部署流程。

分别触发复制的流,在ModbusPoll中切换到对应的类型,数据均解析正确。

4.按类型Float写入

在Node-Red工作区复制属于Long类型所有节点,将复制的buffer-maker(LongABCD)节点的名称修改为“FloatABCD”,item1的Type改为“float(be)”并确定

将buffer-maker(LongCDAB)节点的名称修改为“FloatCDAB”,item1的Type改为“float(le)”,确认修改。

再将buffer-maker(LongBADC)节点的名称修改为“FloatBADC”,item1的Type改为“float(be)”,点击完成按钮确定。

最后将buffer-maker(LongDCBA)节点的名称修改为“FloatDCBA”,item1的Type改为“float(le)”

确定之后部署流程。

依次触发Float流,在ModbusPoll设置对应的类型,显示结果全部正确。

  1. 按类型Double写入

在Node-Red工作区复制属于Float类型所有节点,将复制的buffer-maker(FloatABCD)节点的名称修改为“DoubleABCDEFGH”,item1的Type改为“double(be)”并确定

将buffer-maker(FloatCDAB)节点的名称修改为“DoubleGHEFCDAB”,item1的Type改为“double(le)”,确定修改。

再将buffer-maker(FloatBADC)节点的名称修改为“DoubleBADCFEHG”,item1的Type改为“double(be)”,确定。

最后将buffer-maker(FloatDCBA)节点的名称修改为“DoubleHGFEDCBA”,item1的Type改为“double(le)”

确定之后部署流程。

依次触发Double类型所属的流,在ModbusPoll设置对应的类型,显示结果正确无误。

2.线圈(coils register)

在Node-Red工作区复制第一行,也就是inject([33,444,5555])、buffer-maker(Signed)节点 和function(holding)节点,并将其连接到Modbus-Server节点,将inject([33,444,5555])节点的值改为“[true,false,true]”并确定

将buffer-maker(Signed)节点命名为“Bool”,item1的Type选择“bool”,其余不变

再将function(holding)节点命名为“coils”,在函数标签输入如下代码:

msg.payload = {
'value': msg.payload,
'register': 'coils',
'address': 0,
'disableMsgOutput': 0
};
return msg;

确定修改并部署流程。

触发流后,ModbusPoll显示正确。

  • Modbus-Flex-Server

点击Node-Red右上角的主菜单->流程->增加,增加一个流程

复制Modbus-Server所属流程的所有节点到新增流程,并将Modbus-Server节点替换为Modbus-Flex-Server节点。

双击Modbus-Flex-Server节点,记录下端口11502,

切换到标签页面Get Coil,将代码替换为:

function getFlexCoil(addr, unitID) {
    if (unitID === node.unitId &&
        addr >= node.minAddress &&
        addr <= node.splitAddress) {
        let bytes = Math.trunc(addr / 8);
        let bits = Math.trunc(addr % 8);
        let mask = 1 << bits;
        let value = node.coils.readUInt8(bytes) & mask;
        return value;
    }  
}

切换到标签页面Get Discrete,将代码替换为:

function getFlexDiscreteInput(addr, unitID) {
    addr += node.splitAddress
    if (unitID === node.unitId &&
        addr >= node.splitAddress &&
        addr <= node.splitAddress * 2) {
        let address = addr - node.splitAddress;
        let bytes = Math.trunc(address / 8);
        let bits = Math.trunc(address % 8);
        let mask = 1 << bits;
        let value = node.coils.readUInt8(node.bufferFactor * node.splitAddress + bytes) & mask;
        return value
    }  
}

切换到标签页面Get Input,将代码替换为:

function getFlexInputRegister(addr, unitID) {
    if (unitID === node.unitId &&
        addr >= node.minAddress &&
        addr <= node.splitAddress) {
        return node.registers.readUInt16BE(addr * 2)  
    }
}

切换到标签页面Get holding,将代码替换为:

function getFlexHoldingRegsiter(addr, unitID) {
    addr += node.splitAddress
    if (unitID === node.unitId &&
        addr >= node.splitAddress &&
        addr <= node.splitAddress * 2) {
        return node.registers.readUInt16BE(node.splitAddress * node.bufferFactor + (addr - node.splitAddress) * 2)
    }
}

切换到标签页面Set Coil,将代码替换为:

function setFlexCoil(addr, value, unitID) {
    if (unitID === node.unitId &&
        addr >= node.minAddress &&
        addr <= node.splitAddress) {
        let bytes = Math.trunc(addr / 8);
        let bits = Math.trunc(addr % 8);
        let val = node.coils.readUInt8(bytes);
        let mask = 1 << bits;
        if(value){  
            val |= mask;
        }
        else
        {
            mask = ~mask;
            val &= mask;
        }
        node.coils.writeUInt8(val, bytes)  
    }
}

切换到标签页面Set Register,将代码替换为:

function setFlexRegister(addr, value, unitID) {
    addr += node.splitAddress
    if (unitID === node.unitId &&
        addr >= node.splitAddress &&
        addr <= node.splitAddress * 2) {
        node.registers.writeUInt16BE(value, node.splitAddress * node.bufferFactor + (addr - node.splitAddress) * 2)  
    }
}

点击完成按钮确定,部署流程。

将ModbusPoll重新连接到Modbus-Flex-Server的11502端口,在Node-Red逐一触发流进行测试,设置ModbusPoll以相应的类型显示,数据均正确。

Modbus-Flex-Server节点在Node-Red重新部署之后ModbusPoll不需要重连,这一点要比Modbus-Server节点用起来方便。

如果您想获取文章相关的代码、工具等资料,关注公众号NodeRed物联网,回复“nr”即可。

  • 6
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值