ModbusHub

Modbus register maps you can validate.

Vendor documentation for Modbus devices arrives as PDFs, spreadsheets and the occasional scanned image. It is inconsistent, frequently wrong, and almost never states the things a client actually needs — the address convention, the byte order, whether a value was measured or merely transcribed. ModbusHub turns that material into structured data that can be diffed, validated in CI, and consumed by tools.

Status: draft. The v0.4 schema is still moving and no catalog release has been cut. Pin a commit if you build against it.

10
register maps
18,100
registers
14
read back from hardware
16
declared vendor contradictions

The gap between the second and third numbers is the honest summary of this project's state: almost everything here is transcription, not measurement. Every row says which it is.

The two numbers

Modbus's oldest trap is that the number printed in a vendor PDF is often not the number that goes on the wire. This catalog does not guess. It defines two columns, and the relation between them is fixed by definition:

Register = Address + 1

Address   the value placed in the Modbus PDU    starts at 0
Register  the published ordinal                 starts at 1

What varies between documents is only which column the vendor printed. That is declared once per source file, with an anchor a reviewer can check against the PDF:

source:
  addressing:
    documented_as: "register"
    evidence: "hardware_verified"
    anchor:
      documented: 3019
      expect_label: "Power Meter Manufacture Date/Time"

Source files store the number exactly as printed, under the vendor's own column name. The import scripts perform no arithmetic — the other column is derived at build time. Review stays mechanical: open the PDF, compare, the numbers match.

Evidence, not assertion

Every claim carries its provenance, and the level is recorded per register, not per file. A map may mix levels; a reader must always be able to tell which rows were measured and which were only read off a page.

printed
the document says so explicitly
frame_example
the document shows a wire-level frame
structural
it follows necessarily from the data
inferred
only third-party implementations agree
hardware_verified
it was measured on a real device
unverified
unknown

A map whose addressing evidence is unverified cannot be promoted to verification.status: tested. The validator enforces it.

Contradictions are declared

Vendor documents contradict themselves. When they do, the contradiction is written down rather than quietly corrected — the validator refuses an address overlap that has not been declared.

A real one, from an ENTES MPR register table: the Format column says uint64 while Word Counts says 2. Address spacing settles it, and an older edition of the same table independently confirms the resolution.

document_errors:
  - id: type-98
    registers: [98]
    kind: type_conflict
    documented: "Format = uint64, Word Counts = 2"
    resolution: use_documented

The same mechanism records when two firmware editions of one table assign different meanings to the same address — a failure that returns a plausible wrong number rather than an error.

Checks travel with the data

A register map tells you where a value lives. It does not tell you whether the installation is sane. Commissioning check lists reference measurements, never addresses, so one list runs against a Schneider meter, an ENTES analyzer or a KAEL controller unchanged:

- id: WIR1
  severity: error
  reads: [active_power.phase.*]
  rule: "all_same_sign(active_power.L1, active_power.L2, active_power.L3)"
  on_fail: "One phase draws power in the opposite direction —
            reversed CT or wrong voltage reference."

Checks carry provenance too. evidence: means the check was run on real hardware and this was the result; basis: means a vendor document supports it but nothing has been measured. A check claiming both is rejected.

What is in the catalog today

Nine devices across three manufacturers. This table is generated from the catalog itself, so it cannot drift from what is actually in the repository.

Manufacturer Device Category Registers Verification
Schneider ElectricPM810Power meter1,05614 measured
ENTESMPR 3X-4XPower analyzer1,921document only
KAELMULTISER-01-PC-TFTPower analyzer2,426document only
KAELMULTISER-02-PC-TFTPower analyzer2,472document only
KAELMULTISER-03-PC-TFTPower analyzer2,630document only
KAELMULTISER-04-PC-TFTPower analyzer3,011document only
KAELMULTISER-05-PC-TFTPower analyzer3,014document only
KAELVARkombi-18-PC-TFTPF controller1,385document only
KAELVARkombi-12-PCPF controller185document only

The full list, with per-map evidence breakdowns and every declared contradiction, is in devices.md. What is not done yet — starting with a live capture tool that makes promotion from printed to hardware_verified mechanical — is in ROADMAP.md.

Get started

git clone https://github.com/ModbusHub/registry.git
cd registry
python3 -m pip install -r tools/requirements.txt

python3 tools/validate.py   # schema + cross-file rules
python3 tests/run.py        # valid fixtures pass, invalid ones must fail
python3 tools/export.py     # build dist/catalog.json

Adding a device? Start with the device authoring guide (Türkçe) — a step-by-step walk from a vendor PDF to a validated entry, using the hardware-verified PM810 as the worked example. The short version: transcribe the vendor's numbers verbatim, declare documented_as, provide an anchor, and never silently shift an address.