modbus

package module
v1.0.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 12, 2023 License: MIT Imports: 10 Imported by: 1

README

Go modbus stack

Description

This package is a go implementation of the modbus protocol. It aims to provide a simple-to-use, high-level API to interact with modbus devices using native Go types.

Both client and server components are available.

The client supports the following modes:

  • modbus RTU (serial, over both RS-232 and RS-485),
  • modbus TCP (a.k.a. MBAP),
  • modbus TCP over TLS (a.k.a. MBAPS or Modbus Security),
  • modbus TCP over UDP (a.k.a. MBAP over UDP),
  • modbus RTU over TCP (RTU tunneled in TCP for use with e.g. remote serial ports or cheap TCP to serial bridges),
  • modbus RTU over UDP (RTU tunneled in UDP).

Please note that UDP transports are not part of the Modbus specification. Some devices expect MBAP (modbus TCP) framing in UDP packets while others use RTU frames instead. The client support both so if unsure, try with both udp:// and rtuoverudp:// schemes.

The server supports:

  • modbus TCP (a.k.a. MBAP),
  • modbus TCP over TLS (a.k.a. MBAPS or Modbus Security).

A CLI client is available in cmd/modbus-cli.go and can be built with

$ go build -o modbus-cli cmd/modbus-cli.go
$ ./modbus-cli --help

Getting started

$ go get github.com/simonvetter/modbus

Using the client

import (
    "github.com/simonvetter/modbus"
)

func main() {
    var client  *modbus.ModbusClient
    var err      error

    // for a TCP endpoint
    // (see examples/tls_client.go for TLS usage and options)
    client, err = modbus.NewClient(&modbus.ClientConfiguration{
        URL:      "tcp://hostname-or-ip-address:502",
        Timeout:  1 * time.Second,
    })
    // note: use udp:// for modbus TCP over UDP

    // for an RTU (serial) device/bus
    client, err = modbus.NewClient(&modbus.ClientConfiguration{
        URL:      "rtu:///dev/ttyUSB0",
        Speed:    19200,                   // default
        DataBits: 8,                       // default, optional
        Parity:   modbus.PARITY_NONE,      // default, optional
        StopBits: 2,                       // default if no parity, optional
        Timeout:  300 * time.Millisecond,
    })

    // for an RTU over TCP device/bus (remote serial port or
    // simple TCP-to-serial bridge)
    client, err = modbus.NewClient(&modbus.ClientConfiguration{
        URL:      "rtuovertcp://hostname-or-ip-address:502",
        Speed:    19200,                   // serial link speed
        Timeout:  1 * time.Second,
    })
    // note: use rtuoverudp:// for modbus RTU over UDP

    if err != nil {
        // error out if client creation failed
    }

    // now that the client is created and configured, attempt to connect
    err = client.Open()
    if err != nil {
        // error out if we failed to connect/open the device
        // note: multiple Open() attempts can be made on the same client until
        // the connection succeeds (i.e. err == nil), calling the constructor again
        // is unnecessary.
        // likewise, a client can be opened and closed as many times as needed.
    }

    // read a single 16-bit holding register at address 100
    var reg16   uint16
    reg16, err  = client.ReadRegister(100, modbus.HOLDING_REGISTER)
    if err != nil {
      // error out
    } else {
      // use value
      fmt.Printf("value: %v", reg16)        // as unsigned integer
      fmt.Printf("value: %v", int16(reg16)) // as signed integer
    }

    // read 4 consecutive 16-bit input registers starting at address 100
    var reg16s  []uint16
    reg16s, err = client.ReadRegisters(100, 4, modbus.INPUT_REGISTER)

    // read the same 4 consecutive 16-bit input registers as 2 32-bit integers
    var reg32s  []uint32
    reg32s, err = client.ReadUint32s(100, 2, modbus.INPUT_REGISTER)

    // read the same 4 consecutive 16-bit registers as a single 64-bit integer
    var reg64   uint64
    reg64, err  = client.ReadUint64(100, modbus.INPUT_REGISTER)

    // read the same 4 consecutive 16-bit registers as a slice of bytes
    var regBs   []byte
    regBs, err  = client.ReadBytes(100, 8, modbus.INPUT_REGISTER)

    // by default, 16-bit integers are decoded as big-endian and 32/64-bit values as
    // big-endian with the high word first.
    // change the byte/word ordering of subsequent requests to little endian, with
    // the low word first (note that the second argument only affects 32/64-bit values)
    client.SetEncoding(modbus.LITTLE_ENDIAN, modbus.LOW_WORD_FIRST)

    // read the same 4 consecutive 16-bit input registers as 2 32-bit floats
    var fl32s   []float32
    fl32s, err  = client.ReadFloat32s(100, 2, modbus.INPUT_REGISTER)

    // write -200 to 16-bit (holding) register 100, as a signed integer
    var s int16 = -200
    err         = client.WriteRegister(100, uint16(s))

    // Switch to unit ID (a.k.a. slave ID) #4
    client.SetUnitId(4)

    // write 3 floats to registers 100 to 105
    err         = client.WriteFloat32s(100, []float32{
        3.14,
        1.1,
        -783.22,
    })

    // write 0x0102030405060708 to 16-bit (holding) registers 10 through 13
    // (8 bytes i.e. 4 consecutive modbus registers)
    err         = client.WriteBytes(10, []byte{
        0x01, 0x02, 0x03, 0x04,
        0x05, 0x06, 0x07, 0x08,
    })

    // close the TCP connection/serial port
    client.Close()
}

Using the server component

See:

Supported function codes, golang object types and endianness/word ordering

Function codes:

  • Read coils (0x01)
  • Read discrete inputs (0x02)
  • Read holding registers (0x03)
  • Read input registers (0x04)
  • Write single coil (0x05)
  • Write single register (0x06)
  • Write multiple coils (0x0f)
  • Write multiple registers (0x10)

Go object types:

  • Booleans (coils and discrete inputs)
  • Bytes (input and holding registers)
  • Signed/Unisgned 16-bit integers (input and holding registers)
  • Signed/Unsigned 32-bit integers (input and holding registers)
  • 32-bit floating point numbers (input and holding registers)
  • Signed/Unsigned 64-bit integers (input and holding registers)
  • 64-bit floating point numbers (input and holding registers)

Byte encoding/endianness/word ordering:

  • Little and Big endian for byte slices and 16-bit integers
  • Little and Big endian, with and without word swap for 32 and 64-bit integers and floating point numbers.

Logging

Both client and server objects will log to stdout by default. This behavior can be overriden by passing a log.Logger object through the Logger property of ClientConfiguration/ServerConfiguration.

TODO (in no particular order)

  • Add RTU (serial) support to the server
  • Add more tests
  • Add diagnostics register support
  • Add fifo register support
  • Add file register support

Dependencies

License

MIT.

Documentation

Index

Constants

View Source
const (
	PARITY_NONE uint = 0
	PARITY_EVEN uint = 1
	PARITY_ODD  uint = 2

	HOLDING_REGISTER RegType = 0
	INPUT_REGISTER   RegType = 1

	// endianness of 16-bit registers
	BIG_ENDIAN    Endianness = 1
	LITTLE_ENDIAN Endianness = 2

	// word order of 32-bit registers
	HIGH_WORD_FIRST WordOrder = 1
	LOW_WORD_FIRST  WordOrder = 2
)

Variables

This section is empty.

Functions

This section is empty.

Types

type ClientConfiguration

type ClientConfiguration struct {
	// URL sets the client mode and target location in the form
	// <mode>://<serial device or host:port> e.g. tcp://plc:502
	URL string
	// Speed sets the serial link speed (in bps, rtu only)
	Speed uint
	// DataBits sets the number of bits per serial character (rtu only)
	DataBits uint
	// Parity sets the serial link parity mode (rtu only)
	Parity uint
	// StopBits sets the number of serial stop bits (rtu only)
	StopBits uint
	// Timeout sets the request timeout value
	Timeout time.Duration
	// Logger provides a custom sink for log messages.
	// If nil, messages will be written to stdout.
	Logger *log.Logger
}

Modbus client configuration object.

type Endianness

type Endianness uint

type Error

type Error string
const (

	// errors
	ErrConfigurationError      Error = "configuration error"
	ErrRequestTimedOut         Error = "request timed out"
	ErrIllegalFunction         Error = "illegal function"
	ErrIllegalDataAddress      Error = "illegal data address"
	ErrIllegalDataValue        Error = "illegal data value"
	ErrServerDeviceFailure     Error = "server device failure"
	ErrAcknowledge             Error = "request acknowledged"
	ErrServerDeviceBusy        Error = "server device busy"
	ErrMemoryParityError       Error = "memory parity error"
	ErrGWPathUnavailable       Error = "gateway path unavailable"
	ErrGWTargetFailedToRespond Error = "gateway target device failed to respond"
	ErrBadCRC                  Error = "bad crc"
	ErrShortFrame              Error = "short frame"
	ErrProtocolError           Error = "protocol error"
	ErrBadUnitId               Error = "bad unit id"
	ErrBadTransactionId        Error = "bad transaction id"
	ErrUnknownProtocolId       Error = "unknown protocol identifier"
	ErrUnexpectedParameters    Error = "unexpected parameters"
)

func (Error) Error

func (me Error) Error() (s string)

Error implements the error interface.

type ModbusClient

type ModbusClient struct {
	// contains filtered or unexported fields
}

Modbus client object.

func NewClient

func NewClient(conf *ClientConfiguration) (mc *ModbusClient, err error)

NewClient creates, configures and returns a modbus client object.

func (*ModbusClient) Close

func (mc *ModbusClient) Close() (err error)

Closes the underlying transport.

func (*ModbusClient) Open

func (mc *ModbusClient) Open() (err error)

Opens the underlying transport (network socket or serial line).

func (*ModbusClient) ReadBytes

func (mc *ModbusClient) ReadBytes(addr uint16, quantity uint16, regType RegType) (values []byte, err error)

Reads one or multiple 16-bit registers (function code 03 or 04) as bytes. A per-register byteswap is performed if endianness is set to LITTLE_ENDIAN.

func (*ModbusClient) ReadCoil

func (mc *ModbusClient) ReadCoil(addr uint16) (value bool, err error)

Reads a single coil (function code 01).

func (*ModbusClient) ReadCoils

func (mc *ModbusClient) ReadCoils(addr uint16, quantity uint16) (values []bool, err error)

Reads multiple coils (function code 01).

func (*ModbusClient) ReadDiscreteInput

func (mc *ModbusClient) ReadDiscreteInput(addr uint16) (value bool, err error)

Reads a single discrete input (function code 02).

func (*ModbusClient) ReadDiscreteInputs

func (mc *ModbusClient) ReadDiscreteInputs(addr uint16, quantity uint16) (values []bool, err error)

Reads multiple discrete inputs (function code 02).

func (*ModbusClient) ReadFloat32

func (mc *ModbusClient) ReadFloat32(addr uint16, regType RegType) (value float32, err error)

Reads a single 32-bit float register.

func (*ModbusClient) ReadFloat32s

func (mc *ModbusClient) ReadFloat32s(addr uint16, quantity uint16, regType RegType) (values []float32, err error)

Reads multiple 32-bit float registers.

func (*ModbusClient) ReadFloat64

func (mc *ModbusClient) ReadFloat64(addr uint16, regType RegType) (value float64, err error)

Reads a single 64-bit float register.

func (*ModbusClient) ReadFloat64s

func (mc *ModbusClient) ReadFloat64s(addr uint16, quantity uint16, regType RegType) (values []float64, err error)

Reads multiple 64-bit float registers.

func (*ModbusClient) ReadRawBytes

func (mc *ModbusClient) ReadRawBytes(addr uint16, quantity uint16, regType RegType) (values []byte, err error)

Reads one or multiple 16-bit registers (function code 03 or 04) as bytes. No byte or word reordering is performed: bytes are returned exactly as they come off the wire, allowing the caller to handle encoding/endianness/word order manually.

func (*ModbusClient) ReadRegister

func (mc *ModbusClient) ReadRegister(addr uint16, regType RegType) (value uint16, err error)

Reads a single 16-bit register (function code 03 or 04).

func (*ModbusClient) ReadRegisters

func (mc *ModbusClient) ReadRegisters(addr uint16, quantity uint16, regType RegType) (values []uint16, err error)

Reads multiple 16-bit registers (function code 03 or 04).

func (*ModbusClient) ReadUint32

func (mc *ModbusClient) ReadUint32(addr uint16, regType RegType) (value uint32, err error)

Reads a single 32-bit register.

func (*ModbusClient) ReadUint32s

func (mc *ModbusClient) ReadUint32s(addr uint16, quantity uint16, regType RegType) (values []uint32, err error)

Reads multiple 32-bit registers.

func (*ModbusClient) ReadUint64

func (mc *ModbusClient) ReadUint64(addr uint16, regType RegType) (value uint64, err error)

Reads a single 64-bit register.

func (*ModbusClient) ReadUint64s

func (mc *ModbusClient) ReadUint64s(addr uint16, quantity uint16, regType RegType) (values []uint64, err error)

Reads multiple 64-bit registers.

func (*ModbusClient) SetEncoding

func (mc *ModbusClient) SetEncoding(endianness Endianness, wordOrder WordOrder) (err error)

Sets the encoding (endianness and word ordering) of subsequent requests.

func (*ModbusClient) SetUnitId

func (mc *ModbusClient) SetUnitId(id uint8) (err error)

Sets the unit id of subsequent requests.

func (*ModbusClient) WriteBytes

func (mc *ModbusClient) WriteBytes(addr uint16, values []byte) (err error)

Writes the given slice of bytes to 16-bit registers starting at addr. A per-register byteswap is performed if endianness is set to LITTLE_ENDIAN. Odd byte quantities are padded with a null byte to fall on 16-bit register boundaries.

func (*ModbusClient) WriteCoil

func (mc *ModbusClient) WriteCoil(addr uint16, value bool) (err error)

Writes a single coil (function code 05)

func (*ModbusClient) WriteCoils

func (mc *ModbusClient) WriteCoils(addr uint16, values []bool) (err error)

Writes multiple coils (function code 15)

func (*ModbusClient) WriteFloat32

func (mc *ModbusClient) WriteFloat32(addr uint16, value float32) (err error)

Writes a single 32-bit float register.

func (*ModbusClient) WriteFloat32s

func (mc *ModbusClient) WriteFloat32s(addr uint16, values []float32) (err error)

Writes multiple 32-bit float registers.

func (*ModbusClient) WriteFloat64

func (mc *ModbusClient) WriteFloat64(addr uint16, value float64) (err error)

Writes a single 64-bit float register.

func (*ModbusClient) WriteFloat64s

func (mc *ModbusClient) WriteFloat64s(addr uint16, values []float64) (err error)

Writes multiple 64-bit float registers.

func (*ModbusClient) WriteRawBytes

func (mc *ModbusClient) WriteRawBytes(addr uint16, values []byte) (err error)

Writes the given slice of bytes to 16-bit registers starting at addr. No byte or word reordering is performed: bytes are pushed to the wire as-is, allowing the caller to handle encoding/endianness/word order manually. Odd byte quantities are padded with a null byte to fall on 16-bit register boundaries.

func (*ModbusClient) WriteRegister

func (mc *ModbusClient) WriteRegister(addr uint16, value uint16) (err error)

Writes a single 16-bit register (function code 06).

func (*ModbusClient) WriteRegisters

func (mc *ModbusClient) WriteRegisters(addr uint16, values []uint16) (err error)

Writes multiple 16-bit registers (function code 16).

func (*ModbusClient) WriteUint32

func (mc *ModbusClient) WriteUint32(addr uint16, value uint32) (err error)

Writes a single 32-bit register.

func (*ModbusClient) WriteUint32s

func (mc *ModbusClient) WriteUint32s(addr uint16, values []uint32) (err error)

Writes multiple 32-bit registers.

func (*ModbusClient) WriteUint64

func (mc *ModbusClient) WriteUint64(addr uint16, value uint64) (err error)

Writes a single 64-bit register.

func (*ModbusClient) WriteUint64s

func (mc *ModbusClient) WriteUint64s(addr uint16, values []uint64) (err error)

Writes multiple 64-bit registers.

type RegType

type RegType uint

type WordOrder

type WordOrder uint

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL