A high-performance Modbus toolkit written in Go, supporting Modbus TCP and RTU over TCP protocols, with complete Master and Slave functionality.
- ✅ Modbus TCP
- ✅ RTU over TCP
- Read Coils (Function Code 01)
- Read Discrete Inputs (Function Code 02)
- Read Holding Registers (Function Code 03)
- Read Input Registers (Function Code 04)
- Write Single Coil (Function Code 05)
- Write Single Register (Function Code 06)
- Write Multiple Coils (Function Code 15)
- Write Multiple Registers (Function Code 16)
- Read Device Identification (Function Code 43)
- Respond to all Master-supported function codes
- Memory data storage
- Device identification configuration
- Built on high-performance network library gnet
- Pure Go implementation, no C dependencies
- Support for concurrent processing
- Complete error handling mechanism
- Compliant with Modbus protocol specifications
go get -u github.com/veryinf/modbus-kitThe project provides a unified example entry point in the example/main.go file. You can run it using:
go run ./example/main.goThis will present a menu where you can select which example to run:
- Modbus TCP Slave
- Modbus TCP Master
- RTU over TCP Slave
- RTU over TCP Master
package main
import (
"log/slog"
"os"
"github.com/veryinf/modbus-kit/common"
"github.com/veryinf/modbus-kit/slave"
"github.com/panjf2000/gnet/v2"
)
func main() {
// Configure logging
logger := slog.New(slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{Level: slog.LevelDebug}))
slog.SetDefault(logger)
// Create device information
deviceInfo := &slave.DeviceInfo{
Title: "Example Modbus Device",
Identification: &common.DeviceIdentification{
VendorName: "ModbusKit Inc.",
ProductCode: "MK-001",
ProductVersion: "1.0.0",
VendorUrl: "https://github.com/veryinf/modbus-kit",
ProductName: "Modbus Slave Device",
ModelName: "MSD-100",
UserApplicationName: "Modbus Test Application",
},
}
// Create memory data store
store := slave.NewMemoryDataStore()
// Set initial register values
for i := 0; i < 10; i++ {
store.Write(slave.PointTypeHoldingRegister, uint16(i), uint16(i*100))
}
// Create Modbus TCP Slave instance
slaveDevice := slave.NewModbusTCPSlave(1, deviceInfo, store)
tcpServer := common.NewNetServer()
tcpServer.Enroll(&slaveDevice.ModbusDevice)
logger.Info("Starting Modbus TCP Slave server on tcp://0.0.0.0:502")
err := gnet.Run(tcpServer, "tcp://0.0.0.0:502", gnet.WithMulticore(true))
if err != nil {
logger.Error("Server error", "error", err)
}
}package main
import (
"log/slog"
"os"
"time"
"github.com/veryinf/modbus-kit/common"
"github.com/veryinf/modbus-kit/master"
)
func main() {
// Configure logging
logger := slog.New(slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{Level: slog.LevelDebug}))
slog.SetDefault(logger)
// Create TCP client
client := common.NewTCPClient("localhost:502")
// Create Modbus TCP Master instance
tcpMaster := master.NewModbusTCPMaster(&client)
// Read holding registers
registers, err := tcpMaster.ReadHoldingRegisters(1, 0, 5)
if err != nil {
logger.Error("Read holding registers error", "error", err)
return
}
for i, reg := range registers {
logger.Info("Read holding register", "register", i, "value", reg.Value())
}
// Write single register
err = tcpMaster.WriteSingleRegister(1, 0, 12345)
if err != nil {
logger.Error("Write single register error", "error", err)
return
}
logger.Info("Write single register success")
// Read again to verify
registers, err = tcpMaster.ReadHoldingRegisters(1, 0, 1)
if err != nil {
logger.Error("Read holding registers error", "error", err)
return
}
logger.Info("Read updated holding register", "value", registers[0].GetValue())
}package main
import (
"log/slog"
"os"
"github.com/veryinf/modbus-kit/common"
"github.com/veryinf/modbus-kit/slave"
"github.com/panjf2000/gnet/v2"
)
func main() {
// Configure logging
logger := slog.New(slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{Level: slog.LevelDebug}))
slog.SetDefault(logger)
// Create device information
deviceInfo := &slave.DeviceInfo{
Title: "Example RTU Device",
Identification: &common.DeviceIdentification{
VendorName: "ModbusKit Inc.",
ProductCode: "MK-002",
ProductVersion: "1.0.0",
VendorUrl: "https://github.com/veryinf/modbus-kit",
ProductName: "RTU over TCP Device",
ModelName: "RTD-100",
UserApplicationName: "Modbus RTU Test Application",
},
}
// Create memory data store
store := slave.NewMemoryDataStore()
// Set initial values
for i := 0; i < 10; i++ {
store.Write(slave.PointTypeHoldingRegister, uint16(i), uint16(i*100))
}
// Create Modbus RTU over TCP Slave instance
slaveDevice := slave.NewModbusRTUOverTCPSlave(1, deviceInfo, store)
tcpServer := common.NewNetServer()
tcpServer.Enroll(&slaveDevice.ModbusDevice)
logger.Info("Starting RTU over TCP Slave server on tcp://0.0.0.0:502")
err := gnet.Run(tcpServer, "tcp://0.0.0.0:502", gnet.WithMulticore(true))
if err != nil {
logger.Error("Server error", "error", err)
}
}package main
import (
"log/slog"
"os"
"github.com/veryinf/modbus-kit/common"
"github.com/veryinf/modbus-kit/master"
)
func main() {
// Configure logging
logger := slog.New(slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{Level: slog.LevelDebug}))
slog.SetDefault(logger)
// Create TCP client
client := common.NewTCPClient("localhost:502")
// Create RTU over TCP Master instance
rtuMaster := master.NewModbusRTUOverTCPMaster(&client)
// Read holding registers
registers, err := rtuMaster.ReadHoldingRegisters(1, 0, 5)
if err != nil {
logger.Error("Read holding registers error", "error", err)
return
}
for i, reg := range registers {
logger.Info("Read holding register", "register", i, "value", reg.Value())
}
}// TCP Master
client := common.NewTCPClient("localhost:502")
tcpMaster := master.NewModbusTCPMaster(&client)
// RTU over TCP Master
rtuMaster := master.NewModbusRTUOverTCPMaster(&client)bitVector, err := master.ReadCoils(slaveId byte, address uint16, quantity uint16)bitVector, err := master.ReadDiscreteInputs(slaveId byte, address uint16, quantity uint16)registers, err := master.ReadHoldingRegisters(slaveId byte, address uint16, quantity uint8)registers, err := master.ReadInputRegisters(slaveId byte, address uint16, quantity uint8)err := master.WriteSingleCoil(slaveId byte, address uint16, state bool)err := master.WriteSingleRegister(slaveId byte, address uint16, value uint16)err := master.WriteMultipleCoils(slaveId byte, address uint16, values []bool)err := master.WriteMultipleRegisters(slaveId byte, address uint16, registers []*common.Register)info, err := master.ReadDeviceIdentification(slaveId byte)// Create memory data store
store := slave.NewMemoryDataStore()
// Create device information
deviceInfo := &slave.DeviceInfo{
Title: "My Device",
Identification: &common.DeviceIdentification{
VendorName: "My Company",
ProductCode: "PRO-001",
ProductVersion: "1.0.0",
},
}
// Create TCP Slave
slaveDevice := slave.NewModbusTCPSlave(1, deviceInfo, store)tcpServer := common.NewNetServer()
tcpServer.Enroll(&slaveDevice.ModbusDevice)
err := gnet.Run(tcpServer, "tcp://0.0.0.0:502", gnet.WithMulticore(true))modbus-kit/
├── common/ # Common types and utilities
│ ├── bit_vector.go # Bit vector implementation
│ ├── crc.go # CRC checksum
│ ├── data_frame.go # Data frame processing
│ ├── mbap_frame.go # MBAP frame processing
│ ├── mbap_message.go # MBAP message processing
│ ├── register.go # Register implementation
│ ├── rtu_frame.go # RTU frame processing
│ ├── rtu_message.go # RTU message processing
│ ├── tcp_client.go # TCP client
│ ├── tcp_server.go # TCP server
│ └── types.go # Type definitions and constants
├── example/ # Example applications
│ ├── main.go # Main example file
│ ├── tcp_master.go # Modbus TCP Master example
│ ├── tcp_slave.go # Modbus TCP Slave example
│ ├── rtu_over_tcp_master.go # RTU over TCP Master example
│ └── rtu_over_tcp_slave.go # RTU over TCP Slave example
├── master/ # Master functionality
│ ├── modbus_master.go # Core Master implementation
│ ├── rtu_over_tcp.go # RTU over TCP Master
│ └── tcp.go # TCP Master
├── slave/ # Slave functionality
│ ├── modbus_slave.go # Core Slave implementation
│ ├── request_handler.go # Request handling
│ ├── rtu_over_tcp.go # RTU over TCP Slave
│ ├── store.go # Data storage
│ └── tcp.go # TCP Slave
├── README.md # English README
├── README_CN.md # Chinese README
├── go.mod # Go module definition
└── go.sum # Dependency checksums
- Go Version: 1.25+
- Network Library: gnet/v2 - High-performance event-driven network framework
Contributions are welcome! Please follow these steps:
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add some amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
This project is licensed under the MIT License - see the LICENSE file for details.
If you have any questions or suggestions, please contact us through:
- Create an Issue
- gnet - High-performance network library
- Modbus Protocol Specification (http://www.modbus.org/specs.php)