modbus

package
v0.698.0 Latest Latest
Warning

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

Go to latest
Published: Jun 13, 2026 License: AGPL-3.0 Imports: 3 Imported by: 0

Documentation

Overview

Package modbus decodes Modbus RTU and Modbus TCP frames per the Modbus Application Protocol Specification v1.1b3 and the Modbus Messaging Implementation Guide v1.0b. Both serial (RTU) and Ethernet (TCP) variants share the same Protocol Data Unit (PDU) — only the envelope differs.

Wrap-vs-native judgement

Native. Modbus is a fully published industrial control protocol with two well-defined envelopes:

  • RTU: [Address:1][Function:1][Data:0..252][CRC-16:2]
  • TCP: [TransactionID:2][ProtocolID:2][Length:2] [UnitID:1][Function:1][Data:0..252]

Function codes 1..127 dispatch to documented request / response layouts; exception responses set the high bit of the function code and carry a single exception-code byte. CRC-16/Modbus (polynomial 0xA001, init 0xFFFF) is a textbook reflected bit-walker. Pasting a hex blob from Wireshark / Modbus Doctor / a PLC traffic capture is enough — no vendor SDK, no handshake.

What this package covers

  • Envelope auto-detection: TCP MBAP header is recognised by ProtocolID == 0x0000 + Length covering the PDU remainder; everything else falls through to RTU.
  • RTU CRC-16/Modbus validation (poly 0xA001, init 0xFFFF, reflected, no final XOR) — surfaces both the captured CRC and the computed expected value for forensic diffing.
  • Function code dispatch for the well-known operations: 0x01 Read Coils, 0x02 Read Discrete Inputs, 0x03 Read Holding Registers, 0x04 Read Input Registers (all four decoded for both request and response shapes), 0x05 Write Single Coil, 0x06 Write Single Register, 0x07 Read Exception Status, 0x08 Diagnostic, 0x0B Get Comm Event Counter, 0x0C Get Comm Event Log, 0x0F Write Multiple Coils, 0x10 Write Multiple Registers, 0x11 Report Server ID, 0x14 Read File Record, 0x15 Write File Record, 0x16 Mask Write Register, 0x17 Read/Write Multiple Registers, 0x18 Read FIFO Queue, 0x2B Encapsulated Interface (MEI).
  • Exception responses: function code >= 0x80 → original function = code & 0x7F, exception code 0x01-0x0B named (Illegal Function, Illegal Data Address, Illegal Data Value, Server Device Failure, Acknowledge, Server Device Busy, Negative Acknowledge, Memory Parity Error, Gateway Path Unavailable, Gateway Target Device Failed to Respond).
  • Request / response disambiguation via payload shape — when the function code's request and response have different byte layouts (e.g. read functions: request is 4 bytes [start:2][qty:2]; response starts with a byte_count then N data bytes), the body is parsed into whichever shape fits.

What this package does NOT cover (deliberately out of scope)

  • Modbus ASCII envelope (function code + data hex-encoded with LRC; framed by ':' and CRLF). Niche compared to RTU + TCP and easy to add later as a third parser.
  • Sub-function-code MEI / diagnostic / encapsulated- interface deeper decode (0x08, 0x2B): the parent function is named but the sub-function payload is surfaced as raw hex.
  • Modbus over UDP, Modbus+ (the proprietary token-bus dialect), and JBUS dialects — handled by users who extract the standard PDU.
  • Multi-frame reassembly — Modbus is single-frame; if a PDU is split across captured packets the caller must reassemble before passing in.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Body

type Body struct {
	StartAddress    *int   `json:"start_address,omitempty"`
	Quantity        *int   `json:"quantity,omitempty"`
	OutputAddress   *int   `json:"output_address,omitempty"`
	OutputValue     *int   `json:"output_value,omitempty"`
	RegisterAddress *int   `json:"register_address,omitempty"`
	RegisterValue   *int   `json:"register_value,omitempty"`
	AndMask         *int   `json:"and_mask,omitempty"`
	OrMask          *int   `json:"or_mask,omitempty"`
	ByteCount       *int   `json:"byte_count,omitempty"`
	CoilStatuses    []bool `json:"coil_statuses,omitempty"`
	RegisterValues  []int  `json:"register_values,omitempty"`
	SubFunction     *int   `json:"sub_function,omitempty"`
	PayloadHex      string `json:"payload_hex,omitempty"`
}

Body is the per-function structured view. Only the fields relevant to the parsed function code are populated.

type Frame

type Frame struct {
	HexInput      string `json:"hex_input"`
	Format        string `json:"format"`
	TransactionID *int   `json:"transaction_id,omitempty"`
	ProtocolID    *int   `json:"protocol_id,omitempty"`
	LengthField   *int   `json:"length_field,omitempty"`
	UnitID        int    `json:"unit_id"`
	FunctionCode  int    `json:"function_code"`
	FunctionHex   string `json:"function_hex"`
	FunctionName  string `json:"function_name"`
	IsException   bool   `json:"is_exception"`
	ExceptionCode *int   `json:"exception_code,omitempty"`
	ExceptionName string `json:"exception_name,omitempty"`
	DataHex       string `json:"data_hex,omitempty"`
	CRC           string `json:"crc,omitempty"`
	CRCExpected   string `json:"crc_expected,omitempty"`
	CRCValid      bool   `json:"crc_valid,omitempty"`
	Request       *Body  `json:"request,omitempty"`
	Response      *Body  `json:"response,omitempty"`
}

Frame is the decoded view of a Modbus RTU or TCP frame.

func Decode

func Decode(hexBlob string) (*Frame, error)

Decode parses a hex-encoded Modbus frame. The envelope (RTU vs TCP) is auto-detected: a 7-byte MBAP header (ProtocolID == 0x0000 + Length field matching the remainder) is parsed as TCP; everything else is treated as RTU.

Accepts ':', '-', '_', whitespace as separators and a leading '0x' prefix.

func DecodeBytes

func DecodeBytes(b []byte) (*Frame, error)

DecodeBytes dispatches on the byte buffer.

Jump to

Keyboard shortcuts

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