modbus

package
v0.2.0 Latest Latest
Warning

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

Go to latest
Published: May 31, 2022 License: Apache-2.0 Imports: 12 Imported by: 0

README

Simple IoT Modbus

This Simple IoT modbus packet is a package that implements both Modbus client and server functionality. This is a work in progress and currently only supports Modbus RTU, but can easily be extended for TCP and ASCII.

See this test for an example of how to use this library. Substitute the wire simulator with real serial ports. There are also standalone client and server examples.

Why?

  • really want to be able to pass in my own io.ReadWriter into these libs. New and better serial libs are continually coming out, and it seems that hardcoding serial operations in the modbus lib makes this brittle.
  • allows use of https://pkg.go.dev/github.com/simpleiot/simpleiot/respreader to do packet framing. This may not be the most efficient, but is super easy and eliminates the need to parse packets as they come in.
  • passing in io.ReadWriter allows us to easily unit test end to end communication (see end-to-end test above)
  • library in constructed in a modular fashion from lowest units (PDU) on up, so it is easy to test
  • not satisfied with the mbserver way of hooking in register operations. Seems over complicated, and unfinished.
  • want one library for both client/server. This is necessary to test everything, and there is no big reason not to do this.
  • need a library that is maintained, accepts PRs, etc.
  • could not find any good tools for testing modbus (client or server). Most are old apps for windows (I use Linux) and are difficult to use. After messing around too long in a windows VM, I decided its time for a decent command line app that does this. Seems like there should be a simple command line tool that can be made to do this and run on any OS (Go is perfect for building this type of thing).

Documentation

Overview

Package modbus contains modbus RTU/TCP client/server code.

Index

Constants

View Source
const (
	WriteCoilValueOn  uint16 = 0xff00
	WriteCoilValueOff uint16 = 0
)

define valid values for write coil

Variables

View Source
var ErrCRC = errors.New("CRC error")

ErrCRC is returned if a crc check fails

View Source
var ErrNotEnoughData = errors.New("Not enough data to calculate CRC")

ErrNotEnoughData is returned if not enough data

Functions

func CheckRtuCrc added in v0.0.2

func CheckRtuCrc(packet []byte) error

CheckRtuCrc returns error if CRC fails

func DecodeASCIIByte

func DecodeASCIIByte(data []byte) (byte, []byte, error)

DecodeASCIIByte converts type ascii hex bytes to a binary byte

func DecodeASCIIByteEnd

func DecodeASCIIByteEnd(data []byte) (byte, []byte, error)

DecodeASCIIByteEnd converts type ascii hex bytes to a binary byte. This function takes from the end of the slice

func Float32ToRegs added in v0.0.16

func Float32ToRegs(in []float32) []uint16

Float32ToRegs converts float32 values to modbus regs

func HexDump added in v0.0.2

func HexDump(data []byte) string

HexDump provides a string of bytes in hex format

func Int32ToRegs added in v0.0.16

func Int32ToRegs(in []int32) []uint16

Int32ToRegs converts int32 values to modbus regs

func PutUint16Array added in v0.0.2

func PutUint16Array(value ...uint16) []byte

PutUint16Array creates a sequence of uint16 data.

func RegsToFloat32 added in v0.0.5

func RegsToFloat32(in []uint16) []float32

RegsToFloat32 converts modbus regs to float32 values

func RegsToFloat32SwapWords added in v0.0.45

func RegsToFloat32SwapWords(in []uint16) []float32

RegsToFloat32SwapWords converts modbus regs to float32 values

func RegsToInt16 added in v0.0.5

func RegsToInt16(in []uint16) []int16

RegsToInt16 converts modbus regs to int16 values

func RegsToInt32 added in v0.0.5

func RegsToInt32(in []uint16) []int32

RegsToInt32 converts modbus regs to int32 values

func RegsToInt32SwapWords added in v0.0.45

func RegsToInt32SwapWords(in []uint16) []int32

RegsToInt32SwapWords converts modbus regs to int32 values

func RegsToUint32 added in v0.0.5

func RegsToUint32(in []uint16) []uint32

RegsToUint32 converts modbus regs to uint32 values

func RegsToUint32SwapWords added in v0.0.45

func RegsToUint32SwapWords(in []uint16) []uint32

RegsToUint32SwapWords converts modbus regs to uint32 values

func RtuCrc added in v0.0.2

func RtuCrc(buf []byte) uint16

RtuCrc calculates CRC for a Modbus RTU packet

func Uint16Array added in v0.0.2

func Uint16Array(data []byte) []uint16

Uint16Array unpacks 16 bit data values from a buffer (in big endian format)

func Uint32ToRegs added in v0.0.16

func Uint32ToRegs(in []uint32) []uint16

Uint32ToRegs converts uint32 values to modbus regs

Types

type ASCIIADU added in v0.0.2

type ASCIIADU struct {
	Address      byte
	FunctionCode FunctionCode
	Data         []byte
	LRC          byte
	End          []byte // should be "\r\n"
}

ASCIIADU is a modbus protocol data unit

func DecodeASCIIPDU

func DecodeASCIIPDU(data []byte) (ret ASCIIADU, err error)

DecodeASCIIPDU decodes a ASCII modbus packet

func (*ASCIIADU) CheckLRC added in v0.0.2

func (adu *ASCIIADU) CheckLRC() bool

CheckLRC verifies the LRC is valid

func (*ASCIIADU) DecodeFunctionData added in v0.0.2

func (adu *ASCIIADU) DecodeFunctionData() (ret interface{}, err error)

DecodeFunctionData extracts the function data from the PDU

type Client added in v0.0.2

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

Client defines a Modbus client (master)

func NewClient added in v0.0.2

func NewClient(transport Transport, debug int) *Client

NewClient is used to create a new modbus client port must return an entire packet for each Read(). github.com/simpleiot/simpleiot/respreader is a good way to do this.

func (*Client) Close added in v0.0.20

func (c *Client) Close() error

Close closes the client transport

func (*Client) ReadCoils added in v0.0.2

func (c *Client) ReadCoils(id byte, coil, count uint16) ([]bool, error)

ReadCoils is used to read modbus coils

func (*Client) ReadDiscreteInputs added in v0.0.16

func (c *Client) ReadDiscreteInputs(id byte, input, count uint16) ([]bool, error)

ReadDiscreteInputs is used to read modbus discrete inputs

func (*Client) ReadHoldingRegs added in v0.0.4

func (c *Client) ReadHoldingRegs(id byte, reg, count uint16) ([]uint16, error)

ReadHoldingRegs is used to read modbus coils

func (*Client) ReadInputRegs added in v0.0.16

func (c *Client) ReadInputRegs(id byte, reg, count uint16) ([]uint16, error)

ReadInputRegs is used to read modbus coils

func (*Client) SetDebugLevel added in v0.0.6

func (c *Client) SetDebugLevel(debug int)

SetDebugLevel allows you to change debug level on the fly

func (*Client) WriteSingleCoil added in v0.0.16

func (c *Client) WriteSingleCoil(id byte, coil uint16, v bool) error

WriteSingleCoil is used to read modbus coils

func (*Client) WriteSingleReg added in v0.0.16

func (c *Client) WriteSingleReg(id byte, reg, value uint16) error

WriteSingleReg writes to a single holding register

type ExceptionCode added in v0.0.16

type ExceptionCode byte

ExceptionCode represents a modbus exception code

const (
	ExcIllegalFunction              ExceptionCode = 1
	ExcIllegalAddress               ExceptionCode = 2
	ExcIllegalValue                 ExceptionCode = 3
	ExcServerDeviceFailure          ExceptionCode = 4
	ExcAcknowledge                  ExceptionCode = 5
	ExcServerDeviceBusy             ExceptionCode = 6
	ExcMemoryParityError            ExceptionCode = 8
	ExcGatewayPathUnavilable        ExceptionCode = 0x0a
	ExcGatewayTargetFailedToRespond ExceptionCode = 0x0b
)

Defined valid exception codes

func (ExceptionCode) Error added in v0.0.16

func (e ExceptionCode) Error() string

type FuncReadHoldingRegisterResponse

type FuncReadHoldingRegisterResponse struct {
	FunctionCode FunctionCode
	RegCount     byte
	RegValues    []uint16
}

FuncReadHoldingRegisterResponse response to read holding reg

type FuncReadHoldingRegistersRequest

type FuncReadHoldingRegistersRequest struct {
	FunctionCode    FunctionCode
	StartingAddress uint16
	RegCount        uint16
}

FuncReadHoldingRegistersRequest represents the request to read holding reg

type FuncWriteMultipleRegisterRequest

type FuncWriteMultipleRegisterRequest struct {
	FunctionCode    FunctionCode
	StartingAddress uint16
	RegCount        uint16
	ByteCount       byte
	RegValues       []uint16
}

FuncWriteMultipleRegisterRequest represents the request to write multiple regs

type FunctionCode

type FunctionCode byte

FunctionCode represents a modbus function code

const (
	// Bit access
	FuncCodeReadDiscreteInputs FunctionCode = 2
	FuncCodeReadCoils          FunctionCode = 1
	FuncCodeWriteSingleCoil    FunctionCode = 5
	FuncCodeWriteMultipleCoils FunctionCode = 15

	// 16-bit access
	FuncCodeReadInputRegisters         FunctionCode = 4
	FuncCodeReadHoldingRegisters       FunctionCode = 3
	FuncCodeWriteSingleRegister        FunctionCode = 6
	FuncCodeWriteMultipleRegisters     FunctionCode = 16
	FuncCodeReadWriteMultipleRegisters FunctionCode = 23
	FuncCodeMaskWriteRegister          FunctionCode = 22
	FuncCodeReadFIFOQueue              FunctionCode = 24
)

Defined valid function codes

type IoSim added in v0.0.2

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

IoSim simulates a serial port and provides a io.ReadWriter for both ends

func NewIoSim added in v0.0.2

func NewIoSim(debug bool) *IoSim

NewIoSim creates a new IO simulator

func (*IoSim) GetA added in v0.0.2

func (is *IoSim) GetA() io.ReadWriteCloser

GetA returns the A port from a IoSim

func (*IoSim) GetB added in v0.0.2

func (is *IoSim) GetB() io.ReadWriteCloser

GetB returns the B port from a IoSim

type IoSimPort added in v0.0.2

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

IoSimPort one end of a IoSim

func NewIoSimPort added in v0.0.2

func NewIoSimPort(name string, tx chan byte, rx chan byte, debug bool) *IoSimPort

NewIoSimPort returns a new port of an IoSim

func (*IoSimPort) Close added in v0.0.20

func (isp *IoSimPort) Close() error

Close port

func (*IoSimPort) Read added in v0.0.2

func (isp *IoSimPort) Read(data []byte) (int, error)

Read reads data from IoSimPort

func (*IoSimPort) Write added in v0.0.2

func (isp *IoSimPort) Write(data []byte) (int, error)

Write reads data from IoSimPort

type Modbus

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

Modbus is a type that implements modbus ascii communication. Currently, only "sniffing" a network is implemented

func NewModbus

func NewModbus(port io.ReadWriter) *Modbus

NewModbus creates a new Modbus

func (*Modbus) Read

func (m *Modbus) Read() ([]byte, error)

Read returns an ASCII modbus packet. Blocks until a full packet is received or error

type PDU

type PDU struct {
	FunctionCode FunctionCode
	Data         []byte
}

PDU for Modbus packets

func ReadCoils added in v0.0.2

func ReadCoils(address uint16, count uint16) PDU

ReadCoils creates PDU to read coils

func ReadDiscreteInputs added in v0.0.16

func ReadDiscreteInputs(address uint16, count uint16) PDU

ReadDiscreteInputs creates PDU to read descrete inputs

func ReadHoldingRegs added in v0.0.2

func ReadHoldingRegs(address uint16, count uint16) PDU

ReadHoldingRegs creates a PDU to read a holding regs

func ReadInputRegs added in v0.0.16

func ReadInputRegs(address uint16, count uint16) PDU

ReadInputRegs creates a PDU to read input regs

func WriteSingleCoil added in v0.0.16

func WriteSingleCoil(address uint16, v bool) PDU

WriteSingleCoil creates PDU to read coils

func WriteSingleReg added in v0.0.16

func WriteSingleReg(address, value uint16) PDU

WriteSingleReg creates PDU to read coils

func (*PDU) ProcessRequest added in v0.0.2

func (p *PDU) ProcessRequest(regs RegProvider) (bool, PDU, error)

ProcessRequest a modbus request. Registers are read and written through the server interface argument. This function returns any register changes, the modbus respose, and any errors

func (*PDU) RespReadBits added in v0.0.2

func (p *PDU) RespReadBits() ([]bool, error)

RespReadBits reads coils and discrete inputs from a response PDU.

func (*PDU) RespReadRegs added in v0.0.4

func (p *PDU) RespReadRegs() ([]uint16, error)

RespReadRegs reads register values from a response PDU.

func (PDU) String added in v0.0.2

func (p PDU) String() string

type RTU added in v0.0.20

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

RTU defines an RTU connection

func NewRTU added in v0.0.20

func NewRTU(port io.ReadWriteCloser) *RTU

NewRTU creates a new RTU transport

func (*RTU) Close added in v0.0.20

func (r *RTU) Close() error

Close closes the serial port

func (*RTU) Decode added in v0.0.20

func (r *RTU) Decode(packet []byte) (byte, PDU, error)

Decode decodes a RTU packet

func (*RTU) Encode added in v0.0.20

func (r *RTU) Encode(id byte, pdu PDU) ([]byte, error)

Encode encodes a RTU packet

func (*RTU) Read added in v0.0.20

func (r *RTU) Read(p []byte) (int, error)

func (*RTU) Type added in v0.0.20

func (r *RTU) Type() TransportType

Type returns TransportType

func (*RTU) Write added in v0.0.20

func (r *RTU) Write(p []byte) (int, error)

type Reg added in v0.0.2

type Reg struct {
	Address uint16
	Value   uint16
}

Reg defines a Modbus register

type RegProvider added in v0.0.16

type RegProvider interface {
	ReadReg(address int) (uint16, error)
	WriteReg(address int, value uint16) error
	ReadInputReg(address int) (uint16, error)
	ReadDiscreteInput(num int) (bool, error)
	ReadCoil(num int) (bool, error)
	WriteCoil(num int, value bool) error
}

RegProvider is the interface for a register provider. Regs is the canonical implementation.

type Regs added in v0.0.2

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

Regs represents all registers in a modbus device and provides functions to read/write 16-bit and bit values. This register module assumes all register types map into one address space as described in the modbus spec (http://www.modbus.org/docs/Modbus_Application_Protocol_V1_1b3.pdf) on page 6 and 7. All operations on Regs are threadsafe and protected by a mutex.

func (*Regs) AddCoil added in v0.0.2

func (r *Regs) AddCoil(num int)

AddCoil is used to add a discrete io to the register map. Note coils are aliased on top of other registers, so coil 20 would be register 1 bit 4 (16 + 4 = 20).

func (*Regs) AddReg added in v0.0.2

func (r *Regs) AddReg(address int, count int)

AddReg is used to add a modbus register to the server. the callback function is called when the reg is updated The register can be updated by word or bit operations.

func (*Regs) ReadCoil added in v0.0.2

func (r *Regs) ReadCoil(num int) (bool, error)

ReadCoil gets a coil value

func (*Regs) ReadDiscreteInput added in v0.0.16

func (r *Regs) ReadDiscreteInput(num int) (bool, error)

ReadDiscreteInput gets a discrete input

func (*Regs) ReadInputReg added in v0.0.16

func (r *Regs) ReadInputReg(address int) (uint16, error)

ReadInputReg is used to read a modbus input register

func (*Regs) ReadReg added in v0.0.2

func (r *Regs) ReadReg(address int) (uint16, error)

ReadReg is used to read a modbus holding register

func (*Regs) ReadRegFloat32 added in v0.0.16

func (r *Regs) ReadRegFloat32(address int) (float32, error)

ReadRegFloat32 reads a float32 from regs

func (*Regs) ReadRegInt32 added in v0.0.16

func (r *Regs) ReadRegInt32(address int) (int32, error)

ReadRegInt32 reads a int32 from regs

func (*Regs) ReadRegUint32 added in v0.0.16

func (r *Regs) ReadRegUint32(address int) (uint32, error)

ReadRegUint32 reads a uint32 from regs

func (*Regs) WriteCoil added in v0.0.2

func (r *Regs) WriteCoil(num int, value bool) error

WriteCoil writes a coil value

func (*Regs) WriteReg added in v0.0.2

func (r *Regs) WriteReg(address int, value uint16) error

WriteReg is used to write a modbus register

func (*Regs) WriteRegFloat32 added in v0.0.16

func (r *Regs) WriteRegFloat32(address int, value float32) error

WriteRegFloat32 writes a float32 to regs

func (*Regs) WriteRegInt32 added in v0.0.16

func (r *Regs) WriteRegInt32(address int, value int32) error

WriteRegInt32 writes a int32 to regs

func (*Regs) WriteRegUint32 added in v0.0.16

func (r *Regs) WriteRegUint32(address int, value uint32) error

WriteRegUint32 writes a uint32 to regs

type RtuADU added in v0.0.2

type RtuADU struct {
	PDU
	Address byte
	CRC     uint16
}

RtuADU defines an ADU for RTU packets

type Server added in v0.0.2

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

Server defines a server (slave) Current Server only supports Modbus RTU, but could be expanded to do ASCII and TCP.

func NewServer added in v0.0.2

func NewServer(id byte, transport Transport, regs *Regs, debug int) *Server

NewServer creates a new server instance port must return an entire packet for each Read(). github.com/simpleiot/simpleiot/respreader is a good way to do this.

func (*Server) Close added in v0.0.16

func (s *Server) Close() error

Close stops the listening channel

func (*Server) Listen added in v0.0.2

func (s *Server) Listen(errorCallback func(error),
	changesCallback func(), done func())

Listen starts the server and listens for modbus requests this function does not return unless an error occurs The listen function supports various debug levels: 1 - dump packets 9 - dump raw data

type TCP added in v0.0.20

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

TCP defines an TCP connection

func NewTCP added in v0.0.20

func NewTCP(sock net.Conn, timeout time.Duration, clientServer TransportClientServer) *TCP

NewTCP creates a new TCP transport

func (*TCP) Close added in v0.0.20

func (t *TCP) Close() error

Close connection

func (*TCP) Decode added in v0.0.20

func (t *TCP) Decode(packet []byte) (byte, PDU, error)

Decode decodes a TCP packet

func (*TCP) Encode added in v0.0.20

func (t *TCP) Encode(id byte, pdu PDU) ([]byte, error)

Encode encodes a TCP packet

func (*TCP) Read added in v0.0.20

func (t *TCP) Read(p []byte) (int, error)

func (*TCP) Type added in v0.0.20

func (t *TCP) Type() TransportType

Type returns TransportType

func (*TCP) Write added in v0.0.20

func (t *TCP) Write(p []byte) (int, error)

type TCPADU added in v0.0.20

type TCPADU struct {
	PDU
	Address byte
}

TCPADU defines an ADU for TCP packets

type TCPServer added in v0.0.20

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

TCPServer listens for new connections and then starts a modbus listener on the port.

func NewTCPServer added in v0.0.20

func NewTCPServer(id, maxClients int, port string, regs *Regs, debug int) (*TCPServer, error)

NewTCPServer starts a new TCP modbus server

func (*TCPServer) Close added in v0.0.20

func (ts *TCPServer) Close() error

Close stops the server and closes all connections

func (*TCPServer) Listen added in v0.0.20

func (ts *TCPServer) Listen(errorCallback func(error),
	changesCallback func(), done func())

Listen starts the server and listens for modbus requests this function does not return unless an error occurs The listen function supports various debug levels: 1 - dump packets 9 - dump raw data

type Transport added in v0.0.20

type Transport interface {
	io.ReadWriteCloser
	Encode(byte, PDU) ([]byte, error)
	Decode([]byte) (byte, PDU, error)
	Type() TransportType
}

Transport defines an interface that various transports (RTU, TCP, etc) implement and can be passed to clients/servers

type TransportClientServer added in v0.0.20

type TransportClientServer string

TransportClientServer defines if transport is being used for a client or server

const (
	TransportClient TransportClientServer = "client"
	TransportServer                       = "server"
)

define valid client server types

type TransportType added in v0.0.20

type TransportType string

TransportType defines a modbus transport type

const (
	TransportTypeTCP TransportType = "tcp"
	TransportTypeRTU               = "rtu"
)

define valid transport types

Jump to

Keyboard shortcuts

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