User Tools

Site Tools


Modbus Master for Ruby

Joachim Wuttke, FZ Jülich, 2006-13.

The modbus protocol

Basic references:

The serial protocols Modbus/ASCII and Modbus/RTU require checksum calculations. A Ruby driver has been published by Peter Heinrich (http://saphum.com/node/71/backlinks)

For new applications, there is hardly any reason not to use the simpler Modbus/TCP interface. The following code shows how to encapsulate/retrieve messages into/from “protocol data units”.

Modbus master in Ruby

### This is free software
### Placed in the public domain by Joachim Wuttke 2007
### auxiliary functions:
class String
    def to_int16
        return self[0]*256 + self[1]
    end
end
class Integer
    def to_bytes
        return (self >> 8).chr + (self & 0xFF).chr
    end
end
### class for communication with one modbus slave:
class Modbus
    def initialize ipaddr, port, slaveaddr
        @sock = TCPSocket.open ipaddr, port
        @@transaction_no = 0 # a unique number (on class level ?)
        @slave = slaveaddr.chr
    end
    ### modbus TCP encapsulation:
    def send_pdu pdu ## encapsulate protocol_data_unit and send it to slave
        @@transaction_no += 1
        msg  = @@transaction_no.to_bytes   # transaction number
        msg += "\000\000"                  # protocol identifier (always 0)
        msg += (pdu.size+1).to_bytes       # so many bytes will follow
        msg += @slave                      # slave address
        msg += pdu                         # protocol data unit
        @sock.write msg
    end
    def read_pdu     ## receive message from slave and extract pdu
        header = @sock.read 7    # read TCP header
        tin = header[0,2].to_int16
        raise "transaction number mismatch" unless tin == @@transaction_no
        len = header[4,2].to_int16 # length of remaining message
        pdu = @sock.read len-1   # read remaining message
        return pdu
    end
    def query pdu    ## send pdu to slave and receive response
        send_pdu pdu
        return read_pdu
    end
    ### modbus functions:
    def query_holding( addr, nreg ) ## query contents of holding registers
        pdu = query "\003" + addr.to_bytes + nreg.to_bytes
        return pdu[2..-1]
    end
    # ... and so on ...
end
### usage in main program:
# connect once:
modbus = Modbus.new "129.187.1.1", 502, 1
# interact with slave:
puts modbus.query_holding 12, 4 # print contents of registers 12..15