Skip to content

feat: add SunSpec register type conversion and multiple listeners per… - #57

Open
ChristophCaina wants to merge 2 commits into
tiagocoutinho:masterfrom
ChristophCaina:feature/sunspec-format-conversion
Open

feat: add SunSpec register type conversion and multiple listeners per…#57
ChristophCaina wants to merge 2 commits into
tiagocoutinho:masterfrom
ChristophCaina:feature/sunspec-format-conversion

Conversation

@ChristophCaina

Copy link
Copy Markdown
Contributor

Feature: SunSpec Register Type Conversion & Multiple Listeners per Device

Background

The SunSpec Alliance defines a standard for Modbus-based communication with
solar and energy storage devices. However, the SunSpec specification allows
vendors to implement register data in two distinct formats:

  • Integer model: values stored as int16 with a signed scale factor (sunssf)
  • Float model: values stored as float32
    Both representations are fully SunSpec-compliant, but they are not
    interoperable
    . A client expecting float32 will misinterpret int16+SF data,
    and vice versa. This creates a practical problem in mixed-vendor environments
    where multiple devices or energy management systems need to communicate via
    Modbus TCP, but differ in which SunSpec profile they implement.

A concrete example: certain inverter manufacturers implement the integer model
while some energy management systems expect the float model — both following
the SunSpec standard, yet unable to communicate directly. This kind of
incompatibility is not limited to any specific product combination; it is an
inherent consequence of the SunSpec specification permitting both formats.

Since modbus-proxy already solves the "one device, many clients" problem at
the connection level, it is a natural fit to also solve this data type
mismatch at the protocol level — without requiring any changes to the actual
devices or clients.

Changes

1. Multiple listeners per device (listeners)

A new listeners key (plural) allows configuring multiple TCP server
endpoints for a single Modbus device. Each listener can have its own
conversion rules, so different clients can receive data in different formats
while the physical device is only queried once.

The existing listen (singular) key remains fully supported for
backwards compatibility.

devices:
  - modbus:
      url: inverter.local:1502
    listeners:
      - bind: 0:5020          # original format — no conversion
      - bind: 0:5021          # converted format for incompatible client
        register_conversions:
          - address: 40084
            sf_address: 40085
            source_type: int16
            target_type: float32

2. Register type conversion (register_conversions)

Each listener optionally accepts a list of register_conversions. Each rule
describes a single register to convert:

Field Required Description
address yes Modbus register address (base-1) of the value register
sf_address when source_type: int16 Address of the associated scale factor register
source_type yes Data type the real device sends (int16 or float32)
target_type yes Data type this listener should serve (int16 or float32)

Supported conversions:

source_type target_type Direction
int16 float32 Integer model → Float model
float32 int16 Float model → Integer model

Scale factor values are cached from previous replies, so conversions remain
stable even when the scale factor register is not included in every polling
window.

3. Scale factor caching & warmup

Scale factor sunssf values are cached across replies. This ensures stable conversions even when the scale factor register is not polled in every request cycle, which is common in practice.
On startup, all configured scale factor registers are queried automatically before the first client connects. This guarantees that conversions are immediately correct after a restart — no initial polling cycle is needed to warm up the cache.

4. Backwards compatibility

  • The legacy listen (singular) + register_conversions at device level
    continues to work unchanged
  • Devices without register_conversions behave exactly as before
  • No breaking changes to the CLI or existing configuration files

Use cases

This feature is useful wherever a Modbus TCP proxy sits between devices that
use different SunSpec register formats:

  • Energy management systems expecting float32 connected to inverters that
    deliver int16+SF (or vice versa)
  • Legacy SCADA or BMS systems that only support int16 but need to read from
    modern devices delivering float32
  • Multi-vendor installations where different components implement different
    SunSpec profiles
  • Replacing a device with one from a different vendor without updating all
    existing clients

Testing

Validated against a live SunSpec installation:

  • int16+SF values from the real device read on port 5020 (pass-through)
  • Converted float32 values read on port 5021
  • Both sets of values compared against a reference integration reading the
    same registers — differences within expected polling timing margin only
  • Backwards compatibility verified: existing configurations without
    register_conversions behave identically to the previous version

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant