Liebe Forenmitglieder,
ich bin seit einigen Wochen "stiller" Leser und baue mir Stück für Stück meine FHEM Installation auf, bislang mit Erfolg! Jetzt stehe ich allerdings bei einem Problem an für welches ich keine Lösung gefunden habe:
Ein Arduino ist via I2C angebunden und empfängt Befehle im Stil von "<Register> <Wert>". Der <Wert> kann z.B. ein INT oder LONG von einer Verbrauchsmessung sein die auf einem LED Display angezeigt werden soll. Ich möchte gerne RPII2C dazu verwenden. Mit den verschiedenen "Set" Befehlen (https://fhem.de/commandref_DE.html#RPII2C (https://fhem.de/commandref_DE.html#RPII2C)) kann ich ja jeweils nur Bytes (in Hex) versenden. Meine Frage daher: wie kann ich elegant meinen <Wert> (z.B. ein signed int) in einzelne Bytes im Hexformat mit jeweils einem Leerzeichen dazwischen konvertiere (das benötigt der "set" Befehl von RPII2C)? Also z.B.
dec = 2000 (-> binär = 00000111 11010000) -> Hex = 07 D0
Wenn ich also "2000" an das Register "4" vom Device "3" senden möchte müsste der Befehl meiner Meinung nach wie folgt aussehen:
set foo writeByte 3 4 07 D0
Wie komme ich nun elegant von 2000 zu 07 D0?
PS: die I2C Kommunikation etc. läuft alles problemlos, Werte bis zu 255 werden angezeigt.
Hmmmm, ich bin etwas weitergekommen:
$ perl -wle "print join ' ', unpack('H[2]H[2]', pack('s<', 2000));"
d0 07
$
Damit habe ich mein (short) in 2 Bytes zerlegt und in Hex ausgegeben. Leider akzeptiert das aber RPII2C nicht als Eingabe:
set foo writeByte 3 4 {print join ' ', unpack('H[2]H[2]', pack('s<', 2000));}
führt zu
foo: {print is no 1byte hexadecimal value . Wieso wird der Klammerbefehl nicht interpretiert?
Hi,
Was Du machen willst ist Set Magic. Da müssen ein Paar normale Klammern zusätzlich rein:
set foo writeByte 3 4 {(print join ' ', unpack('H[2]H[2]', pack('s<', 2000)))}
Siehe commanref set Befehl.
Gruß Otto
Danke dir, wieder was gelernt! Gestern Abend habe ich noch einen anderen Weg herausgefunden:
{my $bar = join ' ', unpack('H[2]H[2]', pack('s<', 2000));; fhem("set myi2c writeByte 4 3 $bar")}
Geht auch (z.B. in einem Notify). Ist etwas kryptischer aber eventuell einfacher wenn sowieso noch weiterer Perl Code ausgeführt werden sollte.
Am Elegantesten wäre es, wenn ich basierend auf RPII2C ein erweiterten Set Befehl implementieren würde der zusätzlich zu writeByte etc. auch z.B. ein writeLong kennen würde oder ganz generell die übergebenen Daten byteweise aufteilt und dann sendet. Aber das ist glaub ich nicht zielführend: ein universell verwendbare Implementation macht keinen Sinn weil beim Senden der Slave dann wissen muss, wie die Daten wieder zusammengesetzt werden sollen. Das geht ja noch... umgekehrt müsste ich aber dann irgend ein Template System implementieren damit RPII2C weiss, wie es empfangene Daten (via Get) korrekt zusammen setzen muss und dann z.B. in einem Array abspeichert. Sicherlich machbar, sprengt aber meinen Rahmen, ich bleib bei obigem Perl-Einzeiler ;)
Zitat von: Otto123 am 25 August 2021, 00:17:32
Hi,
Was Du machen willst ist Set Magic. Da müssen ein Paar normale Klammern zusätzlich rein:
set foo writeByte 3 4 {(print join ' ', unpack('H[2]H[2]', pack('s<', 2000)))}
Siehe commanref set Befehl.
Gruß Otto
Nachtrag: dein Vorschlag scheint aus irgend einem Grund nicht zu gehen,
{(print join ' ', unpack('H[2]H[2]', pack('s<', 2000)))} gibt nur eine "1" zurück? Mit meiner Variante
{my $bar = join ' ', unpack('H[2]H[2]', pack('s<', 2000));; fhem("set myi2c writeByte 4 3 $bar")} klappt es, beim Arduino kommt dann 208 & 7 an -> das sind 00000111 11010000 was wiederum "2000" entspricht :)
Naja ich habe deinen Code nicht weiter angeschaut, nur die Sache mit set magic erwähnt.
print geht an der Stelle nicht, kannst Du weglassen:
set foo writeByte 3 4 {( join ' ', unpack('H[2]H[2]', pack('s<', 2000)))}
Zitat von: Otto123 am 25 August 2021, 23:27:07
Naja ich habe deinen Code nicht weiter angeschaut, nur die Sache mit set magic erwähnt.
print geht an der Stelle nicht, kannst Du weglassen:
set foo writeByte 3 4 {( join ' ', unpack('H[2]H[2]', pack('s<', 2000)))}
Fantastisch, jetzt geht's! Habe noch gemerkt, dass "writeBlock" sinnvoller ist als "writeByte" da dann alles auf's Mal im Arduino verarbeitet werden kann. Als Zusammenfassung:
set myi2c writeBlock 4 3 {(join ' ', unpack('H[2]H[2]', pack('s<', 2000)))}
- oder -
{my $bar = join ' ', unpack('H[2]H[2]', pack('s<', 2000));; fhem("set myi2c writeBlock 4 3 $bar")}
Schreibt "2000" zerlegt in 2 Bytes in das Register 3 des I2C Slaves mit Adresse 4. Dort kann z.B. folgender Code verwendet werden um daraus wieder unser int zu erhalten:
void receiveEvent(int howMany) {
byte reg = Wire.read();
byte low = Wire.read();
byte high = Wire.read();
int value = high;
value = value * 256 + low;
}
Das ist sehr rudimentär und sollte nur der Inspiration dienen. Danke dir Otto für's helfen!
Zitat von: ansgru am 25 August 2021, 23:19:53
... gibt nur eine "1" zurück? ...
Moin,
noch dazu ein kurzer Satz: print schreibt in die Standardausgabe, die Standardausgabe landet innerhalb von FHEM im Logfile. Bei Erfolg kommt ein true (1) zurück.
Wenn Du also mal ins Log schaust, siehst du die Ausgabe von deinem Versuch mit print.
Gebraucht wird hier aber das Ergebnis als Rückgabe. Also: hier print funktion einfach weglassen, in eigenen funktionen und komplexeren Versuchen in der FHEM Kommandozeile eine Variable $var füllen und mit return $var arbeiten.
Gruß Otto
Ich habe das ganze jetzt in eine Funktion verpackt, danke Otto für die Hinweise!
# In 99_myUtils.pm ablegen...
# Konvertiert jede als Parameter übergebene Nummer entweder in ein "Byte" (8-bit, unsigned) oder ein "long" (32-bit, signed)
sub num2hex {
my $output;
my $packTemplate;
foreach my $i (@_) {
if ($i > 127 || $i < -128) { # Falls es nicht in ein char (signed, -128 bis 127) passt -> long
$packTemplate = 'l>';
} else {
$packTemplate = 'c';
}
$output .= join(' ', unpack('(H2)*', pack($packTemplate, $i))).' ';
}
return $output;
}
Aufruf ist dann super elegant:
set myi2c writeBlock {(num2hex(6,11,20000))}
In diesem Beispiel wird das Register "11" mit "20000" befüllt für den I2C Slave "6".
Später ergänze ich das ganze noch für die Umkehrfunktion -> wenn ich Daten empfangen will.