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 and MQTT.



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 Palette 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 modbus server to connect to.

The settings for this server are stored 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.

If you are having connection problems try changing the TCP Type from default.

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 status 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.

Note: Since I first did this I have found a better method using the trigger node and you an see it here in this video. The dowload link to the flow is also available.

Modbus Read Nodes

These are the modbus getter and modbus flex getter.

Both nodes can be used for reading data using Modbus TCP/IP,TCP RTU and also serial .

The read mode is configured in the server properties as 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 unsigned 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,video2 and the dashboard

download



Related Tutorials

Click to rate this post!
[Total: 7 Average: 4.4]

68 comments

  1. Hi there,

    I am attempting to update the input registers of a Modbus Flex Server. I am currently only able to do so by using the special inject command. This however is quite limiting as it does not allow for block “writes”, or rather updates, to be performed.

    Here is the inject command which is able to update one input register at a time.
    msg.payload = { ‘value’: msg.payload, ‘register’: ‘input’, ‘address’: 1 , ‘disableMsgOutput’ : 0 }; return msg;

    Is there a more efficient way to update the values of the input registers?

    I have also tried writing an array to the special inject command’s value field. However this gives unexpected behavior and does not work. Is there another way or is it safe to say that there is an inherent limitation when it comes to updating flex server input registers?

    Kind Regards,
    Devlin

  2. Hi Steve,
    i want to use the node-red modbus-items from you, but I get every time two errors when I try to read a value:
    – Modbus Failure On State sending Get More About It By Logging.
    – Error: Time out

    What is the normal reason for the first error?

    I configure the server in node-red :
    TYP: TCP
    IP: 192.168.178.58
    Port: 8080
    CONNECTION-TYPE: RTU-Buffered
    Unit-ID: 1

    And the read-node:
    Unit-Id: 1
    Adress: 33001
    Amount: 1
    Poll rate: 5 Seconds

    I use an other software for linux which called “modpoll”, maybe you know it. With these software and the command “./modpoll -m enc -p 8080 -a 1 -t 3:hex -r 33001 192.168.178.58” I can read the value without error.

    The Modus-Setting of the device is:
    Baudrate: 9600
    Data-bits: 8
    Stop-Bits: 1
    Odd/even: Non

    Do you have any idea?

    1. It looks like a connection issue try changing the connection type. It might also be that the modbus device you are using requires a custom node.
      rgds
      Steve

      1. Ok, i will try.
        Is it possible to get a more detailed error-message, e.g. any logfile?

        What do you mean with “custom node”? Which “basic” node should i use for that?

  3. Good afternoon, Steve, I am pretty new to Node-RED let to say Modbus. I am trying to read values from my PV inverter to build an energy overview in Home Assistant. The modbus-flex-getter node allows 4 values (fc, unitid, address, quantity). As far as I understand the manual of the inverter it requires to send a CRC value with each command. The CRC calculates individually from the 4 values mentioned before. I have compiled a separate sequence to calculate the CRC, now I am searching for a way to send a 5-value-command instead of 4 values with the modbus-flex-getter node. Can you help please? Best wishes, Claus
    P.S. you offer an alternative way to Paypal to donate?

    1. Claus
      You don’t need to calculate the crc node-red getter node will do that for you. Just send it the command.Let me know if that doesn’t work and I’ll take a look.
      Rgds
      Steve

      1. this requires that all Modbus servers and clients stick to the standards. The fact that the manufacturer of the inverter does not use the standard port but 8899 let‘s me suspect that also his CRC algorithm is non standard with the motivation to force end user to use the cloud service and prevent them from local access. If I use a simple example in the function node to assign 3, 1, 0x1600, 2 to be the input for the modbus-flex-getter node I get first „Modbus Failure On State sending Get more about it by logging“ and second „Error: Timed out“.
        If ChatGPT transferred the CRC algorithm from the manufacturers spec correctly into JSON than the CRC value should be 50262 in this example. Would be interesting to get a glimpse which CRC is sent from Modbus-flex-getter node. Maybe I van capture the traffic with Wireshark.

        1. I got the same error by try to connect to my solis inverter.
          I solve my problem by using “Telnet” as connection-typ and not “RTU_Buffered” at the node-red modus-config. Maybe you have also luck.

  4. Hi, I have a node-red modbus configured for a Huawai Sun2000 inversor. Until today has been working perfectly, but it has just stopped working showing the message Error: Client Not Ready To Read At State init.

    Nothing has changed in my config. IP is responding and I can telnet port 502 of the inversor.

    Do you know how can I solve this?

    1. Strange that it just stopped working have you installed any new nodes?Have your tried restarting node-red. Do you have anither node-red instance on another machine that you can try.
      Rgds
      Steve

  5. Or maybe, it is a shortcoming of the device….
    The device is a Teltonika router that I query for “Mobile data received this month”.
    SA=193, 2 registers, 32 bit unsigned integer.
    Hmm, that can never be larger than 4294967295 which is 4GB, while the real usage was more than that.

    So I guess there is nothing in the flow that can be done to get this right.

  6. Hi Steve,,

    I have 1 modbus (1 tcp/ip) with 3 ports (502, 503, 504) connected to different sensor. I use 3 modbus-flex-getter to read the sensor outputs. I’ve set port 502 for sensor A, but when I set port B with 503, sensor A’s port change to 503 as well. When I set port C with 504, the port of sensor A and B also changed tobe 504. Is there any special command or anything to fix this?

  7. Hi, I’m trying to figure out the difference between the Modbus Read node and the Modbus Getter nodes. Do you know what the differences are?
    Thanks

  8. Hey Steve, could you briefly explain the “queue commands” and “unit ID’s in parallel” modbus server settings? Thank you!

      1. Steve,
        I am referring to the example with two different paths to avoid failed read/write. You have that at the top of this post with the picture.

        Thank you,
        Dru

  9. The Modbus Server node only generates outputs when you trigger it. The server itself is always listening to requests in the background and updating its internal registers, but it won’t generate outputs until you ping the Modbus Server input. I set an injector node that repeats on a timer but I’m not sure what to inject into it. I don’t really want to make a simulated client request, I just want it to output data. Or ideally I want the modbus server to act kind of like an injector, and just trigger the flow when some client on the network pushes data. Am I using this the way I’m supposed to be?

    In my situation, an eternal device acts like a Modbus TCP client, pushing register updates into Node Red.

    1. You are correct regarding the server. What is the external device and what node is it?
      If you use the ask steve page then you can send me the flow by email and I’ll take a look.
      Rgds
      Steve

  10. Hey Steve,

    I am getting a puzzling result when trying to read a 32 bit floating register which I know exists (I have successfully read it with other hosts). Node-Red posts the error “Modbus exception 2: Illegal data address (register not supported by device)”. If I am 100% certain that the register exists, what else could be causing this? I am subtracting 1 from the address as I had done to successfully read unsigned 16 bit integers (base 0 vs base 1), but that has not helped. Any ideas?

    1. Not really except that the error implies a faulty address so I would just try reading a slightly different address . It is not important at that stage what data you get as long as you don’t get the error.
      If you still have problems send me the relevant bit of the flow and I’ll take a look
      Rgds
      Steve

      1. I’m sorry, I wasn’t very clear on my question. I managed to make the correct communication between the software I’m using, OpenPLC, and Node-red. But I can’t read the data correctly. I have a value that changes from 0 to 100, float, to simulate a temperature sensor. However, when reading node-red presents values ​​between 16500 and 17100. I thought it was something related to the amount of bytes, however when I use the example here on the site for float cases or for integers with 32bytes the program shows the error: “RangeError [ ERR_BUFFER_OUT_OF_BOUNDS]: Attempt to access memory outside buffer bounds”. Thanks a lot for the help!

  11. Thanks !
    Do you think it’s possible to change ip adress and port without edition of node ?
    I have à php local website with an input form wich transmit the ip and port into mysql. I want yo use thoses into the node’s configuration.

      1. Thanks for reply but getter and setter need a “serveur” wich need an IP adress.
        By using : var slave_ip=msg.payload.slave_ip; msg.slave_ip=”192.168.100.202″; what to i have to complete into the adress and port of Server’s getter node parameter ?
        I need to read multiple 32bit double intergers and put them into a mysql database.
        In this database, there is the IP and port of Modbus TCP slaves i need to communicate with.

        Thanks for your help.

        1. Hi
          Have you seen the videos I did on reading 32bit floats and integers. They also come with a flow which you might find useful.

          1. Yes, i’ve seen the video and tried to use your “modbus-video2-flow” but when i modify the “32bit INT function” replacing the ip adress by one of my slave modbus device (Schneider M221) msg.slave_ip=”192.168.1.31″ -> msg.slave_ip=”192.168.100.202″; it dosn’t change anything. Local getter is not connected. I need to change also the adress into Modbus-Getter-node -> Server -> host / 192.168.100.202 what I would like not to have to do because the address can change (coming from mysql)
            And when the Server is in 192.168.100.202 manually, it’s OK, but the Value is not good.
            In my Schneider PLC, i have %MD0 = 111111 but 2986803201 in node-red. Thanks a lot

          2. So changing the ip address has no effect? You want a different ip address in the getter node is that correct. How many addresses are you looking at?
            rgds
            steve

          3. Yes it’s correct, i want to change IP address because it’ not fixed, the target is defined by the user of the system, by a formular in PHP stored in MySQL database.
            To begin i tried to change it into the function node : msg.slave_ip=”192.168.100.202″
            The host parameter into Server into the Getter node was configured in 127.0.0.1.
            It doesn’t work, then i change the host parameter into 192.168.100.202 and it’s OK. If i change the IP address in your function code, it’s doesn’t change anything.
            I don’t understand how can the msg.slave_ip can replace the host ip address of the server.

            var function_code=3;
            var start_address=0;
            var register_qty=2;
            var slave_ip=msg.payload.slave_ip;
            msg.slave_ip=”192.168.100.202″;
            msg.payload={value: msg.payload, ‘fc’: function_code, ‘unitid’: 1, ‘address’: start_address , ‘quantity’: register_qty};
            return msg;

          4. I think that the slave ip address is when the modbus server you are connected to has slave nodes.
            How many modbus servers are you connecting to?

          5. I use Modbus getter to read 2 slaves devices with ip adresses controlled by a web server and mysql. The server is a Raspberry pi with node red, Apache, PHP, MySQL, the slaves are PLC and Wireless gareway in Modbus TCP with modifiable IP address

          6. Each user have its own network architecture and don’t want to install a router

          7. Can you use the ask steve page and contact me and then send me a diagram of your setup as I’m not quite sure I understand it.
            Rgds
            Steve

  12. Hi Steve,
    I am trying to read 208 addresses but the mobus node only allows me to read 125 address quantity. How I can to read the 208 addresses with node red? Thanks and regards.

    1. You will need to do two reads and hold the first read and add the second read to it if you app needs the 208 reads in one go. Does that make sense?
      Rgds
      Steve

      1. Hi Steve,
        thanks for the answer. Why is the address quantity limited to 125? Is it possible change it or is it by protocol? Thanks and regards.

    1. Hi
      This is what I got back
      In the settings, you can choose Serial Expert, then set the port you are communicating with. Set the Serial type to RTU, with the correct baud rate etc. Each device will get its own Unit ID. Set timeout around 2 seconds.

  13. 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

  14. 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

  15. 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.

  16. 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?

  17. 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 *