Changelog¶
Changelog¶
All notable changes to anymodbus are documented here.
The format follows Keep a Changelog, and the project adheres to Semantic Versioning.
Unreleased¶
[0.2.0] - 2026-05-30¶
Adds Modbus-ASCII framing, FC08 diagnostic loopback, FC04 input-register typed
reads, and RS485 cold-start ergonomics — the capabilities requested by
servomexlib for its [modbus-ascii] / AUTO milestone. The public API is
additive and backwards-compatible; the internal framing layer was restructured
behind a narrow Framer strategy seam so RTU / ASCII / (future) TCP share one
response interpreter.
Added¶
- Modbus-ASCII framing.
Bus(stream, framing=Framing.ASCII)selects the:…hex…LRC…CRLF wire format, sharing the existing PDU/register codec unchanged. NewFramingenum (RTU/ASCII) and a thinopen_modbus_ascii(path, *, baudrate, parity, data_bits=8)convenience opener (data_bits=7for the classic 7E1 wire). Sync mirrors included. - Pure LRC primitives at
anymodbus.lrc:lrc8,lrc8_bytes,verify_lrc— mirroringanymodbus.crc(submodule-level, not top-level). - FC08 diagnostic loopback.
Slave.diagnostic_loopback(data=b"\x00\x00")(FC 0x08 sub-function 0x0000, Return Query Data) — a side-effect-free liveness / round-trip probe. PDU codec inanymodbus.pdu; sync mirror. - FC04 input registers in the typed reads.
Slave.read_float/read_int32/read_string(and sync mirrors) acceptsource=RegisterSource.INPUT(FC04) — defaults toHOLDING(FC03). TimingConfig.startup_settle— a one-shot idle wait applied once before the first transaction, to absorb USB-RS485 adapter warm-up.ChecksumErrorbase exception (parent ofCRCErrorand the newLRCError).MockSlaveparity: answers FC08 sub-0 (echo) and can speak ASCII (MockSlave(framing=...),client_slave_pair(framing=...)), so one register bank backs both an RTU and an ASCII bus in hardware-free CI.
Changed¶
- Default
RetryPolicy.retry_onwidened from{CRCError, FrameTimeoutError}to{ChecksumError, FrameTimeoutError}, so ASCIILRCErrorretries by default. RTU behaviour is unchanged (CRCError ⊂ ChecksumError; retry usesisinstance). Note: a direct membership checkCRCError in cfg.retries.retry_onis nowFalseby default even thoughCRCErroris still retried — only the set membership changed, not the behaviour. - FC 0x08 reclassified from "known-unsupported" (raised
ModbusUnsupportedFunctionError) to supported (sub-0 only).CRCErrorreparented fromProtocolErrortoChecksumError(still aProtocolErrortransitively —except CRCError/except ProtocolErrorunaffected). - Internal: the RTU framer was split into
RtuFramer.read_adu(reads one raw frame) plus a sharedinterpret_response_pdu; the module-levelread_response_aduis preserved as a compatibility wrapper.
No breaking changes to documented public API.
0.1.1 - 2026-04-26¶
First post-release polish, driven by the REVIEW.md notes against v0.1.0 ahead of the first downstream consumers (watlowlib + planned alicatlib / sartoriuslib). The package is still alpha; backwards-compatibility shims were not kept.
Added¶
RegisterTypeenum (int16/uint16/int32/uint32/int64/uint64/float32/float64/string) and a type-dispatcheddecode(words, *, type=RegisterType.X, ...)/encode(value, *, type=RegisterType.X, ...)pair inanymodbus.decoders. Recommended entry point for downstream device libraries that load a register schema from configuration.register_count_for(type)helper for schema callers.- New per-type helpers:
decode_int16/encode_int16,decode_int64/encode_int64,decode_float64/encode_float64. decode_string/encode_stringgained abyte_orderkeyword for devices that store strings byte-swapped within each register.encode_stringnow accepts eitherregister_countorbyte_count— the latter is convenient for spec'd-in-bytes fields whose length isn't a multiple of two.Slave.read_string/Slave.write_string(and their sync mirrors) accept eitherregister_countorbyte_count, plusbyte_order.FCshort alias forFunctionCodeat the top-level package surface, fulfilling the long-standing docstring promise.
Changed¶
TimingConfig.inter_char_timeoutrenamed toTimingConfig.inter_char_idleto match the spec term used throughout DESIGN.md. The framer's keyword argument is renamed to match.ModbusUnknownExceptionErroris now a subclass ofModbusExceptionResponse, so callers wanting "any slave-returned exception" canexcept ModbusExceptionResponseand be done. The raw byte is exposed onexception_code(was:code).code_to_exceptionnow returnsModbusExceptionResponseinstead ofModbusError.
Removed¶
BusBusyErrorfrom DESIGN.md — it was documented but never implemented. The bus lock serializes transactions; the right resource-busy signal already comes fromanyio.BusyResourceErroron the underlying stream.
0.1.0 - 2026-04-25¶
Initial release. See DESIGN.md for the full plan.
Added¶
- Repository skeleton:
pyproject.toml,Makefile, CI/docs/publish workflows, pre-commit config, src/tests/docs layout. - Foundational pure-Python pieces:
anymodbus._types(enums + small dataclasses),anymodbus.exceptions(fullModbusErrortree +code_to_exceptiontranslator),anymodbus.crc(CRC-16/Modbus, 256-entry table),anymodbus.config(frozen dataclasses with validation). WordOrderandByteOrdernamed enums for 32-bit value layout, with defaults matching the Modbus Application Protocol spec example (high-word-first, big-endian within word).Bus(half-duplex single-master client) and per-slaveSlavehandle, withopen_modbus_rtu(...)convenience opener overanyserial.- Length-aware RTU
Framerwith timing-gap fallback for unknown function codes and tx-side 3.5-character pre-tx idle gap. - PDU encode/decode for the v0.1 function code set (FC 1–6, 15, 16) and
decodersfor 32-bit float / int values across two registers. Capabilitymodel (SUPPORTED/UNSUPPORTED/UNKNOWN) populated from probe results.- Sync wrapper (
anymodbus.sync) using the same shared-portal pattern asanyserial.sync. anymodbus.testingwithMockSlave,FaultPlan, andclient_slave_pair()for hardware-free integration tests.