Pylontech US2000B 48V Speicher - Modul zum auslesen

Hallo
Marc hat mich per PN ersucht meine Scripte zur Abfrage der Akkus über die Konsole zu posten. Mach ich gerne, wobei ich sie nicht mit dem Ziel sie zu sharen gebaut habe. Ist eher nur Quick&Dirty für mich schnell hingedengelt. Laufen tuns echt brav. Bei der Einrichtung kann gut sein das die ein oder andere Variable erst händisch angelegt werden muß. Manches geht von selbst, manches benötigt Handarbeit.- bzw. dann eben in den Scripten die entsprechende ID austauschen. Inline Doku ist auch eher keine vorhanden.

Beginnen tu ma mit dem Seriellen Port und hängen ihn auf einen Splitter.


Am Splitter hängt dann eine Registervariable welche die Schnittstelle zu den Scripten darstellt.
So sieht der relevante Objektbaum aus. Soweit ich mich erinnere müssen die Kategorien Modul 1 - Modul 4 händisch angelegt und die ID dann im Pylontech_Receive Data eingetragen werden. Die Variablen darin müßten dann automatisch angelegt werden.

Pylontech_SendData wird periodisch aufgerufen (ich triggere es bei mir synchron mit der Datenabfrage vom Wechselrichter) , geht aber natürlich acuh per Timer.
Dieses Script schickt die Abfragebefehle zu den Akkus.

<?php
// check if Pylon is in Console Mode

if (GetValue(19666 /*[PV\Batterie\ConsoleModeAktive]*/) == False) {

IPS_Setproperty (18969 /*[Pylon_Serial_COM12]*/,'BaudRate',1200);
IPS_ApplyChanges(18969 /*[Pylon_Serial_COM12]*/);
IPS_Sleep (1000);

$StringToSend = chr(0x7E).chr(0x32).chr(0x30).chr(0x30).chr(0x31).chr(0x34).chr(0x36).chr(0x38).chr(0x32).chr(0x43).chr(0x30).chr(0x30).chr(0x34).chr(0x38).chr(0x35).chr(0x32).chr(0x30).chr(0x46).chr(0x43).chr(0x43).chr(0x33).chr(0x0D);
 REGVar_SendText(35339 /*[PV\Batterie\Register Variable]*/, $StringToSend);
IPS_Sleep (1000);


IPS_Setproperty (18969 /*[Pylon_Serial_COM12]*/,'BaudRate',115200);
IPS_ApplyChanges(18969 /*[Pylon_Serial_COM12]*/);
IPS_Sleep (1000);

$StringToSend = chr(0x0D).chr(0x0A);
REGVar_SendText(35339 /*[PV\Batterie\Register Variable]*/, $StringToSend);
IPS_Sleep (100);

$StringToSend = chr(0x0D).chr(0x0A);
REGVar_SendText(35339 /*[PV\Batterie\Register Variable]*/, $StringToSend);
IPS_Sleep (100);

$StringToSend = chr(0x0D).chr(0x0A);
REGVar_SendText(35339 /*[PV\Batterie\Register Variable]*/, $StringToSend);
IPS_Sleep (100);



}


$StringToSend = "pwr".chr(0x0A);
REGVar_SendText(35339 /*[PV\Batterie\Register Variable]*/, $StringToSend);
IPS_Sleep (100);

$StringToSend = "stat 1".chr(0x0A);
REGVar_SendText(35339 /*[PV\Batterie\Register Variable]*/, $StringToSend);
IPS_Sleep (100);

$StringToSend = "stat 2".chr(0x0A);
REGVar_SendText(35339 /*[PV\Batterie\Register Variable]*/, $StringToSend);
IPS_Sleep (100);

$StringToSend = "stat 3".chr(0x0A);
REGVar_SendText(35339 /*[PV\Batterie\Register Variable]*/, $StringToSend);
IPS_Sleep (100);

$StringToSend = "stat 4".chr(0x0A);
REGVar_SendText(35339 /*[PV\Batterie\Register Variable]*/, $StringToSend);



IPS_Sleep (100);
IPS_RunScriptWait(59710 /*[PV\Batterie\Scripte\Pylon_AverageData]*/);


#$StringToSend = "getpwr 4".chr(0x0A);
#REGVar_SendText(35339 /*[PV\Batterie\Register Variable]*/, $StringToSend);

Dann gibt es Pylontech_ ReceiveData welcehs über den Splitter-> Registervariable die Antwort vond en Akkus erhält.


<?php

$idModul1 = 20944 /*[PV\Batterie\Modul 1]*/;
$idModul2 = 44776 /*[PV\Batterie\Modul 2]*/;
$idModul3 = 42742 /*[PV\Batterie\Modul 3]*/;
$idModul4 = 35089 /*[PV\Batterie\Modul 4]*/;


$strReceived = $_IPS['VALUE'] ;
#echo  $strReceived;


if (substr($strReceived,2,5) == 'pylon') {
    SetValue(19666 /*[PV\Batterie\ConsoleModeAktive]*/,true);
};

// Auswerten von 'pwr' Anfrage
if (substr($strReceived,0,3) == 'pwr') {

$posStart = strpos($strReceived, chr(0x0D).chr(0x0D).chr(0x0A)."1");
$posEnd = strpos($strReceived, chr(0x0D).chr(0x0D).chr(0x0A)."2");
$strReceivedCut = substr($strReceived,$posStart, $posEnd- $posStart);
processPWR($strReceivedCut,$idModul1);

$posStart = strpos($strReceived, chr(0x0D).chr(0x0D).chr(0x0A)."2");
$posEnd = strpos($strReceived, chr(0x0D).chr(0x0D).chr(0x0A)."3");
$strReceivedCut = substr($strReceived,$posStart, $posEnd- $posStart);
processPWR($strReceivedCut,$idModul2);

$posStart = strpos($strReceived, chr(0x0D).chr(0x0D).chr(0x0A)."3");
$posEnd = strpos($strReceived, chr(0x0D).chr(0x0D).chr(0x0A)."4");
$strReceivedCut = substr($strReceived,$posStart, $posEnd- $posStart);
processPWR($strReceivedCut,$idModul3);

$posStart = strpos($strReceived, chr(0x0D).chr(0x0D).chr(0x0A)."4");
$posEnd = strpos($strReceived, chr(0x0D).chr(0x0D).chr(0x0A)."5");
$strReceivedCut = substr($strReceived,$posStart, $posEnd- $posStart);
processPWR($strReceivedCut,$idModul4);

}

// Auswerten von 'stat' Anfrage kommt viel Zeugs, Cycle Times sind ggfl. brauchbar 

if (substr($strReceived,0,4) == 'stat') {	//String: stat 1

$posStart = strpos($strReceived,'CYCLE Times') +17;	 //String: CR><LF>CYCLE Times     :        2<CR><CR><LF>
$posEnd = strpos($strReceived,chr(0x0D).chr(0x0D),$posStart);
$Cycles = intval(substr($strReceived,$posStart,$posEnd- $posStart));

if (substr($strReceived,5,1) == '1') { 			// 1 is the module #
$idCycles = GetOrCreateVariable('Cycles',1,"", "","",true, $idModul1,"10",false);
SetValue($idCycles,$Cycles);
}

if (substr($strReceived,5,1) == '2') { 			// 1 is the module #
$idCycles = GetOrCreateVariable('Cycles',1,"", "","",true, $idModul2,"10",false);
SetValue($idCycles,$Cycles);
}

if (substr($strReceived,5,1) == '3') { 			// 1 is the module #
$idCycles = GetOrCreateVariable('Cycles',1,"", "","",true, $idModul3,"10",false);
SetValue($idCycles,$Cycles);
}

if (substr($strReceived,5,1) == '4') { 			// 1 is the module #
$idCycles = GetOrCreateVariable('Cycles',1,"", "","",true, $idModul4,"10",false);
SetValue($idCycles,$Cycles);
}

// echo substr($strReceived,$posStart,$posEnd- $posStart);

}




function processPWR($dataString, $idModul) {
$idVolt = GetOrCreateVariable('Spannung',2,"", "~Volt","",true, $idModul,"1",false);
$idCurr = GetOrCreateVariable('Strom',2,"", "~Ampere","",true, $idModul,"2",false);
$idSOC = GetOrCreateVariable('SOC',2,"", "Prozent","",true, $idModul,"3",false);
$idTemp = GetOrCreateVariable('Temperatur',2,"", "~Temperature","",true, $idModul,"4",false);
$idLoVolt = GetOrCreateVariable('LowestVoltage',2,"", "~Volt","",true, $idModul,"5",false);
$idHighVolt = GetOrCreateVariable('HighestVoltage',2,"", "~Volt","",true, $idModul,"6",false);
$idCharge = GetOrCreateVariable('OperationMode',3,"", "","",false, $idModul,"9",false);
$idStatusVolt = GetOrCreateVariable('StatusSpannung',0,"", "~Alert","",true, $idModul,"7",false);
$idStatusCurrent = GetOrCreateVariable('StatusStrom',0,"", "~Alert","",true, $idModul,"8",false);
$idStatusTemp = GetOrCreateVariable('StatusTemperatur',0,"", "~Alert","",true, $idModul,"9",false);

$dataString = trim($dataString);

$dataString = str_replace("    ", " ",$dataString);
$dataString = str_replace("   ", " ",$dataString);
$dataString = str_replace("  ", " ",$dataString);
$dataString = str_replace(" ", " ",$dataString);


#echo $dataString;
#echo chr(0x0D).chr(0x0A);
$arryData = explode (" ",$dataString);

#print_r($arData1);
SetValue ($idVolt, floatval($arryData[1]) / 1000);
SetValue ($idCurr, floatval($arryData[2]) / 1000);
SetValue ($idTemp, floatval($arryData[3])/ 1000);
SetValue ($idLoVolt, floatval($arryData[6])/ 1000);
SetValue ($idHighVolt, floatval($arryData[7])/ 1000);

if ($arryData[8] == "Dischg") { 
SetValue($idCharge,"wird entladen");
}
else { 
SetValue($idCharge,"wird geladen");
}


if ($arryData[9] == "Normal") { 
SetValue($idStatusVolt,false);
}
else { 
SetValue($idStatusVolt,true);
}

if ($arryData[10] == "Normal") { 
SetValue($idStatusCurrent,false);
}
else { 
SetValue($idStatusCurrent,true);
}

if ($arryData[11] == "Normal") { 
SetValue($idStatusTemp,false);
}
else { 
SetValue($idStatusTemp,true);
}

SetValue ($idSOC, floatval($arryData[12]));

}


// ------------------------- IPS GRUNFUNKTIONEN -------------------------------------------------------------------------------------------------------------------------
// Erstellt eine Variable, wenn diese noch nicht existiert
function GetOrCreateVariable($name, $vtyp, $ident, $profil, $ascript, $log, $parentID, $position, $logVisual) // erstellt eine Variable, wenn es noch nicht exestiert
/* 
Wert 1 = Name
Wert 2 = Variablentyp
Wert 3 = Variablen-Ident 
Wert 4 = Variablenprofil 
Wert 5 = ScriptID 
Wert 6 = Logging der Daten (True/False)
Wert 7 = Übergeordnete Instanz, wo die Variable angelegt werden soll 
Wert 8 = Reihenfolge / Sortierung der Variablen
Wert 9 = Anzeige der Variable in der Visualisierung im Webfront deaktiveren oder aktivieren
*/
{
    $ObjId = @IPS_GetObjectIDByName($name, $parentID);
    if ($ObjId === false)
    {
        $ObjId = IPS_CreateVariable($vtyp);
        IPS_SetName($ObjId, $name); //Namen vergeben
        IPS_SetIdent($ObjId, $ident); //ObjectIdent vergeben
        IPS_SetParent($ObjId, $parentID); //Variable unter die Instantz verschieben
        @IPS_SetVariableCustomProfile($ObjId, $profil); //Variablen-Profil zuordnen
        @IPS_SetVariableCustomAction($ObjId, $ascript); //Verknüpft das Script mit der Variable als Actionscript

        // Archive Control ID herrausfinden
        foreach(IPS_GetInstanceList ( ) as $m_id) //Durchsucht alle Instanzen und gibt die InstanzID an $i_id weiter
        {
            $typ = IPS_GetInstance($m_id)['ModuleInfo']['ModuleName']; //Fragt die Instanzen nach den Modulnamen ab
            if ($typ =="Archive Control") $modul_id = $m_id; //Wenn die Instanz den Modulnamen "Archive Control" hat, dann schreibe die ID in $modul_id
        }
        
        $logging = AC_GetLoggingStatus($modul_id,$ObjId);
        if ($logging != 1)
        {
            AC_SetLoggingStatus($modul_id, $ObjId, $log);
            AC_SetGraphStatus($modul_id, $ObjId, $logVisual);
            IPS_ApplyChanges($modul_id);
        }// Ende Logging
        
        IPS_SetPosition($ObjId, $position);
    }
    return $ObjId;
}


Pylontech _Averagedata ist nicht unbedingt notwendig, es macht nur ein paar nachfolgende Berechnungen und bedient Overall Statusflags.
Die IDs der referenzierten Variablen sind händisch einzupflegen.Hatte keine Lust da was generisches zu bauen.


<?php
{
$idPylon = 28031 /*[PV\Batterie]*/;

$idVolt = GetOrCreateVariable('Spannung',2,"", "~Volt","",true, $idPylon,"1",false);
$idCurrent = GetOrCreateVariable('Strom',2,"", "~Ampere","",true, $idPylon,"2",false);
$idSOC = GetOrCreateVariable('SOC',2,"", "Prozent","",true, $idPylon,"3",false);
$idCycles = GetOrCreateVariable('Cycles',2,"", "","",true, $idPylon,"4",false);

$idPower = GetOrCreateVariable('Power',2,"", "","",true, $idPylon,"5",false);


$idDiffVolt1 = GetOrCreateVariable('maxCellDifference1',2,"", "my_mVolt","",true, $idPylon,"1",false);
$idDiffVolt2 = GetOrCreateVariable('maxCellDifference2',2,"", "my_mVolt","",true, $idPylon,"1",false);
$idDiffVolt3 = GetOrCreateVariable('maxCellDifference3',2,"", "my_mVolt","",true, $idPylon,"1",false);
$idDiffVolt4 = GetOrCreateVariable('maxCellDifference4',2,"", "my_mVolt","",true, $idPylon,"1",false);

$idStatusTemp = GetOrCreateVariable('Status Temperatur',0,"", "~Alert","",true, $idPylon,"1",false);
$idStatusSpannung = GetOrCreateVariable('Status Spannung',0,"", "~Alert","",true, $idPylon,"1",false);
$idStatusStrom = GetOrCreateVariable('Status Strom',0,"", "~Alert","",true, $idPylon,"1",false);




SetValue($idVolt, (GetValue(32839 /*[PV\Batterie\Modul 1\Spannung]*/) + GetValue(21456 /*[PV\Batterie\Modul 2\Spannung]*/) + GetValue(23341 /*[PV\Batterie\Modul 3\Spannung]*/) + GetValue(28610 /*[PV\Batterie\Modul 4\Spannung]*/))/4);
SetValue($idCurrent, GetValue(17580 /*[PV\Batterie\Modul 1\Strom]*/) + GetValue(22124 /*[PV\Batterie\Modul 2\Strom]*/) + GetValue(59611 /*[PV\Batterie\Modul 3\Strom]*/) + GetValue(53759 /*[PV\Batterie\Modul 4\Strom]*/));
SetValue($idSOC, (GetValue(29307 /*[PV\Batterie\Modul 1\SOC]*/) + GetValue(26857 /*[PV\Batterie\Modul 2\SOC]*/) + GetValue(39278 /*[PV\Batterie\Modul 3\SOC]*/) + GetValue(32048 /*[PV\Batterie\Modul 4\SOC]*/))/4);

SetValue($idCycles, (GetValue(40579 /*[PV\Batterie\Modul 1\Cycles]*/) + GetValue(58497 /*[PV\Batterie\Modul 2\Cycles]*/) + GetValue(17488 /*[PV\Batterie\Modul 3\Cycles]*/) + GetValue(53570 /*[PV\Batterie\Modul 4\Cycles]*/))/4);

SetValue($idPower, GetValue($idVolt) * GetValue($idCurrent)*-1);


SetValue($idDiffVolt1, (GetValue(19520 /*[PV\Batterie\Modul 1\HighestVoltage]*/) - GetValue(48112 /*[PV\Batterie\Modul 1\LowestVoltage]*/))*1000);
SetValue($idDiffVolt2, (GetValue(19130 /*[PV\Batterie\Modul 2\HighestVoltage]*/) - GetValue(55992 /*[PV\Batterie\Modul 2\LowestVoltage]*/))*1000);
SetValue($idDiffVolt3, (GetValue(53629 /*[PV\Batterie\Modul 3\HighestVoltage]*/) - GetValue(17956 /*[PV\Batterie\Modul 3\LowestVoltage]*/))*1000);
SetValue($idDiffVolt4, (GetValue(31395 /*[PV\Batterie\Modul 4\HighestVoltage]*/) - GetValue(42576 /*[PV\Batterie\Modul 4\LowestVoltage]*/))*1000);

SetValueBoolean($idStatusTemp, (GetValueBoolean(29152 /*[PV\Batterie\Modul 1\StatusTemperatur]*/) or GetValueBoolean(58775 /*[PV\Batterie\Modul 2\StatusTemperatur]*/) or GetValueBoolean(35619 /*[PV\Batterie\Modul 3\StatusTemperatur]*/) or GetValue(24392 /*[PV\Batterie\Modul 4\StatusTemperatur]*/))/4);
SetValueBoolean($idStatusSpannung, (GetValueBoolean(27744 /*[PV\Batterie\Modul 1\StatusSpannung]*/) or GetValueBoolean(57924 /*[PV\Batterie\Modul 2\StatusSpannung]*/) or GetValueBoolean(45008 /*[PV\Batterie\Modul 3\StatusSpannung]*/) or GetValue(34118 /*[PV\Batterie\Modul 4\StatusSpannung]*/))/4);
SetValueBoolean($idStatusStrom, (GetValueBoolean(47077 /*[PV\Batterie\Modul 1\StatusStrom]*/) or GetValueBoolean(43578 /*[PV\Batterie\Modul 2\StatusStrom]*/) or GetValueBoolean(37541 /*[PV\Batterie\Modul 3\StatusStrom]*/) or GetValue(31175 /*[PV\Batterie\Modul 4\StatusStrom]*/))/4);




}
// ------------------------- IPS GRUNFUNKTIONEN -------------------------------------------------------------------------------------------------------------------------
// Erstellt eine Variable, wenn diese noch nicht existiert
function GetOrCreateVariable($name, $vtyp, $ident, $profil, $ascript, $log, $parentID, $position, $logVisual) // erstellt eine Variable, wenn es noch nicht exestiert
/* 
Wert 1 = Name
Wert 2 = Variablentyp
Wert 3 = Variablen-Ident 
Wert 4 = Variablenprofil 
Wert 5 = ScriptID 
Wert 6 = Logging der Daten (True/False)
Wert 7 = Übergeordnete Instanz, wo die Variable angelegt werden soll 
Wert 8 = Reihenfolge / Sortierung der Variablen
Wert 9 = Anzeige der Variable in der Visualisierung im Webfront deaktiveren oder aktivieren
*/
{
    $ObjId = @IPS_GetObjectIDByName($name, $parentID);
    if ($ObjId === false)
    {
        $ObjId = IPS_CreateVariable($vtyp);
        IPS_SetName($ObjId, $name); //Namen vergeben
        IPS_SetIdent($ObjId, $ident); //ObjectIdent vergeben
        IPS_SetParent($ObjId, $parentID); //Variable unter die Instantz verschieben
        @IPS_SetVariableCustomProfile($ObjId, $profil); //Variablen-Profil zuordnen
        @IPS_SetVariableCustomAction($ObjId, $ascript); //Verknüpft das Script mit der Variable als Actionscript

        // Archive Control ID herrausfinden
        foreach(IPS_GetInstanceList ( ) as $m_id) //Durchsucht alle Instanzen und gibt die InstanzID an $i_id weiter
        {
            $typ = IPS_GetInstance($m_id)['ModuleInfo']['ModuleName']; //Fragt die Instanzen nach den Modulnamen ab
            if ($typ =="Archive Control") $modul_id = $m_id; //Wenn die Instanz den Modulnamen "Archive Control" hat, dann schreibe die ID in $modul_id
        }
        
        $logging = AC_GetLoggingStatus($modul_id,$ObjId);
        if ($logging != 1)
        {
            AC_SetLoggingStatus($modul_id, $ObjId, $log);
            AC_SetGraphStatus($modul_id, $ObjId, $logVisual);
            IPS_ApplyChanges($modul_id);
        }// Ende Logging
        
        IPS_SetPosition($ObjId, $position);
    }
    return $ObjId;
}

Ach ja, bevor du beginnst solltest erst mir einer beliebigen Terminalapplikation überprüfen ob Kabel/Schnittstellen und Pylonkonfiguration in Ordnung sind. Sonst suchst ewig im IPS rum und das Problem liegt an der Hardware. Im PV Forum wird ja auch gerne mit den Pylontech gearbeitet. Da gibt es jede Menge Threads mit Infos dazu.

viel Erfolg
Bernhard

Wow,
erst mal vielen Dank. Ich habe ja einen RS232/485 - TCP Konverter. Den muss ich erst mal ans Laufen bekommen. Mit dem Modul von steppe bekomme ich zwar Daten rein, aber da weiß ich nicht, ob das vom WR oder den Pylon’s ist. Da liege ich auf der RS485 Seite.
Die Konsole ist mir aber etwas sympathischer, weil ich damit den Wechselrichter nicht stören kann.
Da habe ich ein wenig Arbeit vor mir das ganze auch zu verstehen.
VG
Marc

Ich fange mal langsam an, um die Skripte zu verstehen.
Im ersten Skript stehen am Anfang zwei System ID’s: 19666 und 18969.
Wo ordne ich die zu?

Ohh man, so ein Mist. :banghead:
Die Prokonsole verschluckt bei Copy&Paste die Bezeichnungen der Ids. So ist das natürlich ganz schwer zum Nachbauen.
Sorry, aber das hab ich ganz übersehen.
Hab jetzt nochmals aus der Lagacy Konsole rauskopiert, da sind nun die Namen der ID mit dabei. Hoffe so ists leichter.

Ich glaub da is nun ein dringender Feature Request für die ProKonsole fällig.

schöne Grüße
Bernhard

@bbernhard
Kannst du mir mal Screenshots von den ID’s (auch der Instanzen) machen? Mir fehlt da noch zu viel, um das umfassend zu verstehen.
Auch den vom Objektbaum? Dann habe ich eine bessere Zuordnung.
Danke im Voraus.
VG
Marc

OK, hab dir die Screenshots upgedated.
Bin wohl etwas aus der Übung :frowning: Seit der Module Hype aufgekommen ist hab ich nix mehr veröffentlicht.

bb

So, da bin ich schon mal ein wenig weiter. Das Skript Pylon_ReceivedData meckert nun folgendes an:
Undefined index: VALUE in /var/lib/symcon/scripts/12456.ips.php on line 9
Das heißt er findet die Register Variable, oder deren Inhalt nicht. Im Debug der Register Variable ist aber etwas drin, wenn ich das Skript Pylon_SendData auslöse.
Ich habe nun eine Takt-Variable erstellt. Wie löst du die Skripte aus? Beide mit „true“?

Servus
Ich denke du hast ReceiveData händisch gestartet ?
Das geht so nicht.
ReceiveData wird automatisch von der Registervariablen gestartet und diese übergibt dann ihren Inhalt an das Script.
Damit das funktioniert mußt du in der Registervariablen als Ziel das ReceiveData Script auswählen.

Falls dein Debug der Registervariablen in etwa so aussieht, dann hast du schon ziemlich gewonnen.

Ich löse das Senddata aus wenn sich eine andere Variable ändert. Diese Variable gehört zu Daten die vom Wechselrichter kommen. So laufen beide Datensätze halbwegs synchron. Das ist wichtig wenn man bspw. Daten zw. Akku und Wechselrichter verrechnen möchte, weils quasi vom selben Zeitpunkt stammen.

Hier übrigens ein Beispiel wie ein Dashboard im Webfront dazu aussehen kann:

schöne Grüße
Bernhard

Hallo miteinander,
ich versuche gerade mein Modul auch für die RS-232 Console fit zu machen und stehe gerade auf dem Schlauch.
Auch mit den Skripten von Fuchskusu habe ich keine funktionstüchtige Anfrage für die Module mit den Adressen ab 2 zustande zu bekommen.

Modul 1 funktioniert mit folgender Anfrage:

15.08.2020, 15:01:10 |             TRANSMIT | 7E 32 30 30 31 34 36 34 32 45 30 30 32 30 31 46 44 33 35 0D 

Könnte mir jemand funktionstüchtige Anfragen für die weiteren Module posten, dass ich den Fehler schneller finde?

Grüße
Stefan

Hallo Marc,
das Thema hat mir keine Ruhe gelassen.
Hier jetzt eine Version zum testen, welche mit RS-232 funktionieren sollte. 1200 Baud.
RS-485 hab ich bei dieser Version noch nicht getestet, da ich die Akkus noch nicht wieder umgesteckt habe.

IPSPylontech_0.4.zip (11.9 KB)

Viel Glück
Stefan

Hallo Stefan,

es hat sich etwas geändert, aber ich bekomme noch keine Werte angezeigt. Das bekomme ich vom Splitter:

TXT: 15.08.2020, 21:08:01 |             TRANSMIT | ~20014642E00201FD35<CR>
HEX: 15.08.2020, 21:08:01 |             TRANSMIT | 7E 32 30 30 31 34 36 34 32 45 30 30 32 30 31 46 44 33 35 0D 
TXT: 15.08.2020, 21:08:02 |             RECEIVED | ~20014600C06E00010F0CF10CF30CF30CF20CF20CF20CF20CF10CF10CF20CF10CF30CF30CF20CF2050BE10BCD0BCD0BD70BD7FFD
HEX: 15.08.2020, 21:08:02 |             RECEIVED | 7E 32 30 30 31 34 36 30 30 43 30 36 45 30 30 30 31 30 46 30 43 46 31 30 43 46 33 30 43 46 33 30 43 46 32 30 43 46 32 30 43 46 32 30 43 46 32 30 43 46 31 30 43 46 31 30 43 46 32 30 43 46 31 30 43 46 33 30 43 46 33 30 43 46 32 30 43 46 32 30 35 30 42 45 31 30 42 43 44 30 42 43 44 30 42 44 37 30 42 44 37 46 46 44 
TXT: 15.08.2020, 21:08:02 |             RECEIVED | 4C22E908802C3500097E49A<CR>
HEX: 15.08.2020, 21:08:02 |             RECEIVED | 34 43 32 32 45 39 30 38 38 30 32 43 33 35 30 30 30 39 37 45 34 39 41 0D 

Und das vom Modul:

TXT: 15.08.2020, 21:08:02 |             Recieve: | {"DataID":"{DA983FFD-8C79-4762-AA1F-38DB2CD9E383}","Buffer":"~20014600C06E00010F0CF10CF30CF30CF20CF20CF20CF20CF10CF10CF20CF10CF30CF30CF20CF2050BE10BCD0BCD0BD70BD7FFD"}
HEX: 15.08.2020, 21:08:02 |             Recieve: | 7B 22 44 61 74 61 49 44 22 3A 22 7B 44 41 39 38 33 46 46 44 2D 38 43 37 39 2D 34 37 36 32 2D 41 41 31 46 2D 33 38 44 42 32 43 44 39 45 33 38 33 7D 22 2C 22 42 75 66 66 65 72 22 3A 22 7E 32 30 30 31 34 36 30 30 43 30 36 45 30 30 30 31 30 46 30 43 46 31 30 43 46 33 30 43 46 33 30 43 46 32 30 43 46 32 30 43 46 32 30 43 46 32 30 43 46 31 30 43 46 31 30 43 46 32 30 43 46 31 30 43 46 33 30 43 46 33 30 43 46 32 30 43 46 32 30 35 30 42 45 31 30 42 43 44 30 42 43 44 30 42 44 37 30 42 44 37 46 46 44 22 7D 
TXT: 15.08.2020, 21:08:02 |              Length: | 104
HEX: 15.08.2020, 21:08:02 |              Length: | 31 30 34 
TXT: 15.08.2020, 21:08:02 |             Recieve: | {"DataID":"{DA983FFD-8C79-4762-AA1F-38DB2CD9E383}","Buffer":"4C22E908802C3500097E49A\r"}
HEX: 15.08.2020, 21:08:02 |             Recieve: | 7B 22 44 61 74 61 49 44 22 3A 22 7B 44 41 39 38 33 46 46 44 2D 38 43 37 39 2D 34 37 36 32 2D 41 41 31 46 2D 33 38 44 42 32 43 44 39 45 33 38 33 7D 22 2C 22 42 75 66 66 65 72 22 3A 22 34 43 32 32 45 39 30 38 38 30 32 43 33 35 30 30 30 39 37 45 34 39 41 5C 72 22 7D 
TXT: 15.08.2020, 21:08:02 |              Length: | 24
HEX: 15.08.2020, 21:08:02 |              Length: | 32 34 

Der Konverter steht auf RS232, 8 DataBits, 1 StopBit, Parity: None, Flow Control: None, FiFo: enable (sonst kommt nichts).

Das Problem, was ich hier sehe ist, dass die empfangenen Daten in zwei Pakete zerstückelt wurden.
Das Modul erwartet für die angefragten Werte eine Länge von 128 Byte in einem Paket.
Die 128 Byte hast du ja - also 104 und 24. Das bedeutet, dass die Anfrage passt.

Vielleicht testest du mal eine „Packing lenght“ von mehr als 128 in Kombination mit „Force transmit“ von 250ms.
Oder den FIFO nochmal ausschalten?

Heureka, du bist mein Held. Ich habe Werte!
Da bin ich jetzt schon einen riesigen Schritt weiter. Jetzt wäre ein nächster Schritt die Abfrage in kürzere Abstände zu bekommen, aber da weiß ich wie es geht, weil ich das im Versuch schon hatte.
Wichtiger sind mir dann Einzelwerte der Batterien. So sind bei mir die Ladezyklen alle unterschiedlich, weil ich immer weiter aufgerüstet habe und einen Endstand von 8 Packs zu bekommen, derezeit habe ich 5.
Falls du mir da noch Tipps geben kannst, würde ich da auch unterstützen dein Modul zu perfektionieren.
Morgen werde ich mit den neuen Erkenntnissen auch noch mal die RS485 testen, welche auch am WR hängt. Vielleicht war das Problem da ähnlich gelagert.

ABER, bis hierhin mein großer Dank für deine Mühen. Hab noch ein schönes Wochenende.

Gruß
Marc

Kleiner Hinweis. Das Stack Modul findet keine Module. Da scheint auch noch ein Bug zu sein. :wink:

Was hast du jetzt anders eingestellt an deinem RS232-TCP Adapter?
Dass der Stack funktioniert, müssen die Module unterhalb des Stacks liegen.

Willst du bei den Abfragezeiten weniger als 1 Minute?

Hallo Stefan,

das mit der Anordnung wusste ich nicht. Aber jetzt ist es klar. Ich habe jetzt mal 3 Packs eingerichtet und die Abfrage-Module unterhalb des Stack angeordnet. Das scheint zu funktionieren.
Was mir nur auffällt, wenn ich gleiche Abfragezeiten nehme, dann verschluckt der sich, also habe ich die Zeiten bei den einzelnen Modulen hoch gesetzt und die vom Stack runter.
Ich habe das Skript des Stack jetzt insoweit geändert, dass ich eine Abfragezeit in „Sekunden“ einstellen kann. Das funktioniert auch.
Hier mal ein Screenshot mit 3 Packs (sonst wird es zu unübersichtlch).


Bildschirmfoto 2020-08-16 um 10.10.49.png

Ich benötige kürzere Abfrageintervalle, weil ich mit den Werten noch Berechnungen durchführe, zum Regeln.
Eigentlich müssten die Aktualisierungen der Module deaktiviert werden, wenn sie unterhalb vom Stack liegen.
Aber so bin ich schon mal sehr zufrieden.

Das Stack-Modul sollte die Update-Timer der darunterliegenden Module deaktivieren. (Nach der ersten Aktualisierung im Stack, glaub ich - müsste ich nachschauen) Dann sollte nur noch der Update-Timer des Stacks aktiv sein.

Die Umstellung von Minuten auf Sekunden ist ja kein Problem, hast du ja schon gemacht.

Vielleicht bau ich demnächst mal ein paar weitere Funktionen aus dem Protokoll ein, mal sehen, ob ich mir die Zeit nehme.

Grüße

Das Modul ist wirklich klasse. Ich habe mich ja jetzt schon mehrfach bedankt. Tolle Arbeit.
Ein paar Ideen hätte ich noch, aber alles zu seiner Zeit.
Eine Idee, die mir Spontan einfällt. Es wäre toll, wenn die Modulvariablen einen Index erhalten würden, damit man direkt im Variablennamen auch den entsprechenden Pack hat. So muss man die zu Fuss umbenennen.
Und im Stack noch die Gesamt- und Restkapazität in KWh. :smiley:

Wie meinst du das mit dem Index? Kannst du mir davon einen Screenshot machen?
Die kWh sind kein Problem, nehme ich mal mit auf.
Ich möchte auch noch die Einstellung RS232/RS485 in den Splitter verlegen, dass man das nicht in jedem Modul einstellen muss.
Dann wird es wahrscheinlich noch eine Stackadresse geben - ist nur schwierig zu testen, da muss ich mal die Verdrahtung und Adressierung meiner Module ändern um das zu simulieren.

So. Gerade noch ein bisschen rumgespielt. Wieder auf RS-485 umgebaut. Die Version 0.4 funktioniert nicht mit RS-485.
RS-232 und RS-485 von Pylontech verhalten sich irgendwie unterschiedlich (nicht nur Adresse +1)

Wie es aktuell ausschaut, kann man mehrere Stacks nur mit dem LV-Hub von Pylontech verbinden, aber ich weiß es nicht genau.

Wenn jemand mehr als 8 Module mit einem LV-Hub hat, kann er sich ja hier melden.

Jetzt hab ich erst mal keine Lust mehr hier weiter zu analysieren.

Grüße
Stefan