Ankündigung

Einklappen
Keine Ankündigung bisher.

Berechnung erst nach vollständiger Modbus-Abfrage durchführen

Einklappen
X
 
  • Filter
  • Zeit
  • Anzeigen
Alles löschen
neue Beiträge

    Berechnung erst nach vollständiger Modbus-Abfrage durchführen

    Hallo zusammen,

    ich stehe gerade etwas auf dem Schlauch. Ich würde gerne eine Berechnung im Logikeditor zeit- bzw. Ereignisgesteuert bzw. nach abgeschlossener Modbus-Abfrage durchführen, ich weiß aber nicht wie.

    Ich nutze einen Modbus Master, um alle 20 Sek. die Leistungswerte meiner PV-Anlage abzufragen. Das klappt auch wunderbar. Die Leistungswerte müssen allerdings noch mit einem variablen, ebenfalls von der PV-Anlage gelieferten Skalierungsfaktor (dem Exponenten zur Basis 10) multipliziert werden. Auch das klappt, führt aber zu folgendem Problem:

    Offenbar werden Leistungswert und Skalierungsfaktor nicht gleichzeitig abgefragt, sondern nacheinander. Das führt dazu, dass meine Berechnung zunächst mit dem aktuellen Leistungswert und dem Skalierungsfaktor aus der vorherigen Abfrage durchgeführt wird. Erst nachdem auch der Skalierungsfaktor abgefragt wurde, wird die Berechnung nochmal durchgeführt, diesmal mit dem aktuellen Skalierungsfaktor. D.h. immer wenn sich der Skalierungsfaktor ändert, habe ich kurzfristig einen falschen Wert im System.

    Ich lasse die Werte per Webrequest ebenfalls alle 20 Sek. in eine InfluxDB schreiben. Selten, aber doch gelegentlich wird aufgrund der beschriebenen Problematik ein um den Faktor 10 zu hoher Wert in die Datenbank geschrieben.

    Mein Wunsch wäre also, dass die Berechnung erst nach vollständiger Abfrage der Modbus-Werte durchgeführt wird. Wie kann ich das realisieren?

    Vielen Dank für eure Hilfe!​

    Logik-1.jpg

    Logik-2.jpg

    Grafana.jpg
    Zuletzt geändert von liveislife; 13.10.2024, 11:29.

    #2
    Ich kann dir nichts zum EIB-PC sagen, hatte aber mit Homeassistant das genau gleiche Problem. Siehe hier: https://knx-user-forum.de/forum/%C3%...t-scale-factor

    Ich habe auf SunSpec als Integration umgeschwenkt, da ich auch die werte einzeln abgefragt habe und wollte damit rechnen. Mein Ergebnis waren genau diese Sprünge, wenn der Scalefactor noch nicht aktuell war.

    Die Sunspec-Integration macht das anders: Es werden nicht alle Modbus-Adressen nacheinander einzeln abgefragt, sondern fragt alles unmittelbar hintereinander ab (Multi-Read). Erst dann werden die Variablen getrennt und dann gerechnet. Somit entsteht das Problem nicht mehr.

    Wie du das mit deiner Hardware umsetzen kannst, die Frage kann ich leider nicht beantworten...

    Kommentar


      #3
      Danke für den Hinweis. Ich habe auch einen Fronius WR. Meine Wärmepumpe empfängt ebenfalls Daten des Wechselrichters und verlangt zwingend "int + SF". Ich bin deshalb leider festgelegt.

      Ich lese die Daten auch mit dem Fronius OpenHAB-Binding aus, das funktioniert einwandfrei ohne Skalierungsfehler. Das Binding nutzt dazu die Fronius API. Ich möchte allerdings nach und nach die Komplexität des Systems reduzieren (s. hier: https://knx-user-forum.de/forum/%C3%B6ffentlicher-bereich/knx-eib-forum/knx-einsteiger/1974442-smart-home-vererben) und versuche deshalb, möglichst viel durch den EibPC machen zu lassen. Das klappt auch ziemlich gut, wenn da nicht die Details wären

      Aber vielleicht gibt es ja noch eine Lösung. Man könnte wohl auch die Daten auch wie OpenHAB per JSON API abfragen (https://www.fronius.com/de-ch/switze...olar-api-json-). Aber ob ich das hinbekomme...

      Wie bist Du denn an die SunSpec-Daten gekommen, was ist das HACS und was macht es?
      Zuletzt geändert von liveislife; 13.10.2024, 16:08.

      Kommentar


        #4
        HACS = HomeAssistantCommunityStore : da wo Entwickler Erweiterungen ablegen können, die nicht zum Homeassistant gehören.
        Da gibts dann diese Erweiterung: https://github.com/CJNE/ha-sunspec

        Das schöne dabei: über die UI wählst du einfach deine Konfiguration aus (Wechselrichter, usw) und der legt direkt alle Daten automatisch an, inklusive der kompletten Skalierung.

        Bringt dich aber leider nicht weiter…

        Aber eventuell kannst du ja aus dem Github-Repository Ideen sammeln, wie der das umsetzt und das auf den eibPC umbauen…

        Kommentar


          #5
          Zitat von tsb2001 Beitrag anzeigen
          Aber eventuell kannst du ja aus dem Github-Repository Ideen sammeln, wie der das umsetzt und das auf den eibPC umbauen…
          Das übersteigt meine Kompetenzen, aber trotzdem Danke für den Hinweis.

          Kommentar


            #6
            Vielleicht bringt dich das etwas weiter. Ist quasi ähnlich, der liest alle wichtigen Daten ab Adresse 40265 mit 88 Zählern nach oben durchgängig aus:
            Kommt zwar auch aus dem Homeassistant, aber der Weg sollte in allen Systemen ähnlich funktionieren und umzusetzen sein.
            Code:
                  - name: reading_inverter_multiple_raw
                    slave: 1
                    count: 88
                    address: 40265
                    scan_interval: 2
                    data_type: custom
                    # Registers and positions in custom structure
                    # 0  DCA_SF
                    # 1  DCV_SF
                    # 2. DCW_SF DC Energy scaling factor
                    # 3. DCWH_SF
                    # 4  1_ID
                    # 5  1_DCA
                    # 6. 1_DCV
                    # 7. 1_DCW Energy string 1
                    # 8  2_ID
                    # 9. 2_DCA
                    # 10 2_DCV
                    # 11 2_DCW Energy string 2
                    # 12 3_ID
                    # 13 3_DCA
                    # 14 3_DCV
                    # 15 3_DCW Energy to battery, charging
                    # 16 4_ID
                    # 17 4_DCA
                    # 18 4_DCV
                    # 19 4_DCW Energy from battery, discharging
                    structure: ">hhhh8x H16xHHH16x H16xHHH16x H16xHHH16x H16xHHH16x"​
            Danach werden die Daten als struct ausgefiltert und nur das verwendet, was genutzt werden soll (kommt aus Python) https://docs.python.org/3/library/struct.html:
            Code:
            ">hhhh8x H16xHHH16x H16xHHH16x H16xHHH16x H16xHHH16x"
            ​
            Zur Erklärung:
            • 4 "h" sind die short integer von den Scale-Factors mit jeweils 2 Byte (Modbus 40266, 40267, 40268, 40269)
            • 8 "x" (als jeweils ein Byte) wird ignoriert (Modbus 40270-40273)
            • 1 "H" ist dann die uint16 von der "Input ID (1_ID) (Modbus 40274)
            • 16 "x" (als jeweils ein Byte) wird ignoriert (Modbus 40275-40283) wäre der Name als string vom Input 1 -braucht man nicht
            • 3 "H" hintereinander sind dann die uint16 des Stroms, der Spannung und der Leistung vom MPP1 (40284-40286)
            ...und der Rest wiederholt sich...

            Dann wird das ausgelesene Ergebnis aus der Variable "reading_inverter_multiple_raw" in Einzelteile gesplittet und jedem Wert eine separate Variable zugewiesen:
            Code:
                  derived_energy_pv_output_string2:
                    friendly_name: "PV DC Output String 2"
                    value_template: "{{ (((states('sensor.reading_inverter_multiple_raw').split(',')[11] | int)) * (10 ** ((states('sensor.reading_inverter_multiple_raw').split(',')[2] | int)) )) |round(0)}}"
                    unit_of_measurement: 'W'
            Hier nimmt man den "sensor.reading_inverter_multiple_raw" und splittet den an der elften Stelle (2. MPP Energie in W als Rohdatum) und multipliziert den mit der 2. Stelle (dem Skalierungsfaktor für die Energie)

            Durch das "Abholen" aller Werte hintereinander hat man dann in einem Rutsch die richtigen Skalierungsfaktoren zu dem Rohwert.

            So kann man das machen - wie das im eibPC umsetzbar ist, kann ich nicht beantworten.
            Ich hoffe aber dir damit aufs Pferd geholfen zu haben - mit ein bisschen Glück klappts dann auch mit dem Reiten...

            Meine Idee bei deinem grafischen Editor:
            Du fragst nicht die eine Variable ab, sondern direkt die ganzen Bytes hintereinander. Dann müsstest du die Variablen daraus extrahieren (splitten) und dann könntest du rechnen. Ob und wie das geht - da bin ich überfragt, da ich das System von Dir nicht kenne.

            Viele Infos (und auch das Beispiel von oben) habe ich von dieser Seite: https://community.home-assistant.io/...bus-tcp/264577

            Kommentar


              #7
              Mega, vielen Dank für Deine Mühe! Ich brauche allerdings etwas Zeit, um mich da einzuarbeiten. Ich werde berichten, wie weit ich gekommen bin.

              Kommentar


                #8
                Nach einem ersten Test: Wenn ich das richtig verstehe funktioniert der EibPC wie folgt:
                1. Ich kann auswählen, ab welchem Holding-Register gelesen werden soll (hier: 40265)
                2. Wie viele weitere Register gelesen werden sollen, bestimmt der Datentyp der Variable, in die der Wert des Holding-Registers geschrieben wird (s. Screenshot 2).
                3. Als Datentypen stehen mir die im Screenshot 3 dargestellten Typen zur Verfügung.
                Ergebnis bisher: Wenn ich z.B. c14 auswähle, liefert mir die Abfrage das Wort "String 1". Wähle ich u64, bekomme ich eine längere Ziffernfolge. Wähle ich c1400 oder gar c65534 bekomme ich eine Fehlermeldung.


                Vielleicht kann ja jemand, der ebenfalls einen EibPC hat sagen, ob eine Abfrage mit 88 Zählern überhaupt möglich ist.

                EibPC-0.png EibPC-1.png EibPC-2.png
                Zuletzt geändert von liveislife; 14.10.2024, 19:19.

                Kommentar


                  #9
                  Hi,

                  aus Sicht des EibPC ist die Abfrage von vielen Werten üder einen cXXX kein Problem. Vermutlich hat aber die Gegenstelle da eine Grenze. Laut Screenshot sind es aber immer benachbarte Register, also zB 40084, 40085.
                  Lege dir eine u32-Variable an und frage nur die Adresse 40084 ab. Dann solltest du die 40085 mitbekommen, falls der WR Modbus Function Code 3 unterstützt.
                  Dann kannst du mit dem Shift-Knoten um die Konstante 16 und Typkonvertierungen den Wert jeweils als S16 konvrtieren.

                  Kommentar


                    #10
                    Zitat von ;n1986904
                    Vermutlich hat aber die Gegenstelle da eine Grenze.
                    Die Gegenstelle (hier der Fronius) hat keine Beschränkung. Mit dem Sunspec werden alle Modbus-Register in einem Rutsch ausgelesen und dann verarbeitet.

                    Kommentar


                      #11
                      Zitat von foobar0815 Beitrag anzeigen
                      falls der WR Modbus Function Code 3 unterstützt.

                      Das scheint so zu sein:​
                      fronius2.png

                      Dann kannst du mit dem Shift-Knoten um die Konstante 16 und Typkonvertierungen den Wert jeweils als S16 konvrtieren.
                      Das hat teilweise geklappt.
                      1. Die Variable habe ich als u32 angelegt.
                      2. Die aktuelle Leistung (der Wert des ersten Registers) bekomme ich mit dem Bitshift und der Schiebeweite "-16" als s08 und der anschließenden Typenkonvertierung des Bitshift-Knotens von u32 auf s16 (nicht: "automatisch") korrekt angezeigt.
                      3. Der Scale-Faktor (Wert des zweiten Registers) klappt allerdings noch nicht. Als Wert für den Bitshift verwende ich hier "16" (s08). Wenn sich der Scale-Faktor ändert, ändert sich auch der Wert des Ausgangs des Bitshift-Knotes, aber das Ergebnis stimmt nicht. Der Wert nach der Typkonvertierung ist immer "0". Nur wenn der Scale-Faktor tatsächlich "0" ist, zeigt mir schon der Ausgang des Bitshift-Knotens den Wert "0". Ist der Scale-Factor von null abweichend (positiv oder negativ), zeigt mir der Ausgang des Bitshift-Knotes eine aus mehreren Ziffern bestehende Zahl an, aber nach der Typkonvertierung wird darus immer wieder "0".
                      fronius3.png

                      Vielen Dank für eure Hilfe!

                      Kommentar


                        #12
                        Zu 3: kein Shift, nur Convert nach s16. Du willst ja nur die untersten 16 Bit haben. Wenn du um 16 nach links (positiv) schiebst, schiebst du ja 16 Nullen von rechts rein.

                        Kommentar


                          #13
                          Schön, die ganzen Bäume hier, aber wo ist denn jetzt der Wald? Danke, da hätte ich auch selbst drauf kommen können.

                          Es bleibt aber noch ein Problem: Register 40088 hält einen Wert, Register 40092 den Scale-Faktor. Es liegen also drei Register zwischen den benötigten Daten. Mit einer u64 Variablen kann ich gleichzeitig die Register 40088 bis 40091 abgreifen, aber leider nicht mehr 40092.

                          Wie kann ich hier vorgehen? Vielen Dank für eure Mühe!

                          Kommentar


                            #14
                            Du nimmst in dem Fall einen c10, der liest 5 Register zu je 2 Byte. Mit stringcast(str, 0s16, 8) holst du dann den Faktor.

                            Kommentar


                              #15
                              Danke für den Hinweis, leider klappt es noch nicht.

                              Ich habe im Experten die Register 40088 - 40092 abfragen lassen. Einmal als s16 (liefert die korrekten Werte) und einmal als String mit der Länge c2 und anschließendem stringcast zum testen. Dann sollten doch der c2-Wert (nach stringcast) dem s16-Wert entsprechen? Das ist leider nicht der Fall.

                              Ergänzend habe ich Testweise das Register 40088 als c10 auslesen lassen und darauf den von Dir vorgeschlagenen stringcast angewandt. Auch hier wird leider nicht der korrekte Wert geliefert. Was mache ich falsch?

                              Code:
                              mm1=modbusmaster($172.20.10.16$, 502u16, 10u32, 240)
                              status_mm1=0
                              HR_40088s16=0s16
                              HR_40089s16=0s16
                              HR_40090s16=0s16
                              HR_40091s16=0s16
                              HR_40092s16=0s16
                              HR_40088c2=$$c2
                              HR_40089c2=$$c2
                              HR_40090c2=$$c2
                              HR_40091c2=$$c2
                              HR_40092c2=$$c2
                              HR_40092c10=$$c10
                              
                              if cycle(0,10) then {
                              status_mm1=readmodbus(mm1, MB_HOLDING_REGISTER, 40087u16, HR_40088s16, BIG_ENDIAN, BIG_ENDIAN);
                              status_mm1=readmodbus(mm1, MB_HOLDING_REGISTER, 40088u16, HR_40089s16, BIG_ENDIAN, BIG_ENDIAN);
                              status_mm1=readmodbus(mm1, MB_HOLDING_REGISTER, 40089u16, HR_40090s16, BIG_ENDIAN, BIG_ENDIAN);
                              status_mm1=readmodbus(mm1, MB_HOLDING_REGISTER, 40090u16, HR_40091s16, BIG_ENDIAN, BIG_ENDIAN);
                              status_mm1=readmodbus(mm1, MB_HOLDING_REGISTER, 40091u16, HR_40092s16, BIG_ENDIAN, BIG_ENDIAN);
                              status_mm1=readmodbus(mm1, MB_HOLDING_REGISTER, 40087u16, HR_40088c2, BIG_ENDIAN, BIG_ENDIAN);
                              status_mm1=readmodbus(mm1, MB_HOLDING_REGISTER, 40088u16, HR_40089c2, BIG_ENDIAN, BIG_ENDIAN);
                              status_mm1=readmodbus(mm1, MB_HOLDING_REGISTER, 40089u16, HR_40090c2, BIG_ENDIAN, BIG_ENDIAN);
                              status_mm1=readmodbus(mm1, MB_HOLDING_REGISTER, 40090u16, HR_40091c2, BIG_ENDIAN, BIG_ENDIAN);
                              status_mm1=readmodbus(mm1, MB_HOLDING_REGISTER, 40091u16, HR_40092c2, BIG_ENDIAN, BIG_ENDIAN);
                              status_mm1=readmodbus(mm1, MB_HOLDING_REGISTER, 40087u16, HR_40092c10, BIG_ENDIAN, BIG_ENDIAN);
                              } endif
                              
                              HR_40088_st=stringcast(HR_40088c2,0s16,0u16)
                              HR_40089_st=stringcast(HR_40089c2,0s16,0u16)
                              HR_40090_st=stringcast(HR_40090c2,0s16,0u16)
                              HR_40091_st=stringcast(HR_40091c2,0s16,0u16)
                              HR_40092_st=stringcast(HR_40092c2,0s16,0u16)
                              HR_40092_2_st=stringcast(HR_40092c10,0s16,8u16)​
                              Ich sehe auch, dass z.B. der Wert des Registers 40092 als c2 springt, obwohl der Wert als s16 unverändert bleibt:

                              fronius4.png fronius5.png

                              Kommentar

                              Lädt...
                              X