Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
77 commits
Select commit Hold shift + click to select a range
341f034
feat(cmd/modbus-cli): support writing single coils
frzifus Nov 22, 2021
cbbbae8
Add connect delay
andig Dec 23, 2021
689bbd9
Use current go idioms
andig Dec 23, 2021
3feef1c
Merge pull request #44 from andig/feature/connect-delay
gq0 Dec 29, 2021
d6cde5f
Merge pull request #43 from andig/fix/goish
guelfey Jan 10, 2022
66aa1fd
fix: print original function code for exceptions
guelfey Jan 10, 2022
619e2e6
Merge pull request #47 from grid-x/fix/exception_function_code
guelfey Jan 10, 2022
9ebd2ff
fix: retry reading query response on transaction id mismatch
guelfey Feb 8, 2022
c7b3bba
Merge pull request #49 from grid-x/fix/retry_transaction_id_mismatch
guelfey Feb 10, 2022
f1937e7
feat(tcpclient): added skipping of transaction mismatches by more tha…
Carelo Apr 14, 2022
0daecbb
Merge pull request #51 from grid-x/fix/retry_transaction_id_mismatch
Carelo Apr 19, 2022
6d61d5f
Validate result length againt request
andig May 23, 2022
8b2c9ef
Wrap open error with device name for context
andig Aug 29, 2022
006eee7
Merge pull request #54 from andig/patch-1
guelfey Aug 29, 2022
9486cf6
Use time.Since
andig Nov 6, 2022
ca87e58
Fix staticcheck findings (yoda questions, result not used)
andig Nov 6, 2022
8cf18f1
Align implementation with RTU over TCP client
andig Nov 6, 2022
b2c4d95
Simplify initialising length
andig Nov 6, 2022
63789f0
Check recovery deadline only once
andig Nov 6, 2022
6bf5f3b
Merge pull request #57 from andig/fix/since
guelfey Nov 9, 2022
2ff1134
fix(cmd/modbus-cli): use correct register length as byte length divid…
Nov 18, 2022
e5052be
Merge pull request #61 from grid-x/fix/cli_write_multiple_registers
dammarco Nov 18, 2022
ff6c484
feat(cmd/modbus-cli): any non-zero value will lead to single coil on
frzifus Nov 19, 2022
8cdd929
Merge pull request #42 from frzifus/feat/cmd_support_coil_write
guelfey Nov 21, 2022
64ad927
feat(modbus-cli): add option to print out different encoding options
Nov 25, 2022
05892cd
refactor(cmd/modbus-cli): use tabwriter to format the output for all …
Dec 19, 2022
8b31e0d
Merge pull request #62 from grid-x/feat/extend_cli_with_all_encodings
dammarco Jan 5, 2023
63091d0
chore(Makefile): use go 1.19
dammarco Mar 8, 2023
888f05e
Merge pull request #65 from grid-x/feat/upgrade_to_go_1_19
dammarco Mar 8, 2023
2371399
fix(cmd/modbus-cli): fix byte write function to also work with floats
dammarco Mar 8, 2023
c9ee23b
feat(cmd/modbus-cli): extend cli tool to add a forcerd order in the f…
dammarco Mar 8, 2023
44180bf
feat(Makefile): add test for the modbus-cli tool
dammarco Mar 8, 2023
70c8d66
Merge pull request #60 from andig/feature/simplify-recovery
hnicolaysen Apr 11, 2023
02a89c3
fix(modbus/tcpclient): fix errTransactionIDMismatch with no ProtocolR…
hnicolaysen Apr 5, 2023
b867b6a
Merge pull request #66 from grid-x/fix/transactionID_mismatch
hnicolaysen Apr 12, 2023
5dd11fc
docs(*): make go doc comments compliant with `gofmt` by replacing dou…
hnicolaysen Apr 12, 2023
93c0e20
Merge pull request #72 from grid-x/doc/go_doc_comments_with_gofmt
hnicolaysen May 2, 2023
c8847a4
Simplify logger implementation
andig Apr 10, 2023
8f3641c
chore: ignore location for generated binaries by goreleaser
dammarco May 4, 2023
98610f7
feat(modbus-cli): add .goreleaser config for modbus-cli
dammarco May 4, 2023
9fd07c8
feat(modbus-cli): add options in the Makefile to generate releases fo…
dammarco May 4, 2023
7e81d6e
chore: refactor readme to use markdown format and add documentation f…
dammarco May 4, 2023
7112c6d
Merge pull request #74 from grid-x/feat/modbus_cli_goreleaser
dammarco May 10, 2023
e90d491
Merge pull request #64 from grid-x/feat/cli_improve_modbus_write_feat
dammarco May 11, 2023
552a7c7
Use embedded fields directly
andig Apr 10, 2023
30f9d28
Fix typo
andig Apr 10, 2023
24c04da
Merge pull request #58 from andig/fix/staticcheck
gq0 Jul 6, 2023
fa24ebf
Merge pull request #71 from andig/chore/remove-indirect-logger
gq0 Jul 6, 2023
f88c63f
Merge pull request #67 from andig/feature/align-embedded-fields
gq0 Jul 6, 2023
d9fefd3
Merge pull request #53 from andig/feature/length-check
gq0 Jul 13, 2023
aaeb082
Implementing a recent logger and changing the severity level from Inf…
MontmirailValentin Jan 5, 2024
5e8be8a
Cannot use slog, we are still in Go 1.13
MontmirailValentin Jan 5, 2024
6d14636
Make the New() function private. It is only related to logger so chan…
MontmirailValentin Jan 5, 2024
8eeb8b6
Adding a breaking point as accepted by frzifus
MontmirailValentin Jan 21, 2024
59965a7
Update to Go 1.21 since slog is only available starting from 1.21
MontmirailValentin Jan 21, 2024
401c496
Update all packages to their latest versions. We still need a baselin…
MontmirailValentin Jan 21, 2024
79617db
feat(*): bump to Go version 1.21
hnicolaysen Feb 2, 2024
8084aab
Merge pull request #83 from grid-x/feat/bump_go_1.21
hnicolaysen Feb 6, 2024
f078006
Fix race in 'TestSerialCloseIdle()'
srebhan Feb 1, 2024
63dc149
Fix race in 'TestTCPTransporter()'
srebhan Feb 1, 2024
0d4922f
Merge pull request #82 from srebhan/fix_races
hnicolaysen Feb 14, 2024
4237d62
feat: Implement RTU over UDP
StefanNienhuis Feb 18, 2024
d0431e7
docs: Document UDP protocol
StefanNienhuis Feb 18, 2024
47a6268
fmt: Clean up RTU over UDP comments
StefanNienhuis Feb 23, 2024
6e290f2
fix: Add length checks to RTU over UDP
StefanNienhuis Feb 23, 2024
130d65d
Merge pull request #85 from StefanNienhuis/feat/rtu-over-udp
dammarco Apr 15, 2024
b70fb8d
Fixing merge conflict after the upgrade to Go 1.21
MontmirailValentin Apr 28, 2024
02314cc
Merge pull request #80 from ValentinMontmirail/feat/severityLogger
hnicolaysen Apr 29, 2024
79d3fae
Introduce custom error for invalid data-sizes
srebhan Feb 1, 2024
0a13a3d
Return data if response is too large, fixes #52
srebhan Feb 1, 2024
f14741f
Comment new error type
srebhan Feb 1, 2024
828f9ad
Make clear expected and actual are in bytes
srebhan Feb 12, 2024
5255f1c
Merge pull request #81 from srebhan/fix_issue_52
dammarco May 2, 2024
1334895
Return to minimal logger interface
andig May 3, 2024
bbc42ff
Prepare a full revert
andig May 3, 2024
a602f3f
Split adapter
andig May 3, 2024
a743f13
Simplify test
andig May 3, 2024
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
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
.idea/**

dist/
40 changes: 40 additions & 0 deletions .goreleaser.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
builds:
- main: ./cmd/modbus-cli
env:
- CGO_ENABLED=0
goos:
- linux
- windows
- darwin
goarch:
- amd64
- arm64
- arm
goarm:
- 7
# Ignore ARM32 build for both macOS and Windows
ignore:
- goos: darwin
goarch: arm
- goos: windows
goarch: arm
archives:
- id: modbus-cli
name_template: >-
modbus-cli_
{{- if eq .Os "darwin" }}mac
{{- else }}{{ .Os }}{{ end }}_{{ .Arch }}
format_overrides:
- goos: windows
format: zip
checksum:
name_template: 'checksums.txt'
snapshot:
name_template: "{{ incpatch .Version }}-next"
changelog:
sort: asc
filters:
exclude:
- '^docs:'
- '^test:'

9 changes: 8 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
GO_FILES := $(shell find . -type f -name "*.go")
GO_BUILD := CGO_ENABLED=0 go build -ldflags "-w -s"
GO_TOOLS := registry.gridx.de/gridx/base-images:modbus-dev-1.15.latest
GO_TOOLS := public.ecr.aws/gridx/base-images:modbus-dev-1.21.latest
DOCKER_RUN := docker run --init --rm -v $$PWD:/go/src/github.com/grid-x/modbus -w /go/src/github.com/grid-x/modbus
GO_RUN := ${DOCKER_RUN} ${GO_TOOLS} bash -c

Expand All @@ -13,6 +13,7 @@ test:
diagslave -m tcp -p 5020 & diagslave -m enc -p 5021 & go test -run TCP -v $(shell glide nv)
socat -d -d pty,raw,echo=0 pty,raw,echo=0 & diagslave -m rtu /dev/pts/1 & go test -run RTU -v $(shell glide nv)
socat -d -d pty,raw,echo=0 pty,raw,echo=0 & diagslave -m ascii /dev/pts/3 & go test -run ASCII -v $(shell glide nv)
go test -v -count=1 github.com/grid-x/modbus/cmd/modbus-cli

.PHONY: lint
lint:
Expand All @@ -22,6 +23,9 @@ lint:
build:
go build

release:
goreleaser release --skip-publish --skip-validate --clean

ci_test:
${GO_RUN} "make test"

Expand All @@ -30,3 +34,6 @@ ci_lint:

ci_build:
${GO_RUN} "make build"

ci_release:
${GO_RUN} "goreleaser release --skip-publish --skip-validate --rm-dist"
117 changes: 91 additions & 26 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,31 +1,29 @@
go modbus
=========
# go modbus
Fault-tolerant, fail-fast implementation of Modbus protocol in Go.

Supported functions
-------------------
# Supported functions

Bit access:
* Read Discrete Inputs
* Read Coils
* Write Single Coil
* Write Multiple Coils
- Read Discrete Inputs
- Read Coils
- Write Single Coil
- Write Multiple Coils

16-bit access:
* Read Input Registers
* Read Holding Registers
* Write Single Register
* Write Multiple Registers
* Read/Write Multiple Registers
* Mask Write Register
* Read FIFO Queue

Supported formats
-----------------
* TCP
* Serial (RTU, ASCII)

Usage
-----
- Read Input Registers
- Read Holding Registers
- Write Single Register
- Write Multiple Registers
- Read/Write Multiple Registers
- Mask Write Register
- Read FIFO Queue

# Supported formats
- TCP
- Serial (RTU, ASCII)
- UDP

# Usage
Basic usage:
```go
// Modbus TCP
Expand Down Expand Up @@ -73,6 +71,73 @@ client := modbus.NewClient(handler)
results, err := client.ReadDiscreteInputs(15, 2)
```

References
----------
- [Modbus Specifications and Implementation Guides](http://www.modbus.org/specs.php)
# Modbus-CLI

We offer a CLI tool to read/write registers.

## Usage

For simplicity, the following examples are all using Modbus TCP.
For Modbus RTU, replace the address field and use the `rtu-` arguments in order to use different baudrates, databits, etc.
```sh
./modbus-cli -address=rtu:///dev/ttyUSB0 -rtu-baudrate=57600 -rtu-stopbits=2 -rtu-parity=N -rtu-databits=8 ...
```

For Modbus UDP, replace the address field with a UDP address.
```sh
./modbus-cli --address=udp://127.0.0.1:502 ...
```

### Reading Registers

Read 1 register and get raw result
```sh
./modbus-cli -address=tcp://127.0.0.1:502 -quantity=1 -type-parse=raw -register=42
```

Read 1 register and decode result as uint16
```sh
./modbus-cli -address=tcp://127.0.0.1:502 -quantity=1 -type-parse=uint16 -register=42
```

Read 1 register and get all possible decoded results
```sh
./modbus-cli -address=tcp://127.0.0.1:502 -quantity=1 -type-parse=all -register=42
```

Read 2 registers and decode result as uint32
```sh
./modbus-cli -address=tcp://127.0.0.1:502 -quantity=2 -type-parse=uint32 -register=42
```

Read 2 registers and get all possible decoded results
```sh
./modbus-cli -address=tcp://127.0.0.1:502 -quantity=2 -type-parse=all -register=42
```

Reading multiple registers is only possible in the raw format
```sh
./modbus-cli -address=tcp://127.0.0.1:502 -quantity=16 -type-parse=raw -register=42
```

### Writing Registers

Write 1 register
```sh
./modbus-cli -address=tcp://127.0.0.1:502 -fn-code=0x06 -type-exec=uint16 -register=42 -write-value=7
```

Write 2 registers
```sh
./modbus-cli -address=tcp://127.0.0.1:502 -fn-code=0x10 -type-exec=uint32 -register=42 -write-value=7
```

## Release

To release the Modbus-CLI tool, run either `make release` if you have installed `goreleaser` or `make ci_release`.
The generated files can be found in the directory in the `dist` directory.

Take the `.tar.gz` and `.zip` files and create a new GitHub release.

# References
- [Modbus Specifications and Implementation Guides](http://www.modbus.org/specs.php)
17 changes: 8 additions & 9 deletions ascii_over_tcp_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,16 +35,16 @@ type asciiTCPTransporter struct {
}

func (mb *asciiTCPTransporter) Send(aduRequest []byte) (aduResponse []byte, err error) {
mb.tcpTransporter.mu.Lock()
defer mb.tcpTransporter.mu.Unlock()
mb.mu.Lock()
defer mb.mu.Unlock()

// Make sure port is connected
if err = mb.tcpTransporter.connect(); err != nil {
if err = mb.connect(); err != nil {
return
}
// Start the timer to close when idle
mb.tcpTransporter.lastActivity = time.Now()
mb.tcpTransporter.startCloseTimer()
mb.lastActivity = time.Now()
mb.startCloseTimer()
// Set write and read timeout
var timeout time.Time
if mb.Timeout > 0 {
Expand All @@ -55,14 +55,13 @@ func (mb *asciiTCPTransporter) Send(aduRequest []byte) (aduResponse []byte, err
}

// Send the request
mb.tcpTransporter.logf("modbus: send %q\n", aduRequest)
mb.logf("modbus: send %q\n", aduRequest)
if _, err = mb.conn.Write(aduRequest); err != nil {
return
}
// Get the response
var n int
var n, length int
var data [asciiMaxSize]byte
length := 0
for {
if n, err = mb.conn.Read(data[length:]); err != nil {
return
Expand All @@ -79,6 +78,6 @@ func (mb *asciiTCPTransporter) Send(aduRequest []byte) (aduResponse []byte, err
}
}
aduResponse = data[:length]
mb.tcpTransporter.logf("modbus: recv %q\n", aduResponse)
mb.logf("modbus: recv %q\n", aduResponse)
return
}
42 changes: 17 additions & 25 deletions asciiclient.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ func NewASCIIClientHandler(address string) *ASCIIClientHandler {
handler.Address = address
handler.Timeout = serialTimeout
handler.IdleTimeout = serialIdleTimeout
handler.serialPort.Logger = handler // expose the logger
return handler
}

Expand All @@ -54,13 +53,14 @@ func (mb *asciiPackager) SetSlave(slaveID byte) {
mb.SlaveID = slaveID
}

// Encode encodes PDU in a ASCII frame:
// Start : 1 char
// Address : 2 chars
// Function : 2 chars
// Data : 0 up to 2x252 chars
// LRC : 2 chars
// End : 2 chars
// Encode encodes PDU in an ASCII frame:
//
// Start : 1 char
// Address : 2 chars
// Function : 2 chars
// Data : 0 up to 2x252 chars
// LRC : 2 chars
// End : 2 chars
func (mb *asciiPackager) Encode(pdu *ProtocolDataUnit) (adu []byte, err error) {
var buf bytes.Buffer

Expand Down Expand Up @@ -166,36 +166,28 @@ func (mb *asciiPackager) Decode(adu []byte) (pdu *ProtocolDataUnit, err error) {
// asciiSerialTransporter implements Transporter interface.
type asciiSerialTransporter struct {
serialPort
Logger logger
}

func (mb *asciiSerialTransporter) Printf(format string, v ...interface{}) {
if mb.Logger != nil {
mb.Logger.Printf(format, v...)
}
}

func (mb *asciiSerialTransporter) Send(aduRequest []byte) (aduResponse []byte, err error) {
mb.serialPort.mu.Lock()
defer mb.serialPort.mu.Unlock()
mb.mu.Lock()
defer mb.mu.Unlock()

// Make sure port is connected
if err = mb.serialPort.connect(); err != nil {
if err = mb.connect(); err != nil {
return
}
// Start the timer to close when idle
mb.serialPort.lastActivity = time.Now()
mb.serialPort.startCloseTimer()
mb.lastActivity = time.Now()
mb.startCloseTimer()

// Send the request
mb.serialPort.logf("modbus: send % x\n", aduRequest)
mb.logf("modbus: send % x\n", aduRequest)
if _, err = mb.port.Write(aduRequest); err != nil {
return
}
// Get the response
var n int
var n, length int
var data [asciiMaxSize]byte
length := 0
for {
if n, err = mb.port.Read(data[length:]); err != nil {
return
Expand All @@ -212,7 +204,7 @@ func (mb *asciiSerialTransporter) Send(aduRequest []byte) (aduResponse []byte, e
}
}
aduResponse = data[:length]
mb.serialPort.logf("modbus: recv % x\n", aduResponse)
mb.logf("modbus: recv % x\n", aduResponse)
return
}

Expand All @@ -231,7 +223,7 @@ func writeHex(buf *bytes.Buffer, value []byte) (err error) {
return
}

// readHex decodes hexa string to byte, e.g. "8C" => 0x8C.
// readHex decodes hex string to byte, e.g. "8C" => 0x8C.
func readHex(data []byte) (value byte, err error) {
var dst [1]byte
if _, err = hex.Decode(dst[:], data[0:2]); err != nil {
Expand Down
2 changes: 1 addition & 1 deletion asciiclient_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ func TestASCIIDecoding(t *testing.T) {
t.Fatal(err)
}

if 3 != pdu.FunctionCode {
if pdu.FunctionCode != 3 {
t.Fatalf("Function code: expected %v, actual %v", 15, pdu.FunctionCode)
}
expected := []byte{0x13, 0x89, 0, 0x0A}
Expand Down
Loading