??Modbus协议中,32位整数的存储格式会因具体的数据类型而有所不同。Modbus协议对32位无符号整数的存储方式是将该整数分解为两个16位的寄存器来处理,第一个元素是高16位,第二个元素是低16位,具体的字节顺序可以有四种选择:无符号整数大端序、无符号整数小端序、无符号整数大端字节交换和无符号整数小端字节交换。例如,一个无符号的32位整数0x12345678在Modbus中的存储形式可能会是:0x12 0x34 0x56 0x78,具体取决于所选择的字节顺序。其中:
大端序Big-Endian(BE)
:将最高位字节存储在最低的地址位置;小端序(LE)
:则是将最高位字节存储在最高的地址位置;无符号整数大端字节交换和小端字节交换(BIG_ENDIAN和LITTLE_ENDIAN)
:指将每个字节的顺序颠倒,但不考虑符号位。/** 使用无符号整数大端序(BE)的转换方式,将一个32位整数,转换为一个包含两个元素的数组,
* 其中第一个元素是高16位,第二个元素是低16位 */
function int32ToArrayBE(num) {
const high = (num >> 16) & 0xFFFF;
const low = num & 0xFFFF;
return [high, low];
}
实现步骤:
//将一个包含两个元素的数组转换为一个32位整数,其中第一个元素是高16位,第二个元素是低16位。
//转换方式是先将第一个元素左移16位,然后与第二个元素进行按位或操作。
function arrayToInt32BE(data) {
return (data[0] << 16) | data[1];
}
??modbus支持对字符串类型的数据进行读写操作,进行字符串类型数据的操作时,是基于ASCII码格式进行的,一个字符占一个字节,支持字母、中文已经符号。
实现步骤:
/**将一个字符串,获取每个字符的Unicode编码,转换为字节数组 */
function stringToByteArray(str) {
let byteArray = [];
for (let i = 0; i < str.length; i++) {
byteArray.push(str.charCodeAt(i));
}
return byteArray;
}
实现步骤:
//将Unicode编码字节数组转换为对应的字符
msg.payload = String.fromCharCode(...msg.payload.slice(0, 8));
//有需要的话可以去掉字符串中的null和结尾的空字符
msg.payload = msg.payload.trim().replace(/\u0000/g, '');
return msg;
示例如下:
如我们在modsim32中输入
-55
,实际显示在计算机上的是65481
有符号16word转换
??有符号16word转换成补码是占一位地址,通过modbus读取过来是十进制整数,我们需要将其通过node-red的modbus协议读取对应地址下的数据,该数据为补码,需要将其转换为实际的有符号16位整数
。有两种转换方式,如下示例所示:
/** 有符号整数补码转换转成16位有符号整数:
* 如-80的补码为65456,读取出来是65456,需要通过该函数转换成-80
*/
function Signed16ToInt16Be(num){
//32767是16位带符号证书能表示的最大正数,当数值从32767再增加1时,就会上溢变成负数。
//即mod通信中所有的负数都是用补码形式存储(如‘-80’实际读\写出来是‘65456’)
//需要检测是否发生了从正数到负数的转换,即是否发生了上溢或下溢,如果有,进行转换
return num >= 32768 ? num - 65536 : num;
}
?? 直接将负数写入对应地址时,不生效。需要先将其转换成补码,然后再将补码写入地址。js将负数转换成补码的函数如下:
/**
* 负数转成对应的补码
* 在计算机中,负数通常使用补码表示,如'-80'用补码表示为'65456'.
*/
function Int16BeToSigned16(num) {
//如果数字小于0,将其转换成十进制补码:65536是2的16次幂,也就是2^16
if(num < 0){
num += 65536
}
return num;
}
实战示例如下:
有符号32word转换
[
{
"id": "1e64108c77baa639",
"type": "inject",
"z": "f77425d62009612d",
"name": "",
"props": [
{
"p": "payload"
},
{
"p": "topic",
"vt": "str"
}
],
"repeat": "",
"crontab": "",
"once": false,
"onceDelay": 0.1,
"topic": "",
"payload": "",
"payloadType": "date",
"x": 130,
"y": 580,
"wires": [
[
"7dc4362a647fb9f4"
]
]
},
{
"id": "7dc4362a647fb9f4",
"type": "modbus-getter",
"z": "f77425d62009612d",
"name": "",
"showStatusActivities": false,
"showErrors": false,
"showWarnings": true,
"logIOActivities": false,
"unitid": "2",
"dataType": "HoldingRegister",
"adr": "99",
"quantity": "1",
"server": "e7cbfcc879655084",
"useIOFile": false,
"ioFile": "",
"useIOForPayload": false,
"emptyMsgOnFail": false,
"keepMsgProperties": false,
"delayOnStart": false,
"startDelayTime": "",
"x": 300,
"y": 580,
"wires": [
[
"b156bb1bacfff01f",
"1b8c974bcad376f7",
"4ec96886cf6ddcfc"
],
[]
]
},
{
"id": "b156bb1bacfff01f",
"type": "buffer-parser",
"z": "f77425d62009612d",
"name": "",
"data": "payload",
"dataType": "msg",
"specification": "spec",
"specificationType": "ui",
"items": [
{
"type": "int16be",
"name": "item1",
"offset": 0,
"length": 1,
"offsetbit": 0,
"scale": "1",
"mask": ""
}
],
"swap1": "",
"swap2": "",
"swap3": "",
"swap1Type": "swap",
"swap2Type": "swap",
"swap3Type": "swap",
"msgProperty": "payload",
"msgPropertyType": "str",
"resultType": "keyvalue",
"resultTypeType": "return",
"multipleResult": false,
"fanOutMultipleResult": false,
"setTopic": true,
"outputs": 1,
"x": 470,
"y": 580,
"wires": [
[
"38805fdde3a44395"
]
]
},
{
"id": "38805fdde3a44395",
"type": "debug",
"z": "f77425d62009612d",
"name": "parse转换",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "payload",
"targetType": "msg",
"statusVal": "",
"statusType": "auto",
"x": 620,
"y": 580,
"wires": []
},
{
"id": "1b8c974bcad376f7",
"type": "function",
"z": "f77425d62009612d",
"name": "函数转换",
"func": "msg.payload = Signed16ToInt16Be(msg.payload);\nreturn msg;\n/** 有符号整数补码转换转成16位有符号整数:\n * 如-80的补码为65456,读取出来是65456,需要通过该函数转换成-80\n */\nfunction Signed16ToInt16Be(num){\n //32767是16位带符号证书能表示的最大正数,当数值从32767再增加1时,就会上溢变成负数。\n //即mod通信中所有的负数都是用补码形式存储(如‘-80’实际读\\写出来是‘65456’)\n //需要检测是否发生了从正数到负数的转换,即是否发生了上溢或下溢,如果有,进行转换\n return num >= 32768 ? num - 65536 : num;\n}",
"outputs": 1,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 460,
"y": 620,
"wires": [
[
"d76859e443cd98fc"
]
]
},
{
"id": "d76859e443cd98fc",
"type": "debug",
"z": "f77425d62009612d",
"name": "js函数转换",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "payload",
"targetType": "msg",
"statusVal": "",
"statusType": "auto",
"x": 630,
"y": 620,
"wires": []
},
{
"id": "769c583e15f9b628",
"type": "inject",
"z": "f77425d62009612d",
"name": "",
"props": [
{
"p": "payload"
},
{
"p": "topic",
"vt": "str"
}
],
"repeat": "",
"crontab": "",
"once": false,
"onceDelay": 0.1,
"topic": "",
"payload": "",
"payloadType": "date",
"x": 110,
"y": 720,
"wires": [
[
"c69cb082f4d5391f"
]
]
},
{
"id": "c758cfc4fd1cdcaf",
"type": "function",
"z": "f77425d62009612d",
"name": "(写)有符号数字",
"func": "msg.payload = Int16BeToSigned16(msg.payload);\nreturn msg;\n/** \n * 负数转成对应的补码\n * 在计算机中,负数通常使用补码表示,如'-80'用补码表示为'65456'.\n */\nfunction Int16BeToSigned16(num) {\n //如果数字小于0,将其转换成十进制补码:65536是2的16次幂,也就是2^16\n if(num < 0){\n num += 65536\n }\n return num;\n}",
"outputs": 1,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 430,
"y": 720,
"wires": [
[
"4d2f5446435e62d1",
"d5d6bcc7a95cd04b"
]
]
},
{
"id": "4d2f5446435e62d1",
"type": "modbus-write",
"z": "f77425d62009612d",
"name": "",
"showStatusActivities": false,
"showErrors": false,
"showWarnings": true,
"unitid": "2",
"dataType": "MHoldingRegisters",
"adr": "99",
"quantity": "1",
"server": "e7cbfcc879655084",
"emptyMsgOnFail": false,
"keepMsgProperties": false,
"delayOnStart": false,
"startDelayTime": "",
"x": 640,
"y": 720,
"wires": [
[
"b71462a8db1024f6"
],
[]
]
},
{
"id": "b71462a8db1024f6",
"type": "debug",
"z": "f77425d62009612d",
"name": "debug 55",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "payload",
"targetType": "msg",
"statusVal": "",
"statusType": "auto",
"x": 820,
"y": 720,
"wires": []
},
{
"id": "d5d6bcc7a95cd04b",
"type": "debug",
"z": "f77425d62009612d",
"name": "补码",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "payload",
"targetType": "msg",
"statusVal": "",
"statusType": "auto",
"x": 610,
"y": 680,
"wires": []
},
{
"id": "4ec96886cf6ddcfc",
"type": "debug",
"z": "f77425d62009612d",
"name": "直接读取",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "payload",
"targetType": "msg",
"statusVal": "",
"statusType": "auto",
"x": 460,
"y": 540,
"wires": []
},
{
"id": "c69cb082f4d5391f",
"type": "function",
"z": "f77425d62009612d",
"name": "赋值",
"func": "msg.payload = -90;\nreturn msg;",
"outputs": 1,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 250,
"y": 720,
"wires": [
[
"c758cfc4fd1cdcaf",
"19cc1b023537db9f"
]
]
},
{
"id": "19cc1b023537db9f",
"type": "debug",
"z": "f77425d62009612d",
"name": "负数",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "payload",
"targetType": "msg",
"statusVal": "",
"statusType": "auto",
"x": 370,
"y": 680,
"wires": []
},
{
"id": "db6d11e622be6be8",
"type": "comment",
"z": "f77425d62009612d",
"name": "3.1.2 16位负数写操作",
"info": "",
"x": 140,
"y": 680,
"wires": []
},
{
"id": "cd5db39529d4197c",
"type": "comment",
"z": "f77425d62009612d",
"name": "3.1.1 16位负数读操作",
"info": "",
"x": 140,
"y": 540,
"wires": []
},
{
"id": "17422aec125ee1e5",
"type": "comment",
"z": "f77425d62009612d",
"name": " 2.1 字符串写操作",
"info": "",
"x": 120,
"y": 320,
"wires": []
},
{
"id": "5e6d79e257936ac0",
"type": "inject",
"z": "f77425d62009612d",
"name": "",
"props": [
{
"p": "payload"
},
{
"p": "topic",
"vt": "str"
}
],
"repeat": "",
"crontab": "",
"once": false,
"onceDelay": 0.1,
"topic": "",
"payload": "",
"payloadType": "date",
"x": 110,
"y": 360,
"wires": [
[
"60a74c2ac2108159"
]
]
},
{
"id": "60a74c2ac2108159",
"type": "function",
"z": "f77425d62009612d",
"name": "赋值",
"func": "var str ='你好,Marry';\nmsg.payload = stringToByteArray(str);\nreturn msg;\n/**将一个字符串,获取每个字符的Unicode编码,转换为字节数组 */\nfunction stringToByteArray(str) {\n let byteArray = [];\n for (let i = 0; i < str.length; i++) {\n byteArray.push(str.charCodeAt(i));\n }\n return byteArray;\n}",
"outputs": 1,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 230,
"y": 360,
"wires": [
[
"48ccae615ccc0b4c",
"ab1f56889919d10b"
]
]
},
{
"id": "48ccae615ccc0b4c",
"type": "modbus-write",
"z": "f77425d62009612d",
"name": "",
"showStatusActivities": false,
"showErrors": false,
"showWarnings": true,
"unitid": "2",
"dataType": "MHoldingRegisters",
"adr": "99",
"quantity": "8",
"server": "e7cbfcc879655084",
"emptyMsgOnFail": false,
"keepMsgProperties": false,
"delayOnStart": false,
"startDelayTime": "",
"x": 380,
"y": 360,
"wires": [
[],
[]
]
},
{
"id": "ab1f56889919d10b",
"type": "debug",
"z": "f77425d62009612d",
"name": "转换后",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "payload",
"targetType": "msg",
"statusVal": "",
"statusType": "auto",
"x": 350,
"y": 320,
"wires": []
},
{
"id": "3e34769f2f93e394",
"type": "inject",
"z": "f77425d62009612d",
"name": "",
"props": [
{
"p": "payload"
},
{
"p": "topic",
"vt": "str"
}
],
"repeat": "",
"crontab": "",
"once": false,
"onceDelay": 0.1,
"topic": "",
"payload": "",
"payloadType": "date",
"x": 110,
"y": 460,
"wires": [
[
"2060ceec23e827b0"
]
]
},
{
"id": "2060ceec23e827b0",
"type": "modbus-getter",
"z": "f77425d62009612d",
"name": "",
"showStatusActivities": false,
"showErrors": false,
"showWarnings": true,
"logIOActivities": false,
"unitid": "2",
"dataType": "HoldingRegister",
"adr": "99",
"quantity": "8",
"server": "e7cbfcc879655084",
"useIOFile": false,
"ioFile": "",
"useIOForPayload": false,
"emptyMsgOnFail": false,
"keepMsgProperties": false,
"delayOnStart": false,
"startDelayTime": "",
"x": 280,
"y": 460,
"wires": [
[
"674886974e9f1f90",
"b88737723cab66a8"
],
[]
]
},
{
"id": "674886974e9f1f90",
"type": "function",
"z": "f77425d62009612d",
"name": "函数转换",
"func": "//将Unicode编码字节数组转换为对应的字符\nmsg.payload = String.fromCharCode(...msg.payload.slice(0, 8));\n//有需要的话可以去掉字符串中的null和结尾的空字符\nmsg.payload = msg.payload.trim().replace(/\\u0000/g, '');\nreturn msg;",
"outputs": 1,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 460,
"y": 460,
"wires": [
[
"c6d5115b578ded31"
]
]
},
{
"id": "b88737723cab66a8",
"type": "debug",
"z": "f77425d62009612d",
"name": "直接读取",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "payload",
"targetType": "msg",
"statusVal": "",
"statusType": "auto",
"x": 440,
"y": 420,
"wires": []
},
{
"id": "c6d5115b578ded31",
"type": "debug",
"z": "f77425d62009612d",
"name": "js函数转换",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "payload",
"targetType": "msg",
"statusVal": "",
"statusType": "auto",
"x": 610,
"y": 460,
"wires": []
},
{
"id": "629c93d9a5836ab0",
"type": "comment",
"z": "f77425d62009612d",
"name": " 2.2 字符串读操作",
"info": "",
"x": 120,
"y": 420,
"wires": []
},
{
"id": "e2b2a3da4860c56b",
"type": "function",
"z": "f77425d62009612d",
"name": "数组转换",
"func": "msg.payload = int32ToArrayBE(msg.payload);\nreturn msg;\n/** 使用无符号整数大端序(BE)的转换方式,\n * 将一个32位整数,转换为一个包含两个元素的数组,\n * 其中第一个元素是高16位,第二个元素是低16位 */\nfunction int32ToArrayBE(num) {\n const high = (num >> 16) & 0xFFFF;\n const low = num & 0xFFFF;\n return [high, low];\n}",
"outputs": 1,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 360,
"y": 80,
"wires": [
[
"11e1bfef49c08c4d",
"54919ad9449f2987"
]
]
},
{
"id": "2057a2e62539d669",
"type": "inject",
"z": "f77425d62009612d",
"name": "",
"props": [
{
"p": "payload"
},
{
"p": "topic",
"vt": "str"
}
],
"repeat": "",
"crontab": "",
"once": false,
"onceDelay": 0.1,
"topic": "",
"payload": "",
"payloadType": "date",
"x": 110,
"y": 80,
"wires": [
[
"c858d03ce2f7e8a2"
]
]
},
{
"id": "11e1bfef49c08c4d",
"type": "modbus-write",
"z": "f77425d62009612d",
"name": "",
"showStatusActivities": false,
"showErrors": false,
"showWarnings": true,
"unitid": "2",
"dataType": "MHoldingRegisters",
"adr": "99",
"quantity": "2",
"server": "e7cbfcc879655084",
"emptyMsgOnFail": false,
"keepMsgProperties": false,
"delayOnStart": false,
"startDelayTime": "",
"x": 540,
"y": 80,
"wires": [
[],
[]
]
},
{
"id": "c858d03ce2f7e8a2",
"type": "function",
"z": "f77425d62009612d",
"name": "赋值",
"func": "msg.payload = 3276780;\nreturn msg;",
"outputs": 1,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 230,
"y": 80,
"wires": [
[
"e2b2a3da4860c56b",
"1438b02a5f7824dc"
]
]
},
{
"id": "1438b02a5f7824dc",
"type": "debug",
"z": "f77425d62009612d",
"name": "原始",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "payload",
"targetType": "msg",
"statusVal": "",
"statusType": "auto",
"x": 350,
"y": 40,
"wires": []
},
{
"id": "54919ad9449f2987",
"type": "debug",
"z": "f77425d62009612d",
"name": "数组",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "payload",
"targetType": "msg",
"statusVal": "",
"statusType": "auto",
"x": 510,
"y": 40,
"wires": []
},
{
"id": "1da67750bf2f0eb1",
"type": "comment",
"z": "f77425d62009612d",
"name": "1.1 32word写操作",
"info": "",
"x": 130,
"y": 20,
"wires": []
},
{
"id": "dd7a84e126fda707",
"type": "inject",
"z": "f77425d62009612d",
"name": "",
"props": [
{
"p": "payload"
},
{
"p": "topic",
"vt": "str"
}
],
"repeat": "",
"crontab": "",
"once": false,
"onceDelay": 0.1,
"topic": "",
"payload": "",
"payloadType": "date",
"x": 110,
"y": 200,
"wires": [
[
"7d73e7b22a1358f0"
]
]
},
{
"id": "7d73e7b22a1358f0",
"type": "modbus-getter",
"z": "f77425d62009612d",
"name": "",
"showStatusActivities": false,
"showErrors": false,
"showWarnings": true,
"logIOActivities": false,
"unitid": "2",
"dataType": "HoldingRegister",
"adr": "99",
"quantity": "2",
"server": "e7cbfcc879655084",
"useIOFile": false,
"ioFile": "",
"useIOForPayload": false,
"emptyMsgOnFail": false,
"keepMsgProperties": false,
"delayOnStart": false,
"startDelayTime": "",
"x": 260,
"y": 200,
"wires": [
[
"4bf2a9d5169fa147",
"9908852fe20a2380",
"58decbf3f6230bed"
],
[]
]
},
{
"id": "4bf2a9d5169fa147",
"type": "function",
"z": "f77425d62009612d",
"name": "函数转换",
"func": "/** 使用无符号整数大端序(Big-Endian)的转换方式,将一个一个包含两个元素的数组,转换为一个32位整数 */\nfunction arrayToInt32BE(data) {\n return (data[0] << 16) | data[1];\n}\nmsg.payload = arrayToInt32BE(msg.payload);\nreturn msg;",
"outputs": 1,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 440,
"y": 200,
"wires": [
[
"275aa03cf2080e7d"
]
]
},
{
"id": "275aa03cf2080e7d",
"type": "debug",
"z": "f77425d62009612d",
"name": "js函数转换",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "payload",
"targetType": "msg",
"statusVal": "",
"statusType": "auto",
"x": 590,
"y": 200,
"wires": []
},
{
"id": "9908852fe20a2380",
"type": "debug",
"z": "f77425d62009612d",
"name": "直接读取",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "payload",
"targetType": "msg",
"statusVal": "",
"statusType": "auto",
"x": 440,
"y": 160,
"wires": []
},
{
"id": "58decbf3f6230bed",
"type": "buffer-parser",
"z": "f77425d62009612d",
"name": "",
"data": "payload",
"dataType": "msg",
"specification": "spec",
"specificationType": "ui",
"items": [
{
"type": "int32be",
"name": "item1",
"offset": 0,
"length": 1,
"offsetbit": 0,
"scale": "1",
"mask": ""
}
],
"swap1": "",
"swap2": "",
"swap3": "",
"swap1Type": "swap",
"swap2Type": "swap",
"swap3Type": "swap",
"msgProperty": "payload",
"msgPropertyType": "str",
"resultType": "keyvalue",
"resultTypeType": "return",
"multipleResult": false,
"fanOutMultipleResult": false,
"setTopic": true,
"outputs": 1,
"x": 450,
"y": 240,
"wires": [
[
"342f36c555ec1252"
]
]
},
{
"id": "342f36c555ec1252",
"type": "debug",
"z": "f77425d62009612d",
"name": "parse转换",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "payload",
"targetType": "msg",
"statusVal": "",
"statusType": "auto",
"x": 600,
"y": 240,
"wires": []
},
{
"id": "e1215e288585f302",
"type": "comment",
"z": "f77425d62009612d",
"name": "1.2 32word读操作",
"info": "",
"x": 130,
"y": 140,
"wires": []
},
{
"id": "e7cbfcc879655084",
"type": "modbus-client",
"name": "tcp@192.168.2.105:1024",
"clienttype": "tcp",
"bufferCommands": true,
"stateLogEnabled": false,
"queueLogEnabled": false,
"failureLogEnabled": true,
"tcpHost": "192.168.2.105",
"tcpPort": "1024",
"tcpType": "DEFAULT",
"serialPort": "/dev/ttyUSB",
"serialType": "RTU-BUFFERD",
"serialBaudrate": "9600",
"serialDatabits": "8",
"serialStopbits": "1",
"serialParity": "none",
"serialConnectionDelay": "100",
"serialAsciiResponseStartDelimiter": "0x3A",
"unit_id": "1",
"commandDelay": "1",
"clientTimeout": "1000",
"reconnectOnTimeout": false,
"reconnectTimeout": "2000",
"parallelUnitIdsAllowed": true,
"showWarnings": true,
"showLogs": true
}
]