{"id":8010,"date":"2024-02-06T17:31:41","date_gmt":"2024-02-06T16:31:41","guid":{"rendered":"https:\/\/blog.fh-kaernten.at\/ingmarsretro\/?p=8010"},"modified":"2024-11-26T08:07:15","modified_gmt":"2024-11-26T07:07:15","slug":"pylontech-pv-akkustatus-im-homeassistant","status":"publish","type":"post","link":"https:\/\/blog.fh-kaernten.at\/ingmarsretro\/2024\/02\/06\/pylontech-pv-akkustatus-im-homeassistant\/","title":{"rendered":"Pylontech PV-Akkustatus im HomeAssistant"},"content":{"rendered":"<div class=\"pvc_clear\"><\/div>\n<p id=\"pvc_stats_8010\" class=\"pvc_stats all  \" data-element-id=\"8010\" style=\"\"><i class=\"pvc-stats-icon medium\" aria-hidden=\"true\"><svg aria-hidden=\"true\" focusable=\"false\" data-prefix=\"far\" data-icon=\"chart-bar\" role=\"img\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\" viewBox=\"0 0 512 512\" class=\"svg-inline--fa fa-chart-bar fa-w-16 fa-2x\"><path fill=\"currentColor\" d=\"M396.8 352h22.4c6.4 0 12.8-6.4 12.8-12.8V108.8c0-6.4-6.4-12.8-12.8-12.8h-22.4c-6.4 0-12.8 6.4-12.8 12.8v230.4c0 6.4 6.4 12.8 12.8 12.8zm-192 0h22.4c6.4 0 12.8-6.4 12.8-12.8V140.8c0-6.4-6.4-12.8-12.8-12.8h-22.4c-6.4 0-12.8 6.4-12.8 12.8v198.4c0 6.4 6.4 12.8 12.8 12.8zm96 0h22.4c6.4 0 12.8-6.4 12.8-12.8V204.8c0-6.4-6.4-12.8-12.8-12.8h-22.4c-6.4 0-12.8 6.4-12.8 12.8v134.4c0 6.4 6.4 12.8 12.8 12.8zM496 400H48V80c0-8.84-7.16-16-16-16H16C7.16 64 0 71.16 0 80v336c0 17.67 14.33 32 32 32h464c8.84 0 16-7.16 16-16v-16c0-8.84-7.16-16-16-16zm-387.2-48h22.4c6.4 0 12.8-6.4 12.8-12.8v-70.4c0-6.4-6.4-12.8-12.8-12.8h-22.4c-6.4 0-12.8 6.4-12.8 12.8v70.4c0 6.4 6.4 12.8 12.8 12.8z\" class=\"\"><\/path><\/svg><\/i> <img decoding=\"async\" width=\"16\" height=\"16\" alt=\"Loading\" src=\"https:\/\/blog.fh-kaernten.at\/ingmarsretro\/wp-content\/plugins\/page-views-count\/ajax-loader-2x.gif\" border=0 \/><\/p>\n<div class=\"pvc_clear\"><\/div>\n<p style=\"text-align: justify\">Update 26.11.2024:<br \/>\nAufgrund vieler Anfragen nach der Platine und weil vielleicht nicht jeder daran interessiert ist, sie selber zu zeichnen, stelle ich die Gerberdaten zum Dowload zur Verf\u00fcgung:<\/p>\n<p><a href=\"http:\/\/blog.fh-kaernten.at\/ingmarsretro\/files\/2024\/11\/pylontec_mqttinterface_v1.0_gerber.zip\">pylontec_mqttinterface_v1.0_gerber<\/a><\/p>\n<p style=\"text-align: justify\">Wer eine Photovoltaik Anlage in seinem Eigenheim aufgebaut hat, verwendet vielleicht sogar einen Energiespeicher. In diesem Beispiel handelt es sich um eine Offgrid Anlage, die mit zwei Modulen des Herstellers Pylontech ausgestattet ist. Die Pylontech Akkus der Type US3000C haben eine Ausgangs Spannung von 48V. Die Nennkapazit\u00e4t betr\u00e4gt 3500Wh. Die verbauten Zellen sind LiFePO4 Zellen und die nutzbare Kapazit\u00e4t ist laut Datenblatt mit 3374Wh angegeben. Die Akkus sind so gebaut, dass sie mit weiteren Pylontech-Akkus parallelgeschaltet werden k\u00f6nnen. Das intern verbaute BMS (BatterieManagementSystem) kommuniziert \u00fcber eine sogenannte &#8222;Link&#8220; Schnittstelle mit den anderen Pylontech Batteriemodulen. Ein als &#8222;Master&#8220; konfigurierter Akku erledigt den Datenaustausch zum Wechselrichter. Hier stellt Pylontech den CAN- oder RS485 Bus als Schnittstelle zur Verf\u00fcgung. Will man jedoch Informationen \u00fcber die einzelnen Zellen (Spannungen, Str\u00f6me, Ladungen, Temperaturen etc.) haben, so gibt es an jedem Modul noch eine Schnittstelle mit der Bezeichnung &#8222;Console&#8220;. Das ist eine RS232 Schnittstelle \u00fcber die man direkt mit dem BMS des Akkus kommunizieren kann. Dieser Port wird auch genutzt, um die Firmware des BMS zu aktualisieren. Ich rate aber DRINGEND davon ab, mit Firmwareupdates und Flashsoftware daran herum zu spielen. Das ist dem Hersteller oder dem Haftungstr\u00e4ger vorbehalten.<\/p>\n<p style=\"text-align: justify\">Da man \u00fcber diese Schnittstelle aber auch einiges an Informationen \u00fcber die im Akku verbauten Zellen erf\u00e4hrt, ist das ein interessanter Zugang. So hatte ich anfangs einen Laptop mit einem Terminal angeschlossen und konnte so die einzelnen Zellenspannungen und vor allem den evtl. unterschiedlichen Ladezustand der parallel geschalteten Module entdecken und monitoren. So dachte ich mir, w\u00e4re es doch eine gute Idee, wenn man diese Infos in seiner Hausautomatisierung zur Verf\u00fcgung hat und dort visualisieren und f\u00fcr Steuerungszwecke nutzen kann.<\/p>\n<p style=\"text-align: justify\">Da f\u00fcr uns Freaks und Technikinteressierte Begriffe wie Homeassistant, Docker, Proxmox, HomeMatic, NodeRed usw. durchaus bekannt sind, dachte ich mir, diese Daten sollen auch im Homeassistant zu Entit\u00e4ten werden. So war schnell wieder ein kleines neues Projekt geschaffen. Mein Plan: die Daten der Serien Schnittstelle auslesen und per MQTT an den Homeassistant senden.<\/p>\n<p style=\"text-align: justify\">Bevor ich mich nun aber mit dem Zerlegen der Datenstrings, die \u00fcber den seriellen Port herauspurzeln besch\u00e4ftige, bem\u00fchte ich zuerst einmal die Suchmaschinen. Vielleicht hat sich ja schon jemand anderes mit diesem Thema besch\u00e4ftigt.&nbsp; Und genau so war es auch. Auf GitHub wurde ich unter dem Begriff &#8222;pylontec2mqtt&#8220; f\u00fcndig. Unter <a href=\"https:\/\/github.com\/irekzielinski\/Pylontech-Battery-Monitoring\">https:\/\/github.com\/irekzielinski\/Pylontech-Battery-Monitoring<\/a> ist ein Projekt gehostet, das mittels ESP8266 die seriellen Daten vom Port abholt und per MQTT und Wifi zum Homeassistant Server sendet. Ein Fork mit einer Weiterentwicklung dieses Projektes ist unter <a href=\"https:\/\/github.com\/hidaba\/PylontechMonitoring\">https:\/\/github.com\/hidaba\/PylontechMonitoring<\/a> zu finden.<\/p>\n<p style=\"text-align: justify\">Warum ich das Projekt trotz des einfachen Nachbaus hier im Blog ver\u00f6ffentliche? Ich habe die Schaltung ein wenig optimiert und in ein Layout gepackt und den Code etwas angepasst. Das Ergebnis m\u00f6chte ich hier teilen. Mir war es in wichtig, einen vern\u00fcnftigen Aufbau auf einer Platine zu haben, die mit einem USB A-B Kabel f\u00fcr die Spannungsversorgung und einem LAN-RJ45 Kabel f\u00fcr die Datenverbindung angesteckt wird. Dabei wollte ich eine &#8222;solide&#8220; USB Steckverbindung verwenden (nicht die Fragilen Mini- oder Mikro USB Steckverbindungen)<\/p>\n<p style=\"text-align: justify\">Auf einer Lochrasterplatine und mit den \u00fcblichen Entwicklungsplatinen habe ich mir auf die Schnelle ein Funktionsmuster &#8222;zusammen gestrickt&#8220; um darauf die Software anpassen zu k\u00f6nnen.<\/p>\n<div id=\"attachment_8000\" style=\"width: 484px\" class=\"wp-caption aligncenter\"><a href=\"http:\/\/blog.fh-kaernten.at\/ingmarsretro\/files\/2024\/02\/IMG20240102144240-scaled.jpg\"><img fetchpriority=\"high\" decoding=\"async\" aria-describedby=\"caption-attachment-8000\" class=\"wp-image-8000 size-large\" src=\"http:\/\/blog.fh-kaernten.at\/ingmarsretro\/files\/2024\/02\/IMG20240102144240-1024x770.jpg\" alt=\"\" width=\"474\" height=\"356\" srcset=\"https:\/\/blog.fh-kaernten.at\/ingmarsretro\/files\/2024\/02\/IMG20240102144240-1024x770.jpg 1024w, https:\/\/blog.fh-kaernten.at\/ingmarsretro\/files\/2024\/02\/IMG20240102144240-300x226.jpg 300w, https:\/\/blog.fh-kaernten.at\/ingmarsretro\/files\/2024\/02\/IMG20240102144240-768x578.jpg 768w, https:\/\/blog.fh-kaernten.at\/ingmarsretro\/files\/2024\/02\/IMG20240102144240-1536x1155.jpg 1536w, https:\/\/blog.fh-kaernten.at\/ingmarsretro\/files\/2024\/02\/IMG20240102144240-2048x1540.jpg 2048w\" sizes=\"(max-width: 474px) 100vw, 474px\" \/><\/a><p id=\"caption-attachment-8000\" class=\"wp-caption-text\">Funktionsmuster auf Lochraster<\/p><\/div>\n<p style=\"text-align: justify\">So habe ich zuerst aus den Skizzen im Git-Projekt einen Schaltplan erstellt. An der &#8222;Console&#8220; Schnittstelle liegt ein &#8222;echter&#8220; RS232 Pegel an, der \u00fcber den MAX3232 IC auf eine 5V TTL umgesetzt wird. Mit dem BSS123 FET wird f\u00fcr die Signale RX und TX je ein Pegelwandler auf 3.3V realisiert.<\/p>\n<div id=\"attachment_7992\" style=\"width: 484px\" class=\"wp-caption aligncenter\"><a href=\"http:\/\/blog.fh-kaernten.at\/ingmarsretro\/files\/2024\/02\/SchematicPylan2mqtt.png\"><img decoding=\"async\" aria-describedby=\"caption-attachment-7992\" class=\"wp-image-7992 size-large\" src=\"http:\/\/blog.fh-kaernten.at\/ingmarsretro\/files\/2024\/02\/SchematicPylan2mqtt-1024x706.png\" alt=\"\" width=\"474\" height=\"327\" srcset=\"https:\/\/blog.fh-kaernten.at\/ingmarsretro\/files\/2024\/02\/SchematicPylan2mqtt-1024x706.png 1024w, https:\/\/blog.fh-kaernten.at\/ingmarsretro\/files\/2024\/02\/SchematicPylan2mqtt-300x207.png 300w, https:\/\/blog.fh-kaernten.at\/ingmarsretro\/files\/2024\/02\/SchematicPylan2mqtt-768x529.png 768w, https:\/\/blog.fh-kaernten.at\/ingmarsretro\/files\/2024\/02\/SchematicPylan2mqtt-1536x1058.png 1536w, https:\/\/blog.fh-kaernten.at\/ingmarsretro\/files\/2024\/02\/SchematicPylan2mqtt-2048x1411.png 2048w\" sizes=\"(max-width: 474px) 100vw, 474px\" \/><\/a><p id=\"caption-attachment-7992\" class=\"wp-caption-text\">pylontec2mqtt schematic<\/p><\/div>\n<p style=\"text-align: justify\">Diese 3.3V TTL Pegel verarbeitet der ESP8266 in Form des Wemos D1Mini oder WemosD1Pro Entwicklungsboard, das auf die Platine aufgesteckt wird. Die gesamte Konstruktion habe ich dann in ein kleines Kunststoffgeh\u00e4use gepackt, das bequem \u00fcber die Lan und USB Kabel mit dem Pylontec und einer USB Spannungsquelle zu verbinden ist.<\/p>\n<div id=\"attachment_8004\" style=\"width: 484px\" class=\"wp-caption aligncenter\"><a href=\"http:\/\/blog.fh-kaernten.at\/ingmarsretro\/files\/2024\/02\/layout_tool.jpg\"><img loading=\"lazy\" decoding=\"async\" aria-describedby=\"caption-attachment-8004\" class=\"wp-image-8004 size-large\" src=\"http:\/\/blog.fh-kaernten.at\/ingmarsretro\/files\/2024\/02\/layout_tool-1024x631.jpg\" alt=\"\" width=\"474\" height=\"292\" srcset=\"https:\/\/blog.fh-kaernten.at\/ingmarsretro\/files\/2024\/02\/layout_tool-1024x631.jpg 1024w, https:\/\/blog.fh-kaernten.at\/ingmarsretro\/files\/2024\/02\/layout_tool-300x185.jpg 300w, https:\/\/blog.fh-kaernten.at\/ingmarsretro\/files\/2024\/02\/layout_tool-768x473.jpg 768w, https:\/\/blog.fh-kaernten.at\/ingmarsretro\/files\/2024\/02\/layout_tool.jpg 1261w\" sizes=\"(max-width: 474px) 100vw, 474px\" \/><\/a><p id=\"caption-attachment-8004\" class=\"wp-caption-text\">Layout im Designtool<\/p><\/div>\n<p style=\"text-align: justify\">Im Bild oben ist der Layout Entwurf dargestellt. Die Platine und die Lage der Bauteile wurden vor der Fertigung nochmals mit der Vorschau \u00fcberpr\u00fcft und dann beim Hersteller des Vertrauens bestellt.<\/p>\n<div id=\"attachment_8006\" style=\"width: 484px\" class=\"wp-caption aligncenter\"><a href=\"http:\/\/blog.fh-kaernten.at\/ingmarsretro\/files\/2024\/02\/layout.jpg\"><img loading=\"lazy\" decoding=\"async\" aria-describedby=\"caption-attachment-8006\" class=\"wp-image-8006 size-large\" src=\"http:\/\/blog.fh-kaernten.at\/ingmarsretro\/files\/2024\/02\/layout-1024x632.jpg\" alt=\"\" width=\"474\" height=\"293\" srcset=\"https:\/\/blog.fh-kaernten.at\/ingmarsretro\/files\/2024\/02\/layout-1024x632.jpg 1024w, https:\/\/blog.fh-kaernten.at\/ingmarsretro\/files\/2024\/02\/layout-300x185.jpg 300w, https:\/\/blog.fh-kaernten.at\/ingmarsretro\/files\/2024\/02\/layout-768x474.jpg 768w, https:\/\/blog.fh-kaernten.at\/ingmarsretro\/files\/2024\/02\/layout.jpg 1026w\" sizes=\"(max-width: 474px) 100vw, 474px\" \/><\/a><p id=\"caption-attachment-8006\" class=\"wp-caption-text\">Vorschau der Platine vor der Fertigung<\/p><\/div>\n<p style=\"text-align: justify\">Nach kaum zwei Wochen Wartezeit hielt ich dann die Leeren Platinen in H\u00e4nden und konnte sie mit den Bauteilen best\u00fccken.<\/p>\n<div id=\"attachment_7996\" style=\"width: 484px\" class=\"wp-caption aligncenter\"><a href=\"http:\/\/blog.fh-kaernten.at\/ingmarsretro\/files\/2024\/02\/1705007526004.png\"><img loading=\"lazy\" decoding=\"async\" aria-describedby=\"caption-attachment-7996\" class=\"wp-image-7996 size-large\" src=\"http:\/\/blog.fh-kaernten.at\/ingmarsretro\/files\/2024\/02\/1705007526004-906x1024.png\" alt=\"\" width=\"474\" height=\"536\" srcset=\"https:\/\/blog.fh-kaernten.at\/ingmarsretro\/files\/2024\/02\/1705007526004-906x1024.png 906w, https:\/\/blog.fh-kaernten.at\/ingmarsretro\/files\/2024\/02\/1705007526004-266x300.png 266w, https:\/\/blog.fh-kaernten.at\/ingmarsretro\/files\/2024\/02\/1705007526004-768x868.png 768w, https:\/\/blog.fh-kaernten.at\/ingmarsretro\/files\/2024\/02\/1705007526004-1359x1536.png 1359w, https:\/\/blog.fh-kaernten.at\/ingmarsretro\/files\/2024\/02\/1705007526004.png 1555w\" sizes=\"(max-width: 474px) 100vw, 474px\" \/><\/a><p id=\"caption-attachment-7996\" class=\"wp-caption-text\">fertig best\u00fcckte Platine<\/p><\/div>\n<p>Im Bild oben ist die fertig best\u00fcckte Platine zu sehen. Hier fehlt nur mehr das Wemosboard mit dem ESP.<\/p>\n<div id=\"attachment_8002\" style=\"width: 484px\" class=\"wp-caption aligncenter\"><a href=\"http:\/\/blog.fh-kaernten.at\/ingmarsretro\/files\/2024\/02\/IMG20240104102508-scaled.jpg\"><img loading=\"lazy\" decoding=\"async\" aria-describedby=\"caption-attachment-8002\" class=\"wp-image-8002 size-large\" src=\"http:\/\/blog.fh-kaernten.at\/ingmarsretro\/files\/2024\/02\/IMG20240104102508-770x1024.jpg\" alt=\"\" width=\"474\" height=\"630\" srcset=\"https:\/\/blog.fh-kaernten.at\/ingmarsretro\/files\/2024\/02\/IMG20240104102508-770x1024.jpg 770w, https:\/\/blog.fh-kaernten.at\/ingmarsretro\/files\/2024\/02\/IMG20240104102508-226x300.jpg 226w, https:\/\/blog.fh-kaernten.at\/ingmarsretro\/files\/2024\/02\/IMG20240104102508-768x1021.jpg 768w, https:\/\/blog.fh-kaernten.at\/ingmarsretro\/files\/2024\/02\/IMG20240104102508-1155x1536.jpg 1155w, https:\/\/blog.fh-kaernten.at\/ingmarsretro\/files\/2024\/02\/IMG20240104102508-1540x2048.jpg 1540w, https:\/\/blog.fh-kaernten.at\/ingmarsretro\/files\/2024\/02\/IMG20240104102508-scaled.jpg 1925w\" sizes=\"(max-width: 474px) 100vw, 474px\" \/><\/a><p id=\"caption-attachment-8002\" class=\"wp-caption-text\">Vergleich zwischen Funktionsmuster und erstem &#8222;Fertigungsmodell&#8220;<\/p><\/div>\n<p style=\"text-align: justify\">Schlussendlich habe ich einen WemosD1 Pro aufgesteckt, da dieser die M\u00f6glichkeit bietet, eine externe WiFi Antenne anzuschlie\u00dfen und somit eine vern\u00fcnftige Funkreichweite zu erhalten.<\/p>\n<p style=\"text-align: justify\"><a href=\"http:\/\/blog.fh-kaernten.at\/ingmarsretro\/files\/2024\/02\/1705007460211.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-7994\" src=\"http:\/\/blog.fh-kaernten.at\/ingmarsretro\/files\/2024\/02\/1705007460211-120x300.png\" alt=\"\" width=\"140\" height=\"350\" srcset=\"https:\/\/blog.fh-kaernten.at\/ingmarsretro\/files\/2024\/02\/1705007460211-120x300.png 120w, https:\/\/blog.fh-kaernten.at\/ingmarsretro\/files\/2024\/02\/1705007460211-409x1024.png 409w, https:\/\/blog.fh-kaernten.at\/ingmarsretro\/files\/2024\/02\/1705007460211-768x1922.png 768w, https:\/\/blog.fh-kaernten.at\/ingmarsretro\/files\/2024\/02\/1705007460211-614x1536.png 614w, https:\/\/blog.fh-kaernten.at\/ingmarsretro\/files\/2024\/02\/1705007460211-818x2048.png 818w, https:\/\/blog.fh-kaernten.at\/ingmarsretro\/files\/2024\/02\/1705007460211.png 1045w\" sizes=\"(max-width: 140px) 100vw, 140px\" \/><\/a>Nach dem Aufspielen der Software und der Inbetriebnahme ist der Webserver des Wemos unter der im Code angegebenen IP Adresse erreichbar. Hier kann auch gleich gepr\u00fcft werden, ob der Pylontech Akku mit dem Wemos kommuniziert. Das Ergebnis sieht dann wie folgt aus.<\/p>\n<div id=\"attachment_8013\" style=\"width: 484px\" class=\"wp-caption alignnone\"><a href=\"http:\/\/blog.fh-kaernten.at\/ingmarsretro\/files\/2024\/02\/pylonwebsite.jpg\"><img loading=\"lazy\" decoding=\"async\" aria-describedby=\"caption-attachment-8013\" class=\"wp-image-8013 size-large\" src=\"http:\/\/blog.fh-kaernten.at\/ingmarsretro\/files\/2024\/02\/pylonwebsite-1024x835.jpg\" alt=\"\" width=\"474\" height=\"387\" srcset=\"https:\/\/blog.fh-kaernten.at\/ingmarsretro\/files\/2024\/02\/pylonwebsite-1024x835.jpg 1024w, https:\/\/blog.fh-kaernten.at\/ingmarsretro\/files\/2024\/02\/pylonwebsite-300x245.jpg 300w, https:\/\/blog.fh-kaernten.at\/ingmarsretro\/files\/2024\/02\/pylonwebsite-768x627.jpg 768w, https:\/\/blog.fh-kaernten.at\/ingmarsretro\/files\/2024\/02\/pylonwebsite.jpg 1091w\" sizes=\"(max-width: 474px) 100vw, 474px\" \/><\/a><p id=\"caption-attachment-8013\" class=\"wp-caption-text\">Webseite des WEMOS<\/p><\/div>\n<p style=\"text-align: justify\">Hier ist zu erkennen, dass beide Akkumodule korrekt erkannt werden. Im n\u00e4chsten Schritt wird \u00fcberpr\u00fcft, ob \u00fcber das MQTT Protokol Nachrichten versandt werden. Die IP Adresse des MQTT Brokers ist auch im Code des Wemos festzulegen. Ich habe in meinem Aufbau den MQTT-Explorer im Homeassistant eingerichtet, um schnell und einfach die MQTT Funktionen pr\u00fcfen zu k\u00f6nnen.<\/p>\n<div id=\"attachment_8015\" style=\"width: 902px\" class=\"wp-caption alignnone\"><a href=\"http:\/\/blog.fh-kaernten.at\/ingmarsretro\/files\/2024\/02\/mqttexplorer.jpg\"><img loading=\"lazy\" decoding=\"async\" aria-describedby=\"caption-attachment-8015\" class=\"wp-image-8015 size-full\" src=\"http:\/\/blog.fh-kaernten.at\/ingmarsretro\/files\/2024\/02\/mqttexplorer.jpg\" alt=\"\" width=\"892\" height=\"569\" srcset=\"https:\/\/blog.fh-kaernten.at\/ingmarsretro\/files\/2024\/02\/mqttexplorer.jpg 892w, https:\/\/blog.fh-kaernten.at\/ingmarsretro\/files\/2024\/02\/mqttexplorer-300x191.jpg 300w, https:\/\/blog.fh-kaernten.at\/ingmarsretro\/files\/2024\/02\/mqttexplorer-768x490.jpg 768w\" sizes=\"(max-width: 892px) 100vw, 892px\" \/><\/a><p id=\"caption-attachment-8015\" class=\"wp-caption-text\">MQTT Explorer<\/p><\/div>\n<p style=\"text-align: justify\">Im Bild oben ist zu erkennen, dass auch die Daten \u00fcber MQTT korrekt ankommen. Jetzt ist es nur mehr notwendig, im Homeassistant ein Sensor yaml file anzulegen, um die topics im als Entit\u00e4ten zur Verf\u00fcgung zu stellen.&nbsp; Dazu habe ich im <em><strong>configuration.yaml<\/strong><\/em> folgenden code hinzugef\u00fcgt:<\/p>\n<div class=\"code-wrapper\">\n<pre id=\"htmlText\" style=\"font-family: monospace;color: #000000;background-color: #f8f8ff;font-size: 11pt;border: 1px dashed #999999;padding: 5px;overflow: auto;width: 100%\"><code>mqtt:\r\n  sensor:\r\n#Pylontec Akku Serial Readout (ESP32 192.168.xxx.yyy)\r\n    - state_topic: \"ingmarsretro\/pylontec\/ESP_WiFi_RSSI\"\r\n      name: \"Pylontec_RSSI\"\r\n      unit_of_measurement: dB\r\n      \r\n    - state_topic: \"ingmarsretro\/pylontec\/availability\"\r\n      name: \"Pylontec_Status\"\r\n      \r\n    - state_topic: \"ingmarsretro\/pylontec\/currentDC\"\r\n      name: \"DC-Strom\"\r\n      unit_of_measurement: \"mA\"\r\n      \r\n    - state_topic: \"ingmarsretro\/pylontec\/getPowerDC\"  \r\n      name: \"getPower DC\"\r\n      unit_of_measurement: \"W\"\r\n      \r\n    - state_topic: \"ingmarsretro\/pylontec\/powerIN\"  \r\n      name: \"Power IN\"\r\n      unit_of_measurement: \"W\"  \r\n      \r\n    - state_topic: \"ingmarsretro\/pylontec\/estPowerAC\"\r\n      name: \"Pylontec_estPowerAC\"\r\n      unit_of_measurement: Watt  \r\n      \r\n    - state_topic: \"ingmarsretro\/pylontec\/soc\"\r\n      name: \"Pylontec_SOC\"\r\n      unit_of_measurement: \"%\"  \r\n      \r\n    - state_topic: \"ingmarsretro\/pylontec\/temp\"\r\n      name: \"Pylontec_temperature\"\r\n      unit_of_measurement: \"\u00b0C\"\r\n      \r\n    - state_topic: \"ingmarsretro\/pylontec\/battery_count\"\r\n      name: \"Pylontec_BatteryCount\"\r\n      unit_of_measurement: \"pcs\"      \r\n      \r\n    - state_topic: \"ingmarsretro\/pylontec\/base_state\"\r\n      name: \"Pylontec_BaseState\"\r\n      \r\n    - state_topic: \"ingmarsretro\/pylontec\/is_normal\"  \r\n      name: \"Pylontec_is_normal\"\r\n\r\n    - state_topic: \"ingmarsretro\/pylontec\/powerOUT\"\r\n      name: \"Pylontec_powerOUT\"\r\n      unit_of_measurement: Watt\r\n      \r\n# Pylontech battery module 0      \r\n      \r\n    - state_topic: \"ingmarsretro\/pylontec\/0\/current\"\r\n      name: \"Pylontec_Battery0_current\"\r\n      unit_of_measurement: \"A\"\r\n\r\n    - state_topic: \"ingmarsretro\/pylontec\/0\/voltage\"\r\n      name: \"Pylontec_Battery0_voltage\"\r\n      unit_of_measurement: \"V\"\r\n      \r\n    - state_topic: \"ingmarsretro\/pylontec\/0\/soc\"\r\n      name: \"Pylontec_Battery0_soc\"\r\n      unit_of_measurement: \"%\"\r\n      \r\n    - state_topic: \"ingmarsretro\/pylontec\/0\/charging\"\r\n      name: \"Pylontec_Battery0_charging\"\r\n      \r\n    - state_topic: \"ingmarsretro\/pylontec\/0\/discharging\"\r\n      name: \"Pylontec_Battery0_discharging\"\r\n     \r\n    - state_topic: \"ingmarsretro\/pylontec\/0\/idle\"\r\n      name: \"Pylontec_Battery0_idle\"\r\n      \r\n    - state_topic: \"ingmarsretro\/pylontec\/0\/state\"\r\n      name: \"Pylontec_Battery0_state\"\r\n      \r\n    - state_topic: \"ingmarsretro\/pylontec\/0\/temp\"\r\n      name: \"Pylontec_Battery0_temp\"\r\n      unit_of_measurement: \"\u00b0C\"\r\n      \r\n# Pylontech battery module 1      \r\n      \r\n    - state_topic: \"ingmarsretro\/pylontec\/1\/current\"\r\n      name: \"Pylontec_Battery1_current\"\r\n      unit_of_measurement: \"A\"\r\n\r\n    - state_topic: \"ingmarsretro\/pylontec\/1\/voltage\"\r\n      name: \"Pylontec_Battery1_voltage\"\r\n      unit_of_measurement: \"V\"\r\n      \r\n    - state_topic: \"ingmarsretro\/pylontec\/1\/soc\"\r\n      name: \"Pylontec_Battery1_soc\"\r\n      unit_of_measurement: \"%\"\r\n      \r\n    - state_topic: \"ingmarsretro\/pylontec\/1\/charging\"\r\n      name: \"Pylontec_Battery1_charging\"\r\n      \r\n    - state_topic: \"ingmarsretro\/pylontec\/1\/discharging\"\r\n      name: \"Pylontec_Battery1_discharging\"\r\n     \r\n    - state_topic: \"ingmarsretro\/pylontec\/1\/idle\"\r\n      name: \"Pylontec_Battery1_idle\"\r\n      \r\n    - state_topic: \"ingmarsretro\/pylontec\/1\/state\"\r\n      name: \"Pylontec_Battery1_state\"\r\n      \r\n    - state_topic: \"ingmarsretro\/pylontec\/1\/temp\"\r\n      name: \"Pylontec_Battery1_temp\"\r\n      unit_of_measurement: \"\u00b0C\"\r\n      \r\n<\/code><\/pre>\n<p><button id=\"copy-button\">Copy<\/button><\/p>\n<p style=\"text-align: justify\">Auf der Homeassistant Website k\u00f6nnte die Visualisierung dann beispielsweise so aussehen:<\/p>\n<p><a href=\"http:\/\/blog.fh-kaernten.at\/ingmarsretro\/files\/2024\/02\/haWAB.jpg\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone wp-image-8046 size-large\" src=\"http:\/\/blog.fh-kaernten.at\/ingmarsretro\/files\/2024\/02\/haWAB-1024x547.jpg\" alt=\"\" width=\"474\" height=\"253\" srcset=\"https:\/\/blog.fh-kaernten.at\/ingmarsretro\/files\/2024\/02\/haWAB-1024x547.jpg 1024w, https:\/\/blog.fh-kaernten.at\/ingmarsretro\/files\/2024\/02\/haWAB-300x160.jpg 300w, https:\/\/blog.fh-kaernten.at\/ingmarsretro\/files\/2024\/02\/haWAB-768x410.jpg 768w, https:\/\/blog.fh-kaernten.at\/ingmarsretro\/files\/2024\/02\/haWAB.jpg 1341w\" sizes=\"(max-width: 474px) 100vw, 474px\" \/><\/a><\/p>\n<p style=\"text-align: justify\">Zu guter Letzt poste ich unten noch den angepassten Code. Die zum Kompilieren notwendigen Libraries und weiteren Infos sind den oben angef\u00fchrten GitHub Links zu entnehmen.<\/p>\n<div class=\"code-wrapper\">\n<pre id=\"htmlText\" style=\"font-family: monospace;color: #000000;background-color: #f8f8ff;font-size: 11pt;border: 1px dashed #999999;padding: 5px;overflow: auto;width: 100%\"><code>#include &lt;ESP8266WiFi.h&gt;\r\n#include &lt;ESP8266mDNS.h&gt;\r\n#include &lt;ArduinoOTA.h&gt;\r\n#include &lt;ESP8266WebServer.h&gt;\r\n#include &lt;circular_log.h&gt;\r\n#include &lt;ArduinoJson.h&gt;\r\n#include &lt;NTPClient.h&gt;\r\n#include &lt;ESP8266TimerInterrupt.h&gt;\r\n\r\n\/\/+++ START CONFIGURATION +++\r\n\r\n\/\/IMPORTANT: Specify your WIFI settings:\r\n#define WIFI_SSID \"wifiname\"\r\n#define WIFI_PASS deinpasswort1234\"\r\n#define WIFI_HOSTNAME \"mppsolar-pylontec\"\r\n\r\n\/\/Uncomment for static ip configuration\r\n#define STATIC_IP\r\n  IPAddress local_IP(192, 168, xxx, yyy);\r\n  IPAddress subnet(255, 255, 255, 0);\r\n  IPAddress gateway(192, 168, xxx, zzz);\r\n  IPAddress primaryDNS(192, 168, xxx, zzz);\r\n\r\n\/\/Uncomment for authentication page\r\n\/\/#define AUTHENTICATION\r\n\/\/set http Authentication\r\nconst char* www_username = \"admin\";\r\nconst char* www_password = \"password\";\r\n\r\n\r\n\/\/IMPORTANT: Uncomment this line if you want to enable MQTT (and fill correct MQTT_ values below):\r\n#define ENABLE_MQTT\r\n\r\n\/\/ Set offset time in seconds to adjust for your timezone, for example:\r\n\/\/ GMT +1 = 3600\r\n\/\/ GMT +1 = 7200\r\n\/\/ GMT +8 = 28800\r\n\/\/ GMT -1 = -3600\r\n\/\/ GMT 0 = 0\r\n#define GMT 3600\r\n\r\n\/\/NOTE 1: if you want to change what is pushed via MQTT - edit function: pushBatteryDataToMqtt.\r\n\/\/NOTE 2: MQTT_TOPIC_ROOT is where battery will push MQTT topics. For example \"soc\" will be pushed to: \"home\/grid_battery\/soc\"\r\n#define MQTT_SERVER        \"192.168.xx.broker\"\r\n#define MQTT_PORT          1883\r\n#define MQTT_USER          \"\"\r\n#define MQTT_PASSWORD      \"\"\r\n#define MQTT_TOPIC_ROOT    \"ingmarsretro\/pylontec\/\"  \/\/this is where mqtt data will be pushed\r\n#define MQTT_PUSH_FREQ_SEC 2  \/\/maximum mqtt update frequency in seconds\r\n\r\n\/\/+++   END CONFIGURATION +++\r\n\r\n#ifdef ENABLE_MQTT\r\n#include &lt;PubSubClient.h&gt;\r\nWiFiClient espClient;\r\nPubSubClient mqttClient(espClient);\r\n#endif \/\/ENABLE_MQTT\r\n\r\n\/\/text response\r\nchar g_szRecvBuff[7000];\r\n\r\nconst long utcOffsetInSeconds = GMT;\r\nchar daysOfTheWeek[7][12] = {\"Sunday\", \"Monday\", \"Tuesday\", \"Wednesday\", \"Thursday\", \"Friday\", \"Saturday\"};\r\n\/\/ Define NTP Client to get time\r\nWiFiUDP ntpUDP;\r\nNTPClient timeClient(ntpUDP, \"pool.ntp.org\", utcOffsetInSeconds);\r\n\r\n\r\nESP8266WebServer server(80);\r\ncircular_log&lt;7000&gt; g_log;\r\nbool ntpTimeReceived = false;\r\nint g_baudRate = 0;\r\n\r\nvoid Log(const char* msg)\r\n{\r\n  g_log.Log(msg);\r\n}\r\n\r\n\/\/Define Interrupt Timer to Calculate Power meter every second (kWh)\r\n#define USING_TIM_DIV1 true                                             \/\/ for shortest and most accurate timer\r\nESP8266Timer ITimer;\r\nbool setInterval(unsigned long interval, timer_callback callback);      \/\/ interval (in microseconds)\r\n#define TIMER_INTERVAL_MS 1000\r\n\r\n\/\/Global Variables for the Power Meter - accessible from the calculating interrupt und from main\r\nunsigned long powerIN = 0;       \/\/WS gone in to the BAttery\r\nunsigned long powerOUT = 0;      \/\/WS gone out of the Battery\r\n\/\/Global Variables for the Power Meter - \u00dcberlauf\r\nunsigned long powerINWh = 0;       \/\/WS gone in to the BAttery\r\nunsigned long powerOUTWh = 0;      \/\/WS gone out of the Battery\r\n\r\nvoid setup() {\r\n  \r\n  memset(g_szRecvBuff, 0, sizeof(g_szRecvBuff)); \/\/clean variable\r\n  \r\n  pinMode(LED_BUILTIN, OUTPUT); \r\n  digitalWrite(LED_BUILTIN, HIGH);\/\/high is off\r\n  \r\n  \/\/ put your setup code here, to run once:\r\n  WiFi.mode(WIFI_STA);\r\n  WiFi.persistent(false); \/\/our credentialss are hardcoded, so we don't need ESP saving those each boot (will save on flash wear)\r\n  WiFi.hostname(WIFI_HOSTNAME);\r\n  #ifdef STATIC_IP\r\n     WiFi.config(local_IP, gateway, subnet, primaryDNS);\r\n  #endif\r\n  WiFi.begin(WIFI_SSID, WIFI_PASS);\r\n\r\n  for(int ix=0; ix&lt;10; ix++)\r\n  {\r\n    Log(\"Wait for WIFI Connection\");\r\n    if(WiFi.status() == WL_CONNECTED)\r\n    {\r\n      break;\r\n    }\r\n\r\n    delay(1000);\r\n  }\r\n\r\n  ArduinoOTA.setHostname(WIFI_HOSTNAME);\r\n  ArduinoOTA.begin();\r\n  server.on(\"\/\", handleRoot);\r\n  server.on(\"\/log\", handleLog);\r\n  server.on(\"\/req\", handleReq);\r\n  server.on(\"\/jsonOut\", handleJsonOut);\r\n  server.on(\"\/reboot\", [](){\r\n    #ifdef AUTHENTICATION\r\n    if (!server.authenticate(www_username, www_password)) {\r\n      return server.requestAuthentication();\r\n    }\r\n    #endif\r\n    ESP.restart();\r\n  });\r\n  \r\n  server.begin(); \r\n  \r\n  timeClient.begin();\r\n\r\n  \r\n#ifdef ENABLE_MQTT\r\n  mqttClient.setServer(MQTT_SERVER, MQTT_PORT);\r\n#endif\r\n\r\n  Log(\"Boot event\");\r\n  \r\n}\r\n\r\nvoid handleLog()\r\n{\r\n  #ifdef AUTHENTICATION\r\n  if (!server.authenticate(www_username, www_password)) {\r\n    return server.requestAuthentication();\r\n  } \r\n  #endif\r\n  server.send(200, \"text\/html\", g_log.c_str());\r\n}\r\n\r\nvoid switchBaud(int newRate)\r\n{\r\n  if(g_baudRate == newRate)\r\n  {\r\n    return;\r\n  }\r\n  \r\n  if(g_baudRate != 0)\r\n  {\r\n    Serial.flush();\r\n    delay(20);\r\n    Serial.end();\r\n    delay(20);\r\n  }\r\n\r\n  char szMsg[50];\r\n  snprintf(szMsg, sizeof(szMsg)-1, \"New baud: %d\", newRate);\r\n  Log(szMsg);\r\n  \r\n  Serial.begin(newRate);\r\n  g_baudRate = newRate;\r\n\r\n  delay(20);\r\n}\r\n\r\nvoid waitForSerial()\r\n{\r\n  for(int ix=0; ix&lt;150;ix++)\r\n  {\r\n    if(Serial.available()) break;\r\n    delay(10);\r\n  }\r\n}\r\n\r\nint readFromSerial()\r\n{\r\n  memset(g_szRecvBuff, 0, sizeof(g_szRecvBuff));\r\n  int recvBuffLen = 0;\r\n  bool foundTerminator = true;\r\n  \r\n  waitForSerial();\r\n  \r\n  while(Serial.available())\r\n  {\r\n    char szResponse[256] = \"\";\r\n    const int readNow = Serial.readBytesUntil('&gt;', szResponse, sizeof(szResponse)-1); \/\/all commands terminate with \"$$\\r\\n\\rpylon&gt;\" (no new line at the end)\r\n    if(readNow &gt; 0 &amp;&amp; \r\n       szResponse[0] != '\\0')\r\n    {\r\n      if(readNow + recvBuffLen + 1 &gt;= (int)(sizeof(g_szRecvBuff)))\r\n      {\r\n        Log(\"WARNING: Read too much data on the console!\");\r\n        break;\r\n      }\r\n      \r\n      strcat(g_szRecvBuff, szResponse);\r\n      recvBuffLen += readNow;\r\n\r\n      if(strstr(g_szRecvBuff, \"$$\\r\\n\\rpylon\"))\r\n      {\r\n        strcat(g_szRecvBuff, \"&gt;\"); \/\/readBytesUntil will skip this, so re-add\r\n        foundTerminator = true;\r\n        break; \/\/found end of the string\r\n      }\r\n\r\n      if(strstr(g_szRecvBuff, \"Press [Enter] to be continued,other key to exit\"))\r\n      {\r\n        \/\/we need to send new line character so battery continues the output\r\n        Serial.write(\"\\r\");\r\n      }\r\n\r\n      waitForSerial();\r\n    }\r\n  }\r\n\r\n  if(recvBuffLen &gt; 0 )\r\n  {\r\n    if(foundTerminator == false)\r\n    {\r\n      Log(\"Failed to find pylon&gt; terminator\");\r\n    }\r\n  }\r\n\r\n  return recvBuffLen;\r\n}\r\n\r\nbool readFromSerialAndSendResponse()\r\n{\r\n  const int recvBuffLen = readFromSerial();\r\n  if(recvBuffLen &gt; 0)\r\n  {\r\n    server.sendContent(g_szRecvBuff);\r\n    return true;\r\n  }\r\n\r\n  return false;\r\n}\r\n\r\nbool sendCommandAndReadSerialResponse(const char* pszCommand)\r\n{\r\n  switchBaud(115200);\r\n\r\n  if(pszCommand[0] != '\\0')\r\n  {\r\n    Serial.write(pszCommand);\r\n  }\r\n  Serial.write(\"\\n\");\r\n\r\n  const int recvBuffLen = readFromSerial();\r\n  if(recvBuffLen &gt; 0)\r\n  {\r\n    return true;\r\n  }\r\n\r\n  \/\/wake up console and try again:\r\n  wakeUpConsole();\r\n\r\n  if(pszCommand[0] != '\\0')\r\n  {\r\n    Serial.write(pszCommand);\r\n  }\r\n  Serial.write(\"\\n\");\r\n\r\n  return readFromSerial() &gt; 0;\r\n}\r\n\r\nvoid handleReq()\r\n{\r\n  #ifdef AUTHENTICATION\r\n  if (!server.authenticate(www_username, www_password)) {\r\n    return server.requestAuthentication();\r\n  }\r\n  #endif\r\n  bool respOK;\r\n  if(server.hasArg(\"code\") == false)\r\n  {\r\n    respOK = sendCommandAndReadSerialResponse(\"\");\r\n  }\r\n  else\r\n  {\r\n    respOK = sendCommandAndReadSerialResponse(server.arg(\"code\").c_str());\r\n  }\r\n\r\n  handleRoot();\r\n}\r\n\r\n\r\n\r\nvoid handleJsonOut()\r\n{\r\n  #ifdef AUTHENTICATION\r\n  if (!server.authenticate(www_username, www_password)) {\r\n    return server.requestAuthentication();\r\n  }\r\n  #endif\r\n  if(sendCommandAndReadSerialResponse(\"pwr\") == false)\r\n  {\r\n    server.send(500, \"text\/plain\", \"Failed to get response to 'pwr' command\");\r\n    return;\r\n  }\r\n\r\n  parsePwrResponse(g_szRecvBuff);\r\n  prepareJsonOutput(g_szRecvBuff, sizeof(g_szRecvBuff));\r\n  server.send(200, \"application\/json\", g_szRecvBuff);\r\n}\r\n\r\nvoid handleRoot() {\r\n  #ifdef AUTHENTICATION\r\n  if (!server.authenticate(www_username, www_password)) {\r\n    return server.requestAuthentication();\r\n  }\r\n  #endif\r\n  timeClient.update(); \/\/get ntp datetime\r\n  unsigned long days = 0, hours = 0, minutes = 0;\r\n  unsigned long val = os_getCurrentTimeSec();\r\n  days = val \/ (3600*24);\r\n  val -= days * (3600*24);\r\n  hours = val \/ 3600;\r\n  val -= hours * 3600;\r\n  minutes = val \/ 60;\r\n  val -= minutes*60;\r\n\r\n  time_t epochTime = timeClient.getEpochTime();\r\n  String formattedTime = timeClient.getFormattedTime();\r\n  \/\/Get a time structure\r\n  struct tm *ptm = gmtime ((time_t *)&amp;epochTime); \r\n  int currentMonth = ptm-&gt;tm_mon+1;\r\n\r\n  static char szTmp[9500] = \"\";  \r\n  long timezone= GMT \/ 3600;\r\n  snprintf(szTmp, sizeof(szTmp)-1, \"&lt;html&gt;&lt;b&gt;Pylontech Battery&lt;\/b&gt;&lt;br&gt;Time GMT: %s (%s %d)&lt;br&gt;Uptime: %02d:%02d:%02d.%02d&lt;br&gt;&lt;br&gt;free heap: %u&lt;br&gt;Wifi RSSI: %d&lt;BR&gt;Wifi SSID: %s\", \r\n            formattedTime, \"GMT \", timezone,\r\n            (int)days, (int)hours, (int)minutes, (int)val, \r\n            ESP.getFreeHeap(), WiFi.RSSI(), WiFi.SSID().c_str());\r\n\r\n  strncat(szTmp, \"&lt;BR&gt;&lt;a href='\/log'&gt;Runtime log&lt;\/a&gt;&lt;HR&gt;\", sizeof(szTmp)-1);\r\n  strncat(szTmp, \"&lt;form action='\/req' method='get'&gt;Command:&lt;input type='text' name='code'\/&gt;&lt;input type='submit'&gt; &lt;a href='\/req?code=pwr'&gt;PWR&lt;\/a&gt; | &lt;a href='\/req?code=pwr%201'&gt;Power 1&lt;\/a&gt; |  &lt;a href='\/req?code=pwr%202'&gt;Power 2&lt;\/a&gt; | &lt;a href='\/req?code=pwr%203'&gt;Power 3&lt;\/a&gt; | &lt;a href='\/req?code=pwr%204'&gt;Power 4&lt;\/a&gt; | &lt;a href='\/req?code=help'&gt;Help&lt;\/a&gt; | &lt;a href='\/req?code=log'&gt;Event Log&lt;\/a&gt; | &lt;a href='\/req?code=time'&gt;Time&lt;\/a&gt;&lt;br&gt;\", sizeof(szTmp)-1);\r\n  \/\/strncat(szTmp, \"&lt;form action='\/req' method='get'&gt;Command:&lt;input type='text' name='code'\/&gt;&lt;input type='submit'&gt;&lt;a href='\/req?code=pwr'&gt;Power&lt;\/a&gt; | &lt;a href='\/req?code=help'&gt;Help&lt;\/a&gt; | &lt;a href='\/req?code=log'&gt;Event Log&lt;\/a&gt; | &lt;a href='\/req?code=time'&gt;Time&lt;\/a&gt;&lt;br&gt;\", sizeof(szTmp)-1);\r\n  strncat(szTmp, \"&lt;textarea rows='80' cols='180'&gt;\", sizeof(szTmp)-1);\r\n  \/\/strncat(szTmp, \"&lt;textarea rows='45' cols='180'&gt;\", sizeof(szTmp)-1);\r\n  strncat(szTmp, g_szRecvBuff, sizeof(szTmp)-1);\r\n  strncat(szTmp, \"&lt;\/textarea&gt;&lt;\/form&gt;\", sizeof(szTmp)-1);\r\n  strncat(szTmp, \"&lt;\/html&gt;\", sizeof(szTmp)-1);\r\n  \/\/send page\r\n  server.send(200, \"text\/html\", szTmp);\r\n}\r\n\r\nunsigned long os_getCurrentTimeSec()\r\n{\r\n  static unsigned int wrapCnt = 0;\r\n  static unsigned long lastVal = 0;\r\n  unsigned long currentVal = millis();\r\n\r\n  if(currentVal &lt; lastVal)\r\n  {\r\n    wrapCnt++;\r\n  }\r\n\r\n  lastVal = currentVal;\r\n  unsigned long seconds = currentVal\/1000;\r\n  \r\n  \/\/millis will wrap each 50 days, as we are interested only in seconds, let's keep the wrap counter\r\n  return (wrapCnt*4294967) + seconds;\r\n}\r\n\r\nvoid wakeUpConsole()\r\n{\r\n  switchBaud(1200);\r\n\r\n  \/\/byte wakeUpBuff[] = {0x7E, 0x32, 0x30, 0x30, 0x31, 0x34, 0x36, 0x38, 0x32, 0x43, 0x30, 0x30, 0x34, 0x38, 0x35, 0x32, 0x30, 0x46, 0x43, 0x43, 0x33, 0x0D};\r\n  \/\/Serial.write(wakeUpBuff, sizeof(wakeUpBuff));\r\n  Serial.write(\"~20014682C0048520FCC3\\r\");\r\n  delay(1000);\r\n\r\n  byte newLineBuff[] = {0x0E, 0x0A};\r\n  switchBaud(115200);\r\n  \r\n  for(int ix=0; ix&lt;10; ix++)\r\n  {\r\n    Serial.write(newLineBuff, sizeof(newLineBuff));\r\n    delay(1000);\r\n\r\n    if(Serial.available())\r\n    {\r\n      while(Serial.available())\r\n      {\r\n        Serial.read();\r\n      }\r\n      \r\n      break;\r\n    }\r\n  }\r\n}\r\n\r\n#define MAX_PYLON_BATTERIES 8\r\n\r\nstruct pylonBattery\r\n{\r\n  bool isPresent;\r\n  long  soc;     \/\/Coulomb in %\r\n  long  voltage; \/\/in mW\r\n  long  current; \/\/in mA, negative value is discharge\r\n  long  tempr;   \/\/temp of case or BMS?\r\n  long  cellTempLow;\r\n  long  cellTempHigh;\r\n  long  cellVoltLow;\r\n  long  cellVoltHigh;\r\n  char baseState[9];    \/\/Charge | Dischg | Idle\r\n  char voltageState[9]; \/\/Normal\r\n  char currentState[9]; \/\/Normal\r\n  char tempState[9];    \/\/Normal\r\n  char time[20];        \/\/2019-06-08 04:00:29\r\n  char b_v_st[9];       \/\/Normal  (battery voltage?)\r\n  char b_t_st[9];       \/\/Normal  (battery temperature?)\r\n\r\n  bool isCharging()    const { return strcmp(baseState, \"Charge\")   == 0; }\r\n  bool isDischarging() const { return strcmp(baseState, \"Dischg\")   == 0; }\r\n  bool isIdle()        const { return strcmp(baseState, \"Idle\")     == 0; }\r\n  bool isBalancing()   const { return strcmp(baseState, \"Balance\")  == 0; }\r\n  \r\n\r\n  bool isNormal() const\r\n  {\r\n    if(isCharging()    == false &amp;&amp;\r\n       isDischarging() == false &amp;&amp;\r\n       isIdle()        == false &amp;&amp;\r\n       isBalancing()   == false)\r\n    {\r\n      return false; \/\/base state looks wrong!\r\n    }\r\n\r\n    return  strcmp(voltageState, \"Normal\") == 0 &amp;&amp;\r\n            strcmp(currentState, \"Normal\") == 0 &amp;&amp;\r\n            strcmp(tempState,    \"Normal\") == 0 &amp;&amp;\r\n            strcmp(b_v_st,       \"Normal\") == 0 &amp;&amp;\r\n            strcmp(b_t_st,       \"Normal\") == 0 ;\r\n  }\r\n};\r\n\r\nstruct batteryStack\r\n{\r\n  int batteryCount;\r\n  int soc;  \/\/in %, if charging: average SOC, otherwise: lowest SOC\r\n  int temp; \/\/in mC, if highest temp is &gt; 15C, this will show the highest temp, otherwise the lowest\r\n  long currentDC;    \/\/mAh current going in or out of the battery\r\n  long avgVoltage;    \/\/in mV\r\n  char baseState[9];  \/\/Charge | Dischg | Idle | Balance | Alarm!\r\n\r\n  \r\n  pylonBattery batts[MAX_PYLON_BATTERIES];\r\n\r\n  bool isNormal() const\r\n  {\r\n    for(int ix=0; ix&lt;MAX_PYLON_BATTERIES; ix++)\r\n    {\r\n      if(batts[ix].isPresent &amp;&amp; \r\n         batts[ix].isNormal() == false)\r\n      {\r\n        return false;\r\n      }\r\n    }\r\n\r\n    return true;\r\n  }\r\n\r\n  \/\/in Wh\r\n  long getPowerDC() const\r\n  {\r\n    return (long)(((double)currentDC\/1000.0)*((double)avgVoltage\/1000.0));\r\n  }\r\n\r\n  \/\/ power in Wh in charge\r\n  float powerIN() const\r\n  {\r\n    if (currentDC &gt; 0) {\r\n       return (float)(((double)currentDC\/1000.0)*((double)avgVoltage\/1000.0));\r\n    } else {\r\n       return (float)(0);\r\n    }\r\n  }\r\n  \r\n  \/\/ power in Wh in discharge\r\n  float powerOUT() const\r\n  {\r\n    if (currentDC &lt; 0) {\r\n       return (float)(((double)currentDC\/1000.0)*((double)avgVoltage\/1000.0)*-1);\r\n    } else {\r\n       return (float)(0);\r\n    }\r\n  }\r\n\r\n  \/\/Wh estimated current on AC side (taking into account Sofar ME3000SP losses)\r\n  long getEstPowerAc() const\r\n  {\r\n    double powerDC = (double)getPowerDC();\r\n    if(powerDC == 0)\r\n    {\r\n      return 0;\r\n    }\r\n    else if(powerDC &lt; 0)\r\n    {\r\n      \/\/we are discharging, on AC side we will see less power due to losses\r\n      if(powerDC &lt; -1000)\r\n      {\r\n        return (long)(powerDC*0.94);\r\n      }\r\n      else if(powerDC &lt; -600)\r\n      {\r\n        return (long)(powerDC*0.90);\r\n      }\r\n      else\r\n      {\r\n        return (long)(powerDC*0.87);\r\n      }\r\n    }\r\n    else\r\n    {\r\n      \/\/we are charging, on AC side we will have more power due to losses\r\n      if(powerDC &gt; 1000)\r\n      {\r\n        return (long)(powerDC*1.06);\r\n      }\r\n      else if(powerDC &gt; 600)\r\n      {\r\n        return (long)(powerDC*1.1);\r\n      }\r\n      else\r\n      {\r\n        return (long)(powerDC*1.13);\r\n      }\r\n    }\r\n  }\r\n};\r\n\r\nbatteryStack g_stack;\r\n\r\n\r\nlong extractInt(const char* pStr, int pos)\r\n{\r\n  return atol(pStr+pos);\r\n}\r\n\r\nvoid extractStr(const char* pStr, int pos, char* strOut, int strOutSize)\r\n{\r\n  strOut[strOutSize-1] = '\\0';\r\n  strncpy(strOut, pStr+pos, strOutSize-1);\r\n  strOutSize--;\r\n  \r\n  \r\n  \/\/trim right\r\n  while(strOutSize &gt; 0)\r\n  {\r\n    if(isspace(strOut[strOutSize-1]))\r\n    {\r\n      strOut[strOutSize-1] = '\\0';\r\n    }\r\n    else\r\n    {\r\n      break;\r\n    }\r\n\r\n    strOutSize--;\r\n  }\r\n}\r\n\r\n\/* Output has mixed \\r and \\r\\n\r\npwr\r\n\r\n@\r\n\r\nPower Volt   Curr   Tempr  Tlow   Thigh  Vlow   Vhigh  Base.St  Volt.St  Curr.St  Temp.St  Coulomb  Time                 B.V.St   B.T.St  \r\n\r\n1     49735  -1440  22000  19000  19000  3315   3317   Dischg   Normal   Normal   Normal   93%      2019-06-08 04:00:30  Normal   Normal  \r\n\r\n....   \r\n\r\n8     -      -      -      -      -      -      -      Absent   -        -        -        -        -                    -        -       \r\n\r\nCommand completed successfully\r\n\r\n$$\r\n\r\npylon\r\n*\/\r\nbool parsePwrResponse(const char* pStr)\r\n{\r\n  if(strstr(pStr, \"Command completed successfully\") == NULL)\r\n  {\r\n    return false;\r\n  }\r\n  \r\n  int chargeCnt    = 0;\r\n  int dischargeCnt = 0;\r\n  int idleCnt      = 0;\r\n  int alarmCnt     = 0;\r\n  int socAvg       = 0;\r\n  int socLow       = 0;\r\n  int tempHigh     = 0;\r\n  int tempLow      = 0;\r\n\r\n  memset(&amp;g_stack, 0, sizeof(g_stack));\r\n\r\n  for(int ix=0; ix&lt;MAX_PYLON_BATTERIES; ix++)\r\n  {\r\n    char szToFind[32] = \"\";\r\n    snprintf(szToFind, sizeof(szToFind)-1, \"\\r\\r\\n%d     \", ix+1);\r\n\r\n    const char* pLineStart = strstr(pStr, szToFind);\r\n    if(pLineStart == NULL)\r\n    {\r\n      return false;\r\n    }\r\n\r\n    pLineStart += 3; \/\/move past \\r\\r\\n\r\n\r\n    extractStr(pLineStart, 55, g_stack.batts[ix].baseState, sizeof(g_stack.batts[ix].baseState));\r\n    if(strcmp(g_stack.batts[ix].baseState, \"Absent\") == 0)\r\n    {\r\n      g_stack.batts[ix].isPresent = false;\r\n    }\r\n    else\r\n    {\r\n      g_stack.batts[ix].isPresent = true;\r\n      extractStr(pLineStart, 64, g_stack.batts[ix].voltageState, sizeof(g_stack.batts[ix].voltageState));\r\n      extractStr(pLineStart, 73, g_stack.batts[ix].currentState, sizeof(g_stack.batts[ix].currentState));\r\n      extractStr(pLineStart, 82, g_stack.batts[ix].tempState, sizeof(g_stack.batts[ix].tempState));\r\n      extractStr(pLineStart, 100, g_stack.batts[ix].time, sizeof(g_stack.batts[ix].time));\r\n      extractStr(pLineStart, 121, g_stack.batts[ix].b_v_st, sizeof(g_stack.batts[ix].b_v_st));\r\n      extractStr(pLineStart, 130, g_stack.batts[ix].b_t_st, sizeof(g_stack.batts[ix].b_t_st));\r\n      g_stack.batts[ix].voltage = extractInt(pLineStart, 6);\r\n      g_stack.batts[ix].current = extractInt(pLineStart, 13);\r\n      g_stack.batts[ix].tempr   = extractInt(pLineStart, 20);\r\n      g_stack.batts[ix].cellTempLow    = extractInt(pLineStart, 27);\r\n      g_stack.batts[ix].cellTempHigh   = extractInt(pLineStart, 34);\r\n      g_stack.batts[ix].cellVoltLow    = extractInt(pLineStart, 41);\r\n      g_stack.batts[ix].cellVoltHigh   = extractInt(pLineStart, 48);\r\n      g_stack.batts[ix].soc            = extractInt(pLineStart, 91);\r\n\r\n      \/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/ Post-process \/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\r\n      g_stack.batteryCount++;\r\n      g_stack.currentDC += g_stack.batts[ix].current;\r\n      g_stack.avgVoltage += g_stack.batts[ix].voltage;\r\n      socAvg += g_stack.batts[ix].soc;\r\n\r\n      if(g_stack.batts[ix].isNormal() == false){ alarmCnt++; }\r\n      else if(g_stack.batts[ix].isCharging()){chargeCnt++;}\r\n      else if(g_stack.batts[ix].isDischarging()){dischargeCnt++;}\r\n      else if(g_stack.batts[ix].isIdle()){idleCnt++;}\r\n      else{ alarmCnt++; } \/\/should not really happen!\r\n\r\n      if(g_stack.batteryCount == 1)\r\n      {\r\n        socLow = g_stack.batts[ix].soc;\r\n        tempLow  = g_stack.batts[ix].cellTempLow;\r\n        tempHigh = g_stack.batts[ix].cellTempHigh;\r\n      }\r\n      else\r\n      {\r\n        if(socLow &gt; g_stack.batts[ix].soc){socLow = g_stack.batts[ix].soc;}\r\n        if(tempHigh &lt; g_stack.batts[ix].cellTempHigh){tempHigh = g_stack.batts[ix].cellTempHigh;}\r\n        if(tempLow &gt; g_stack.batts[ix].cellTempLow){tempLow = g_stack.batts[ix].cellTempLow;}\r\n      }\r\n      \r\n    }\r\n  }\r\n\r\n  \/\/now update stack state:\r\n  g_stack.avgVoltage \/= g_stack.batteryCount;\r\n  g_stack.soc = socLow;\r\n\r\n  if(tempHigh &gt; 15000) \/\/15C\r\n  {\r\n    g_stack.temp = tempHigh; \/\/in the summer we highlight the warmest cell\r\n  }\r\n  else\r\n  {\r\n    g_stack.temp = tempLow; \/\/in the winter we focus on coldest cell\r\n  }\r\n\r\n  if(alarmCnt &gt; 0)\r\n  {\r\n    strcpy(g_stack.baseState, \"Alarm!\");\r\n  }\r\n  else if(chargeCnt == g_stack.batteryCount)\r\n  {\r\n    strcpy(g_stack.baseState, \"Charge\");\r\n    g_stack.soc = (int)(socAvg \/ g_stack.batteryCount);\r\n  }\r\n  else if(dischargeCnt == g_stack.batteryCount)\r\n  {\r\n    strcpy(g_stack.baseState, \"Dischg\");\r\n  }\r\n  else if(idleCnt == g_stack.batteryCount)\r\n  {\r\n    strcpy(g_stack.baseState, \"Idle\");\r\n  }\r\n  else\r\n  {\r\n    strcpy(g_stack.baseState, \"Balance\");\r\n  }\r\n\r\n\r\n  return true;\r\n}\r\n\r\nvoid prepareJsonOutput(char* pBuff, int buffSize)\r\n{\r\n  memset(pBuff, 0, buffSize);\r\n  snprintf(pBuff, buffSize-1, \"{\\\"soc\\\": %d, \\\"temp\\\": %d, \\\"currentDC\\\": %ld, \\\"avgVoltage\\\": %ld, \\\"baseState\\\": \\\"%s\\\", \\\"batteryCount\\\": %d, \\\"powerDC\\\": %ld, \\\"estPowerAC\\\": %ld, \\\"isNormal\\\": %s}\", g_stack.soc, \r\n                                                                                                                                                                                                            g_stack.temp, \r\n                                                                                                                                                                                                            g_stack.currentDC, \r\n                                                                                                                                                                                                            g_stack.avgVoltage, \r\n                                                                                                                                                                                                            g_stack.baseState, \r\n                                                                                                                                                                                                            g_stack.batteryCount, \r\n                                                                                                                                                                                                            g_stack.getPowerDC(), \r\n                                                                                                                                                                                                            g_stack.getEstPowerAc(),\r\n                                                                                                                                                                                                            g_stack.isNormal() ? \"true\" : \"false\");\r\n}\r\n\r\nvoid loop() {\r\n#ifdef ENABLE_MQTT\r\n  mqttLoop();\r\n#endif\r\n  \r\n  ArduinoOTA.handle();\r\n  server.handleClient();\r\n\r\n  \/\/if there are bytes availbe on serial here - it's unexpected\r\n  \/\/when we send a command to battery, we read whole response\r\n  \/\/if we get anything here anyways - we will log it\r\n  int bytesAv = Serial.available();\r\n  if(bytesAv &gt; 0)\r\n  {\r\n    if(bytesAv &gt; 63)\r\n    {\r\n      bytesAv = 63;\r\n    }\r\n    \r\n    char buff[64+4] = \"RCV:\";\r\n    if(Serial.readBytes(buff+4, bytesAv) &gt; 0)\r\n    {\r\n      digitalWrite(LED_BUILTIN, LOW);\r\n      delay(5);\r\n      digitalWrite(LED_BUILTIN, HIGH);\/\/high is off\r\n\r\n      Log(buff);\r\n    }\r\n  }\r\n}\r\n\r\n#ifdef ENABLE_MQTT\r\n#define ABS_DIFF(a, b) (a &gt; b ? a-b : b-a)\r\nvoid mqtt_publish_f(const char* topic, float newValue, float oldValue, float minDiff, bool force)\r\n{\r\n  char szTmp[16] = \"\";\r\n  snprintf(szTmp, 15, \"%.2f\", newValue);\r\n  if(force || ABS_DIFF(newValue, oldValue) &gt; minDiff)\r\n  {\r\n    mqttClient.publish(topic, szTmp, false);\r\n  }\r\n}\r\n\r\nvoid mqtt_publish_i(const char* topic, int newValue, int oldValue, int minDiff, bool force)\r\n{\r\n  char szTmp[16] = \"\";\r\n  snprintf(szTmp, 15, \"%d\", newValue);\r\n  if(force || ABS_DIFF(newValue, oldValue) &gt; minDiff)\r\n  {\r\n    mqttClient.publish(topic, szTmp, false);\r\n  }\r\n}\r\n\r\nvoid mqtt_publish_s(const char* topic, const char* newValue, const char* oldValue, bool force)\r\n{\r\n  if(force || strcmp(newValue, oldValue) != 0)\r\n  {\r\n    mqttClient.publish(topic, newValue, false);\r\n  }\r\n}\r\n\r\nvoid pushBatteryDataToMqtt(const batteryStack&amp; lastSentData, bool forceUpdate \/* if true - we will send all data regardless if it's the same *\/)\r\n{\r\n  mqtt_publish_f(MQTT_TOPIC_ROOT \"soc\",          g_stack.soc,                lastSentData.soc,                0, forceUpdate);\r\n  mqtt_publish_f(MQTT_TOPIC_ROOT \"temp\",         (float)g_stack.temp\/1000.0, (float)lastSentData.temp\/1000.0, 0.1, forceUpdate);\r\n  mqtt_publish_i(MQTT_TOPIC_ROOT \"currentDC\",    g_stack.currentDC,          lastSentData.currentDC,          1, forceUpdate);\r\n  mqtt_publish_i(MQTT_TOPIC_ROOT \"estPowerAC\",   g_stack.getEstPowerAc(),    lastSentData.getEstPowerAc(),   10, forceUpdate);\r\n  mqtt_publish_i(MQTT_TOPIC_ROOT \"battery_count\",g_stack.batteryCount,       lastSentData.batteryCount,       0, forceUpdate);\r\n  mqtt_publish_s(MQTT_TOPIC_ROOT \"base_state\",   g_stack.baseState,          lastSentData.baseState            , forceUpdate);\r\n  mqtt_publish_i(MQTT_TOPIC_ROOT \"is_normal\",    g_stack.isNormal() ? 1:0,   lastSentData.isNormal() ? 1:0,   0, forceUpdate);\r\n  mqtt_publish_i(MQTT_TOPIC_ROOT \"getPowerDC\",   g_stack.getPowerDC(),       lastSentData.getPowerDC(),       1, forceUpdate);\r\n  mqtt_publish_i(MQTT_TOPIC_ROOT \"powerIN\",      g_stack.powerIN(),          lastSentData.powerIN(),          1, forceUpdate);\r\n  mqtt_publish_i(MQTT_TOPIC_ROOT \"powerOUT\",     g_stack.powerOUT(),         lastSentData.powerOUT(),         1, forceUpdate);\r\n\r\n  \/\/ publishing details\r\n  for (int ix = 0; ix &lt; g_stack.batteryCount; ix++) {\r\n    char ixBuff[50];\r\n    String ixBattStr = MQTT_TOPIC_ROOT + String(ix) + \"\/voltage\";\r\n    ixBattStr.toCharArray(ixBuff, 50);\r\n    mqtt_publish_f(ixBuff, g_stack.batts[ix].voltage \/ 1000.0, lastSentData.batts[ix].voltage \/ 1000.0, 0, forceUpdate);\r\n    ixBattStr = MQTT_TOPIC_ROOT + String(ix) + \"\/current\";\r\n    ixBattStr.toCharArray(ixBuff, 50);\r\n    mqtt_publish_f(ixBuff, g_stack.batts[ix].current \/ 1000.0, lastSentData.batts[ix].current \/ 1000.0, 0, forceUpdate);\r\n    ixBattStr = MQTT_TOPIC_ROOT + String(ix) + \"\/soc\";\r\n    ixBattStr.toCharArray(ixBuff, 50);\r\n    mqtt_publish_i(ixBuff, g_stack.batts[ix].soc, lastSentData.batts[ix].soc, 0, forceUpdate);\r\n    ixBattStr = MQTT_TOPIC_ROOT + String(ix) + \"\/charging\";\r\n    ixBattStr.toCharArray(ixBuff, 50);\r\n    mqtt_publish_i(ixBuff, g_stack.batts[ix].isCharging()?1:0, lastSentData.batts[ix].isCharging()?1:0, 0, forceUpdate);\r\n    ixBattStr = MQTT_TOPIC_ROOT + String(ix) + \"\/discharging\";\r\n    ixBattStr.toCharArray(ixBuff, 50);\r\n    mqtt_publish_i(ixBuff, g_stack.batts[ix].isDischarging()?1:0, lastSentData.batts[ix].isDischarging()?1:0, 0, forceUpdate);\r\n    ixBattStr = MQTT_TOPIC_ROOT + String(ix) + \"\/idle\";\r\n    ixBattStr.toCharArray(ixBuff, 50);\r\n    mqtt_publish_i(ixBuff, g_stack.batts[ix].isIdle()?1:0, lastSentData.batts[ix].isIdle()?1:0, 0, forceUpdate);\r\n    ixBattStr = MQTT_TOPIC_ROOT + String(ix) + \"\/state\";\r\n    ixBattStr.toCharArray(ixBuff, 50);\r\n    mqtt_publish_s(ixBuff, g_stack.batts[ix].isIdle()?\"Idle\":g_stack.batts[ix].isCharging()?\"Charging\":g_stack.batts[ix].isDischarging()?\"Discharging\":\"\", lastSentData.batts[ix].isIdle()?\"Idle\":lastSentData.batts[ix].isCharging()?\"Charging\":lastSentData.batts[ix].isDischarging()?\"Discharging\":\"\", forceUpdate);\r\n    ixBattStr = MQTT_TOPIC_ROOT + String(ix) + \"\/temp\";\r\n    ixBattStr.toCharArray(ixBuff, 50);\r\n    mqtt_publish_f(ixBuff, (float)g_stack.batts[ix].tempr\/1000.0, (float)lastSentData.batts[ix].tempr\/1000.0, 0.1, forceUpdate);\r\n  }\r\n} \r\n\r\nvoid mqttLoop()\r\n{\r\n  \/\/if we have problems with connecting to mqtt server, we will attempt to re-estabish connection each 1minute (not more than that)\r\n  static unsigned long g_lastConnectionAttempt = 0;\r\n\r\n  \/\/first: let's make sure we are connected to mqtt\r\n  const char* topicLastWill = MQTT_TOPIC_ROOT \"availability\";\r\n  if (!mqttClient.connected() &amp;&amp; (g_lastConnectionAttempt == 0 || os_getCurrentTimeSec() - g_lastConnectionAttempt &gt; 60)) {\r\n    if(mqttClient.connect(WIFI_HOSTNAME, MQTT_USER, MQTT_PASSWORD, topicLastWill, 1, true, \"offline\"))\r\n    {\r\n      Log(\"Connected to MQTT server: \" MQTT_SERVER);\r\n      mqttClient.publish(topicLastWill, \"online\", true);\r\n    }\r\n    else\r\n    {\r\n      Log(\"Failed to connect to MQTT server.\");\r\n    }\r\n\r\n    g_lastConnectionAttempt = os_getCurrentTimeSec();\r\n  }\r\n\r\n  \/\/next: read data from battery and send via MQTT (but only once per MQTT_PUSH_FREQ_SEC seconds)\r\n  static unsigned long g_lastDataSent = 0;\r\n  if(mqttClient.connected() &amp;&amp; \r\n     os_getCurrentTimeSec() - g_lastDataSent &gt; MQTT_PUSH_FREQ_SEC &amp;&amp;\r\n     sendCommandAndReadSerialResponse(\"pwr\") == true)\r\n  {\r\n    static batteryStack lastSentData; \/\/this is the last state we sent to MQTT, used to prevent sending the same data over and over again\r\n    static unsigned int callCnt = 0;\r\n    \r\n    parsePwrResponse(g_szRecvBuff);\r\n\r\n    bool forceUpdate = (callCnt % 20 == 0); \/\/push all the data every 20th call\r\n    pushBatteryDataToMqtt(lastSentData, forceUpdate);\r\n    \r\n    callCnt++;\r\n    g_lastDataSent = os_getCurrentTimeSec();\r\n    memcpy(&amp;lastSentData, &amp;g_stack, sizeof(batteryStack));\r\n  }\r\n  \r\n  mqttClient.loop();\r\n}\r\n\r\n#endif \/\/ENABLE_MQTT\r\n\r\n<\/code><\/pre>\n<p><button id=\"copy-button\">Copy<\/button><\/p>\n<\/div>\n<p>&nbsp;<\/p>\n<\/div>\n","protected":false},"excerpt":{"rendered":"<div class=\"pvc_clear\"><\/div>\n<p id=\"pvc_stats_8010\" class=\"pvc_stats all  \" data-element-id=\"8010\" style=\"\"><i class=\"pvc-stats-icon medium\" aria-hidden=\"true\"><svg aria-hidden=\"true\" focusable=\"false\" data-prefix=\"far\" data-icon=\"chart-bar\" role=\"img\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\" viewBox=\"0 0 512 512\" class=\"svg-inline--fa fa-chart-bar fa-w-16 fa-2x\"><path fill=\"currentColor\" d=\"M396.8 352h22.4c6.4 0 12.8-6.4 12.8-12.8V108.8c0-6.4-6.4-12.8-12.8-12.8h-22.4c-6.4 0-12.8 6.4-12.8 12.8v230.4c0 6.4 6.4 12.8 12.8 12.8zm-192 0h22.4c6.4 0 12.8-6.4 12.8-12.8V140.8c0-6.4-6.4-12.8-12.8-12.8h-22.4c-6.4 0-12.8 6.4-12.8 12.8v198.4c0 6.4 6.4 12.8 12.8 12.8zm96 0h22.4c6.4 0 12.8-6.4 12.8-12.8V204.8c0-6.4-6.4-12.8-12.8-12.8h-22.4c-6.4 0-12.8 6.4-12.8 12.8v134.4c0 6.4 6.4 12.8 12.8 12.8zM496 400H48V80c0-8.84-7.16-16-16-16H16C7.16 64 0 71.16 0 80v336c0 17.67 14.33 32 32 32h464c8.84 0 16-7.16 16-16v-16c0-8.84-7.16-16-16-16zm-387.2-48h22.4c6.4 0 12.8-6.4 12.8-12.8v-70.4c0-6.4-6.4-12.8-12.8-12.8h-22.4c-6.4 0-12.8 6.4-12.8 12.8v70.4c0 6.4 6.4 12.8 12.8 12.8z\" class=\"\"><\/path><\/svg><\/i> <img decoding=\"async\" width=\"16\" height=\"16\" alt=\"Loading\" src=\"https:\/\/blog.fh-kaernten.at\/ingmarsretro\/wp-content\/plugins\/page-views-count\/ajax-loader-2x.gif\" border=0 \/><\/p>\n<div class=\"pvc_clear\"><\/div>\n<p>Update 26.11.2024: Aufgrund vieler Anfragen nach der Platine und weil vielleicht nicht jeder daran interessiert ist, sie selber zu zeichnen, stelle ich die Gerberdaten zum Dowload zur Verf\u00fcgung: pylontec_mqttinterface_v1.0_gerber Wer eine Photovoltaik Anlage in seinem Eigenheim aufgebaut hat, verwendet vielleicht sogar einen Energiespeicher. In diesem Beispiel handelt es sich um eine Offgrid Anlage, die mit&hellip; <br \/> <a class=\"read-more\" href=\"https:\/\/blog.fh-kaernten.at\/ingmarsretro\/2024\/02\/06\/pylontech-pv-akkustatus-im-homeassistant\/\">Weiterlesen<\/a><\/p>\n","protected":false},"author":86,"featured_media":7998,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"inline_featured_image":false,"ngg_post_thumbnail":0,"footnotes":""},"categories":[1],"tags":[2510,2514,2509,2404,2508,2515,2516,2517,2518,2506,2504,2505,2511,2513,2512],"class_list":["post-8010","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-allgemeines","tag-akkustatus-im-homeassistant","tag-console-mqtt","tag-homeassistant","tag-mqtt","tag-pylontec2mqtt","tag-pylontech","tag-pylontech-console","tag-pylontech-serial","tag-pylontech2mqtt","tag-us2000","tag-us3000","tag-us3000c","tag-wemos","tag-wemos-d1mini","tag-wemosd1"],"a3_pvc":{"activated":true,"total_views":2291,"today_views":0},"_links":{"self":[{"href":"https:\/\/blog.fh-kaernten.at\/ingmarsretro\/wp-json\/wp\/v2\/posts\/8010","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/blog.fh-kaernten.at\/ingmarsretro\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.fh-kaernten.at\/ingmarsretro\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.fh-kaernten.at\/ingmarsretro\/wp-json\/wp\/v2\/users\/86"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.fh-kaernten.at\/ingmarsretro\/wp-json\/wp\/v2\/comments?post=8010"}],"version-history":[{"count":0,"href":"https:\/\/blog.fh-kaernten.at\/ingmarsretro\/wp-json\/wp\/v2\/posts\/8010\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/blog.fh-kaernten.at\/ingmarsretro\/wp-json\/wp\/v2\/media\/7998"}],"wp:attachment":[{"href":"https:\/\/blog.fh-kaernten.at\/ingmarsretro\/wp-json\/wp\/v2\/media?parent=8010"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.fh-kaernten.at\/ingmarsretro\/wp-json\/wp\/v2\/categories?post=8010"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.fh-kaernten.at\/ingmarsretro\/wp-json\/wp\/v2\/tags?post=8010"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}