Seite 1 von 1
zwei 16bit uint verschieben und verbinden zu float
Verfasst: Dienstag 29. August 2023, 20:10
von kiaralle
Hallo,
ich habe zwei Werte welche 16bit uint sind. Die rufe ich mit minimalmodbus aus einen Wechselrichter ab,
Das Abrufen funktioniert.
Es gehören immer eine High und eine Low-Wert laut zusammen.
Beispiel PV-Leistung = Ppv1_High und Ppv1_Low
Ich beobachte eigentlich immer, das der High-Wert Null ist.
Denke der Aufwand wird zum Schluss nicht nötig sein.
Als Ergebnis möchte ich float haben.
Kann das so funktionieren?
Code: Alles auswählen
float(growatt_1.read_register(18, 1, 4, False) << 16 | growatt_1.read_register(18, 1, 4, False))
So ähnlich habe ich es in Arduino im Code mal gelöst.
Den Code möchte ich in einen dictionary ablegen und später in einer Schleife aufrufen und berechnen lassen.
Danke, Ralf
Re: zwei 16bit uint verschieben und verbinden zu float
Verfasst: Dienstag 29. August 2023, 20:28
von __deets__
Sieht falsch aus, weil zweimal das gleiche Register gelesen wird, oder nicht? Aber wenn es zwei Register sind, und die Reihenfolge stimmt (also das erste auch wirklich die höherwertigen Bytes beinhaltet), dann passt das so.
Re: zwei 16bit uint verschieben und verbinden zu float
Verfasst: Dienstag 29. August 2023, 20:43
von kiaralle
@ _deets_
Gut aufgepasst. Ja copy und paste
Danke für die schnelle Antwort.
Re: zwei 16bit uint verschieben und verbinden zu float
Verfasst: Mittwoch 30. August 2023, 10:35
von DeaD_EyE
Ich glaube nicht, dass es das ist, was du willst. Das Schieben und Verodern bringt nichts. Du hast dann immer noch einen Integer und nicht die Repräsentation eines Floats in Bytes.
Du musst zuerst die beiden Wörter in Bytes aufteilen und z.B. einem bytearray übergeben.
Dann kann man struct verwenden, um einen 32 Bit Float aus dem bytearray zu erzeugen.
Ich habe mal zwei Beispiele geschrieben. Keine Garantie auf Richtigkeit.
Code: Alles auswählen
from __future__ import annotations
from enum import StrEnum
class Endianess(StrEnum):
big = "big"
little = "little"
def float2word(value: float, endianess:Endianess="little") -> tuple[int,int]:
if endianess is Endianess.big:
data = struct.pack(">f", value)
return data[2] | data[3] << 8, data[0] | data[1] << 8
else:
data = struct.pack("<f", value)
return data[0] << 8 | data[1], data[2] << 8 | data[3]
def word2float(word0: int, word1: int, endianess:Endianess="little") -> float:
buffer = bytearray()
if endianess is Endianess.big:
buffer.append(word1 & 0xFF)
buffer.append(word1 >> 8)
buffer.append(word0 & 0xFF)
buffer.append(word0 >> 8)
return struct.unpack(">f", buffer)[0]
else:
buffer.append(word0 >> 8)
buffer.append(word0 & 0xFF)
buffer.append(word1 >> 8)
buffer.append(word1 & 0xFF)
return struct.unpack("<f", buffer)[0]
word0, word1 = float2word(3.14, Endianess.little)
value = word2float(word0, word1, endianess=Endianess.little)
Re: zwei 16bit uint verschieben und verbinden zu float
Verfasst: Mittwoch 30. August 2023, 10:50
von __deets__
Ich bezweifele sehr, dass der TE einen 4-Byte-IEEE754-Wert vorliegen hat, der irgendwie auf zwei Wort-Register abgebildet ist. Das waere ein ziemlich bescheidenes Design.
Re: zwei 16bit uint verschieben und verbinden zu float
Verfasst: Mittwoch 30. August 2023, 10:53
von __blackjack__
Kann aber vorkommen wenn das über eine API geht die nur 16-Bit-Integer-Werte für die Übertragung vorsieht.
Re: zwei 16bit uint verschieben und verbinden zu float
Verfasst: Mittwoch 30. August 2023, 11:00
von __deets__
Koennen tut vieles, klar. Aber modbus kennt prinzipiell Byte-Transfers, auch mehrere. Warum man da jetzt nun auf zweimal zwei Bytes geht, wenn es am Ende doch ein 4-Byte-Wert sein soll, ist erstmal keine schluessige Annahme.
Re: zwei 16bit uint verschieben und verbinden zu float
Verfasst: Mittwoch 30. August 2023, 16:18
von kiaralle
Hilft dass euch weiter.
Ihr fachsimpelt hier so schön, ich bin noch nicht soweit
Der Link geht zur Modbus-Tabelle meines Wechselrichters
https://www.google.com/url?sa=t&rct=j&q ... i=89978449
Re: zwei 16bit uint verschieben und verbinden zu float
Verfasst: Mittwoch 30. August 2023, 17:03
von __deets__
Nicht besonders aussagekraeftig, und ja auch komlpett andere Registerwerte als von dir da angegeben. Aber *wenn* ist das eben doch einfach ein 32-Bit-Wert, den man mit der Einheit (0.1W) multiplizieren muss, um den eigentlichen Wert zu bekommen. Damit hast du schonmal 6.5KW Wertebereich nur mit dem low-Wert, da haengt's jetzt von deinem Setup ab, ob du ueberhaupt jemals Werte jenseits davon siehst, die dann in das hi-word laufen.
Re: zwei 16bit uint verschieben und verbinden zu float
Verfasst: Mittwoch 30. August 2023, 17:28
von DeaD_EyE
__deets__ hat geschrieben: ↑Mittwoch 30. August 2023, 10:50
Ich bezweifele sehr, dass der TE einen 4-Byte-IEEE754-Wert vorliegen hat, der irgendwie auf zwei Wort-Register abgebildet ist.
Ich aber nicht und genau deswegen habe ich das Beispiel geschrieben. Es gibt Geräte, die einen Float so übertragen.
Beim Modbus-Protokoll gibt es keine Funktion, um Gleitkommazahlen zu übertragen.
Ob das nun so richtig ist, kann nur der TE sagen.
Re: zwei 16bit uint verschieben und verbinden zu float
Verfasst: Mittwoch 30. August 2023, 19:22
von kiaralle
Man bekommt von Growatt auch sehr wenig Hilfe.
Die Modbuss-Tabelle scheint eine Tabelle zu sein, welche schon oft geändert wurde und auch bei andere Growatt-Typen verwendet wurde. Also ein Sammelsurium von Registern die nie belegt werden.
Wie oben geschrieben, sind die High-Werte bei meinem Wechselrichter immer 0.
Da könntet ihr mit dem nie erreichten Überlauf in den High-Wert recht haben.
Dafür ist die PV-Leistung des Umrichters zu gering. Es darf nicht mehr wie 6kW sein.
Außer wenn es um die Fließrichtung der Leistung aus dem Batteriespeicher geht.
Da ist High einmal 65535 und dann mal 0.
Low schwankt dann immer je nach Leistung von 65535 bei wenig Leistung in Richtung 0 bei viel Leistung.
Daraus ergibt sich dann der negative oder positive Wert.
Wert = High - Low... wenn ich jetzt richtig liege. Muss ich noch mal schauen.
Bei diesen Registern steht dann
(signed int 32)
Positive:Battery Discharge Power;
Negative:Battery Charge Power;
Danke für eure guten Gedanken. Hat mir trotzdem geholfen.
Warum Werte auslesen, welche eh nie erreicht werden. Ich sperre die High-Register einfach aus.
Rechenzeit ist kostbar
Gruß Ralf
Re: zwei 16bit uint verschieben und verbinden zu float
Verfasst: Mittwoch 30. August 2023, 20:26
von Sirius3
@DeaD_EyE: warum verwendest Du für die float pack, für die ints aber nicht???
Du definierst als Typannotation von endiness Endiness, weist dann aber als default-Argument einen String zu. Das ist Quatsch und zeigt mal wieder, das Typannotationen in den meisten Fällen falsch sind und auch falsch verwendet werden.
Und die Prüfung innerhalb der Funktionen ist damit auch fehlerhaft.
Du mischst die Endinessen codierst das float in big und decodierst die words als little, bzw. umgekehr. Warum tauschst Du bei Big-Endian auch noch die beiden Wörter aus? Damit sind die beiden Endinesse identisch.
Code: Alles auswählen
import struct
from enum import StrEnum
class Endianess(StrEnum):
big = "big"
little = "little"
def float2word(value, endianess=Endianess.little):
op = ">" if Endianess(endianess) is Endianess.big else "<"
data = struct.pack(op + "f", value)
return struct.unpack(op + "HH", data)
def word2float(word0, word1, endianess=Endianess.little)
op = ">" if Endianess(endianess) is Endianess.big else "<"
data = struct.pack(op + "HH", word0, word1)
return struct.unpack(op + "f", data)[0]
@kiaralle: das 65535 liegt ja daran, dass Du signed int32 hast. Und wenn man das als zwei 16bit-Werte vorliegen hat, arbeitet man auch am besten mit struct.pack und unpack.
Re: zwei 16bit uint verschieben und verbinden zu float
Verfasst: Freitag 1. September 2023, 09:00
von DeaD_EyE
Sirius3 hat geschrieben: ↑Mittwoch 30. August 2023, 20:26
@DeaD_EyE: warum verwendest Du für die float pack, für die ints aber nicht???
Die Lösung ist natürlich schöner. Kennst Du das mit dem Wald und den Bäumen?