valiot / modbux

Elixir Modbus library for network and serial communications.
https://hexdocs.pm/modbux
MIT License
40 stars 15 forks source link

read multiple registers #13

Closed tomazbracic closed 2 months ago

tomazbracic commented 3 months ago

Hi,

I want to use Modbux RTU to communicate with 4 slave devices where I want to read multiple registers with one request. I can't find that this is possible in the documentation. Is it? Right now I am filling the map to map correctly values to keys with Task.asyncs where each of them has a master request in it. But I am getting a lot of timeouts. And then when all results are back I fill those into struct then. Sometimes I don't get value on a request, but if I then repeat request I do. My boudrate is 19200 which should be ok.

Can you please suggest what would be the best approach for this? Is this possible with Modbux library?

Thank you in advance.

Tomaz

alde103 commented 3 months ago

Hi,

Are you trying to communicate with the 4 slave at once? Or the question is just to read multiple registers?

To read multiple registers, you can use:

# Starts the Master at "ttyUSB1" (in the example is connected to ttyUSB1)
{:ok, m_pid} = Modbux.Rtu.Master.start_link(tty: "ttyUSB1")
# Read 2 holding registers at 1 (memory address) from the slave 80
resp = Modbux.Rtu.Master.request(m_pid, {:rhr, 80, 1, 2})
# resp == {:ok, [102, 103]}

If you are trying to communicate with the 4 slave at once, I'm afraid it is not possible, since in Modbus RTU, each communication request is directed to a single slave at a time, and the registers being read or written belong to that specific slave. There is no single command in Modbus RTU that allows you to request multiple registers from multiple slaves in one operation. Also, you have to be aware of the physical layer, assuming you are using RS485, since this is a bus, it does not inherently handle multiple messages simultaneously. The only way in which this might work is to have multiple masters connected, each one directly to one slave on different buses. Modbus does support broadcast, but the slaves won't answer any read request.

I hope this helps you.

tomazbracic commented 3 months ago

Hi,

thank you for the answers, but my case with multiple registers is ...let assume my slave 1 has registers like 602, 608, 711, 733, 788, 799, 803, 820, 920, 1012, ..... 3012. For the sake of debate, lets say there are like 30 of them. Is there a way or if not what would be best approach to read them all as soon as possible (at once). This was my original question. To avoid this request / response cycle. To get results faster.

The problem is, there are pairs between them (low_word, high_word) that I have to do some bitwise operations as well. And when I get them all I have to create a structure and send it to cloud for further processing. Now one big issue is, that should do this every second, but Task.asyncs that I use, and Modbux.reader uses it as well if not mistaken, produces a bit of a "leg". I mean every small steps just adds up some tome. And I can see quite a bit of timeouts.

For instance if I request manually, it happens that I don't get back a useful result, but then if I repeat it quickly again... I get it. Is there something I could do for such cases (from the library point of view). I didn't find anything like this.

And yes, I will have like 4-6 slaves. I hope having 1 master will work, from the point of throughput with -b 19200. I will have 1 genserver for that. Unless this 1 genserver won't become a bottleneck (I already noticed a bit higher mailbox size of a master process). Then alternatives like Modbus supervisors - and a genserver for slave? Have to talk with my hw guys as well.

alde103 commented 3 months ago

Hi,

I'm afraid that in Modbus RTU, it is not possible to request non-contiguous registers with a single command. The register read commands (such as Read Holding Registers or Read Input Registers) allow you to specify a range of contiguous registers.

If you need to read registers that are not contiguous, you must send multiple commands, one for each contiguous range of registers you need to read.

Alternatively, if you can read a wider range that includes all the registers you are interested in, you could do that and then simply extract the specific register values you need. This might be more efficient than sending multiple separate commands, depending on the situation and the slave capabilities.

tomazbracic commented 3 months ago

Hi,

I tried to request multiple addresses at once (like 50 of them), but when I get back {:ok, [value1, value2, .... value N]}, the values are not in order. So I can't know what value is for what address. Is there any way to do that? If I knew that values are returned in order, I could easily pattern-match them to get the needed values. But at first glance, I can see values are not being returned in order (of Modbus addresses).

alde103 commented 3 months ago

As far as I know the values should be in order, can you share any logs of those frames??

tomazbracic commented 2 months ago

Ok, I found the typo. My bad. Can I ask about multiple register write command here? Or should I open new "issue"?

In the documentation I can see this

{:fc, slave, address, value} force single coil. {:phr, slave, address, value} preset single holding register. {:fc, slave, address, values} force multiple coils. {:phr, slave, address, values} preset multiple holding registers.

So I assume I can use the last one to write multiple registers (which are in continuous order). So how would a command be for write to registers 14, 15, 16 and 17 a value of 0?

Thank you.

alde103 commented 2 months ago

Yes, you can, there is an example of use case for Modbus TCP here. So, can I close this issue??

tomazbracic commented 2 months ago

Great, thank you. I will close it yes.