How to Use Node-Red with Modbus

Modbus is a de facto standard, truly open and the most widely used network protocol in the industrial manufacturing environment.-ref Modbus Faqs

Because of its popularity there is a growing requirement for reading data and controlling Modbus devices over TCP/IP networks using node-red.



In this tutorial we are going to cover.

Node-red Modbus Nodes

There are a collection of modbus nodes available for node-red which you will need to install.

Go the the menu and select manage Pallete and search for the node-red-contrib-modbus package.

modbus-nodes-install

There are 11 nodes in the package and they are grouped together in the Modbus section.modbus-nodes

The nodes that we are mainly interested in are the read and write nodes.

Modbus Server Configuration

The modus getter and write nodes all require that you configure a server to connect to.

The settings fro this server are store in the server configuration and are available to all nodes in the workspace.

For a TCP/IP server you will need to configure the IP address and the port number. The default modbus server port is 502.

The settings page is shown below:

modbus-server-settings

The important settings are IP address,port, TCP type and unit id.

You can also access this tab from the configuration nodes menu.

configuration-nodes

Node status

The getter and write nodes display the server status under the node as shown below:

node-status

You can see that the green active staus means that we are connected to the modbus server.

As you read or write data you will see the status change.

The status is also available to the status node and you can use the status in your flow to ensure successful read and write. See Using the status node for flow control

Sleeping Nodes

Although the status node can be useful it doesn’t detect what I call sleeping nodes.

When using the getter or write nodes you will notice that if there is long delay between reading or writing (about 2mins) the node appears to go to sleep.

This means that a read or write attempt fails and the node reinitializes. Once it has reinitialized you can then read or write normally.

Unfortunately the node doesn’t send any error messages on any outputs so it isn’t easy to detect.

The solution I’ve used in my flows is the send the read signal in two paths and check which one gets received. To work it relies on the fact that each message has an ID and we can check this id.

Here is the flow

modbus-detect-fail

A normal read/write the function node sees the msg coming first from path1 and so it knows it is ok.

The message should also come in from path 2 and this is used to reset the function node.

However in a failed read/write the function node sees the msg from path 2 but none form path1 and so it knows it has failed and triggers another read.

Modbus Read Nodes

These are the modbus getter and modbus flex getter. Both can be used for reading data using Modbus TCP/IP,TCP RTU and also serial .

The read mode is configured in the server properties shown below:

modbus-server-properties

The modbus getter node has all of the configuration done in the node itself and is used when doing a fixed read.

The modbus flex getter expects you to pass the configuration data in from a preceding node and is recommended when you are doing multiple reads of different values.

modbus-red nodes

The nodes support function codes 1,2,3 and 4 as shown below:

modbus-read-function-codes

Both nodes have two outputs which are essentially the same. I tend to use output 2.

If you pass the output to a debug node and display the complete msg object you will see that you get an output like the one shown below.

modbus-read-output

The node assumes that the register contains a 16 bit integer and if that is the case then you can use the value field to get the data. If the data represents a float or 32 bit integer you will need to use the buffer.

The syntax is msg.values and msg.payload.buffer

The video below gives a quick overview of the nodes and also read examples using both the modbus getter and modbus flex getter nodes.

Modbus Read Register Examples

All of the examples use a flow like that shown below:

modbus-read-flow-1

The function code is used to create the read command and the code will be similar to the code below:

var fc=3;
var sa=0;
var addresses=2;
//var slave_ip=msg.payload.slave_ip;
//msg.slave_ip="192.168.1.31";
msg.payload={value: msg.payload, 'fc': fc, 'unitid': 1, 'address': sa , 'quantity': addresses };
return msg;

The function code, start address(sa) and the number of address will vary.

The inject node is used just to trigger the flow.

The process function node connected to output 2 extracts the data from the modbus server response and the code shown below in the example should be placed there.

As mentioned earlier if you have 16 bit integers in the registers then they are available in the values field and you don’t need to do anything to extract the value.

However if the resisters contain other data types then you will need to work with the buffer object.

Both getter nodes return the data in the msg.payload.buffer field.

The following examples show how to extract a 16 bit integer,a 32 bit integer and a 32bit float from the buffer.

Read 16 Bit Integer

Below is the code from a function code that reads a value of 16 bit Integer buffer. It sends to a modbus server using fc =3 and reads 1 address

 

const buf= Buffer.from(msg.payload.buffer)
//const buf = Buffer.from(msg.payload.buffer);
const value = buf.readUInt16BE();
msg.value = value;
return msg;

Read 32 Bit Integer

Below is the code from a function code that reads a value of 32 bit Integer buffer. It sends to a modbus server using using fc =3 and reads 2 addresses.

const buf = Buffer.from(msg.payload.buffer);
const value = buf.readUInt32BE();
msg.value = value;
return msg;

Read 32 Bit Float

Below is the code from a function code that reads a value of 32 bit Integer buffer. It sends to a modbus server using using fc =3 and reads 2 addresses.

const buf = Buffer.from(msg.payload.buffer);
const value = buf.readFloatBE();
msg.value = value;
return msg;

You should note that BE (big Endian) is the normal but if your data is in Little Endian format use LE instead of BE.

This is covered in the first three minutes of Node-red Modbus video2

Reading Coil Example

To read coils we change the function code to 1. The number to read should be divisible by 8. If you ask it to read 6 it will actually show you 8.

The return value contains the coil data as true and false. The command fc=1 sa=0 and addresses= 6 returns the following:

modbus-read-coils

To extract the first coil reading I would use msg.values[0].

Signed and Unsigned

All of the examples given above use signed integers. If you need to write signed use readUInt16BE() and readUInt32BE().

BE and LE

All of the examples above use BE format. If you need LE format replace BE with LE.

I.e readUInt16BE() becomes readUInt16LE() etc

See Working with Buffers in Node-red for more details.

Writing data ———>

Resources

Flow for video1

download



Related Tutorials

Click to rate this post!
[Total: 1 Average: 5]

13 comments

  1. Hi Steve,

    I’m trying to read a register from a CAT (EMCP 3.2) via serial. I can read many registers just fine, but i’m trying to figure out how read certain bits of a particular register. As shown in the manual, the register is defined as:

    335 – 1 REGISTER (2 BYTES) LONG – READ
    Bits 15:4 UNUSED
    Bits 3:2 Amber Lamp Status: Bits 00 = off, 01 = on
    Bits 1:0 Red Lamp Status: Bits 00 = off, 01 = on

    I’m not sure how to extract the relevant bits?

    1. I will take a look and send you the function code via email. Go to the ask steve page and contact me so I can send the email.
      Rgds
      Steve

  2. Steve can you point me in the right direction on this. The use of big endian and word swapping has me prepelxed as to how to decode this.

    Reading these registers requires that the address be in the range of 16 to 22 (0x10 and 0x16).
    Also, the address must be evenly divisible by 2. In other words, only address 0x10, 0x12, 0x14
    and 0x16 are valid. Each value is returned as two registers in IEEE 754 floating point format. The
    four data bytes are treated as two individual big endian 16-bit words with the least significant word
    being sent first. In other words, the 32 bit floating point number represented as ABCD is sent as
    CDAB

  3. Thanks for this blog, information is pretty thin on the ground.
    Do you have an example of config for a modbus rtu connction please?
    I have python code that works, but I do not know how to convert that into something that works in node red.
    I can always use the code as a fall back, but I’d prefer not to.
    Any help would be much appreciated.

    1. Hi
      No never done it. However if you let me know a bit more about it I will run it past someone who does that a lot and see if they can help.

  4. halo steve
    ‘ve learned over time, that the more modbus data I read, the more RAM memory it uses, and it’s very systemic. is there anything I can do with the problem?

      1. I use 2Gb Ram, every time I observe, there is always an increase in memory when I add a modbus node. and can exceed up to 80%, resulting in slow performance. I am using armbian Linux.
        Is it possible that the nodes are always read and never deleted, thus making the memory full?

  5. Hi Steve,

    Do you experience working with TCP clients for a longer period of time? I have an example where I am reading a TCP modbus client (not on my local network) with 3 modbus read nodes. It works great for a few hours, and after that it just stops working. The Modbus Queue Info node shows that it has thousands of messages in the queue, and the client does not get queries. I am trying to send in a resetQueue, but nothing happens. Nothing works other than re-deploying the flow.

    1. Hi
      Do you have a long delay between reads as I’ve noticed that it seems to loose the connection after a few minutes of inactivity. Sending a read request resets it.
      rgds
      steve

Leave a Reply

Your email address will not be published. Required fields are marked *