modbus

package module
v1.0.0 Latest Latest
Warning

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

Go to latest
Published: Sep 18, 2025 License: MIT Imports: 7 Imported by: 0

README

Go Modbus RTU over TCP Client

A robust and easy-to-use Go package for communicating with Modbus devices using the RTU protocol over a TCP/IP network.

This client is designed for stability and resilience, featuring automatic connection management and self-healing capabilities, making it ideal for industrial applications and IoT projects.

Features

  • Thread-Safe: Share a single client instance across multiple goroutines without race conditions.
  • Automatic Connection Handling: No need to manually connect. The client establishes a connection on the first request.
  • Self-Healing: Automatically reconnects if the network connection is lost, retrying the last request once.
  • Fluent API: Simple and intuitive methods for all common Modbus functions (ReadHoldingRegister, WriteCoil, etc.).
  • Built-in Data Parsing: Supports common data types like uint16, int16, uint32, and int32 out of the box.
  • Scaling Factor Support: Easily handle fixed-point decimal values (e.g., reading a temperature of 25.5 from a register value of 255).
  • Debug Mode: An optional debug mode to print raw request/response frames for troubleshooting.

Installation

To get the package, run the following command in your project directory:

go get gitea.infinite-ai.solutions/flux/modbus

Quick Start

Here is a basic example of how to instantiate the client and read a holding register.

package main

import (
	"fmt"
	"log"
	"time"
	"gitea.infinite-ai.solutions/flux/modbus" // <-- Adjust import path
)

func main() {
	// 1. Create a new Modbus client
	// Parameters: IP, Port, Device ID, Request Timeout, Write Delay
	mb := modbus.NewModbusRTUoverTCPClient("192.168.1.10", 502, 1, 5*time.Second, 300*time.Millisecond)
	
	// Optional: Enable debug mode to see the raw Modbus frames
	mb.Debug()

	// 2. Read a holding register
	// We want to read register 100, which is a single uint16. 
	// The device stores temperature, and a value of 255 means 25.5 degrees.
	// So we use a scaling factor of 10 (ScalingFactorMinus_1).
	registerAddress := uint16(100)
	quantity := uint16(1) // for uint16/int16 this is always 1, for uint32/int32 it's 2
	dataType := modbus.ModbusDataTypeUint16
	scalingFactor := modbus.ScalingFactorMinus_1

	value, err := mb.ReadHoldingRegister(registerAddress, quantity, dataType, scalingFactor)
	if err != nil {
		log.Fatalf("Failed to read holding register: %v", err)
	}

	fmt.Printf("Successfully read value: %.1f\n", value) // Output: Successfully read value: 25.5

	// 3. Write to a register
	// Write the value 28.0 to the same register. The client handles scaling automatically.
	err = mb.WriteRegister(registerAddress, 28.0, scalingFactor)
	if err != nil {
		log.Fatalf("Failed to write register: %v", err)
	}

	fmt.Println("Successfully wrote new value to register.")

	// 4. Clean up when done (optional, but good practice in long-running apps)
	mb.Disconnect()
}

API Overview

Client Initialization
  • NewModbusRTUoverTCPClient(ip, port, deviceID, timeout, writeDelay): Creates and configures a new client.
Reading Data
  • ReadHoldingRegister(register, quantity, dataType, factor): Reads one or more holding registers.
  • ReadInputRegister(register, quantity, dataType, factor): Reads one or more input registers.
  • ReadCoil(register): Reads a single digital output (coil).
  • ReadCoils(register, quantity): Reads multiple coils.
  • ReadDiscreteInput(register): Reads a single digital input.
Writing Data
  • WriteRegister(register, value, factor): Writes a floating-point value to a single holding register (scaling is applied).
  • WriteMultipleRegisters(register, values): Writes a slice of uint16 values to consecutive registers.
  • WriteCoil(register, value): Writes a boolean state (true/false) to a single coil.
Connection and Debugging
  • Disconnect(): Closes the network connection.
  • Debug(): Enables logging of Modbus frames to the console.

Data Types and Scaling Factors

When reading registers, you must specify how to interpret the bytes.

ModbusDataType
  • ModbusDataTypeUint16: An unsigned 16-bit integer (reads 1 register).
  • ModbusDataTypeInt16: A signed 16-bit integer (reads 1 register).
  • ModbusDataTypeUint32: An unsigned 32-bit integer (reads 2 consecutive registers).
  • ModbusDataTypeInt32: A signed 32-bit integer (reads 2 consecutive registers).
ScalingFactor

Many devices use fixed-point arithmetic. A ScalingFactor makes it easy to convert these values. The factor is a divisor.

  • ScalingFactorMinus_0: Divides by 1 (no change).
  • ScalingFactorMinus_1: Divides by 10.
  • ScalingFactorMinus_2: Divides by 100.
  • ScalingFactorMinus_3: Divides by 1000.

Example: A device measures voltage and stores 2305 in a register to represent 230.5V. To read this, you would use ScalingFactorMinus_1. The library will return the floating-point value 230.5.

Error Handling

The client is designed to be resilient. If a network error (e.g., connection reset) occurs, it will automatically attempt to reconnect and resend the request once. If the second attempt fails, the function will return an error.

Your code should always check for errors returned by the API methods. These errors can be network-related or Modbus-specific exceptions (e.g., "Illegal Data Address") sent by the device.

Concurrency

The client is safe for concurrent use. A sync.Mutex protects all network operations, ensuring that only one request is active on the TCP connection at a time. You can safely share a single Modbus client instance across multiple goroutines.

Documentation

Index

Constants

View Source
const (
	// Read Functions
	FuncReadCoils            byte = 0x01 // Read Coils (1-bit)
	FuncReadDiscreteInputs   byte = 0x02 // Read Discrete Inputs (1-bit)
	FuncReadHoldingRegisters byte = 0x03 // Read Holding Registers (16-bit)
	FuncReadInputRegisters   byte = 0x04 // Read Input Registers (16-bit)

	// Write Functions
	FuncWriteSingleCoil        byte = 0x05 // Write Single Coil (1-bit)
	FuncWriteSingleRegister    byte = 0x06 // Write Single Register (16-bit)
	FuncWriteMultipleCoils     byte = 0x0F // Write Multiple Coils (1-bit)
	FuncWriteMultipleRegisters byte = 0x10 // Write Multiple Registers (16-bit)

	// Diagnostics & Masking
	FuncMaskWriteRegister     byte = 0x16 // Mask Write Register
	FuncReadWriteMultipleRegs byte = 0x17 // Read/Write Multiple Registers

	// Custom & Advanced (Some devices support these)
	FuncReadFIFOQueue         byte = 0x18 // Read FIFO Queue
	FuncEncapsulatedInterface byte = 0x2B // Encapsulated Interface Transport

	// Error Codes (Modbus Exception Responses)
	FuncExceptionOffset byte = 0x80 // Add to function code to indicate an error
)

Function Codes for Modbus operations.

Variables

This section is empty.

Functions

This section is empty.

Types

type Modbus

type Modbus interface {
	// Disconnect closes the underlying network connection.
	Disconnect()
	// ReadHoldingRegister reads one or more holding registers from the device.
	ReadHoldingRegister(register uint16, quantity uint16, dataType ModbusDataType, factor ScalingFactor) (float64, error)
	// ReadInputRegister reads one or more input registers from the device.
	ReadInputRegister(register uint16, quantity uint16, dataType ModbusDataType, factor ScalingFactor) (float64, error)
	// ReadCoil reads the status of a single coil (digital output).
	ReadCoil(register uint16) (bool, error)
	// ReadCoils reads the status of multiple coils.
	ReadCoils(register uint16, quantity uint16) ([]bool, error)
	// ReadDiscreteInput reads the status of a single discrete input (digital input).
	ReadDiscreteInput(register uint16) (bool, error)
	// WriteRegister writes a single value to a holding register.
	WriteRegister(register uint16, value float64, factor ScalingFactor) error
	// WriteCoil sets the status of a single coil.
	WriteCoil(register uint16, value bool) error
	// WriteMultipleRegisters writes a slice of values to consecutive holding registers.
	WriteMultipleRegisters(register uint16, values []uint16) error
	// Debug enables or disables debug logging to the console.
	Debug()
}

Modbus defines the interface for a Modbus RTU over TCP client.

func NewModbusRTUoverTCPClient

func NewModbusRTUoverTCPClient(ip string, port uint, deviceID byte, timeout time.Duration, writeDelay time.Duration) Modbus

NewModbusRTUoverTCPClient creates and configured a new Modbus RTU over TCP client. It does not establish a connection immediately; the connection is made on the first request.

type ModbusDataType

type ModbusDataType string

ModbusDataType represents supported Modbus register data types for parsing.

const (
	ModbusDataTypeUint16  ModbusDataType = "uint16"
	ModbusDataTypeInt16   ModbusDataType = "int16"
	ModbusDataTypeUint32  ModbusDataType = "uint32"
	ModbusDataTypeInt32   ModbusDataType = "int32"
	ModbusDataTypeFloat32 ModbusDataType = "float32"
)

Supported Modbus data types for register parsing.

type ScalingFactor

type ScalingFactor uint

ScalingFactor defines a multiplier to convert a fixed-point Modbus value into a floating-point number. For example, a value of 1234 with ScalingFactorMinus_2 becomes 12.34.

const (
	// ScalingFactorMinus_0 represents a scaling factor of 1 (10^0). No change.
	ScalingFactorMinus_0 ScalingFactor = 1
	// ScalingFactorMinus_1 represents a scaling factor of 10 (10^1). Divides by 10.
	ScalingFactorMinus_1 ScalingFactor = 10
	// ScalingFactorMinus_2 represents a scaling factor of 100 (10^2). Divides by 100.
	ScalingFactorMinus_2 ScalingFactor = 100
	// ScalingFactorMinus_3 represents a scaling factor of 1000 (10^3). Divides by 1000.
	ScalingFactorMinus_3 ScalingFactor = 1000
)

Common scaling factors. The names indicate the power of 10.

Jump to

Keyboard shortcuts

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