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
Spis treści
  1. Dlaczego sam numer rejestru nie wystarcza
  2. Znalezienie adresu bazowego i znacznika
  3. Przejście łańcucha modeli
  4. Współczynniki skali, typy danych i wartości zastępcze
  5. Odczyt jednej wartości od początku do końca
  6. Przeniesienie całości do Home Assistant
  7. Pytania i odpowiedzi
  8. Źródła

Numer rejestru znaleziony we wpisie na forum działa. Działa do najbliższego firmware’u, do kolejnego modelu tego samego falownika, do chwili dołączenia drugiego urządzenia odkładającego tę samą wartość gdzie indziej. Wtedy numer przestaje się zgadzać i nic tego nie mówi: Modbus podaje to, co stoi pod tym adresem, a liczby prawdopodobnej nie da się odróżnić od poprawnej.

Z tego powodu istnieje SunSpec. To spis położony na przestrzeni rejestrów, a jego odczytanie kosztuje cztery kroki działające jednakowo w każdym falowniku z tą obsługą.

Mapa rejestrów od adresu 40000 ze znacznikiem SunS, dwoma nagłówkami modeli wraz z blokami i znacznikiem końca, obok rozkodowanie jednej wartości wraz z jej rejestrem skali
Łańcuch opisuje sam siebie: każdy model podaje własną długość, adres następnego wynika więc z bieżącego. Znaleźć trzeba tylko początek.

Dlaczego sam numer rejestru nie wystarcza

Modbus nie zna nazw, jednostek ani typów. Odczyt daje szesnaście bitów, a każda wykładnia tych bitów odbywa się po stronie czytającego. Rejestr o zawartości 1234 może oznaczać 1234 waty, 123,4 wolta albo 12,34 ampera – a protokół nic o tym nie mówi.

SunSpec dokłada trzy rzeczy, a wszystkie trzy stoją w samej przestrzeni rejestrów: znacznik mówiący, że spis istnieje, łańcuch bloków mówiący, jakie pomiary są i gdzie, oraz na każdą grupę wartości współczynnik skali mówiący, gdzie stoi przecinek.

Praktycznym skutkiem jest to, że raz napisany czytnik działa również przy następnym urządzeniu. Waży to więcej, niż brzmi, bo rozwiązanie alternatywne – tabela adresów na każdego producenta i każdy stan firmware’u – jest właśnie tym, co po cichu się dezaktualizuje.

Znalezienie adresu bazowego i znacznika

Spis zaczyna się pod jednym z trzech adresów, a dwa pierwsze rejestry mówią, czy zaczyna się właśnie tam. Zawierają cztery znaki SunS, jako wartość 32-bitowa 0x53756E53.

from pymodbus.client import ModbusTcpClient

c = ModbusTcpClient("192.168.1.50", port=502)
c.connect()

for basis in (40000, 0, 50000):
    r = c.read_holding_registers(basis, count=2, slave=1)
    if not r.isError() and r.registers == [0x5375, 0x6E53]:
        print("SunSpec od", basis)
        break

W tym krótkim kawałku kodu tkwią dwa kamienie potknięcia. Pierwszym jest przesunięcie o jeden, na którym każdy raz się przewraca: dokumentacja liczy rejestry od jedynki, a protokół adresuje je od zera. Adres wydrukowany w podręczniku jako 40001 jest na łączu przesunięciem 40000, a odczyt pod 40001 daje drugą połowę znacznika i żadnej zgodności.

Drugim jest adres urządzenia. Falownik z wbudowanym licznikiem odpowiada jako dwa urządzenia na tym samym połączeniu, każde z własnym identyfikatorem i własnym spisem SunSpec. Odczyt mocy licznika pod identyfikatorem falownika daje wartość, która nie jest błędem i nie jest prawdą. Które identyfikatory są w użyciu, pokazuje strona Modbus w interfejsie webowym urządzenia – i tam też Modbus TCP zostaje w ogóle włączony, bo w większości falowników jest fabrycznie wyłączony.

Przejście łańcucha modeli

Po znaczniku następuje szereg bloków, a każdy blok zaczyna się od dwóch rejestrów: numeru modelu i długości. Długość dodana do bieżącego położenia daje początek następnego bloku, a numer modelu 65535 kończy łańcuch.

pos = basis + 2
while True:
    kopf = c.read_holding_registers(pos, count=2, slave=1).registers
    modell, laenge = kopf[0], kopf[1]
    if modell == 0xFFFF:
        break
    print(f"model {modell:5d}  dlugosc {laenge:3d}  dane od {pos + 2}")
    pos += 2 + laenge

Zwykły falownik odpowiada trzema albo czterema modelami, a numery mówią, czym każdy z nich jest.

Model Zawartość
1 Wspólne: producent, oznaczenie modelu, numer seryjny, wersja firmware’u
101, 102, 103 Falownik jedno-, dwu-, trójfazowy – liczby całkowite ze współczynnikami skali
111, 112, 113 Te same pomiary jako liczby zmiennoprzecinkowe, bez współczynników skali
160 Poszczególne łańcuchy, jeden powtarzalny blok na każdy regulator MPP
201 do 204 Licznik, po jednym modelu na rodzaj przyłączenia

To, czy urządzenie oferuje 103 czy 113, bywa zwykle ustawieniem, a nie właściwością. Wiele falowników ma przełącznik między liczbami całkowitymi ze współczynnikami skali a zmiennym przecinkiem, i to on wyznacza, która z dwóch rodzin modeli pojawi się w łańcuchu. Czytnik oczekujący jednego, a zastający drugie, nie melduje w ogóle żadnych pomiarów – objaw mylący dla ustawienia, którego zmiany nikt nie pamięta.

Współczynniki skali, typy danych i wartości zastępcze

Wewnątrz bloku modelu położenie wartości stoi w specyfikacji i liczone jest od początku danych. Prąd fazy A siedzi na przesunięciu 2, a należący do niego współczynnik skali na przesunięciu 5.

Model 103, dane od 40071

  przesuniecie 1   A      uint16   prad calkowity
  przesuniecie 2   AphA   uint16   faza A
  przesuniecie 3   AphB   uint16   faza B
  przesuniecie 4   AphC   uint16   faza C
  przesuniecie 5   A_SF   int16    wspolczynnik skali dla wszystkich czterech

  40072 = 1234        wartosc surowa
  40075 = 0xFFFE      uzupelnienie do dwoch, czyli −2

  1234 × 10⁻² = 12,34 A

Współczynnik skali jest wykładnikiem dziesiętnym ze znakiem i obowiązuje dla całej grupy wartości, a nie dla jednej. Dlatego pojawia się raz na cztery prądy i dlatego prąd odczytany bez niego daje liczbę stukrotnie za dużą – a ta w instalacji domowej nadal wygląda na wiarygodny pomiar.

W praktyce liczą się jeszcze trzy właściwości kodowania. Wartości zajmujące dwa rejestry stoją słowem starszym z przodu, co odpowiada konwencji Modbusa i co niemal każda biblioteka robi poprawnie sama z siebie. Pola tekstowe mają stałą długość i są dopełniane bajtami zerowymi, a nie nimi kończone. A wartość, której urządzenie nie prowadzi, nie wynosi zera, lecz jest wartością zastępczą: 0x8000 dla int16, 0xFFFF dla uint16, 0x80000000 dla int32. Czytnik traktujący je jako liczby melduje 65535 woltów – a reguła je odrzucająca ma trzy linie.

LEER = {"int16": 0x8000, "uint16": 0xFFFF,
        "int32": 0x80000000, "uint32": 0xFFFFFFFF}

def wert(roh, typ, sf):
    if roh == LEER.get(typ):
        return None
    return roh * (10 ** sf)

Odczyt jednej wartości od początku do końca

Wszystkie części razem dają czytnik, który model odnajduje, zamiast zakładać jego adres.

def modell_finden(c, basis, gesucht, slave=1):
    pos = basis + 2
    while True:
        m, laenge = c.read_holding_registers(pos, count=2, slave=slave).registers
        if m == 0xFFFF:
            return None
        if m == gesucht:
            return pos + 2, laenge
        pos += 2 + laenge

def vorzeichen(r):
    return r - 0x10000 if r > 0x7FFF else r

start, _ = modell_finden(c, 40000, 103)
block    = c.read_holding_registers(start, count=6, slave=1).registers

strom = block[1]                 # AphA, przesuniecie 2 liczone od jedynki
sf    = vorzeichen(block[4])     # A_SF, przesuniecie 5
print(f"{strom * 10 ** sf:.2f} A")

Warto odczytywać cały blok jednym żądaniem, a nie każdą wartość osobno. Odczyt Modbusa kosztuje niezależnie od długości kilka milisekund czasu obiegu – sześć rejestrów w jednym wywołaniu kosztuje więc tyle, co jeden rejestr w jednym wywołaniu. Ważniejsze zaś: wszystkie sześć wartości pochodzi wtedy z tej samej chwili. Sześć osobnych odczytów prądu, napięcia i mocy daje trzy wielkości do siebie niepasujące, a zbudowane na nich sprawdzenie wiarygodności odzywa się bez powodu.

Uwaga o częstotliwości odpytywania: falownik nie jest bazą danych. Żądanie co pięć sekund jest bezproblemowe, raz na sekundę bywa w części urządzeń granicą, a szybciej wytwarza przekroczenia czasu wyglądające na usterki sieci. Same wartości pomiarowe i tak rzadko odświeżają się częściej niż co sekundę.

Przeniesienie całości do Home Assistant

Gdy adresy są znane, ten sam odczyt działa również opisowo. Integracja Modbus w Home Assistant zajmuje się typem danych i znakiem; wpisać trzeba jedynie współczynnik skali jako liczbę, bo integracja nie odczytuje go z urządzenia.

modbus:
  - name: wechselrichter
    type: tcp
    host: 192.168.1.50
    port: 502
    sensors:
      - name: "Prad faza A"
        slave: 1
        address: 40072
        data_type: uint16
        scale: 0.01
        precision: 2
        unit_of_measurement: "A"
        device_class: current
        state_class: measurement
        scan_interval: 10
      - name: "Moc czynna"
        slave: 1
        address: 40083
        data_type: int16
        scale: 1
        precision: 0
        unit_of_measurement: "W"
        device_class: power
        state_class: measurement
        scan_interval: 10

Wpisany na stałe scale jest kompromisem tej drogi i warto odnotować, skąd się bierze. Współczynnik skali odczytano raz powyższym skryptem; stoi w urządzeniu i praktycznie nigdy się nie zmienia, ale gwarancji na to nie ma. Po aktualizacji firmware’u porównanie wartości wyświetlanej z ekranem falownika trwa dziesięć sekund i wyłapuje stukrotność natychmiast.

Ostatnie pole rozstrzyga, czy wartość trafi do statystyki energii, a nie tylko na kartę. Wartość mocy w watach to device_class: power ze state_class: measurement; stan licznika w kilowatogodzinach to device_class: energy ze state_class: total_increasing. Jedno z drugim nie jest wymienne, a wybrać jako źródło energii da się wyłącznie to drugie.

Pytania i odpowiedzi

Dlaczego współczynnik skali jest zapisany w urządzeniu, a nie ustalony w specyfikacji?

Dlatego, że szesnaście bitów mieści ograniczony zakres liczb, a ten sam model obowiązuje dla urządzeń bardzo różnej wielkości. Moc jako int16 ze współczynnikiem 0 sięga tylko do 32 767 watów; większy falownik musi przejść na kroki co dziesięć, a mały może ze współczynnikiem ujemnym podawać wartości z większą rozdzielczością. O proporcji między zakresem a rozdzielczością decyduje producent, a rejestr informuje czytnik o tym wyborze.

Na co trzeba uważać przy stanie licznika przeznaczonym dla statystyki energii Home Assistant?

Całkowita wyprodukowana energia stoi w modelu falownika jako licznik narastający zajmujący dwa rejestry, ze starszym słowem z przodu, w watogodzinach i z własnym współczynnikiem skali. W integracji Modbus powstaje z niego czujnik z 32-bitowym typem danych bez znaku, device_class: energy i state_class: total_increasing, jak opisano w artykule.

Pułapka tkwi w samym total_increasing. Home Assistant traktuje spadek wartości jako początek nowego cyklu licznika. Jeśli falownik raz poda 0, a potem znowu prawdziwy stan, statystyka zaliczy całą energię od uruchomienia instalacji do tej jednej godziny.

Przy licznikach narastających 0 jest zarazem właśnie wartością zastępczą: w odróżnieniu od int16 i uint16 w typach licznikowych specyfikacji SunSpec zero oznacza „nieprowadzone”, a tabela LEER z artykułu potrzebuje dla nich dodatkowego wpisu. W Home Assistant opcja nan_value integracji Modbus zamienia taką surową wartość w stan „niedostępny”, a niedostępny czujnik nie wpływa na statystykę.

Jak duże ryzyko niesie włączenie Modbus TCP w falowniku?

Modbus TCP nie zna ani uwierzytelniania, ani szyfrowania. Każde urządzenie, które dociera do portu 502 falownika, może czytać te same rejestry co Home Assistant, a jeśli zapis jest dozwolony, także zmieniać ustawienia, na przykład nałożyć ograniczenie mocy. Ryzyko ograniczają trzy środki:

  • Sam odczyt. Jeśli interfejs webowy urządzenia daje wybór, do wszystkiego z artykułu wystarcza dostęp tylko do odczytu.
  • Brak przekierowania portu. Otwarcie portu na routerze czyni z domowej instalacji urządzenie, do którego dotrze każdy skaner w internecie.
  • Osobny segment sieci dla falownika, do którego dostęp ma wyłącznie komputer z Home Assistant. W obrębie jednego segmentu żadna zapora nie widzi połączenia, dlatego reguła działa dopiero między dwoma segmentami.
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.

Get in Touch

Briefly describe your project or inquiry for a tailored response. This site is protected by reCAPTCHA.

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ą.

Artykuły i kategorie

CCTV

Śledź tę kategorię przez RSS

Cloud & AI

Wszystkie artykuły w tej kategorii (11) Śledź tę kategorię przez RSS

Data Privacy

Wszystkie artykuły w tej kategorii (18) Śledź tę kategorię przez RSS

Digital Analytics

Wszystkie artykuły w tej kategorii (58) Śledź tę kategorię przez RSS

Digital Marketing

Wszystkie artykuły w tej kategorii (37) Śledź tę kategorię przez RSS

IT & Networks

Wszystkie artykuły w tej kategorii (17) Śledź tę kategorię przez RSS

Music Production

Wszystkie artykuły w tej kategorii (16) Śledź tę kategorię przez RSS

Raspberry Pi

Śledź tę kategorię przez RSS

SaaS & Internet Earning

Artykuły w przygotowaniu

Smart Home

Wszystkie artykuły w tej kategorii (18) Śledź tę kategorię przez RSS

Tworzenie stron internetowych

Wszystkie artykuły w tej kategorii (11) Śledź tę kategorię przez RSS

Wtyczki i triki WordPress

Wszystkie artykuły w tej kategorii (13) Śledź tę kategorię przez RSS