lumapu / ahoy

Various tools, examples, and documentation for communicating with Hoymiles microinverters
https://ahoydtu.de
Other
952 stars 224 forks source link

Feature Request: Device Information ( `0x15` `REQ_ARW_DAT_ALL` ) SubCmd Kommandos #145

Open stefan123t opened 2 years ago

stefan123t commented 2 years ago

Alle zum MainCmd 0x15 REQ_ARW_DAT_ALL bzw. dem. Device Information gehörenden SubCmd Kommandos implementieren:

Subtask: #178

aschiffler commented 2 years ago

Hi,

die eigentliche Funktion ist schon implementiert.

  1. via der rest api -- die dann z.B. per ajax call aus der setup o.ä. aufgrufen werden kann -- wird entsprechend der payload eines der oben genannten Kommandos gesetzt. Siehe: https://github.com/grindylow/ahoy/blob/aca98b6911c22d667ef6a5bb7184dba7dd51ff5c/tools/esp8266/app.cpp#L885
  2. In der Funktion sendTimePacket ist dieses Kommando als parameter vorhanden und wird entsprechend in das zu sendende Packet eingebaut. (Inklusive des zuletzt epfangenen Alarm Message Index) https://github.com/grindylow/ahoy/blob/aca98b6911c22d667ef6a5bb7184dba7dd51ff5c/tools/esp8266/hmRadio.h#L198
  3. Der Aufruf von sendTimePacket erfolgt hier:https://github.com/grindylow/ahoy/blob/aca98b6911c22d667ef6a5bb7184dba7dd51ff5c/tools/esp8266/app.cpp#L457 stets mit dem aktuellen / letzten "InfoCmd" als Parameter
  4. Im Abarbeiten der Antwort wird entsprechend des gesetzten "InfoCmd" unterschieden und aktuell noch keine spezifische Verarbeitung. Das Erstellen / Erzeugen der Payload aus den Antwort-frames funktioniert bereits unabhängig welches InfoCmd gesendet wurde. https://github.com/grindylow/ahoy/blob/aca98b6911c22d667ef6a5bb7184dba7dd51ff5c/tools/esp8266/app.cpp#L255

ToDo's / Ideen für die weitere Umsetzung: R1: Es Bedarf zwingend einer Command-Queue da in der Antwort keine Information enthalten ist auf welche Anfrage die Antwort kam. Lediglich könnte man die Anzahl der Bytes als Plausibilität testen. Akutell ist im Prinzip eine Queue implementiert aber nur mit der Länge "1 Kommando". Wird während man auf die Antwort wartet ein neues anderes Kommando via rest api od. mqtt gesetzt läuft das Antwort-Verarbeiten falsch. --> Wie ist das in OpenDTU umgesetzt? R2: Um die Antworten im WebUI darzustellen bzw. asynchron anzufragen via rest api benötigen wir entweder den async webserver oder wir müssen einen zweiten port bereitstellen und dort mit SSE / WS antworten.

R3: Parser für die InfoCmds erstellen.

Wer Ansätze / Vorschläge hat bitte einfach Bezug nehmen auf die Anfroderungen R1 / R2 / R3 die m.E. zunächst notwendig sind bevor die Funktion implementiert wird.

Ein aufwendigerer alternativer Ansatz -- aber m.E. der beste Weg -- wäre per git sub modul das repository https://github.com/tbnobody/OpenDTU einzubinden und dort die Quellen unter /lib/Hoymiles/* 1:1 zu verwenden. Dann wäre was das Thema / Modul Applikation <-> Inverter alles einheitlich.

stefan123t commented 2 years ago

@aschiffler bzgl.

R1: eine Command Queue ist für die noch anstehenden Commands von der API / UI notwendig. Sollte also eventuell auch als Liste in der UI angezeigt werden können, zumindest deren Länge. Hier ist aber zu unterscheiden, die DTU-Pro (wie auch unsere Ahoy/OpenDTU) kann immer nur ein Command aktuell ausführen, das steht dann als aktuelles Command im Uart_CurRecMainCmd bzw. Uart_CurRecSubCmd. Es gibt außerdem noch ein CurRecSendPackageDataType mit dem die aktuelle UsartNrf_Process_*() Methode gewählt wird, der das Parsen der Antwort überlassen wird. D.h. erst wenn das Uart_CurRecMainCmd wieder auf einen default Wert gesetzt wird, kann ein neues Kommando aus der API / UI Command Queue oder aus dem Scheduler gesetzt werden. Wie wir das Interleaving zwischen Scheduler und Command Queue umsetzen sollen, da bin ich noch unschlüßig, evtl. einfach Scheduler Commands auch ans Ende der Command Queue ? Wie lang sollte die Command Queue sein, und welche Parameter / Struktur sollten die Objekte in der Queue haben ?

R2: AsyncWebServer gibt es im Branch asyncWeb, es gab aber einen Merge Conflict #107, den ich offenbar nicht richtig auflösen konnte. Einziger Schwachpunkt aktuell bricht der AsyncWebServer unser OTA Update Methode. Hier wird empfohlen auf Elegant OTA zu wechseln. @lumapu ist da m.W. aktuell dran ...

R3: Vielleicht können wir das schon mal nach vorne ziehen, dafür müssen die entsprechenden Strukturen zur Ablage der Daten (AlarmData/AlarmUpdate, InverterDevInform_Simple/InverterDevInform_All, etc.) angelegt werden und dann die Parser die Daten dort rein schreiben. Ich vermute das geht analog zu den Parser-Strukturen für RealTimeRunData_Debug und RealTimeRunData_Reality . Diese muß die DTU entweder im RAM halten oder wie die DTU-Pro in ihrer Memory.c Klasse auf das SPIFFS/LittleFS in die jeweiligen Dateien schreiben. Hierfür wäre dann evtl. eine echte SD Karte sinnvoll ?

stefan123t commented 2 years ago

R3: den Vorschlag die Code Basis von @tbnobody zu verwenden finde ich nicht schlecht. Das hatte ich mit dem Aufräumen der app.cpp ebenfalls vor. Leider bin ich bisher gescheitert den OpenDTU Code auf dem ESP8266 zum Laufen zu bekommen. Ich hänge immer an den Interrupt Handlern die über std::bind als Callback angebunden werden. Meine bisheriger Informationsstand ist daß das für den ESP nicht als Methode einer Klasse unterstützt wird. Man müsste die Interrupt Handler also als globale Funktionen / Methoden auslagern. Ich weiß nicht ob das wirklich ein Problem ist ?

aschiffler commented 2 years ago

R3: Memory, daran habe ich gar nicht gedacht, dass der Speicher evtl. knapp wird wenn wir die ganzen Parser implementieren. R3: Lib, callbacks: Es scheint ich war da an einem ähnlichen Punkt mit dem "binden" der MQTT Callback. https://github.com/grindylow/ahoy/blob/aca98b6911c22d667ef6a5bb7184dba7dd51ff5c/tools/esp8266/app.cpp#L166 Das habe ich nur hinbekommen wenn in der mqtt Klasse die Instanz des PubSubclients public ist und ich direkt "durchgreifen" kann auf die Methode setCallback.

stefan123t commented 2 years ago

R3: Richtig, die setCallback Methode muß public sein! Willst Du mal versuchen OpenDTU (bereits mit ESPAsyncWebServer) auf ESP8266 zu portieren ? Dann hätten wir einen sauberen Stand um die aktuell in AhoyDTU erfolgten Erweiterungen nachzuziehen ?

lumapu commented 2 years ago

ich hätte gerne den ESP8266 weiterentwickelt und nicht die OpenDTU kopiert - was genau ist dort aus deiner Sicht besser Aufgelöst? Für app und main habe ich fest vor den Webserver Teil zu extrahieren. Zudem ist der Async-Webserver auf dem besten Weg bald in den main-branch zu kommen. Bzgl. OTA: warum meinst du, dass das mit Async nicht ginge - das ist doch bereits in meinem aktuellen branch möglich - ohne zusätzliche libs.

lumapu commented 2 years ago

R3: Memory, daran habe ich gar nicht gedacht, dass der Speicher evtl. knapp wird wenn wir die ganzen Parser implementieren. R3: Lib, callbacks: Es scheint ich war da an einem ähnlichen Punkt mit dem "binden" der MQTT Callback.

https://github.com/grindylow/ahoy/blob/aca98b6911c22d667ef6a5bb7184dba7dd51ff5c/tools/esp8266/app.cpp#L166

Das habe ich nur hinbekommen wenn in der mqtt Klasse die Instanz des PubSubclients public ist und ich direkt "durchgreifen" kann auf die Methode setCallback.

ja das liegt daran, dass du versuchst direkt auf mClient zuzugreifen. Wenn dieser Member private sein soll, dann müsstest du einen public wrapper in der mqtt Klasse bauen. Wäre sauberer, da man dadurch weiß, das mClient nicht noch sonst irgendwo im Code manipuliert wird.

stefan123t commented 2 years ago

Vielleicht sollten wir ein eigenes Issue zum Refaktorisieren des bestehenden AhoyDTU Codes machen ? Wie gesagt ich finde mich leider zwischen esp8266.cpp, main.cpp und app.cpp aktuell nicht immer zu Recht.

<offtopic>

ich hätte gerne den ESP8266 weiterentwickelt und nicht die OpenDTU kopiert - was genau ist dort aus deiner Sicht besser gelöst?

Wie an anderer Stelle schon gesagt finde ich den Scheduler mit den EVERY_XXX Defines in OpenDTU sehr einfach zu lesen. Vermutlich auch kein Hexenwerk das nach AhoyDTU zu portieren und die verschiedenen Ticker und sonstigen Counter zu vereinheitlichen.

Vielleicht ist das aber auch ein guter Ansatzpunkt um den bestehenden Code zu entwirren ? Momentan haben wir einen Scheduler der m.W. in app.cpp über den loop verteilt ist. Wenn wir den rausziehen und dann die einzelnen Blöcke des loop codes in anderen Klassen aufrufen wird es vermutlich übersichtlicher.

Für app und main habe ich fest vor den Webserver Teil zu extrahieren. Zudem ist der Async-Webserver auf dem besten Weg bald in den main-branch zu kommen.

Ja das ist der Hauptgrund, das hat tbnobody an dieser Stelle in OpenDTU ja schon gemacht. Bestimmt auch kein Problem das nochmal in AhoyDTU zu machen. Aber ich fände es halt gut wenn die beiden Projekte wieder zu einander finden würden. Und da halte ich den Code von OpenDTU aktuell etwas einfacher lesbar, erweiterbar und wartbar. Aber er läuft halt nicht auf dem ESP8266. Vielleicht können wir auch einige der Webserver Klassen oder Methoden von OpenDTU verwenden, um hier das wieder etwas zu vereinheitlichen ? Obwohl ich mit Vue.js ehrlich gesagt noch nichts gemacht habe, weshalb ich das nicht wirklich beurteilen kann, ob das Sinn macht.

Bzgl. OTA: warum meinst du, dass das mit Async nicht ginge - das ist doch bereits in meinem aktuellen branch möglich - ohne zusätzliche libs.

Ich habe keinen Bedarf für OTA aber ich hatte mslookup im PR 107 zum AsyncWebServer so verstanden, daß es mit Async nicht funktioniert und das Selbe steht dort https://github.com/grindylow/ahoy/pull/107#issuecomment-1211563142. Ich würde nur ungern die Erwartungen der Nutzer die den ESP8266 schon irgendwo montiert haben wo sie nur noch mit OTA flashen können enttäuschen.

</offtopic>

lumapu commented 2 years ago

Ich verwende fast ausschließlich OTA mit und ohne Async-Webserver

stefan123t commented 2 years ago

Ich würde vorschlagen für den o.g. Scheduler und eventuell auch die Command Queue das bestehende Issue #78 zu verwenden.

aschiffler commented 2 years ago

einen public wrapper in der mqtt Klasse bauen. Wäre sauberer, da man dadurch weiß, das mClient nicht noch sonst irgendwo im Code manipuliert wird.

@lumapu Ja aber genau dieser Public Wrapper geht nicht wegen dem Methodenaufruf. Der Public-Wrapper übergibt dann eine Funktion app::XXXX an den PubSubClient und nicht std::func (void*) oder so ähnlich. Ich habe dazu einiges im I-Net gelesen und man müssten dann die Methode setCallback des PubSubClient anders abauen. --> das habe ich dann gelassen.

lumapu commented 2 years ago

ok, ich werde da auch nochmal drauf schauen, evtl. finde ich ja eine Lösung

lumapu commented 2 years ago

@aschiffler jetzt ist mClient wieder private

aschiffler commented 2 years ago

@lumapu Klasse! Das habe ich nicht gefunden mit dieser Callback Signature

aschiffler commented 2 years ago

R1 ist nun erledigt. Man kann vie iv->enqueCommand(subCmd) jedes oben genannte SubCmd in die Reihe stellen. Der Parser muss dann in hmdefines.h als Byte assignment hinterlegt werden. Die geparsten Daten sind aber nur solange im array 'record' im inverter Objekt bis ein neues Kommando geparst wird.

R2 ist nun Baustelle. SSE wäre mein Vorschlag.

stefan123t commented 2 years ago

R2: comparison of SSE (server side events, half-duplex, ESP->Client push) vs WebSocket (full-duplex, ESP<->Client): https://developer.ibm.com/articles/wa-http-server-push-with-websocket-sse/

According to Random Nerd example SSE may require ESPAsyncWebServer too, though the general effort being HTML5 seems lower:

https://randomnerdtutorials.com/esp8266-nodemcu-web-server-sent-events-sse/

stefan123t commented 1 year ago

@lumapu wir hatten heute im Discord #ahoy-esp8266 diskutiert wie wir die Integration von HM- und MI-Wechselrichter weiter voranbringen können. Hier geht es ebenfalls um das Queuing von Kommandos die zum WR geschickt werden müssen und deren Antworten ausgewertet werden sollen:

Man müsste dann in den ersten beiden Fällen auch nicht mehr von MI/HM reden sondern vielleicht eher von: