LW IT Solutions logo LW IT Solutions logo LW IT Solutions
« Przegląd bloga /Smart Home/Tutorials / Tutorial: Odczyt rejestrów SunSpec z falownika PV...
Ten artykuł w innych językach:

Tutorial: Odczyt rejestrów SunSpec z falownika PV przez Modbus TCP

Tutorial: Odczyt rejestrów SunSpec z falownika PV przez Modbus TCP

Management Summary

Falownik słoneczny zamienia energię paneli w użyteczny prąd i udostępnia pomiary do monitoringu. SunSpec nadaje wartościom wspólne znaczenie, a Modbus jest sposobem komunikacji do ich odczytu. Pozwala to połączyć instalację słoneczną z niezależnym systemem obserwacji. Artykuł wyjaśnia znaczenie miejsc zapisu danych, jednostek i przeliczników właściwych urządzeniu: działające połączenie może dawać wiarygodnie wyglądające, lecz błędne liczby, a brak odczytu nie powinien stawać się sztucznym zerem.

Spis treści
  1. 1. Odszukanie mapy bez pomylenia numeracji
  2. 2. Przejście nagłówków modeli
  3. 3. Spójne dekodowanie prądu fazy A
  4. 4. Odczyt z ograniczeniami i kontrolą błędów
  5. Inne typy i częstotliwość odpytywania
  6. Home Assistant: przykład o określonym zakresie
  7. Pytania i odpowiedzi
  8. Źródła

Rejestr Modbus to ponumerowane miejsce przechowujące 16 bitów. SunSpec określa, jak grupy takich rejestrów opisują pomiary i informacje o urządzeniu. Łańcuch modeli pozwala odnaleźć obsługiwane bloki zamiast kopiować adres z innego falownika. Nadal potrzebne są właściwa konfiguracja urządzenia i definicje modeli. Nie każdy pomiar jest dostępny w każdym falowniku.

Poradnik odczytuje prąd fazy A z całkowitoliczbowego modelu falownika 103. Przykład Python korzysta z interfejsu PyModbus 3.16.1 sprawdzonego 24 października 2026 i wykonuje wyłącznie odczyty rejestrów holding. Host, port i identyfikator jednostki zależą od urządzenia. Włączenie dostępu i wybór mapy rejestrów wymagają dokumentacji producenta.

1. Odszukanie mapy bez pomylenia numeracji

Standardowe adresy protokołu SunSpec to 0, 40000 i 50000, liczone od zera. Pierwsze dwa rejestry zawierają 0x5375 i 0x6E53, czyli razem znaki SunS. Te liczby są już adresami protokołu.

Instrukcja może używać przesunięć od zera, numerów rejestrów od jedynki lub tradycyjnych oznaczeń 4xxxx dla rejestrów holding. Zapis „40001” nie zawsze oznacza więc adres 40000. W tradycyjnej notacji może oznaczać pierwszy rejestr holding, czyli adres protokołu 0. Udokumentowana konwencja i znaleziony znacznik muszą być zgodne.

Licznik i falownik mogą korzystać z osobnych identyfikatorów jednostki, odrębnych połączeń lub różnych modeli we wspólnej mapie. Ani drugi identyfikator, ani konkretne ustawienie fabryczne nie są uniwersalne. Poprawna odpowiedź Modbus sama nie dowodzi, że odpowiedziało właściwe urządzenie.

Przykładowa mapa SunSpec z adresami od zera, dwurejestrowymi nagłówkami i modelem końcowym; prąd fazy A i sprawdzony współczynnik skali
Przykładowe adresy dotyczą tylko tego łańcucha. Nazwy i przesunięcia wymagają odpowiedniej definicji modelu.

2. Przejście nagłówków modeli

Po dwóch rejestrach znacznika każdy nagłówek zawiera identyfikator modelu i długość. Długość obejmuje rejestry danych po dwurejestrowym nagłówku. Następny nagłówek znajduje się zatem pod adresem bieżący_nagłówek + 2 + długość. Model końcowy 0xFFFF ma długość 0.

Identyfikatory modeli wskazują układy danych. Nazwy, jednostki i pozycje punktów pochodzą jednak z opublikowanych definicji, a nie z pełnego słownika wysyłanego przez każde urządzenie. Nieznane modele można pominąć według długości; znane nadal wymagają odpowiedniego dekodera.

  • 1: wspólna identyfikacja urządzenia.
  • 101/102/103: całkowitoliczbowe modele falownika: jednofazowy, split phase i trójfazowy.
  • 111/112/113: odpowiednia rodzina zmiennoprzecinkowa, z innym układem danych.
  • 160: rozszerzenie dla wielu trackerów MPP; grupy wejść nie oznaczają zawsze pojedynczych fizycznych łańcuchów.
  • 201–204: całkowitoliczbowe modele liczników dla różnych układów przyłączenia.

Obsługiwane modele i ewentualny wybór formatu zależą od produktu. Model 102 oznacza split phase, a nie dowolną instalację dwufazową. Przykład celowo zgłasza błąd, jeśli nie występuje dokładnie jedna instancja modelu 103.

3. Spójne dekodowanie prądu fazy A

Wszystkie poniższe przesunięcia liczone są od zera względem pierwszego rejestru danych, bez ID i długości: A jest pod 0, AphA pod 1, AphB pod 2, AphC pod 3, a A_SF pod 4. Gdy dane zaczynają się pod 40071, faza A jest pod 40072, a współczynnik skali pod 40075. Odległość adresów wynosi trzy rejestry.

Prąd jest bez znaku, a współczynnik skali jest dziesiętnym wykładnikiem ze znakiem. Surowe 1234 i wykładnik −2 dają 1234 × 10^-2 = 12,34 A. Inny wykładnik oznacza inny mnożnik. Brak prądu 0xFFFF lub brak skali 0x8000 nie może zostać przeliczony na pomiar.

4. Odczyt z ograniczeniami i kontrolą błędów

Przykład sprawdza połączenie, długość odpowiedzi, granice modeli, znacznik końca i obie wartości nieprawidłowe. Używa device_id zgodnie z dokumentacją PyModbus. Starsze przykłady z slave wymagają dostosowania do używanej wersji.

Python
from pymodbus.client import ModbusTcpClient
from pymodbus.exceptions import ModbusException


def read_words(client, address, count, unit):
    if not 1 <= count <= 125 or not 0 <= address <= 65536 - count:
        raise ValueError("Invalid register range")
    response = client.read_holding_registers(
        address, count=count, device_id=unit
    )
    if response.isError():
        raise RuntimeError(f"Modbus error at {address}: {response}")
    words = response.registers
    if len(words) != count or any(not 0 <= x <= 65535 for x in words):
        raise RuntimeError("Incomplete or invalid response")
    return words


def find_base(client, unit):
    failures = []
    for base in (40000, 0, 50000):
        try:
            if read_words(client, base, 2, unit) == [0x5375, 0x6E53]:
                return base
        except (ModbusException, OSError, RuntimeError) as exc:
            failures.append(f"{base}: {exc}")
    raise RuntimeError("No SunS marker found; " + "; ".join(failures))


def discover(client, base, unit):
    position = base + 2
    models = []
    for _ in range(256):
        model, length = read_words(client, position, 2, unit)
        if model == 0xFFFF:
            if length != 0:
                raise RuntimeError("Invalid end model length")
            return models
        next_position = position + 2 + length
        if model == 0 or length == 0 or next_position > 65534:
            raise RuntimeError("Invalid model header or address range")
        models.append((model, position + 2, length))
        position = next_position
    raise RuntimeError("Model scan limit reached")


def phase_a_current(client, models, unit):
    matches = [m for m in models if m[0] == 103]
    if len(matches) != 1:
        raise RuntimeError("Exactly one model 103 required")
    _, start, length = matches[0]
    if length < 5:
        raise RuntimeError("Model 103 is too short")
    block = read_words(client, start, 5, unit)
    raw, sf_raw = block[1], block[4]
    if raw == 0xFFFF or sf_raw == 0x8000:
        return None
    exponent = sf_raw - 65536 if sf_raw >= 32768 else sf_raw
    if not -10 <= exponent <= 10:
        raise RuntimeError("Invalid SunSpec scale factor")
    return raw * (10.0 ** exponent)


def main():
    unit = 1  # Device-specific configuration
    client = ModbusTcpClient("192.168.1.50", port=502, timeout=3)
    try:
        if not client.connect():
            raise RuntimeError("Connection failed")
        base = find_base(client, unit)
        models = discover(client, base, unit)
        print("Base:", base, "Models:", models)
        current = phase_a_current(client, models, unit)
        print("No valid phase A current" if current is None
              else f"{current:.2f} A")
    finally:
        client.close()


if __name__ == "__main__":
    main()

Limit 256 nagłówków zabezpiecza ten przykład; nie jest limitem liczby modeli SunSpec. Brak znacznika, modelu lub poprawnej odpowiedzi przerywa obliczenie zamiast wypisywać pozornie wiarygodną liczbę. Kod obsługuje jedną instancję modelu 103; kilka instancji wymaga jawnego wyboru. Testy korzystały z symulowanego klienta, alternatywnych adresów bazowych, brakujących wartości i uszkodzonych nagłówków. Nie nawiązano połączenia z falownikiem.

Inne typy i częstotliwość odpytywania

SunSpec określa starsze słowo jako pierwsze dla wartości wielorejestrowych. Sam Modbus definiuje przesyłanie rejestrów, a nie wszystkie układy wielosłowowe producentów. Wartości ze znakiem wymagają dekodowania uzupełnienia do dwóch; zmiennoprzecinkowe odpowiedniego formatu IEEE i kontroli wartości nieprawidłowych. Tekst zajmuje stały obszar rejestrów i może być zakończony lub dopełniony bajtami zerowymi.

Nieprawidłowe wzorce bitów zależą od typu SunSpec: przykładowo 0x8000 dla int16, 0xFFFF dla uint16 i zero jako „niezakumulowane” dla acc32. Typy acc32 i uint32 nie są wymienne tylko dlatego, że zajmują po dwa rejestry.

Odczyt bloku zmniejsza liczbę transmisji, lecz nie gwarantuje jednoczesnego fizycznego próbkowania. Funkcja 03 dopuszcza maksymalnie 125 rejestrów w żądaniu; urządzenie może narzucać niższy limit. Częstotliwość odpytywania, liczba połączeń i tempo aktualizacji wynikają z dokumentacji urządzenia. Interwał pięciu sekund nie jest uniwersalnie bezproblemowy dla każdego produktu lub współdzielonej bramy.

Home Assistant: przykład o określonym zakresie

Ta konfiguracja dotyczy wyłącznie pokazanej mapy: dane modelu 103 zaczynają się pod 40071, a A_SF = -2. Stałe scale: 0.01 jest mnożnikiem, nie wykładnikiem. Home Assistant nie wykrywa z tego YAML automatycznie modelu SunSpec ani współczynnika skali.

YAML
modbus:
  - name: inverter
    type: tcp
    host: 192.168.1.50
    port: 502
    sensors:
      - name: "Prąd fazy A"
        slave: 1
        address: 40072
        input_type: holding
        data_type: uint16
        nan_value: 65535
        scale: 0.01
        precision: 2
        unit_of_measurement: "A"
        device_class: current
        state_class: measurement
        scan_interval: 10

nan_value wyklucza nieprawidłowy surowy prąd i według obecnej dokumentacji daje stan unknown. Nie sprawdza osobnego rejestru skali. Stała konfiguracja pasuje tylko przy nadal poprawnej mapie i skali; po zmianie firmware’u lub konfiguracji modeli potrzebne jest ponowne rozpoznanie i porównanie.

Moc w watach używa device_class: power i state_class: measurement. Energia wymaga device_class: energy, obsługiwanej jednostki energii oraz total lub total_increasing zależnie od resetowania. Obecne widoki energii Home Assistant mogą również korzystać z pomiarów mocy. Energię można też obliczyć z mocy i czasu: stałe 1 kW przez dwie godziny daje 2 kWh. Przy zmiennej mocy dokładność zależy od częstotliwości pomiarów i metody obliczeń.

Pytania i odpowiedzi

Dlaczego skala należy do modelu?

Wykładnik ze znakiem łączy zakres liczbowy z rozdzielczością. Dla poprawnej mocy int16 wykładnik 0 pozwala na maksymalnie 32767 W; dodatnie wykładniki zwiększają zakres kosztem większych kroków. Przywołana specyfikacja SunSpec określa punkty skali jako statyczne. Zmiany konfiguracji urządzenia lub firmware’u nadal uzasadniają kontrolę wynikowej mapy.

Co może uszkodzić statystyki energii?

Nieprawidłowe zero, po którym wraca rzeczywisty licznik całkowity, może zostać odczytane przy total_increasing jako reset i ogromny nowy przyrost. Znaczenie mają więc typ punktu i zachowanie przy resetach. Dla akumulatora zdefiniowanego w modelu nieprawidłowe surowe zero wymaga wykluczenia. Tej reguły nie można bez sprawdzenia przenosić do innych czujników, dla których zero jest poprawne. Nieznane dane powinny pozostać nieznane zamiast przyjmować sztuczne zero.

Jak ograniczyć dostęp?

Klasyczny Modbus TCP na porcie 502 nie ma wbudowanego uwierzytelniania ani szyfrowania. Modbus Security jest oddzielnym protokołem opartym na TLS i wymaga obsługi urządzenia. W tym poradniku wystarcza odczyt. Ograniczona ścieżka sieciowa i brak publicznego przekierowania portu zmniejszają ekspozycję. Zapora routująca zwykle nie kontroluje bezpośredniego ruchu w tej samej podsieci; zapory hostów, listy ACL przełączników i izolacja klientów mogą jednak taki ruch ograniczać.

Lukas Wójcik

Lukas Wójcik

Systems architect and technology enthusiast specializing in scalable tracking solutions, GMP Stack (GA4 & GTM), and robust backend architectures. Advocate for clean code and privacy-first design.

Kontakt

Masz pytanie lub pomysł na projekt internetowy? Napisz krótko, o co chodzi. Formularz korzysta z reCAPTCHA i formconfirmed do ochrony przed spamem.

Napisanie komentarza

Doświadczenia z innymi urządzeniami lub integracjami oraz pytania o konfigurację są tu mile widziane.

Adres e-mail nie jest publikowany. Pola obowiązkowe oznaczono gwiazdką.