node-red-contrib-modbus-rtu 1.0.24
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.txt +6 -0
- package/README.md +34 -0
- package/examples/crc only.js +91 -0
- package/examples/dynamic.js +91 -0
- package/examples/gui.js +91 -0
- package/modbus_rtu.html +172 -0
- package/modbus_rtu.js +109 -0
- package/package.json +29 -0
package/LICENSE.txt
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
Copyright 2026 Do Duy Cop
|
|
2
|
+
|
|
3
|
+
Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies.
|
|
4
|
+
|
|
5
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
6
|
+
|
package/README.md
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# node-red-contrib-modbus-rtu
|
|
2
|
+
|
|
3
|
+
Node-RED function build content modbus-rtu
|
|
4
|
+
|
|
5
|
+
Code by Do Duy Cop
|
|
6
|
+
|
|
7
|
+
## Configuration
|
|
8
|
+
|
|
9
|
+
nothing
|
|
10
|
+
|
|
11
|
+
## Examples
|
|
12
|
+
|
|
13
|
+
FUNCTION = CRC ONLY:
|
|
14
|
+
|
|
15
|
+
msg.payload = Buffer.from('010300040001', "hex")
|
|
16
|
+
|
|
17
|
+
FUNCTION = DYNANIC
|
|
18
|
+
|
|
19
|
+
pre node: msg.payload = {id: 2, fn:3, addr: 4, count: 5}
|
|
20
|
+
|
|
21
|
+
FUNCTION = OTHER
|
|
22
|
+
|
|
23
|
+
use GUI:
|
|
24
|
+
|
|
25
|
+
msg.payload = {id:2, fn: 3, addr: 4, count: 5}
|
|
26
|
+
|
|
27
|
+
## Changelog
|
|
28
|
+
|
|
29
|
+
- 1.0.0: initial release
|
|
30
|
+
- 1.0.1 .. 1.0.11: fix bug
|
|
31
|
+
|
|
32
|
+
## TODO
|
|
33
|
+
|
|
34
|
+
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
[
|
|
2
|
+
{
|
|
3
|
+
"id": "57e69d4db547e69c",
|
|
4
|
+
"type": "inject",
|
|
5
|
+
"z": "2b535346fadd97c6",
|
|
6
|
+
"name": "",
|
|
7
|
+
"props": [
|
|
8
|
+
{
|
|
9
|
+
"p": "payload"
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
"p": "topic",
|
|
13
|
+
"vt": "str"
|
|
14
|
+
}
|
|
15
|
+
],
|
|
16
|
+
"repeat": "",
|
|
17
|
+
"crontab": "",
|
|
18
|
+
"once": false,
|
|
19
|
+
"onceDelay": 0.1,
|
|
20
|
+
"topic": "",
|
|
21
|
+
"payload": "",
|
|
22
|
+
"payloadType": "date",
|
|
23
|
+
"x": 320,
|
|
24
|
+
"y": 260,
|
|
25
|
+
"wires": [
|
|
26
|
+
[
|
|
27
|
+
"fd36733c93672c41"
|
|
28
|
+
]
|
|
29
|
+
]
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
"id": "fd36733c93672c41",
|
|
33
|
+
"type": "function",
|
|
34
|
+
"z": "2b535346fadd97c6",
|
|
35
|
+
"name": "function 1",
|
|
36
|
+
"func": "msg.payload = Buffer.from('010300040001', \"hex\")\n//msg.payload={id:5, fn:6, addr:7, count:8}\nreturn msg;",
|
|
37
|
+
"outputs": 1,
|
|
38
|
+
"timeout": 0,
|
|
39
|
+
"noerr": 0,
|
|
40
|
+
"initialize": "",
|
|
41
|
+
"finalize": "",
|
|
42
|
+
"libs": [],
|
|
43
|
+
"x": 480,
|
|
44
|
+
"y": 260,
|
|
45
|
+
"wires": [
|
|
46
|
+
[
|
|
47
|
+
"9f529aa85f9dc7e9"
|
|
48
|
+
]
|
|
49
|
+
]
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
"id": "51c08ea65917c4fd",
|
|
53
|
+
"type": "debug",
|
|
54
|
+
"z": "2b535346fadd97c6",
|
|
55
|
+
"name": "debug 1",
|
|
56
|
+
"active": true,
|
|
57
|
+
"tosidebar": true,
|
|
58
|
+
"console": false,
|
|
59
|
+
"tostatus": false,
|
|
60
|
+
"complete": "false",
|
|
61
|
+
"statusVal": "",
|
|
62
|
+
"statusType": "auto",
|
|
63
|
+
"x": 860,
|
|
64
|
+
"y": 260,
|
|
65
|
+
"wires": []
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
"id": "9f529aa85f9dc7e9",
|
|
69
|
+
"type": "modbus_rtu",
|
|
70
|
+
"z": "2b535346fadd97c6",
|
|
71
|
+
"slave_id": "",
|
|
72
|
+
"fn": "0",
|
|
73
|
+
"addr": "",
|
|
74
|
+
"count": "",
|
|
75
|
+
"x": 650,
|
|
76
|
+
"y": 260,
|
|
77
|
+
"wires": [
|
|
78
|
+
[
|
|
79
|
+
"51c08ea65917c4fd"
|
|
80
|
+
]
|
|
81
|
+
]
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
"id": "bdc5aaf0856b106d",
|
|
85
|
+
"type": "global-config",
|
|
86
|
+
"env": [],
|
|
87
|
+
"modules": {
|
|
88
|
+
"node-red-contrib-modbus-rtu": "1.0.23"
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
]
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
[
|
|
2
|
+
{
|
|
3
|
+
"id": "57e69d4db547e69c",
|
|
4
|
+
"type": "inject",
|
|
5
|
+
"z": "2b535346fadd97c6",
|
|
6
|
+
"name": "",
|
|
7
|
+
"props": [
|
|
8
|
+
{
|
|
9
|
+
"p": "payload"
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
"p": "topic",
|
|
13
|
+
"vt": "str"
|
|
14
|
+
}
|
|
15
|
+
],
|
|
16
|
+
"repeat": "",
|
|
17
|
+
"crontab": "",
|
|
18
|
+
"once": false,
|
|
19
|
+
"onceDelay": 0.1,
|
|
20
|
+
"topic": "",
|
|
21
|
+
"payload": "",
|
|
22
|
+
"payloadType": "date",
|
|
23
|
+
"x": 400,
|
|
24
|
+
"y": 260,
|
|
25
|
+
"wires": [
|
|
26
|
+
[
|
|
27
|
+
"fd36733c93672c41"
|
|
28
|
+
]
|
|
29
|
+
]
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
"id": "fd36733c93672c41",
|
|
33
|
+
"type": "function",
|
|
34
|
+
"z": "2b535346fadd97c6",
|
|
35
|
+
"name": "function 1",
|
|
36
|
+
"func": "//msg.payload = Buffer.from('010300040001', \"hex\")\nmsg.payload={id:5, fn:6, addr:7, count:8}\nreturn msg;",
|
|
37
|
+
"outputs": 1,
|
|
38
|
+
"timeout": 0,
|
|
39
|
+
"noerr": 0,
|
|
40
|
+
"initialize": "",
|
|
41
|
+
"finalize": "",
|
|
42
|
+
"libs": [],
|
|
43
|
+
"x": 560,
|
|
44
|
+
"y": 260,
|
|
45
|
+
"wires": [
|
|
46
|
+
[
|
|
47
|
+
"9f529aa85f9dc7e9"
|
|
48
|
+
]
|
|
49
|
+
]
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
"id": "51c08ea65917c4fd",
|
|
53
|
+
"type": "debug",
|
|
54
|
+
"z": "2b535346fadd97c6",
|
|
55
|
+
"name": "debug 1",
|
|
56
|
+
"active": true,
|
|
57
|
+
"tosidebar": true,
|
|
58
|
+
"console": false,
|
|
59
|
+
"tostatus": false,
|
|
60
|
+
"complete": "false",
|
|
61
|
+
"statusVal": "",
|
|
62
|
+
"statusType": "auto",
|
|
63
|
+
"x": 980,
|
|
64
|
+
"y": 260,
|
|
65
|
+
"wires": []
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
"id": "9f529aa85f9dc7e9",
|
|
69
|
+
"type": "modbus_rtu",
|
|
70
|
+
"z": "2b535346fadd97c6",
|
|
71
|
+
"slave_id": "",
|
|
72
|
+
"fn": "-1",
|
|
73
|
+
"addr": "",
|
|
74
|
+
"count": "",
|
|
75
|
+
"x": 780,
|
|
76
|
+
"y": 260,
|
|
77
|
+
"wires": [
|
|
78
|
+
[
|
|
79
|
+
"51c08ea65917c4fd"
|
|
80
|
+
]
|
|
81
|
+
]
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
"id": "79ed984d489d398d",
|
|
85
|
+
"type": "global-config",
|
|
86
|
+
"env": [],
|
|
87
|
+
"modules": {
|
|
88
|
+
"node-red-contrib-modbus-rtu": "1.0.23"
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
]
|
package/examples/gui.js
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
[
|
|
2
|
+
{
|
|
3
|
+
"id": "57e69d4db547e69c",
|
|
4
|
+
"type": "inject",
|
|
5
|
+
"z": "2b535346fadd97c6",
|
|
6
|
+
"name": "",
|
|
7
|
+
"props": [
|
|
8
|
+
{
|
|
9
|
+
"p": "payload"
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
"p": "topic",
|
|
13
|
+
"vt": "str"
|
|
14
|
+
}
|
|
15
|
+
],
|
|
16
|
+
"repeat": "",
|
|
17
|
+
"crontab": "",
|
|
18
|
+
"once": false,
|
|
19
|
+
"onceDelay": 0.1,
|
|
20
|
+
"topic": "",
|
|
21
|
+
"payload": "",
|
|
22
|
+
"payloadType": "date",
|
|
23
|
+
"x": 320,
|
|
24
|
+
"y": 260,
|
|
25
|
+
"wires": [
|
|
26
|
+
[
|
|
27
|
+
"fd36733c93672c41"
|
|
28
|
+
]
|
|
29
|
+
]
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
"id": "fd36733c93672c41",
|
|
33
|
+
"type": "function",
|
|
34
|
+
"z": "2b535346fadd97c6",
|
|
35
|
+
"name": "function 1",
|
|
36
|
+
"func": "msg.payload = Buffer.from('010300040001', \"hex\")\n//msg.payload={id:5, fn:6, addr:7, count:8}\nreturn msg;",
|
|
37
|
+
"outputs": 1,
|
|
38
|
+
"timeout": 0,
|
|
39
|
+
"noerr": 0,
|
|
40
|
+
"initialize": "",
|
|
41
|
+
"finalize": "",
|
|
42
|
+
"libs": [],
|
|
43
|
+
"x": 480,
|
|
44
|
+
"y": 260,
|
|
45
|
+
"wires": [
|
|
46
|
+
[
|
|
47
|
+
"9f529aa85f9dc7e9"
|
|
48
|
+
]
|
|
49
|
+
]
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
"id": "51c08ea65917c4fd",
|
|
53
|
+
"type": "debug",
|
|
54
|
+
"z": "2b535346fadd97c6",
|
|
55
|
+
"name": "debug 1",
|
|
56
|
+
"active": true,
|
|
57
|
+
"tosidebar": true,
|
|
58
|
+
"console": false,
|
|
59
|
+
"tostatus": false,
|
|
60
|
+
"complete": "false",
|
|
61
|
+
"statusVal": "",
|
|
62
|
+
"statusType": "auto",
|
|
63
|
+
"x": 860,
|
|
64
|
+
"y": 260,
|
|
65
|
+
"wires": []
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
"id": "9f529aa85f9dc7e9",
|
|
69
|
+
"type": "modbus_rtu",
|
|
70
|
+
"z": "2b535346fadd97c6",
|
|
71
|
+
"slave_id": "4",
|
|
72
|
+
"fn": "3",
|
|
73
|
+
"addr": "5",
|
|
74
|
+
"count": "6",
|
|
75
|
+
"x": 670,
|
|
76
|
+
"y": 260,
|
|
77
|
+
"wires": [
|
|
78
|
+
[
|
|
79
|
+
"51c08ea65917c4fd"
|
|
80
|
+
]
|
|
81
|
+
]
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
"id": "254946bb1ed91a5b",
|
|
85
|
+
"type": "global-config",
|
|
86
|
+
"env": [],
|
|
87
|
+
"modules": {
|
|
88
|
+
"node-red-contrib-modbus-rtu": "1.0.23"
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
]
|
package/modbus_rtu.html
ADDED
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
<script type="text/javascript">
|
|
2
|
+
RED.nodes.registerType('modbus_rtu', {
|
|
3
|
+
category: 'function',
|
|
4
|
+
color: '#fff000',
|
|
5
|
+
defaults: {
|
|
6
|
+
slave_id: {
|
|
7
|
+
value: ""
|
|
8
|
+
},
|
|
9
|
+
fn: {
|
|
10
|
+
value: "0"
|
|
11
|
+
},
|
|
12
|
+
addr: {
|
|
13
|
+
value: ""
|
|
14
|
+
},
|
|
15
|
+
count: {
|
|
16
|
+
value: ""
|
|
17
|
+
},
|
|
18
|
+
},
|
|
19
|
+
inputs: 1,
|
|
20
|
+
outputs: 1,
|
|
21
|
+
icon: 'bridge.png',
|
|
22
|
+
label: function() {
|
|
23
|
+
if (this.fn == "-1") {
|
|
24
|
+
return "ModbusRTU fn=DYNAMIC";
|
|
25
|
+
} else if (this.fn == "-2") {
|
|
26
|
+
return "CHECK CRC";
|
|
27
|
+
} else if (this.fn == "0") {
|
|
28
|
+
return "CRC ONLY";
|
|
29
|
+
} else {
|
|
30
|
+
return `ModbusRTU fn=${this.fn}`;
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
oneditprepare: function() {
|
|
34
|
+
// Hàm kiểm tra và ẩn hiện
|
|
35
|
+
function updateVisibility() {
|
|
36
|
+
var fnValue = $("#node-input-fn").val();
|
|
37
|
+
if (fnValue === "0") {//CRC
|
|
38
|
+
$(".modbus-fields").hide();
|
|
39
|
+
$(".modbus-dynamic").hide();
|
|
40
|
+
$(".modbus-crc").show(); //show crc only
|
|
41
|
+
$(".modbus-check-crc").hide();
|
|
42
|
+
} else if (fnValue == "-1") {
|
|
43
|
+
$(".modbus-fields").hide();
|
|
44
|
+
$(".modbus-dynamic").show(); //show dynamic only
|
|
45
|
+
$(".modbus-crc").hide();
|
|
46
|
+
$(".modbus-check-crc").hide();
|
|
47
|
+
} else if (fnValue == "-2") {
|
|
48
|
+
$(".modbus-fields").hide();
|
|
49
|
+
$(".modbus-dynamic").hide();
|
|
50
|
+
$(".modbus-crc").hide();
|
|
51
|
+
$(".modbus-check-crc").show(); //show check crc
|
|
52
|
+
} else {
|
|
53
|
+
$(".modbus-fields").show(); //show gui input fields
|
|
54
|
+
$(".modbus-dynamic").hide();
|
|
55
|
+
$(".modbus-crc").hide();
|
|
56
|
+
$(".modbus-check-crc").hide();
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Gọi ngay khi mở cửa sổ cấu hình
|
|
61
|
+
updateVisibility();
|
|
62
|
+
|
|
63
|
+
// Lắng nghe sự kiện thay đổi của select
|
|
64
|
+
$("#node-input-fn").on("change", function() {
|
|
65
|
+
updateVisibility();
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
</script>
|
|
70
|
+
<script type="text/html" data-template-name="modbus_rtu">
|
|
71
|
+
<div class="form-row">
|
|
72
|
+
<h3>modbus_rtu Build payload msg</h3><hr>
|
|
73
|
+
<div/>
|
|
74
|
+
<div class="form-row">
|
|
75
|
+
<label for="node-input-fn"><i class="fa fa-tag"></i> Function</label>
|
|
76
|
+
<select id="node-input-fn" style="width:70%">
|
|
77
|
+
<option value=0>CRC ONLY</option>
|
|
78
|
+
<option value=-2>CHECK CRC</option>
|
|
79
|
+
<option value=-1>DYNAMIC by msg.payload.fn</option>
|
|
80
|
+
<option value=1>01 Read Coils (0x)</option>
|
|
81
|
+
<option value=2>02 Read Discrete Inputs [1x]</option>
|
|
82
|
+
<option value=3 selected>03 Read Holding Registers (4x)</option>
|
|
83
|
+
<option value=4>04 Read Input Registers (3x)</option>
|
|
84
|
+
<option value=5>05 Write Single Coil</option>
|
|
85
|
+
<option value=6>06 Write Single Register</option>
|
|
86
|
+
<option value=15>15 Write Multiple Coils</option>
|
|
87
|
+
<option value=16>16 Write Multiple Registers</option>
|
|
88
|
+
</select>
|
|
89
|
+
</div>
|
|
90
|
+
<div class="modbus-fields">
|
|
91
|
+
<div class="form-row">
|
|
92
|
+
<label for="node-input-slave_id"><i class="fa fa-tag"></i> Slave id</label>
|
|
93
|
+
<input type="text" id="node-input-slave_id" value="1" placeholder="id">
|
|
94
|
+
</div>
|
|
95
|
+
|
|
96
|
+
<div class="form-row">
|
|
97
|
+
<label for="node-input-addr"><i class="fa fa-tag"></i> Address</label>
|
|
98
|
+
<input type="text" value="0" id="node-input-addr" placeholder="Address begin">
|
|
99
|
+
</div>
|
|
100
|
+
|
|
101
|
+
<div class="form-row">
|
|
102
|
+
<label for="node-input-count"><i class="fa fa-tag"></i> Count</label>
|
|
103
|
+
<input type="text" value="1" id="node-input-count" placeholder="Quantity">
|
|
104
|
+
</div>
|
|
105
|
+
</div>
|
|
106
|
+
<hr>
|
|
107
|
+
<i class="fa fa-bomb"></i> Algorithm
|
|
108
|
+
<pre class="modbus-fields">//Modbus RTU: không phụ thuộc payload đầu vào
|
|
109
|
+
const data = ModbusMsg(id, fn, addr, count);
|
|
110
|
+
msg.payload = add_crc16(data, out_crc);
|
|
111
|
+
msg.crc = out_crc</pre>
|
|
112
|
+
<pre class="modbus-crc">//CRC ONLY:
|
|
113
|
+
//input (pre node): phụ thuộc vào payload của node trước ở dạng Buffer
|
|
114
|
+
//output:
|
|
115
|
+
const data = msg.payload;
|
|
116
|
+
msg.payload = add_crc16(data);</pre>
|
|
117
|
+
<pre class="modbus-check-crc">//CHECK CRC:
|
|
118
|
+
//input: phụ thuộc vào payload của node trước ở dạng Buffer
|
|
119
|
+
//output: msg if CRC==0 else msg.crc=crc
|
|
120
|
+
const data = msg.payload;
|
|
121
|
+
msg.payload = add_crc16(data, out_crc);
|
|
122
|
+
msg.crc = out_crc
|
|
123
|
+
if(out_crc==0)return msg;</pre>
|
|
124
|
+
<pre class="modbus-dynamic">//DYNAMIC:
|
|
125
|
+
//input (pre node): phụ thuộc vào payload của node trước ở dạng json
|
|
126
|
+
msg.payload = {id:2,fn:3,addr:4,count:5}
|
|
127
|
+
//output:
|
|
128
|
+
const id=msg.payload.id;
|
|
129
|
+
const fn=msg.payload.fn;
|
|
130
|
+
const addr=msg.payload.addr;
|
|
131
|
+
const count=msg.payload.count;
|
|
132
|
+
const data = ModbusMsg(id, fn, addr, count);
|
|
133
|
+
msg.payload = add_crc16(data);</pre>
|
|
134
|
+
<hr>
|
|
135
|
+
Code by Do Duy Cop
|
|
136
|
+
</script>
|
|
137
|
+
|
|
138
|
+
<script type="text/html" data-help-name="modbus_rtu">
|
|
139
|
+
<p>modbus_rtu. Code by Do Duy Cop</p>
|
|
140
|
+
|
|
141
|
+
<h3>Inputs</h3>
|
|
142
|
+
<dl class="message-properties">
|
|
143
|
+
<dt>id<span class="property-type">int</span></dt>
|
|
144
|
+
<dd>required</dd>
|
|
145
|
+
|
|
146
|
+
<dt>fn<span class="property-type">int</span></dt>
|
|
147
|
+
<dd>required</dd>
|
|
148
|
+
|
|
149
|
+
<dt>addr<span class="property-type">int</span></dt>
|
|
150
|
+
<dd>required</dd>
|
|
151
|
+
|
|
152
|
+
<dt>count<span class="property-type">int</span></dt>
|
|
153
|
+
<dd>required</dd>
|
|
154
|
+
</dl>
|
|
155
|
+
|
|
156
|
+
<h3>Outputs</h3>
|
|
157
|
+
<dl class="message-properties">
|
|
158
|
+
<dt>payload <span class="property-type">Buffer</span></dt>
|
|
159
|
+
<dd>Output</dd>
|
|
160
|
+
</dl>
|
|
161
|
+
|
|
162
|
+
<h3>Details</h3>
|
|
163
|
+
<dl class="message-properties">
|
|
164
|
+
<p>fn = 0 : <br>
|
|
165
|
+
msg.payload = msg.payload + 2 byte crc16<hr>
|
|
166
|
+
fn > 0:<br>
|
|
167
|
+
id = 0 then get msg.payload.id<br>
|
|
168
|
+
addr = 0 then get msg.payload.addr<br>
|
|
169
|
+
count = 0 then get msg.payload.count
|
|
170
|
+
</p>
|
|
171
|
+
</dl>
|
|
172
|
+
</script>
|
package/modbus_rtu.js
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
module.exports = function (RED) {
|
|
2
|
+
function MODBUS_Node(config) {
|
|
3
|
+
RED.nodes.createNode(this, config);
|
|
4
|
+
const node = this;
|
|
5
|
+
|
|
6
|
+
function ModbusMsg(slaveAddress, functionCode, addressRegister, numberRegisters) {
|
|
7
|
+
var payloadBuffer = Buffer.allocUnsafe(6); // Tăng kích thước payloadBuffer lên 6 byte để chứa cả slaveAddress
|
|
8
|
+
payloadBuffer.writeUInt8(slaveAddress, 0); // Ghi slaveAddress vào vị trí đầu tiên của payloadBuffer
|
|
9
|
+
payloadBuffer.writeUInt8(functionCode, 1); // Ghi functionCode vào vị trí thứ hai của payloadBuffer
|
|
10
|
+
payloadBuffer.writeUInt16BE(addressRegister, 2); // Ghi addressRegister vào vị trí thứ ba của payloadBuffer
|
|
11
|
+
payloadBuffer.writeUInt16BE(numberRegisters, 4); // Ghi numberRegisters vào vị trí thứ năm của payloadBuffer
|
|
12
|
+
return payloadBuffer;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function add_crc(payload, out_crc) {
|
|
16
|
+
const talbeAbs = [
|
|
17
|
+
0x0000, 0xCC01, 0xD801, 0x1400, 0xF001, 0x3C00, 0x2800, 0xE401,
|
|
18
|
+
0xA001, 0x6C00, 0x7800, 0xB401, 0x5000, 0x9C01, 0x8801, 0x4400
|
|
19
|
+
];
|
|
20
|
+
function crc16(buf) {
|
|
21
|
+
let crc = 0xFFFF;
|
|
22
|
+
for (let i = 0; i < buf.length; i++) {
|
|
23
|
+
let ch = buf[i];
|
|
24
|
+
crc = talbeAbs[(ch ^ crc) & 15] ^ (crc >> 4);
|
|
25
|
+
crc = talbeAbs[((ch >> 4) ^ crc) & 15] ^ (crc >> 4);
|
|
26
|
+
}
|
|
27
|
+
return crc;
|
|
28
|
+
}
|
|
29
|
+
var crc = crc16(payload);
|
|
30
|
+
var crcBuffer = Buffer.alloc(2); // Allocate a buffer to hold CRC (2 bytes)
|
|
31
|
+
crcBuffer.writeUInt16LE(crc, 0); // Write CRC value into the buffer (little-endian)
|
|
32
|
+
out_crc.crc = crc;
|
|
33
|
+
// Concatenate payload buffer and CRC buffer
|
|
34
|
+
var newPayload = Buffer.concat([payload, crcBuffer]);
|
|
35
|
+
return newPayload;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
node.on('input', function (msg) {
|
|
39
|
+
var id, fn, addr, count, data = [], out_crc = { crc: " " };
|
|
40
|
+
try {
|
|
41
|
+
fn = parseInt(config.fn);
|
|
42
|
+
if (fn == 0) { //CRC only
|
|
43
|
+
data = msg.payload;
|
|
44
|
+
msg.payload = add_crc(data, out_crc);
|
|
45
|
+
node.status({ fill: "green", shape: "dot", text: "Add CRC16: 0x" + out_crc.crc.toString(16)});
|
|
46
|
+
node.send(msg);
|
|
47
|
+
return;
|
|
48
|
+
} else if (fn == -2) { //CHECK CRC
|
|
49
|
+
data = msg.payload;
|
|
50
|
+
msg.payload = add_crc(data, out_crc);
|
|
51
|
+
if(out_crc==0){
|
|
52
|
+
node.status({ fill: "green", shape: "dot", text: "CRC == 0 : OK" + out_crc.crc.toString(16)});
|
|
53
|
+
node.send(msg);
|
|
54
|
+
}else{
|
|
55
|
+
node.status({ fill: "red", shape: "ring", text: "CRC error: 0x" + out_crc.crc.toString(16) });
|
|
56
|
+
}
|
|
57
|
+
return;
|
|
58
|
+
} else if (fn == -1) { //dynamic
|
|
59
|
+
fn = parseInt(msg.payload.fn);
|
|
60
|
+
id = parseInt(msg.payload.id);
|
|
61
|
+
addr = parseInt(msg.payload.addr);
|
|
62
|
+
count = parseInt(msg.payload.count);
|
|
63
|
+
} else if (fn > 0) { // GUI
|
|
64
|
+
id = parseInt(config.slave_id);
|
|
65
|
+
addr = parseInt(config.addr);
|
|
66
|
+
count = parseInt(config.count);
|
|
67
|
+
}
|
|
68
|
+
if (isNaN(fn) || fn<0 || fn>255){
|
|
69
|
+
node.status({ fill: "red", shape: "ring", text: 'Error: fn error' });
|
|
70
|
+
node.warn('Error: fn error or NaN');
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
if (isNaN(id) || id < 1 || id > 255) {
|
|
74
|
+
node.status({ fill: "red", shape: "ring", text: 'Error: id not in 1..255' });
|
|
75
|
+
node.warn('Error: id not in 1..255 or NaN');
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if (isNaN(addr) || addr < 0) {
|
|
80
|
+
node.status({ fill: "red", shape: "ring", text: "Error: addr < 0" });
|
|
81
|
+
node.warn('Error: addr < 0 or NaN');
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
if (isNaN(count) || count < 1) {
|
|
85
|
+
node.status({ fill: "red", shape: "ring", text: "Error: count < 1" });
|
|
86
|
+
node.warn('Error: count < 1 or NaN');
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
data = ModbusMsg(id, fn, addr, count);
|
|
90
|
+
var out_crc = { crc: " " };
|
|
91
|
+
msg.payload = add_crc(data, out_crc);
|
|
92
|
+
msg.crc = out_crc.crc;
|
|
93
|
+
node.status({ fill: "green", shape: "dot", text: `ModbusRTU(${id},${fn},${addr},${count})` });
|
|
94
|
+
node.send(msg);
|
|
95
|
+
} catch (e) {
|
|
96
|
+
var err = e.message;
|
|
97
|
+
if (err == 'The "list[0]" argument must be an instance of Buffer or Uint8Array. Received an instance of Object') {
|
|
98
|
+
err = 'pre node must be format: msg.payload = {id: 2, fn:3, addr: 4, count: 5}';
|
|
99
|
+
node.status({ fill: "red", shape: "ring", text: "Error format" });
|
|
100
|
+
} else {
|
|
101
|
+
node.status({ fill: "red", shape: "ring", text: "Error" });
|
|
102
|
+
}
|
|
103
|
+
node.warn(err);
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
RED.nodes.registerType("modbus_rtu", MODBUS_Node, {});
|
|
109
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "node-red-contrib-modbus-rtu",
|
|
3
|
+
"version": "1.0.24",
|
|
4
|
+
"description": "Node-RED function build content modbus_rtu - Code by DoDuyCop",
|
|
5
|
+
"author": "duycop",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "https://github.com/duycop/node-red-contrib-modbus-rtu.git"
|
|
10
|
+
},
|
|
11
|
+
"bugs": {
|
|
12
|
+
"url": "https://github.com/duycop/node-red-contrib-modbus-rtu/issues"
|
|
13
|
+
},
|
|
14
|
+
"keywords": [
|
|
15
|
+
"node-red",
|
|
16
|
+
"modbus",
|
|
17
|
+
"crc16",
|
|
18
|
+
"modbus_rtu"
|
|
19
|
+
],
|
|
20
|
+
"engines": {
|
|
21
|
+
"node": ">=18.0.0"
|
|
22
|
+
},
|
|
23
|
+
"node-red": {
|
|
24
|
+
"version": ">=4.1.0",
|
|
25
|
+
"nodes": {
|
|
26
|
+
"modbus_rtu": "modbus_rtu.js"
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|