Anbindung Verstärker an iPS

Hallo zusammen,

ich bin erfolgreich von FHEM auf FB7390 zu iPS umgestiegen.
Es war teilweise etwas holprig aber nun läuft zumindest alles so (oder besser) wie mit FHEM.
Der Grund weshalb ich umgestiegen bin ist aber unter anderem die integration meiner Stereoanlage.

Ich habe einen NAD Verstärker welcher eine RS232 Schnittstelle besitzt. Von NAD selbst habe ich mal ein kleines tool heruntergeladen mit welchem ich Befehle absenden kann z.B. Main.Power =on.
Das ganze habe ich auch erfolgreich testen können mit einem USB/RS232 Kabel an meinem PC.

Nun würde ich das aber gern automatisieren.

Sprich, nach Betätigung in webfront soll der HM Aktor die Steckdose an welcher der Verstärker angeschlossen ist schalten und dann soll der Verstärker vom Standby in „on“ geschaltet werde.

Die Hm Befehle sind mir denke ich mitlerweile klar, bei dem Rest weiß ich ehrlich gesagt erst gar nicht wo ich anfangen soll?!

Ich habe mir mal die Module „Entertainment“ und „AudioMax“ angeschaut aber dort werden Verstäker nur über TCP angesprochen und wie die Skripte arbeiten ist mir auch nicht schlüssig.

Gibt es jemanden, der sich die Zeit nehmen würde und mich „ein wenig an die Handzu nehmen“?

  • Wie sage ich iPS das ich über COM (virtuell) kommunizieren will?
  • Welche Befehle muss ich benutzen um mit dem Verstärker zu kommunizieren?

Hey Kiter11,

bin zwar kein Crack wie so mancher hier, aber ich werde mal versuchen dir zu helfen. Was ich noch nicht ganz verstanden habe, wie wird denn der Verstärker an IP-Symcon angebunden? Ist das dauerhaft mit einem USB/RS232 Kabel?

Falls ja, denn so verstehe ich das, musst du als erstes in IP-Symcon eine neue Serial Port Instanz anlegen. Diese kannst du dann per Script ansprechen und Daten senden bzw. empfangen. Das ist ein bischen versteckt, deshalb anbei ein Screenshot.

Mit folgender Funktion, sendest du Daten über den Serial Port.



COMPort_SendText(12345 /*[ID von deinem Serial Port]*/, "Ich werde verschickt");

Sollen die empfangenen Daten verarbeitet werden, weil sie z.B. Statusinformationen enthalten, meld dich nochmal.

Viele Grüße soundman33 :slight_smile:

Bild_1.png

Hallo,

bin jetzt erst heim gekommen und werde es heut nicht mehr schaffen zu testen, aber danke schon mal für Deine Unterstützung.

Zu Deinen Fragen:

Der Verstärker ist dauerhalt mit dem USB/RS232 Kabel verbunden (evtl irgendwann mal mit LAN/RS232) aber in jedem Fall auf dem Rechner auf welchem auch IPS läuft als virtueller COM Port eingerichtet.

Laut Hersteller sendet der Receiver auch Antworten.
Hier ein Beispiel aus der Manual:

Example 1
This example will describe exactly what is sent when the Identification of the unit is queried.
The above is accomplished by sending the following ASCII character sequence:
Command: Main.Model?<CR>
If the identification string of the unit was “T775” then the response to the above command would be:
Response: Main.Model=T775<CR>

C355_RS232_Commands.pdf (5.84 KB)

nad_rs232_2.02.pdf (11.5 KB)

Hey,

na das mit den commands ist ja perfekt, da spart man sich jede Menge Arbeit.

Dann kannst du einfach versuchen über das Script folgendes zu verschicken:


COMPort_SendText(12345 /*[ID von deinem Serial Port]*/, "Main.Model?<CR>"); 

Da muss ich aber auch oft basteln, weil ich die Umwandlung von ASCII in Hex nicht immer nachvollziehen kann.

Hier kann die helfen: asciitable

Noch ein Tipp, der bei sowas extrem viel hilft, wenn du in den Einstellungen von deiner SerialPort Instanz bist gibt es links unten ein „Debug“ Button, damit kannst du dann sehen was gesendet und empfangen wird.

Willst du die empfangenen Daten jetzt verarbeiten, ist es wichtig diese in einer Register Variable zwischen zu speichern. (Vorausgesetzt du benötigst mehrere empfangene Datensätze)

Siehe hier für mehr Infos: Register Variable

Da bei solchen Daten oft Steuer- oder Startzeichen gesendet werden, brauchst du als nächstes noch einen Cutter, der dir diese wegschnippelt.

Geht aber auch in einem Script mit PHP, in diesem schreibst du dann auch die Werte in Variablen.

Na versuch dich damit mal, und wenn Fragen sind, einfach stellen. :stuck_out_tongue:

Gruß soundman33 :slight_smile:

P.S.: An alle die mitlesen, wenn ich was falsches erzähle bitte meldet euch (wie gesagt bin kein Crack)

@soundmann: Paßt schon, keinen Fehler gesehen.

Als Referenz wie man eine Kommunikation über Rs232 schön aufsetzt könnte das Plugwise Projekt dienen.
Auch wenns hier um gnaz etwas anderes geht so sind alle wesentlichen Elemente drinnen und sauber gecodet sichtbar.
-> String über RS232 raus
-> Response empfangen
-> darauf reagiere und neuen String raus.
Dazu noch die Verwndung vom Cutter und diverses HEX/DEZ umgewandle

gruß
bb

Hallo,

danke für eure Unterstützung.

ICh habe nun eine Instanz „serieller Port“ eingerichtet (COM 5) => Das tool vom Hersteller des Verstärkers funktioniert auch mit diesem Port.

Als Testskript habe ich bisher foilgendes versucht:

  1. COMPort_SendText(53926 /[Serial Port COM 5]/, „<CR>Main.Power=On<CR>“);

  2. COMPort_SendText(53926 /[Serial Port COM 5]/, „Main.Power=On<CR>“);

  3. COMPort_SendText(53926 /[Serial Port COM 5]/, „Main.Power=On“);

All das führte leider nicht zum Erfolg.

Was hat es denn mit der ASCItabelle auf sich??
Was muss ich von wo nach wo umwandeln??

Probiers mal mit:

<?
$TX_BUF = "Main.Power=On";
$result = COMPort_SendText(53926, $TX_BUF.chr(13));
?>

Und beobachte mal am Debug der COM-Schnittstelle was passiert.

Versuch das mal so:

PHP-Code:


<? 

COMPort_SendText(53926 /*[Serial Port COM 5]*/, "Main.Power=On".chr(13)); 

?>


Hallo,

habe all eure Vorschläge versucht und das ist das Ergebnis im Log leider ohne Erfolg am Verstärker:

dump.txt (476 Bytes)

Wer lesen kann ist klar im Vorteil:

All communication should be done at a rate of 115200 bps with 8 data bits, 1 stop bit and no parity bits.
No flow control should be performed.

Hatte 9600Bautrate eingetragen, mit dem Tool vom Hersteller hats trotzdem geklappt, aber wer weiß wie das im Hintergrund funktioniert.

Versuch 1)

$TX_BUF = „Main.Power=Off“;
$result = COMPort_SendText(53926, $TX_BUF.chr(13));

Versuch 2)
COMPort_SendText(53926 /[Serial Port COM 5]/, „Main.Power=On“.chr(13));

HABEN BEIDE GEKLAPPT!!!

SUPER, DANKE!!!

Noch ein Versuch (habe jetzt erst das PDF gelesen)…

<?
$TX_BUF = "Main.Power=On";
$result = COMPort_SendText(53926, chr(13).$TX_BUF.chr(13));
echo result;
?> 

Schon mal im Debug geschaut?
Doppelklick auf die COM-Schnittstelle und dann Reiter „Debug“.

So, ich schon wieder,

iPS ist natürlich schneller als meine Hardware.

Wenn ich also mehrere Befehle hintereinander senden will, muss ich pausieren.
Ich hatte mir gedacht, abzuwarten bis die Schnittstelle geantwortet hat.

Ihr habt soetwas oben bereits angesprochen und ich habe mir die registerVariable mal in der Doku durchgelesen.

Kann mir jemand erklären wie ich die richtig einsetze?
Wo hänge ich das skript hin und mit welchem Ereignis löse ich es aus?

Anbei mal ein Log von mir mit dem senden und empfangenen Wert.

dump.txt (279 Bytes)

Der Empfangene Wert ist gestückelt. Diesen würde ich gern als gesamtes in einer if Schleife abfragen und wenn er dem gesendeten entspricht dann fortfaren.

Oder macht man das ganz anders?

Versuch ich mich mal wieder, :wink:

als erstes müssen die ankommenden Daten, auf die Daten begrenzt werden, die relevant sind. Das machst du mit einem Cutter. D.h. du musst dir jetzt die Zeichen die du nicht benötigst entfernen: z.B. so etwas |

In deinem Fall müsste dann so etwas überbleiben:


Main.
Powe
r=On

Auch der Cutter hat einen Debug Button, so dass du checken kannst was überbleibt. Falls es nicht gleich ersichtlich ist, auch mal in die HEX Ansicht wechseln, da werden dann auch z.B. Leerzeichen sichtbar.

Diese „reinen“ Daten werden dann an eine Register Variable übergeben welche diese zwischen speichert, bis ein Datensatz sinnvoll bzw. komplett ist. Das heist hier wären drei empfangene Datensätze notwendig. Die Code Beispiele in der Dokumentation sind hier sehr hilfreich. Da gibt es jetzt verschiedene Wege zu checken ob die Daten komplett sind. Einer wäre die Länge des Bufferinhalts über strlen zu checken (am einfachsten, aber auch am unzuverlässigsten)

Was in deinem Fall wäre:

Main.Power=On

Also hier nochmal als Ablauf:

SerialPort → Cutter → Register Variable → Script → IPS Variable

Das ist jetzt nur mal so als Grundlage Quick&Dirty du musst noch die Zahlen bei längster bzw. kürzester Datensatz anpassen. Diese Script musst du dann deiner Register Variable als Aktionsscirpt zuweisen.



// wenn das Skript von einer RegisterVariable-Instanz aus aufgerufen worden ist
if ($_IPS['SENDER'] == "RegisterVariable")
{
    // bereits im Puffer der Instanz vorhandene Daten in $data kopieren
    $data  = RegVar_GetBuffer($_IPS['INSTANCE']);

    
    //kürzester kompletter Datensatz -> Alles was größer ist, ist komplett
    if(strlen($data) >= 8){
    	
    	// Gibt alle Daten die Länger sind als 8 im Fenster Meldung aus
    	echo $data;	
    	
    }else{
    
    	// neu empfangene Daten an $data anhängen
    	$data .= $_IPS['VALUE'];	
    	
    };
    
    
    //Leer den Buffer aus wenn der längste Datensatz empfangen wurde
        
	if(strlen($data)>=15){
		
		$data = "";
		
		
	};
  
    // Inhalt von $data im Puffer der RegisterVariable-Instanz speichern
    RegVar_SetBuffer($_IPS['INSTANCE'], $data);
}


Und wegen dem Serial Port der die Daten sendet, den kannst du hiermit kurz warten lassen ips-sleep

Hoffe es hilft, und es sind keine Fehler drin :cool:

Hallo und schon mal vielen Dank für Deine Mühe.

Ich habe mich mal durchgekämpft. (Allein den Cutter und die Registervariable zu finden)

So sieht es nun aus:

a) dem Cutter habe ich als übergeordnete Instanz den Com5 angegeben. Cut-Einstellungen habe ich erstmal
keine vorgenommen.

b) der Registervariablen habe ich als übergeordnete Instanz den Cutter zugewiesen und als Ziel ID mein Skript,
welches ich unter die Registervariable einegpflegt habe, zugewiesen. Der Inhalt des Skript ist dein Quellcode.

Dann habe ich das ganze mal laufen lassen mit folgendem Ergebnis:

com.txt (970 Bytes)
cutter.txt (2.09 KB)
registervariable.txt (1.37 KB)

Was ich noch nicht verstehe:

  • Mal bekomme ich über den Com5 als Antwort alles in einer Zeile, mal aufgeteilt in drei. Wie bekomme ich das
    „zusammengeschnitten“? Ich habe schon etwas herumprobiert aber naja…

  • Mir ist das Endergebnis nicht klar. Wo steht das jetzt? Kann ich mir das irgendwo anschauen? Und wie werte ich
    das weiter aus?

PS. Tut mir leid das ich soviel Frage, aber ich arbeite wie zu Anfang beschrieben erst seit 3 Wochen mit iPS und das ist nun mein erstes „interessantes“ Projekt bei dem ich leider weinig Vorkenntnisse mitbringe.
Die Einbindung von Momematic und den Licht Ein/Aus Skripten war im Nachhinein ja eher „einfach“.

Mit dem Cutter kenn ich mich nicht aus.
Aber ich würde über ein Script alles was kommt in eine Variablen aneinanderhängen. Bis das Zeichen „chr(13)“ erkannt wird.
Dann hast Du einen kompleten Datensatz, den man dann weiter auswerten kann.
switch, if, Zahlen auswerten was auch immer.
Als kleine Erklärung:
Der Comport ist so schnell, dass er nicht immer den Puffer mit einer kompletten Zeile füllt. Es weiss auch gar nicht wann ein Datensatz endet.
Das muss Du schon als Programmierer übernehmen.

Herzlichen Glückwunsch Du hast Dein erstes Protokoll implementiert. :smiley:

cu…

Hier mal eine alte Implementierung von mir für den Onkyo AV-Verstärker.
Der Onkyo verwendet nicht „13“ als Trennzeichen, sondern 26 (0x1A).
Ansonsten kürzt er auch die Befehle ab.
In dem Beispiel hier wird die Lautstärke ausgelesen:
Onkyo_Empfang.png
Mit ‚!1‘ beginnen bei dem immer die Befehle.
‚MVL‘ bedeutet wohl Mainvolume.
‚30‘ ist dann die Lautstärke.
Das Zeichen dahinter ist das Steuerzeichen 0x1A.

Ich hab es so strukuriert, dass das Script die Variablen unterhalb der Dummyinstanz AV_Verstärker durchsucht.
Findet er in dem Infobereich den entsprechenden Befehl wird der Wert der empfangen wurde dort hineingeschrieben.
Onkyo_Puffer.png
Onkyo_Info.png

Der Verstärker möchte aber, das man jeden Befehl pollt. Das macht ein anderes Script. Aber das kannst Du ja schon. :wink:

Hier das Script „rx“. Ich hab die ganzen auskommentierten Zeilen dringelassen.
So siehst Du, dass so ein Script nicht vom Himmel fällt. Mann muss schon einiges ausprobieren und wieder umschmeissen.
Aber so lernt man am besten.

<?



  // bereits im Puffer der Instanz vorhandene Daten in $data kopieren
  $data  = RegVar_GetBuffer($_IPS['INSTANCE']);
  // neu empfangene Daten an $data anhängen
  $data .= $_IPS['VALUE'];

  // TEST

    // IPS_LogMessage("Onkyo", "Data: ". $data);
      // TEST ENDE


   // wenn das Trennzeichen 0x1A in $data gefunden worden ist
  if (strpos($data, chr(hexdec('1a'))))
  {
       // $data in durch 0x1A separierte Datensätze zerlegen
      $datasets = explode(chr(hexdec('1a')), $data);

       // alle nicht durch 0x1A terminierten Datensätze ausgeben
      for ($i = 0; $i < count($datasets)-1; $i++)
      {
            // IPS_LogMessage("Onkyo", "empfangener Datensatz: ".$datasets[$i]);
         // echo "empfangener Datensatz: ".$datasets[$i]."
";

            // Onkyo Befehl parsen
         sscanf($datasets[$i], "!1%3c%3c".chr(hexdec('1a')) , $command, $hexvalue);

            $value = doubleval(hexdec($hexvalue));

            // Negative Werte?
            if (strpos($datasets[$i], "-"))
               $value *= -1;
            // echo "value2: ".$value."
";
            // echo "Hab dich.\r";
            // echo $command ."\r";
            // echo $value . "\r";
            // echo "Hex: ".$hexvalue."
";

         // Elterninstanz holen
            $parent = IPS_GetParent($_IPS['INSTANCE']);
            $childs = IPS_GetChildrenIDs($parent);
            foreach ($childs as $child)
            {
                if ($child <> $_IPS['SELF'])
                {
                    $obj = IPS_GetObject($child);
                    //print_r($obj);
                    if ($obj['ObjectType'] == 2)
                    {
                        $info = $obj['ObjectInfo'];

                        if ($info == $command)
                        {
                            //print_r($obj);
                            $var =  IPS_GetVariable($child);

                            // Abfangen, wenn der Onkyo einen ungültigen Wert zurück liefert.
                            if ($hexvalue == 'N/A')
                            {
                               SetValue($child, GetValue($child)); // Ziel Variable antriggern.
                               break;
                           }
                            // print_r($var);
                           if ($var['VariableValue']['ValueType'] == 0)
                           {  // Wenn Boolean
                              if ($value == 0)
                                  SetValueBoolean($child, false);
                                else
                                   SetValueBoolean($child, true);
                           }
                           elseif($var['VariableValue']['ValueType'] == 1)
                           {  // wenn Integer
                              SetValueInteger($child, $value);
                            }
                            elseif($var['VariableValue']['ValueType'] == 2)
                            {  // Wenn Float
                               SetValueFloat($child, $value);
                            }
                        }
                   }
                }
            }
      }  // ENDE: Schleife Datensatz

      // $data auf den Inhalt des letzten (unvollständigen) Datensatzes setzen
      $data = $datasets[count($datasets) - 1];
  }

  // Inhalt von $data im Puffer der RegisterVariable-Instanz speichern
  RegVar_SetBuffer($_IPS['INSTANCE'], $data);

?>

Viel Spass beim tüfteln.

cu…

Hey Kiter11,

Und das ist dann genau der Einsatzbereich einer Register Variable und einem Aktionsscript. :wink:

Noch ein Tipp: Alles was du dir innerhalb eines Scripts per echo oder print_r() ausgeben lässt, wird unter Meldungen angezeigt.

Zum leichteren auffinden:

Meldungsansicht

http://www.ip-symcon.de/produkt/screenshots/

Gruß soundman33

Hallo,

das ist doch ganz schön viel auf einmal.
Wie mache ich das denn, das die Registervariable alle „einzelteile“ aneinander hängt? (Mir fehlt da noch etwas die syntax)

Dann noch eine Frage:

Wenn am Ende nun im Puffer die Antwort steht, wo und wie kann ich die auswerten?

So wie ich es verstanden habe in dem Skript welches an der Registervariablen hängt, sprich hintendran oder.
Dann nämlich mal eine ganz bescheidene Frage:

ich habe folgenden code hinten an das registervariablen skript angehängt:

    // Inhalt von $data im Puffer der RegisterVariable-Instanz speichern
    RegVar_SetBuffer($_IPS['INSTANCE'], $data);
}
echo $data;
echo $Gesendet; 
//echo $_IPS;

if ($data == $Gesendet)
{
    SetValueBoolean(22052 /*[Merker\Test]*/, true);
}
else
{
    SetValueBoolean(22052 /*[Merker\Test]*/, false);
}

?>

In der Meldung steht nun folgendes (nach dem Sendebefehl über Com5):

Daum/Uhrzeit-----RegisterVariable-----Main.Power=On
------------------------------------------------Main.Power=On

(- = sollen die Spaltentrennung darstellen! :wink: )

müsste jetzt nicht mein Testmerker true führen??? (Syntax falsch???)

Damit liest du die Daten aus dem Puffer und hängst die neuen dran:

   

 // bereits im Puffer der Instanz vorhandene Daten in $data kopieren
    $data  = RegVar_GetBuffer($_IPS['INSTANCE']);
    // neu empfangene Daten an $data anhängen
    $data .= $_IPS['VALUE'];

und dann muss das ganze natürlich wieder in den Puffer geschrieben werden:

    // Inhalt von $data im Puffer der RegisterVariable-Instanz speichern
    RegVar_SetBuffer($_IPS['INSTANCE'], $data);

Und somit lädst du die bereits enthaltenen Daten, fügst die neuen an und schreibst das alles wieder in den Puffer.

Dann noch eine Frage:

Wenn am Ende nun im Puffer die Antwort steht, wo und wie kann ich die auswerten?

Die Daten stehen jetzt nach dem „.=“ in $data zu verfügung bzw. über RegVar_GetBuffer($_IPS[‚INSTANCE‘])
Und schau dir auch noch die hier mal an: Systemvariablen

So wie ich es verstanden habe in dem Skript welches an der Registervariablen hängt, sprich hintendran oder.

Jepp, so ist es! :slight_smile:

Dann nämlich mal eine ganz bescheidene Frage:

ich habe folgenden code hinten an das registervariablen skript angehängt:

    // Inhalt von $data im Puffer der RegisterVariable-Instanz speichern
    RegVar_SetBuffer($_IPS['INSTANCE'], $data);
}
echo $data;
echo $Gesendet; 
//echo $_IPS;

if ($data == $Gesendet)
{
SetValueBoolean(22052 /[Merker\Test]/, true);
}
else
{
SetValueBoolean(22052 /[Merker\Test]/, false);
}

?>

In der Meldung steht nun folgendes (nach dem Sendebefehl über Com5):

Daum/Uhrzeit-----RegisterVariable-----Main.Power=On
------------------------------------------------Main.Power=On

(- = sollen die Spaltentrennung darstellen! :wink: )

müsste jetzt nicht mein Testmerker true führen??? (Syntax falsch???)

So da wirds jetzt schwierig, da ich nicht sehe wie und wo du $Gesendet definierst und füllst. Bitte immer die ganzen Scripte posten. Sonst ist helfen nicht einfach…

Ich versuch es trotzdem mal, so wie ich das sehe ist eine deiner Variablen leer.

Was passiert denn wenn du es so schreibst:


    // Inhalt von $data im Puffer der RegisterVariable-Instanz speichern
    RegVar_SetBuffer($_IPS['INSTANCE'], $data);
}
echo $data;
//echo $Gesendet; 
//echo $_IPS;

if ($data == "Main.Power=On" )
{
    SetValueBoolean(22052 /*[Merker\Test]*/, true);
}
else
{
    SetValueBoolean(22052 /*[Merker\Test]*/, false);
}

?>  


hm, bei mir kommen echo Ausgaben nur in der Ausgabeansicht an.
Für das Meldefenster muss es schon IPS_LogMesssage sein.

cu…