Einfach als comfoair.pl anlegen und "irgendwo" in edomi speichern. Meine ganzen Files hängen in www/msw....
Danach ausführbar machen und den ttyUSB0 ggf. anpassen.
Du kannst dann testweise die Kommunikation mit der Comfoair testen, indem Du das perl auf der Console ausführst
./comfoair.pl liest die Daten aus.
./comfoair.pl stufe 2 setzt die Stufe 2 (Achtung: Gross-Kleinschr.)
Code:
#!/usr/bin/perl
#Quelle:
#Idee und Konzeption:
# Plugin zur Ansteuerung einer Zender ComfoAir
# Version 1.6.9 15.07.2014 BETA
# Copyright: swiss (http://knx-user-forum.de/members/swiss.html)
# Aufbau moeglichst so, dass man unterhalb der Einstellungen nichts veraendern muss!
# - Neu mit der Moeglichkeit zur Anbindung über einen Moxa NPort von Fechter65 (http://knx-user-forum.de/members/fechter65.html)
# - Neustrukturierung des Codes von Fechter65 (http://knx-user-forum.de/members/fechter65.html)
# - Besseres Fehlerhandling bei der Verarbeitung der Reuckmeldungen von swiss (http://knx-user-forum.de/members/swiss.html)
# - Neu nun mit direktem abfragen der Stufe nach dem setzen und auswerten der Komforttemperatur von swiss (http://knx-user-forum.de/members/swiss.html)
# - Neu mit der Möglichkeit die Ventilationsstufe direkt zu setzen von swiss (http://knx-user-forum.de/members/swiss.html)
# - Neu mit der Möglichkeit die Ventilatorstufe für Zuluft und Abluft getrennt zu setzen (0% - 100%) von Netsrac80 (http://knx-user-forum.de/members/netsrac80.html)
# - Korrektur Stufe direkt setzen. Senden / empfangen hat nun das gleiche Mapping 1=Auto, 2=Sufe1, 3=Stufe2, 4=Stufe3
# - Neu mit Rückmeldung auf Lesetelegramme
# - Bugfix: Themperatur wird nach dem Verstellen nicht direkt als Status zurückgegeben
#Modifikation für Edomi by MSW
#Pfad zur seriellen Schnittstelle oder dem USB-Seriell-Wandler:
#my $schnittstelle = '/dev/ttyUSB_Comfoair';
my $schnittstelle = '/dev/ttyUSB0';
# Dauer einer Abfrage
my $Zaehler = "2500"; #Mit dieser Variable Zaehler wird beeinflusst, wie lange das Plugin auf den Abschluss einer R¸ckmeldung der KWL wartet; empfohlener Wert f¸r seriell: 2500; f¸r Moxa: 250
# Debug level 0 = nur die wichtigsten Infos, 1 = Alle Zustaende, 2 = Rohdaten (nur f¸r Fehlersuche)
my $debug=0;
#Weitere Variablen die benoetigt werden -> NICHT veraendern!
my $seriel;
my $sin; #Serial Input = Empangener Datenstrom
my $cin; #Counter Input = L‰nge des Datenpackets
my $laenge; #L‰nge des empfangenen Datenstrings nach dem k¸rzen
my $checksum = 0; #Checksumme
my @hex; #Hilfsarray f¸r die Checksummenberechnung
my $x07warschon; #Hilfsvariable f¸r die Checksummenberechnung
my $return_value2;
my $daten;
my $reciv;
my $reciv_all;
my $ack = pack("H*","07F3");
my $rcv_checksum;
use Device::SerialPort;
use Time::Local;
#open (DATEI, ">>comfoair.txt") or die $!;
#print DATEI "Timestamp: " . time() . "\n";
#Einrichten der Seriellen Schnittstelle fuer die Kommunikation mit der ComfoAir
$seriel = Device::SerialPort->new($schnittstelle) || die "Kann $schnittstelle nicht ˆffnen! ($!)\n";
$seriel->baudrate(9600);
$seriel->parity("none");
$seriel->databits(8);
$seriel->stopbits(1);
if($debug>=1){print 'Schnittstelle: ' . $schnittstelle . ' erfolgreich geˆffnet'};
#if(defined($ARGV[0]) && ($ARGV[0] eq 'read') ) {
# $return_value2 = command_read();
#}els
if(defined($ARGV[0]) && ($ARGV[0] eq 'stufe') ) {
if(not defined($ARGV[1])){
print "Stufe fehlt\n";
}elsif(($ARGV[1]>=1) && ($ARGV[1]<=4)) {
$daten = "0099010" . $ARGV[1];
print 'Stufe direkt setzen auf: ' . $ARGV[1] . "\n";
$return_value2 = command_senden($daten);
}
}elsif(defined($ARGV[0]) && ($ARGV[0] eq 'komforttemp') ) {
if(not defined($ARGV[1])){
print "Temp fehlt\n";
}elsif(($ARGV[1]>=15) && ($ARGV[1]<=25)) {
print 'Komforttemperatur setzen auf: ' . $ARGV[1] . "\n";
my $temphex = ($ARGV[1] + 20)*2; #Rechne die Temperatur fuer die Co$
$temphex = sprintf "%x" , $temphex; # Mache aus Integer HEX
$daten = "00D301" . $temphex;
$return_value2 = command_senden($daten);
}
}elsif(defined($ARGV[0]) && ($ARGV[0] eq 'filter') ) {
$daten = "00DB0400000001";
print 'Filter wurde zurueckgesetzt' . "\n";
$return_value2 = command_senden($daten);
}elsif(defined($ARGV[0]) && ($ARGV[0] eq 'debug') ) {
if(not defined($ARGV[1])){
print "Code fehlt\n";
}else {
$debug=2;
print 'Kommando Senden: ' . $ARGV[1] . "\n";
$debug=2;
$daten=$ARGV[1];
$return_value2 = command_senden($daten);
}
}else{
# print "Bitte mit Parameter starten\n";
}
$return_value2 = command_read();
#print DATEI "\n";
#close (DATEI);
sub command_read{
#Daten der Anlage abfragen
$daten = "00DD00";
$return_value2 = command_senden($daten);
#Temperaturen
$daten = "00D100";
$return_value2 = command_senden($daten);
#Ventilatorstufe
$daten = "00CD00";
$return_value2 = command_senden($daten);
#Status Ventilator
$daten = "000B00";
$return_value2 = command_senden($daten);
#Status Bypass
$daten = "000D00";
$return_value2 = command_senden($daten);
#Fehlercodes
$daten = "00D900";
$return_value2 = command_senden($daten);
if($debug>=1){print 'ENDE Zyklische Abfrage'};
}
sub command_senden{
my $data = $_[0];
if($debug>=2){print 'data: ' . $data;}
$checksum = checksum_berechnen($data);
if($debug>=2){print 'Checksumme aus der Subroutine: '.$checksum;}
my $command = pack("H*","07F0" . $data . $checksum . "070F");
$seriel->write($command); #Befehl an die ComfoAir senden
#$seriel->write(pack("H*","07F000DD008a070F"));
$reciv = '';
$cin = '';
$sin = '';
$|=1;
my $exit=0;
while($exit < $Zaehler)
{
($cin, $sin) = $seriel->read(45);
if($cin > 0){
$sin = unpack "H*", $sin;
$reciv .= $sin;
$exit=0;
}else{
$exit++
}
if($debug>=2){print 'reciv-direkt: ' . $sin . "\n"};
if($reciv =~ /070f/i){
if (substr($reciv,(length($reciv)-6),6) ne '07070f'){
last;
}
}
}#Ende While
$seriel->write($ack); #ACK senden
if($debug>=2){print 'ACK senden' . "\n"};
if($reciv eq ""){
if($debug>=2){print 'FEHLER: Keine Daten empfangen!';}
return;
}
while ((length($reciv) > 3) && (substr($reciv,(length($reciv)-4),4) ne '070f')) #solange das Ende nicht 070f lautet
{
if($debug>=2){print 'String vor Kuerzung Ende: '.$reciv . "\n";}
$reciv = substr($reciv,0,-2); #String um die letzten zwei Zeichen k¸rzen
if($debug>=2){print 'String nach Kuerzung Ende: '.$reciv. "\n";}
}
#Hier wird der empfangene String um Start- und Endbyte gek¸rzt
$laenge = length($reciv); #Laenge des Antworttelegramms ermitteln
$reciv = substr($reciv,0,($laenge-4)); #Entferne 07f0 vom Ende
if(substr($reciv,(length($reciv)-4),4) eq '07f3'){
$reciv = substr($reciv,0,($laenge-4));
if($debug>=2){print 'String ohne 07f3: '.$reciv. "\n";}
}
if($debug>=2){print 'Erste 4 Byte des Datenpakets: '.(substr($reciv,0,4)). "\n";}
while ((length($reciv) > 3) && (substr($reciv,0,4)) ne '07f0'){
$reciv = substr($reciv,2); #falls noch falsche Zeichen am Anfang des Strings enthalten sind, werden diese hier entfernt.
if($debug>=2){print 'reciv gekuerzt: '.$reciv. "\n";}
}
$reciv = substr($reciv,4);
if($debug>=2){print 'String ohne 07f0 am Anfang: '.$reciv. "\n";}
#Test einer Methode falls aussversehen mehrere Datenpakete auf einmal im Datenstring enthalten sind...
if($reciv =~ /07f307f0/i){
my @dataarray=split(/07f307f0/,$reciv);
$reciv = $dataarray[0];
}
#Nun wird die Checksumme gelesen und aus dem Datenstring entfernt
$checksum = 0;
$checksum = substr($reciv,-2,2);
if($debug>=2){print 'Checksumme gelesen: '.$checksum. "\n";}
$laenge = length($reciv); #Laenge des Antworttelegramms ermitteln
$reciv = substr($reciv,0,($laenge-2));
if($debug>=2){print 'Datenpaket ohne Checksumme: '.$reciv. "\n";}
#Hier wird die Subroutine f¸r die Berechnung der Checksumme aufgerufen und das Ergebnis in $rcv_checksum zur¸ck gegeben
$rcv_checksum = checksum_berechnen($reciv);
#######################################################################
#######################################################################
## Ab hier werden die Rueckmeldungen von der ComfoAir ausgewertet... ##
#######################################################################
#######################################################################
if($rcv_checksum eq $checksum){ #Hier wird gepr¸ft ob die Checksumme korrekt ist
if($debug>=2){print 'Checksumme OK '. "\n";}
#Hier werden die doppelten 07 aus dem Antwortstring entfernt.
if($debug>=2){print 'String vor 07 bereinigung: '.$reciv. "\n";}
while ($reciv =~ s/0707/07/) {}
if($debug>=2){print 'String nach 07 bereinigung: '.$reciv. "\n";}
if($reciv =~ /00D209/i){ #Wenn die Temperaturen empfangen wurden
my $t1 = substr($reciv,6,2);
my $t2 = substr($reciv,8,2);
my $t3 = substr($reciv,10,2);
my $t4 = substr($reciv,12,2);
my $t5 = substr($reciv,14,2);
#Hier werden die Temperaturen "decodiert" damit sie einen Sinn ergeben
$t1 = (hex($t1)/2)-20;
$t2 = (hex($t2)/2)-20;
$t3 = (hex($t3)/2)-20;
$t4 = (hex($t4)/2)-20;
$t5 = (hex($t5)/2)-20;
print "Komforttemp: " . $t1 . " °C\n";
print "Aussenluft: " . $t2 . " °C\n";
print "Zuluft: " . $t3 . " °C\n";
print "Abluft: " . $t4 . " °C\n";
print "Fortluft: " . $t5 . " °C\n";
#print DATEI "Komforttemp " . $t1 . "\n";
#print DATEI "Aussenluft " . $t2 . "\n";
#print DATEI "Zuluft" . $t3 . "\n";
#print DATEI "Abluft" . $t4 . "\n";
#print DATEI "Fortluft" . $t5 . "\n";
}elsif($reciv =~ /000C06/i){ #Wenn der Status fuer die Ventilatoren empfangen wurden
my $vent_zul = substr($reciv,6,2);
my $vent_abl = substr($reciv,8,2);
my $speed_zul = substr($reciv,10,4);
my $speed_abl = substr($reciv,14,4);
print 'Status Ventilator Zuluft: ' . hex($vent_zul) . " %\n";
print 'Status Ventilator Abluft: ' . hex($vent_abl) . " %\n";
printf "Drehzahl Ventilator Zuluft: %d U/min\n", (1875000/hex($speed_zul));
printf "Drehzahl Ventilator Abluft: %d U/min\n", (1875000/hex($speed_abl));
}elsif($reciv =~ /00CE0E/i){ #Wenn der Status der Ventilatorenstufe empfangen wurden
my $akt_stufe = substr($reciv,22,2);
#if(hex($akt_stufe) == 1){
# print 'AKT_STUFE: A' . "\n";
#}else{
#print 'AKT_STUFE: ' . (hex($akt_stufe)-1) . "\n";
print 'AKT_STUFE: ' . hex($akt_stufe) . "\n";
#}
}elsif($reciv =~ /000E04/i){ #Wenn der Status fuer die Bypassklappe empfangen wurden
my $bypass_prozent = substr($reciv,6,2);
print 'Bypass: ' . hex($bypass_prozent) . " %\n";
}elsif($reciv =~ /00DE14/i){ #Wenn die Rueckmeldung der Betriebsstunden des Filters empfangen wurden
my $betriebsstunden_filter = substr($reciv,36,4);
if($debug>=3){print 'Betriebsstunden Filter Roh: '.$betriebsstunden_filter;}
print 'Betriebsstunden Filter: ' . hex($betriebsstunden_filter) . "h\n";
print 'Betriebsstunden Stufe 1 (Abwesend): ' . hex(substr($reciv,6,6)) . "h\n";
print 'Betriebsstunden Stufe 2: ' . hex(substr($reciv,12,6)) . "h\n";
print 'Betriebsstunden Stufe 3: ' . hex(substr($reciv,18,6)) . "h\n";
print 'Betriebsstunden Stufe 4: ' . hex(substr($reciv,40,6)) . "h\n";
print 'Betriebsstunden Bypass offen: ' . hex(substr($reciv,32,4)) . "h\n";
#print DATEI 'Betriebsstunden Filter: ' . hex($betriebss$
}elsif($reciv =~ /00DA11/i){ #Wenn die Rueckmeldung der Stoermeldungen empfangen wurden
my $fehlerAlo = substr($reciv,6,2);
my $fehlerAhi = substr($reciv,30,2);
my $fehlerE = substr($reciv,8,2);
my $fehlerFilter = substr($reciv,22,2);
my $fehlerEA = substr($reciv,24,2);
my $numAlo = 'A';
my $numAhi = 'A';
my $numE = 'A';
my $numEA = 'A';
$numAlo .= unpack("B*",pack("H*",$fehlerAlo));
$numAhi .= unpack("B*",pack("H*",$fehlerAhi));
$numE .= unpack("B*",pack("H*",$fehlerE));
$numEA .= unpack("B*",pack("H*",$fehlerEA));
$fehlerAlo = reverse($numAlo); #Wandle den Wert in Binaer und drehe die Reihenfolge um. z.B 0x02 = 00000010 = 010000000
$fehlerAlo = index($fehlerAlo,'1')+1; # Zaehle an welcher Stelle die 1 auftaucht (von links gelesen) z.B. 01000000 = INDEX 2 = Fehler2
if($fehlerAhi ne '00'){
$fehlerAhi = index(reverse($numAhi),'1')+9;
}else{
$fehlerAhi = '';
}
$fehlerE = index(reverse($numE),'1')+1;
$fehlerEA = index(reverse($numEA),'1')+1;
if($fehlerAhi == 16){$fehlerAhi = 0;}
if($fehlerAlo > 0){
print 'Aktueller Fehlercode: A' . $fehlerAlo . "\n";
}elsif($fehlerAhi ne ''){
print 'Aktueller Fehlercode: A' . $fehlerAhi . "\n";
}elsif($fehlerE > 0){
print 'Aktueller Fehlercode: E' . $fehlerE . "\n";
}elsif($fehlerEA > 0){
print 'Aktueller Fehlercode: EA' . $fehlerEA . "\n";
}else{
print 'Aktueller Fehlercode: keiner' . "\n";
}
if(hex($fehlerFilter) > 0){
print 'Aktueller Fehler: Filter Voll' . "\n";
}else{
print 'Aktuell kein Fehler - Filter nicht voll' . "\n";
}
}
}else{
if($debug>=2){print 'Checksumme fehlerhaft! Gelesen: '.$checksum.' Berechnet: '.$rcv_checksum;}
}
} #ENDE Sub command_senden
sub checksum_berechnen {
my $chk_datasum = $_[0];
$rcv_checksum = 0;
my $i;
$chk_datasum = $chk_datasum . "AD"; #+173 fuer die Checksummenberechnung
if($debug>=2){print 'String f¸r die Berechnung der Checksumme: '.$chk_datasum;}
$x07warschon = 0;
$laenge = length($chk_datasum);
for($i = 0; $i< $laenge; $i++) {
my $wertstring = substr($chk_datasum,$i,2);
if($debug>=3){print 'Zahl: '.$wertstring;}
my $wertbetrag = hex($wertstring);
if ($wertbetrag == 7) {
if ($x07warschon == 1) {
$x07warschon = 0;
$i++;
next;
} else {
$x07warschon = 1;
}
}
$rcv_checksum += $wertbetrag;
if($debug>=3){print 'Summe: '.$rcv_checksum;}
$i++;
}
if($debug>=3){print 'Summe def: '.$rcv_checksum;}
if($debug>=2){print 'Checksumme vor der Umwandlung: '.$rcv_checksum;}
$rcv_checksum = sprintf "%x\n" , $rcv_checksum; #Mache aus Integer wieder HEX
if($debug>=2){print 'Checksumme vor der K¸rzung: '.$rcv_checksum;}
$rcv_checksum = substr($rcv_checksum,-3,2); #Verwende nur die letzten beiden Stellen
if($debug>=2){print 'Checksumme nach der K¸rzung: '.$rcv_checksum;}
return $rcv_checksum;
} #Ende checksum_berechnen

Einen Kommentar schreiben: