Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions api.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ type Client interface {
// (1 to 123 registers) in a remote device and returns quantity of
// registers.
WriteMultipleRegisters(address, quantity uint16, value []byte) (results []byte, err error)
// WriteFileRecord writes a block of a Record (1 to 251 registers)
// in a remote device and returns quantity of registers written.
WriteFileRecord(address, quantity uint16, SubReqSize uint16, RecUpdateFileNum []byte, value []byte) (results []byte, err error)
// ReadWriteMultipleRegisters performs a combination of one read
// operation and one write operation. It returns read registers value.
ReadWriteMultipleRegisters(readAddress, readQuantity, writeAddress, writeQuantity uint16, value []byte) (results []byte, err error)
Expand Down
266 changes: 189 additions & 77 deletions client.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,16 @@ func NewClient2(packager Packager, transporter Transporter) Client {
}

// Request:
// Function code : 1 byte (0x01)
// Starting address : 2 bytes
// Quantity of coils : 2 bytes
//
// Function code : 1 byte (0x01)
// Starting address : 2 bytes
// Quantity of coils : 2 bytes
//
// Response:
// Function code : 1 byte (0x01)
// Byte count : 1 byte
// Coil status : N* bytes (=N or N+1)
//
// Function code : 1 byte (0x01)
// Byte count : 1 byte
// Coil status : N* bytes (=N or N+1)
func (mb *client) ReadCoils(address, quantity uint16) (results []byte, err error) {
if quantity < 1 || quantity > 2000 {
err = fmt.Errorf("modbus: quantity '%v' must be between '%v' and '%v',", quantity, 1, 2000)
Expand All @@ -57,18 +60,25 @@ func (mb *client) ReadCoils(address, quantity uint16) (results []byte, err error
err = fmt.Errorf("modbus: response data size '%v' does not match count '%v'", length, count)
return
}
if count != int(quantity) {
err = fmt.Errorf("modbus: response count '%v' does not match request quantity '%v'", count, quantity)
return
}
results = response.Data[1:]
return
}

// Request:
// Function code : 1 byte (0x02)
// Starting address : 2 bytes
// Quantity of inputs : 2 bytes
//
// Function code : 1 byte (0x02)
// Starting address : 2 bytes
// Quantity of inputs : 2 bytes
//
// Response:
// Function code : 1 byte (0x02)
// Byte count : 1 byte
// Input status : N* bytes (=N or N+1)
//
// Function code : 1 byte (0x02)
// Byte count : 1 byte
// Input status : N* bytes (=N or N+1)
func (mb *client) ReadDiscreteInputs(address, quantity uint16) (results []byte, err error) {
if quantity < 1 || quantity > 2000 {
err = fmt.Errorf("modbus: quantity '%v' must be between '%v' and '%v',", quantity, 1, 2000)
Expand All @@ -88,18 +98,25 @@ func (mb *client) ReadDiscreteInputs(address, quantity uint16) (results []byte,
err = fmt.Errorf("modbus: response data size '%v' does not match count '%v'", length, count)
return
}
if count != int(quantity) {
err = fmt.Errorf("modbus: response count '%v' does not match request quantity '%v'", count, quantity)
return
}
results = response.Data[1:]
return
}

// Request:
// Function code : 1 byte (0x03)
// Starting address : 2 bytes
// Quantity of registers : 2 bytes
//
// Function code : 1 byte (0x03)
// Starting address : 2 bytes
// Quantity of registers : 2 bytes
//
// Response:
// Function code : 1 byte (0x03)
// Byte count : 1 byte
// Register value : Nx2 bytes
//
// Function code : 1 byte (0x03)
// Byte count : 1 byte
// Register value : Nx2 bytes
func (mb *client) ReadHoldingRegisters(address, quantity uint16) (results []byte, err error) {
if quantity < 1 || quantity > 125 {
err = fmt.Errorf("modbus: quantity '%v' must be between '%v' and '%v',", quantity, 1, 125)
Expand All @@ -119,18 +136,25 @@ func (mb *client) ReadHoldingRegisters(address, quantity uint16) (results []byte
err = fmt.Errorf("modbus: response data size '%v' does not match count '%v'", length, count)
return
}
if count != int(quantity)*2 {
err = fmt.Errorf("modbus: response count '%v' does not match request quantity '%v'", count, quantity)
return
}
results = response.Data[1:]
return
}

// Request:
// Function code : 1 byte (0x04)
// Starting address : 2 bytes
// Quantity of registers : 2 bytes
//
// Function code : 1 byte (0x04)
// Starting address : 2 bytes
// Quantity of registers : 2 bytes
//
// Response:
// Function code : 1 byte (0x04)
// Byte count : 1 byte
// Input registers : N bytes
//
// Function code : 1 byte (0x04)
// Byte count : 1 byte
// Input registers : N bytes
func (mb *client) ReadInputRegisters(address, quantity uint16) (results []byte, err error) {
if quantity < 1 || quantity > 125 {
err = fmt.Errorf("modbus: quantity '%v' must be between '%v' and '%v',", quantity, 1, 125)
Expand All @@ -150,18 +174,25 @@ func (mb *client) ReadInputRegisters(address, quantity uint16) (results []byte,
err = fmt.Errorf("modbus: response data size '%v' does not match count '%v'", length, count)
return
}
if count != int(quantity)*2 {
err = fmt.Errorf("modbus: response count '%v' does not match request quantity '%v'", count, quantity)
return
}
results = response.Data[1:]
return
}

// Request:
// Function code : 1 byte (0x05)
// Output address : 2 bytes
// Output value : 2 bytes
//
// Function code : 1 byte (0x05)
// Output address : 2 bytes
// Output value : 2 bytes
//
// Response:
// Function code : 1 byte (0x05)
// Output address : 2 bytes
// Output value : 2 bytes
//
// Function code : 1 byte (0x05)
// Output address : 2 bytes
// Output value : 2 bytes
func (mb *client) WriteSingleCoil(address, value uint16) (results []byte, err error) {
// The requested ON/OFF state can only be 0xFF00 and 0x0000
if value != 0xFF00 && value != 0x0000 {
Expand Down Expand Up @@ -196,13 +227,16 @@ func (mb *client) WriteSingleCoil(address, value uint16) (results []byte, err er
}

// Request:
// Function code : 1 byte (0x06)
// Register address : 2 bytes
// Register value : 2 bytes
//
// Function code : 1 byte (0x06)
// Register address : 2 bytes
// Register value : 2 bytes
//
// Response:
// Function code : 1 byte (0x06)
// Register address : 2 bytes
// Register value : 2 bytes
//
// Function code : 1 byte (0x06)
// Register address : 2 bytes
// Register value : 2 bytes
func (mb *client) WriteSingleRegister(address, value uint16) (results []byte, err error) {
request := ProtocolDataUnit{
FunctionCode: FuncCodeWriteSingleRegister,
Expand Down Expand Up @@ -232,15 +266,18 @@ func (mb *client) WriteSingleRegister(address, value uint16) (results []byte, er
}

// Request:
// Function code : 1 byte (0x0F)
// Starting address : 2 bytes
// Quantity of outputs : 2 bytes
// Byte count : 1 byte
// Outputs value : N* bytes
//
// Function code : 1 byte (0x0F)
// Starting address : 2 bytes
// Quantity of outputs : 2 bytes
// Byte count : 1 byte
// Outputs value : N* bytes
//
// Response:
// Function code : 1 byte (0x0F)
// Starting address : 2 bytes
// Quantity of outputs : 2 bytes
//
// Function code : 1 byte (0x0F)
// Starting address : 2 bytes
// Quantity of outputs : 2 bytes
func (mb *client) WriteMultipleCoils(address, quantity uint16, value []byte) (results []byte, err error) {
if quantity < 1 || quantity > 1968 {
err = fmt.Errorf("modbus: quantity '%v' must be between '%v' and '%v',", quantity, 1, 1968)
Expand Down Expand Up @@ -274,15 +311,18 @@ func (mb *client) WriteMultipleCoils(address, quantity uint16, value []byte) (re
}

// Request:
// Function code : 1 byte (0x10)
// Starting address : 2 bytes
// Quantity of outputs : 2 bytes
// Byte count : 1 byte
// Registers value : N* bytes
//
// Function code : 1 byte (0x10)
// Starting address : 2 bytes
// Quantity of outputs : 2 bytes
// Byte count : 1 byte
// Registers value : N* bytes
//
// Response:
// Function code : 1 byte (0x10)
// Starting address : 2 bytes
// Quantity of registers : 2 bytes
//
// Function code : 1 byte (0x10)
// Starting address : 2 bytes
// Quantity of registers : 2 bytes
func (mb *client) WriteMultipleRegisters(address, quantity uint16, value []byte) (results []byte, err error) {
if quantity < 1 || quantity > 123 {
err = fmt.Errorf("modbus: quantity '%v' must be between '%v' and '%v',", quantity, 1, 123)
Expand Down Expand Up @@ -316,15 +356,77 @@ func (mb *client) WriteMultipleRegisters(address, quantity uint16, value []byte)
}

// Request:
// Function code : 1 byte (0x16)
// Reference address : 2 bytes
// AND-mask : 2 bytes
// OR-mask : 2 bytes
//
// Function code : 1 byte (0x15)
// Starting address : 2 bytes
// Quantity of outputs : 2 bytes
// Byte count : 1 byte
// Registers value : N* bytes
//
// Response:
// Function code : 1 byte (0x16)
// Reference address : 2 bytes
// AND-mask : 2 bytes
// OR-mask : 2 bytes
//
// Function code : 1 byte (0x15)
// Starting address : 2 bytes
// Quantity of registers : 2 bytes
func (mb *client) WriteFileRecord(address, quantity uint16, subReqSize uint16, recUpdateFileNum []byte, value []byte) (results []byte, err error) {
wRecControlFieldsLength := uint16(7)

dataFieldsLength := quantity * subReqSize
totalLength := (quantity * (subReqSize + wRecControlFieldsLength)) + 1 //+1 because of Function Code also included
if totalLength < 1 || totalLength > 253 {
err = fmt.Errorf("modbus: total length per modbus request must be between 1 and 253")
return
}
if len(recUpdateFileNum) != 2 {
err = fmt.Errorf("modbus: sub request file number should be a 2 byte value")
return
}
if len(value) < int(address+dataFieldsLength) {
err = fmt.Errorf("modbus: value input parameter length is not enough given address, quantity and subReqSize requested")
return
}
bffrReq := make([]byte, 0)
bffrReq = append(bffrReq, byte(totalLength-1))
for i := int(address); i < int(address+dataFieldsLength); i += int(subReqSize) {
bffrReq = append(bffrReq, 6) //Sub Request reference type
bffrReq = append(bffrReq, recUpdateFileNum...)
recordNumber := []byte{byte(i >> 8), byte(i & 0xFF)}
bffrReq = append(bffrReq, recordNumber...)
bSize := []byte{byte(subReqSize / 2 >> 8), byte(subReqSize / 2 & 0xFF)}
bffrReq = append(bffrReq, bSize...)
valueNext := value[i*int(subReqSize) : (i+1)*int(subReqSize)]
bffrReq = append(bffrReq, valueNext...)
}
request := ProtocolDataUnit{
FunctionCode: FuncCodeWriteFileRecord,
Data: bffrReq,
}
response, err := mb.send(&request)
if err != nil {
return
}
// Fixed response length
if len(response.Data) != int(totalLength) {
err = fmt.Errorf("modbus: response data size '%v' does not match expected '%v'", len(response.Data), int(totalLength))
return
}
results = response.Data[2:]
return
}

// Request:
//
// Function code : 1 byte (0x16)
// Reference address : 2 bytes
// AND-mask : 2 bytes
// OR-mask : 2 bytes
//
// Response:
//
// Function code : 1 byte (0x16)
// Reference address : 2 bytes
// AND-mask : 2 bytes
// OR-mask : 2 bytes
func (mb *client) MaskWriteRegister(address, andMask, orMask uint16) (results []byte, err error) {
request := ProtocolDataUnit{
FunctionCode: FuncCodeMaskWriteRegister,
Expand Down Expand Up @@ -359,17 +461,20 @@ func (mb *client) MaskWriteRegister(address, andMask, orMask uint16) (results []
}

// Request:
// Function code : 1 byte (0x17)
// Read starting address : 2 bytes
// Quantity to read : 2 bytes
// Write starting address: 2 bytes
// Quantity to write : 2 bytes
// Write byte count : 1 byte
// Write registers value : N* bytes
//
// Function code : 1 byte (0x17)
// Read starting address : 2 bytes
// Quantity to read : 2 bytes
// Write starting address: 2 bytes
// Quantity to write : 2 bytes
// Write byte count : 1 byte
// Write registers value : N* bytes
//
// Response:
// Function code : 1 byte (0x17)
// Byte count : 1 byte
// Read registers value : Nx2 bytes
//
// Function code : 1 byte (0x17)
// Byte count : 1 byte
// Read registers value : Nx2 bytes
func (mb *client) ReadWriteMultipleRegisters(readAddress, readQuantity, writeAddress, writeQuantity uint16, value []byte) (results []byte, err error) {
if readQuantity < 1 || readQuantity > 125 {
err = fmt.Errorf("modbus: quantity to read '%v' must be between '%v' and '%v',", readQuantity, 1, 125)
Expand All @@ -392,19 +497,26 @@ func (mb *client) ReadWriteMultipleRegisters(readAddress, readQuantity, writeAdd
err = fmt.Errorf("modbus: response data size '%v' does not match count '%v'", len(response.Data)-1, count)
return
}
if count != int(readQuantity)*2 {
err = fmt.Errorf("modbus: response count '%v' does not match request quantity '%v'", count, readQuantity)
return
}
results = response.Data[1:]
return
}

// Request:
// Function code : 1 byte (0x18)
// FIFO pointer address : 2 bytes
//
// Function code : 1 byte (0x18)
// FIFO pointer address : 2 bytes
//
// Response:
// Function code : 1 byte (0x18)
// Byte count : 2 bytes
// FIFO count : 2 bytes
// FIFO count : 2 bytes (<=31)
// FIFO value register : Nx2 bytes
//
// Function code : 1 byte (0x18)
// Byte count : 2 bytes
// FIFO count : 2 bytes
// FIFO count : 2 bytes (<=31)
// FIFO value register : Nx2 bytes
func (mb *client) ReadFIFOQueue(address uint16) (results []byte, err error) {
request := ProtocolDataUnit{
FunctionCode: FuncCodeReadFIFOQueue,
Expand Down
1 change: 1 addition & 0 deletions modbus.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ const (
FuncCodeReadHoldingRegisters = 3
FuncCodeWriteSingleRegister = 6
FuncCodeWriteMultipleRegisters = 16
FuncCodeWriteFileRecord = 21
FuncCodeReadWriteMultipleRegisters = 23
FuncCodeMaskWriteRegister = 22
FuncCodeReadFIFOQueue = 24
Expand Down
Loading