ya-mouse / modbus-gate

Modbus-TCP <-> RTU/ASCII/TCP/RealCOM gateway
8 stars 6 forks source link

Building on Linux #1

Closed nzfarmer1 closed 9 years ago

nzfarmer1 commented 9 years ago

Any tips for building on Linux? thx.

ya-mouse commented 9 years ago

You need libyaml development package installed in the system.

ya-mouse commented 9 years ago

Currently I'm on heavy mbus-gw development. We're using it in pre-production to gate ext TCP and several RTU behind the number of serial ports with many Modbus-RTU devices on it. Other connection types untested: MBus-TCP <-> many mapped MBus-TCP and MBus-TCP <-> Moxa RealCOM proto. There are several known bugs: 1. double free somewhere 2. epoll loop with no event data (almost 100% CPU load) after inbound connection close/lost, may be some race.

  1. batch request (in one write) with several MBus packets will map to the real destination only for the first packet. *4. illegal instruction on embedded arm (due to very specific HW) I'm wokring to fix it in our embedded env. Spent a lot of time to build GDB-server.
nzfarmer1 commented 9 years ago

Ok I have it installed and running. Perhaps I can help track some bugs. I am looking to solve a similar. Can give me a heads up on running it and the config options? I want to run a slave on the serial side that will requests from multiple clients, then expose the registers via TCP.

nzfarmer1 commented 9 years ago

have tried running

Error message: Error Response address 94 is different from request address 1

ya-mouse commented 9 years ago

Config looks like this:

# Cache page TTL, cached answer will expired in 3 seconds. Every request for the same slave_id, function, address and length would get cached value
ttl: 3
# Number of TCP threads to serve incoming requests (optional)
workers: 4
# Default baud rate (optional)
baud: 115200
# List of endpoints
rtu:
    - type: Modbus-RTU
      device: /dev/ttyS1
      # Map of incoming SlaveId to the destination (real) SlaveId
      - map:
          - src: 113
            dst: 1
          - src: 114
            dst: 2
    - type: Modbus-RTU
      # Override default baud rate value
      baud: 9600
      device: /dev/ttyS2
      - map:
          - src: 10
            dst: 1
          - src: 12
            dst: 16

On the clients side you should configure Modbus-TCP with the following options: host: your gateway IP slave: choose appropriate from 113, 114, 10 or 12 to achive 1, 2 on ttyS1 and 1, 16 on ttyS2 respectively. function (supported 1, 2, 3 and 4), address and number of registers corresponding to the end point devices' specification.

ya-mouse commented 9 years ago

Sorry, just change two "#if 0" to "#if 1" in cfg.c or fetch latest commit from git.

ya-mouse commented 9 years ago

To debug you may run modbus-gw under strace:

strace -fF -etrace=read,write,sendto,recvfrom -x -tt -s 128 ./mbus-gw And do same trace for client side to examine Modbus packets later in case of failure. With the current version there is no known problem with fetching registers from several endpoints.

ya-mouse commented 9 years ago

Just found missed unlock() in cache_update() nearby line 142.

ya-mouse commented 9 years ago

…and fixed up the double free error. Should be much-much stable right now.

nzfarmer1 commented 9 years ago

Well done!

ya-mouse commented 9 years ago

Only a place with buffer overflow stands.

ya-mouse commented 9 years ago

Have fixed a lot of things including async requests for the same slave id on the same port. Tested with 8 ports, 100+ slaves and over 400+ standalone registers. On the ARM hardware it takes around 10 seconds (with 5 sec timeout/request) to read all requested registers at 19200. No buffer overflows, no double free anymore.

nzfarmer1 commented 9 years ago

That's great Anton, I must take another look.

Right now I am developing a Stream library that creates a transparent layer on top of XBees API layer. On the gateway side I can expose ports to the individual streams via UNIX sockets or character special devices then run ModbusRTU on the ARM processors .

With your software can I create a map and say
IP : Port : slaveId1 -> /dev/tty.xxx IP : Port : slaveId2 -> /dev/tty.zzz

ya-mouse commented 9 years ago

Yes, just add:

baud: 115200
rtu:
    - type: Modbus-RTU
      device: /dev/ttt.xxx
      # Map of incoming SlaveId to the destination (real) SlaveId
      - map:
          - src: slaveId1
            dst: slaveId1
    - type: Modbus-RTU
      device: /dev/ttt.yyy
      - map:
          - src: slaveId2
            dst: slaveId2
ya-mouse commented 9 years ago

In the next few days I will rewrite config part to JSON-format to work with already ported cJSON to NuttX.

nzfarmer1 commented 8 years ago

Hi Anton

Hope you are well.

Am just about to try out your updated code.

Here is the use case I have:

  1. We have a Modbus Master that connects via RTU.
  2. We want to map its requests to a TCP Slave listening on a host:port

I thought I could use socat to create a tunnel between the device and a tcp listener but it doesn't seem to work.

i.e.: socat -d -d -v tcp4-connect:127.0.0.1:1503 "/dev/tty.usbserial-A50285BI,raw,ispeed=115200,ospeed=115200"

Andrew

ya-mouse commented 8 years ago

Hi. Modbus RTU and Modbus TCP has a slightly different header. TCP includes TID (Transaction ID), magic number and additional packet length of the following data like in RTU answer. Sametime RTU has CRC (last two bytes). So, you cannot just pass through (wrap) data between Modbus RTU <-> Modbus TCP. My modbus-gateway designed to proxy Modbus TCP to Modbus RTU. gateway acts like a Modbus Master. What kind of Modbus Master do you have? Usually, Master is directly connected to the RS-485 network or so. If your Master is a software (you may configure system ports to connect to), then you need to build virtual serial device and pass it to the remote host. Please pay attention, that the remote side have to be a real serial device accepting Modbus RTU. You can try as a "man socat" stated:

socat PTY,link=$HOME/dev/vmodem0,rawer,wait-slave \ EXEC:'"ssh modemserver.us.org socat - /dev/ttyS0,nonblock,rawer"'

Then you'll have a local $HOME/dev/vmodem0 serial device passing data to the /dev/ttyS0 on modemserver.us.org.

nzfarmer1 commented 8 years ago

Thanks Anton

I battled for a night with this and am yet to succeed. This is where I got to, but am still only getting 1 byte transferred at a time and socket timeouts on the TCP slave.

The Rtu slave works ok, but I'd prefer TCP so I can share it and exchange data - i.e. expose it to another device.

socat -t5 -b8 -d -d -d -d -t15 "tcp4:127.0.0.1:1503,sndlowat=8,readbytes=8,ignoreeof" "file:/dev/tty.usbserial-A50285BI,raw,nonblock,ignoreeof,readbytes=8,waitlock=/tmp/usbserial-A50285BI.lo

Please consider the environment before printing this email.

Any information contained within this email is for the use of the recipient only and is sent in confidence. The information may not be copied, distributed or forwarded to any other parties. This information may not be used by any other person or organization. If you have received this in error, please notify us immediately by return mail and return the message with your notification.

If you would like to stop receiving correspondence from this email address, please confirm by return mail.

On 20/11/2015, at 12:42 am, Anton D. Kachalov notifications@github.com wrote:

Hi. Modbus RTU and Modbus TCP has a slightly different header. TCP includes TID (Transaction ID), magic number and additional packet length of the following data like in RTU answer. Sametime RTU has CRC (last two bytes). So, you cannot just pass through (wrap) data between Modbus RTU <-> Modbus TCP. My modbus-gateway designed to proxy Modbus TCP to Modbus RTU. gateway acts like a Modbus Master. What kind of Modbus Master do you have? Usually, Master is directly connected to the RS-485 network or so. If your Master is a software (you may configure system ports to connect to), then you need to build virtual serial device and pass it to the remote host. Please pay attention, that the remote side have to be a real serial device accepting Modbus RTU. You can try as a "man socat" stated:

socat PTY,link=$HOME/dev/vmodem0,rawer,wait-slave \ EXEC:'"ssh modemserver.us.org socat - /dev/ttyS0,nonblock,rawer"'

Then you'll have a local $HOME/dev/vmodem0 serial device passing data to the /dev/ttyS0 on modemserver.us.org.

— Reply to this email directly or view it on GitHub.

ya-mouse commented 8 years ago

Andrew, if you want to expose RTU device (serial) as the Modbus TCP, you may use my modbus-gateway. It is designed for. Just configure mbus.conf:

# Port speed
baud: 115200
# Cache TTL
ttl: 1
# Timeout for proxied requests
timeout: 1
# RTU config
rtu:
    - type: Modbus-RTU
      device: /dev/tty.usbserial-A50285BI
      # Map of incoming SlaveId to the destination (real) SlaveId
      - map:
          - src: 1
            dst: 1
          - src: 2
            dst: 2
          - src: 3
            dst: 3

And then run mbus-gw from the same directory. Then you'll able to multi-connect to the your RTU device via TCP. You may want to map SlaveID. For one port config just map one-to-one.

nzfarmer1 commented 8 years ago

Thanks Anton

I actually have two use cases. I have developed a reliable datagram protocol that allows me to send addressed messages of individual Xbee wireless nodes - yet appear as a dedicated serial stream to the end devices.

The end devices can then work in either Master or Slave mode. I also encapsulated the Stream library so that the Modbus master/slave on the nodes can use a Hardware Serial; Software Serial; or xStream (my protocol) Serial interface.

Your solition should help for the Slave mode but am not sure about when the end nodes become the master and needs to update a TCP slave.

Please consider the environment before printing this email.

Any information contained within this email is for the use of the recipient only and is sent in confidence. The information may not be copied, distributed or forwarded to any other parties. This information may not be used by any other person or organization. If you have received this in error, please notify us immediately by return mail and return the message with your notification.

If you would like to stop receiving correspondence from this email address, please confirm by return mail.

On 20/11/2015, at 9:30 am, Anton D. Kachalov notifications@github.com wrote:

Andrew, if you want to expose RTU device (serial) as the Modbus TCP, you may use my modbus-gateway. It is designed for. Just configure mbus.conf:

baud: 115200 rtu:

  • type: Modbus-RTU device: /dev/tty.usbserial-A50285BI

    Map of incoming SlaveId to the destination (real) SlaveId

    • map:
      • src: 1 dst: 1
      • src: 2 dst: 2 And then run mbus-gw from the same directory. Then you'll able to multi-connect to the your RTU device.

— Reply to this email directly or view it on GitHub.

nzfarmer1 commented 8 years ago

Ha

All I had to do was add a 6 byte header!

ya-mouse commented 8 years ago

Great! :)

nzfarmer1 commented 8 years ago

Hi Anton

Just want to check again as I might be able to do something with your code.

When you say "incoming SlaveId" do you mean that the src would be an RTU Master, and that your Gateway would then be a TCP Master, and that the dst would be a TCP Slave?

I can socat between my UNIX sockets and psuedo terminal, or change your code to read from a socket instead of a device.

And would it be possible to reverse this scenario and make src be incoming TCP Master and slave be the RTU?

Thanks

Andrew

Yes, just add:

baud: 115200 rtu:

  • type: Modbus-RTU device: /dev/ttt.xxx

    Map of incoming SlaveId to the destination (real) SlaveId

    • map:
      • src: slaveId1 dst: slaveId1
  • type: Modbus-RTU device: /dev/ttt.yyy
    • map:
      • src: slaveId2 dst: slaveId2 — Reply to this email directly or view it on GitHub.
ya-mouse commented 8 years ago

Hello. Let me clarify a few things. modbus-gate acts as Modbus Master and Slave at the same time. Slave mode is to listen for TCP-connections and serve requests for Modbus-TCP and pass it to the RTU (mostly) as a Master. From RTU side modbus-gate acts as a Master:

TCP-client <-> Modbus-TCP <-> Slave ]| GATE |[ Master <-> Modbus-RTU <-> Serial slaves

So, modbus-gate just pass TCP-requests to RTU slaves (serial) and convert an answers back to TCP format.

nzfarmer1 commented 8 years ago

Hi Anton

Thank you! Perhaps a README file in your repo would be good?

I ended up rolling my own yesterday. This interfaces is between either a ModbusTCPMaster and an RTU Slave exposes via a UNIX socket, or provides a TCP Listener for an RTU Slave.

It is nodejs, so the development time was fast, and the run time less so - but it seems fine for our purposes! See attached.

cheers

Andrew

Please consider the environment before printing this email.

Any information contained within this email is for the use of the recipient only and is sent in confidence. The information may not be copied, distributed or forwarded to any other parties. This information may not be used by any other person or organization. If you have received this in error, please notify us immediately by return mail and return the message with your notification.

If you would like to stop receiving correspondence from this email address, please confirm by return mail.

On 11/12/2015, at 3:35 am, Anton D. Kachalov notifications@github.com wrote:

Hello. Let me clarify a few things. modbus-gate acts as Modbus Master and Slave at the same time. Slave mode is to listen for TCP-connections and serve requests for Modbus-TCP and pass it to the RTU (mostly) as a Master. From RTU side modbus-gate acts as a Master:

TCP-client <-> Modbus-TCP <-> Slave ]| GATE |[ Master <-> Modbus-RTU <-> Serial slaves

— Reply to this email directly or view it on GitHub.

ya-mouse commented 8 years ago

Hi, Andrew.

README should be a good idea :) Good luck!