Pure Go Modbus protocol library — typed request/response models, PDU encoding/decoding, and in-memory process image. Zero external dependencies.
- Typed request and response models for FC 01–06, 0F, 10
- TCP and RTU frame parsing and encoding
- In-memory process image with concurrency support
ExecuteRequestdispatch against anyProcessImageimplementation- PDU-level validation (address range, byte count, quantity limits)
- CRC16, Float32 ↔ register utilities
- Human-readable request/response formatting for debugging
go get github.com/LanceAdd/modbus-protocol
import "github.com/LanceAdd/modbus-protocol"
// Build a process image with room for 100 holding registers.
image := modbus.NewMemoryProcessImage(0, 0, 100, 0)
// A raw Modbus RTU frame (with CRC) from a client.
frame := []byte{0x01, 0x03, 0x00, 0x00, 0x00, 0x02, 0xc4, 0x0b}
req, err := modbus.ParseRTURequest(frame)
if err != nil {
panic(err)
}
fmt.Println(modbus.FormatRequest(req))
// → FC=0x03 ReadHoldingRegisters slave=1 addr=0 qty=2
resp, err := modbus.ExecuteRequest(req, image)
if err != nil {
panic(err)
}
fmt.Println(modbus.FormatResponse(resp))
// → FC=0x03 ReadRegisters slave=1 count=2req := modbus.NewReadInputRegistersRequest(modbus.RTU, 1, 0, 80)
payload, _ := modbus.EncodeRTURequestPayload(req)
// payload is CRC-stripped; use an RTU codec to append CRC before sendingreq := modbus.NewWriteMultipleRegistersRequest(modbus.TCP, 1, 100, []uint16{0x42C8, 0x0000})
frame, _ := modbus.EncodeTCPRequest(req, 1) // transaction ID = 1
// frame is a full Modbus TCP ADU ready to writeresp, _ := modbus.ExecuteRequest(req, image)
rtuFrame, _ := modbus.EncodeRTUResponse(resp) // full RTU frame with CRC
tcpFrame, _ := modbus.EncodeTCPResponse(resp) // full TCP ADUdata := []byte{0x01, 0x03, 0x00, 0x00, 0x00, 0x02}
crc := modbus.CRC16(data)
fmt.Printf("%04x\n", crc) // → c40blo, hi := modbus.Float32ToRegisters(3.14, modbus.LowHigh)
val := modbus.RegistersToFloat32(lo, hi, modbus.LowHigh)
fmt.Println(val) // → 3.14| Code | Name | Parse | Encode | Execute |
|---|---|---|---|---|
| 0x01 | Read Coils | ✓ | ✓ | ✓ |
| 0x02 | Read Discrete Inputs | ✓ | ✓ | ✓ |
| 0x03 | Read Holding Registers | ✓ | ✓ | ✓ |
| 0x04 | Read Input Registers | ✓ | ✓ | ✓ |
| 0x05 | Write Single Coil | ✓ | ✓ | ✓ |
| 0x06 | Write Single Register | ✓ | ✓ | ✓ |
| 0x0F | Write Multiple Coils | ✓ | ✓ | ✓ |
| 0x10 | Write Multiple Registers | ✓ | ✓ | ✓ |
Exception responses (function code + 0x80) are supported for all listed function codes.
modbus.TCP // Modbus TCP (MBAP header + PDU)
modbus.RTU // Modbus RTU (PDU + CRC16)ProcessImage is an interface — swap in any backend:
type ProcessImage interface {
ReadCoils(start uint16, quantity uint16) ([]bool, error)
ReadDiscreteInputs(start uint16, quantity uint16) ([]bool, error)
ReadHoldingRegisters(start uint16, quantity uint16) ([]uint16, error)
ReadInputRegisters(start uint16, quantity uint16) ([]uint16, error)
WriteSingleCoil(address uint16, value bool) error
WriteSingleRegister(address uint16, value uint16) error
WriteMultipleCoils(start uint16, values []bool) error
WriteMultipleRegisters(start uint16, values []uint16) error
}MemoryProcessImage is the built-in concurrency-safe implementation.
MIT