Hallo zusammen,
ich habe ein fertiges Modul auf dem ein Mikrocontroller mit Ethernet Schnittstelle sitzt.
Die Ports des Mikrocontrollers lassen sich über die eingebaute Website auf Input, Output, A/D Conv., PWM... konfigurieren.
Über einfache TCP Kommandos lassen sich die Ports auslesen und Outputs setzen. Mit den ECMD Modulen funktioniert das auch super.
nun zu meinem Problem:
Inputs lassen sich so einstellen, das bei einem Triggerevent am Port eine TCP Botschaft gesendet wird.
Nun würde ich das gern mit FHEM auswerten und bei ankommender Botschaft ein notify auslösen. Also ohne pollen.
Gibt es da bereits Lösungen?
Gruß
Klaus
Hi,
wenn Du die gesendete "Botschaft" einstellen kannst, sicher - dann einfach den entspr. Schaltvorgang als URI absetzen.
Sonst bräuchtest Du halt nen port-listener.
Was genau setzt denn der MC ab? Konfigurierbar? Wie weit?
=8-)
Hi Uli,
war ja klar, das ich die wichtigsten Infos vergessen habe ;)
Die Botschaft lässt sich leider nicht ändern. Ich habe mal angefragt ob es möglich ist das nach zurüsten, aber noch keine Antwort.
Der MC hat 13 konfigurierbare Ports. Die von a bis m durchbenannt sind.
Wenn z.B. Port A auf Input mit Trigger auf allen Flanken eingestellt ist und ich mit z.B. Telnet, Socat etc. verbunden bin sendet er bei jedem Pegelwechsel auf High: "a=1" und auf Low: "a=0"
Sieht also so aus, als ob ich einen port-listener brauche.
Gibt es schon ein ähnliches Modul, welches ich anpassen kann?
Grüße
Klaus
ich habe ne Antwort bekommen
In der neuen Firmware kann man die Kommandos anpassen, also werde ich erstmal abwarten :)
hi Klaus,
der port-listener würde mich auch interessieren.
hast du neuigkeiten diesbezüglich?
lg steve
Vielleicht hilft cmdalias:
define a1 cmdalias a=1 AS trigger a1 a1
Zitat von: rudolfkoenig am 16 Dezember 2013, 08:40:17
Vielleicht hilft cmdalias:
define a1 cmdalias a=1 AS trigger a1 a1
Oh, das habe ich bisher übersehen
Leider ist es eine Raw TCP Verbindung.
Das heisst, es wird nur "a=1" gesendet, und nicht "fhemhost:48083/fhem?cmd=a=1".
Und die Verbindung muss von FHEM Seite aus aufgebaut werden.
Zitat von: stevestrong am 16 Dezember 2013, 01:37:23
hi laus,
der port-listener würde mich auch interessieren.
hast du neuigkeiten diesbezüglich?
lg steve
Hatte das Thema aus den Augen verloren.
Vielleicht habe ich über die Feiertage ein bisschen Zeit.
ZitatDas heisst, es wird nur "a=1" gesendet, und nicht "fhemhost:48083/fhem?cmd=a=1".
Ich meinte das telnet Interface, aber der benoetigt auch ein NL.
/fhem?cmd= wird vom FHEMWEB benoetigt zusammen mit einem HTTP header.
Achso, ja darüber hatte ich auch schon nachgedacht.
Gerade habe ich mir das Teil nochmal angeschaut. Es liefert bei Statusänderung der GPIOs den Status aller Pins in Hex zurück. Also wenn ich GPIO1 auf 1 wechsle und der Rest 0 ist: "x=01 " und wenn er auf 0 wechselt "x=00 ".
Als Trennzeichen kommt also nur ein Leerzeichen.
Ausserdem möchte ich auf den gleichen Port auch senden können(um GPIOs die als Output eingestellt sind zu setzen).
Wenn es da nix gibt, kann ich aber auf DevIo aufsetzen oder?
Gibt es da ein einfaches Modulbeispiel, was ich mir zum Verständnis anschauen kann?
wenn du so weit bist das du lesen und schreiben kannst schau dir mal readingsProxy an damit kannst du ein device das alle ports kombiniert hat auf mehrere fhem devices aufsplitten die dann getrennt in fhem darstellbar sind. also mit eigenen icon, eigenen webCmd, ....
in dem thread hier: http://forum.fhem.de/index.php/topic,16374.msg109816.html#msg109816 (http://forum.fhem.de/index.php/topic,16374.msg109816.html#msg109816) findest du weiter unten ein paar beispiele.
gruss
andre
Zitat von: justme1968 am 16 Dezember 2013, 22:15:02
wenn du so weit bist das du lesen und schreiben kannst schau dir mal readingsProxy an damit kannst du ein device das alle ports kombiniert hat auf mehrere fhem devices aufsplitten die dann getrennt in fhem darstellbar sind. also mit eigenen icon, eigenen webCmd, ....
in dem thread hier: http://forum.fhem.de/index.php/topic,16374.msg109816.html#msg109816 (http://forum.fhem.de/index.php/topic,16374.msg109816.html#msg109816) findest du weiter unten ein paar beispiele.
gruss
andre
Du meinst um den Hexwert in einzelne Geräte auseinander zu klabustern?
Wenn ich schon so weit wäre 8)
mit IO::Socket::INET müsste es etwa in der art funktionieren:
- ReadFn definieren
- etwa so ein docket auf und zu machen:sub
iTunes_startUpdater($)
{
my ($hash) = @_;
my $name = $hash->{NAME};
iTunes_stopUpdater($hash,0);
my $conn = IO::Socket::INET->new(PeerAddr=>"$hash->{Host}:$hash->{Port}");
if($conn) {
$hash->{STATE} = "Connected";
$hash->{FD} = $conn->fileno();
$hash->{CD} = $conn; # sysread / close won't work on fileno
$hash->{CONNECTS}++;
$selectlist{$name} = $hash;
Log(GetLogLevel($name,3), "$name: updater connected to $hash->{Host}:$hash->{Port}");
iTunes_requestUpdate($hash);
} else {
iTunes_stopUpdater($hash, 1);
}
}
sub
iTunes_stopUpdater($$)
{
my ($hash, $connect) = @_;
my $name = $hash->{NAME};
return if( !$hash->{CD} );
close($hash->{CD}) if($hash->{CD});
delete($hash->{FD});
delete($hash->{CD});
delete($selectlist{$name});
$hash->{STATE} = "Disconnected";
if($connect) {
Log3 $name, 4, "$name: updater Connect failed.";
} else {
Log3 $name, 4, "$name: updater Disconnected";
}
}
- sobald daten da sind wird die readFn aufgerufen und du kannst dort etwa so lesen: my $buf;
my $ret = sysread($hash->{CD}, $buf, 1024*1024);
if(!defined($ret) || $ret <= 0) {
iTunes_startUpdater($hash);
return;
}
- schreiben entsprechend mit syswrite
alles was iTunes heisst solltest du umbenennen :)
gruss
andre
Danke Andre,
super Erklärung, das empfangen der Daten funktioniert schon.
Aber wenn die Gegenstelle nicht da ist dann versucht sich FHEM endlos zu verbinden, lässt sich da ein timeout einfügen?
Grüße
Klaus
wenn die verbindung nicht geht nicht sofort wieder versuchen sondern mit InternalTimer irgendwann später.
gruss
andre
ich wiederhole nix, jedenfalls nicht vorsätzlich
im define habe ich iTunes_startUpdater (natürlich umbenannt ;)) aufgerufen, das ist alles
oder wird es da endlos wiederholt? Kann ich mir eigentlich nicht vorstellen
schau hin:) es wird bei einem lese fehler immer wieder eine neue verbindung auf gemacht. dir logik musst du für dich ändern.
gruss
andre
erst vermutete ich ja, das ich zu müde war, aber scheinbar bin ich zu blöd ;)
##############################################################################
#
# 51_NetzerUtils.pm
#
##############################################################################
# Modul for NetzerUtils access
#
#
#
##############################################################################
package main;
use strict;
use warnings;
use POSIX;
use Scalar::Util qw(looks_like_number);
use IO::File;
sub NetzerUtils_Initialize($) {
my ($hash) = @_;
$hash->{DefFn} = "NetzerUtils_Define";
$hash->{ReadFn} = "NetzerUtils_Read";
$hash->{GetFn} = "NetzerUtils_Get";
$hash->{SetFn} = "NetzerUtils_Set";
$hash->{AttrFn} = "NetzerUtils_Attr";
$hash->{UndefFn} = "NetzerUtils_Undef";
$hash->{ExceptFn} = "NetzerUtils_Except";
$hash->{AttrList} = "poll_interval loglevel:0,1,2,3,4,5" .
" direction:input,output pud_resistor:off,up,down" .
" interrupt:none,falling,rising,both toggletostate:no,yes";
}
my %setsoutp = (
'on' => 0,
'off' => 0,
'toggle' => 0,
);
my %setsinpt = (
'readValue' => 0,
);
sub NetzerUtils_Define($$) {
my ($hash, $def) = @_;
my @args = split("[ \t]+", $def);
my $menge = int(@args);
if (int(@args) < 3)
{
return "Define: to less arguments. Usage:\n" .
"define <name> NetzerUtils <Host> <port>";
}
#Prüfen, ob GPIO bereits verwendet
#foreach my $dev (devspec2array("TYPE=$hash->{TYPE}")) {
# if ($args[2] eq InternalVal($dev,"RPI_pin","")) {
# return "GPIO $args[2] already used by $dev";
# }
#}
my $name = $args[0];
$hash->{Host} = $args[2];
$hash->{Port} = $args[3];
NetzerUtils_conn($hash);
# create default attributes
#my $msg = CommandAttr(undef, $name . ' direction input');
#return $msg if ($msg);
return undef;
}
sub NetzerUtils_Get($) {
my ($hash) = @_;
my $name = $hash->{NAME};
#my $dir = $attr{$hash->{NAME}}{direction} || "output";
my $dir = "";
my $zustand = undef;
#my $val = fileaccess($hash, "value");
if ( defined ($val) ) {
if ( $val == 1) {
if ($dir eq "output") {$zustand = "on";} else {$zustand = "high";}
} elsif ( $val == 0 ) {
if ($dir eq "output") {$zustand = "off";} else {$zustand = "low";}
}
} else {
Log 1, "$hash->{NAME} GetFn: readout of Pinvalue fail";
}
$hash->{READINGS}{Pinlevel}{VAL} = $zustand;
$hash->{READINGS}{Pinlevel}{TIME} = TimeNow();
return "Current Value for $name: $zustand";
}
sub NetzerUtils_Set($@) {
my ($hash, @a) = @_;
my $name =$a[0];
my $cmd = $a[1];
#my $val = $a[2];
if(defined($attr{$name}) && defined($attr{$name}{"direction"})) {
my $mt = $attr{$name}{"direction"};
if($mt && $mt eq "output") {
if(!defined($setsoutp{$cmd})) { #prüfe ob Kommando in Liste
my $slist = join(' ', keys %setsoutp);
#return 'Unknown argument ' . $cmd . ', choose one of ' . join(' ', keys %setsoutp)
return SetExtensions($hash, $slist, @a);
} else {
if ($cmd eq 'on') {
#fileaccess($hash, "value", "1");
#$hash->{STATE} = 'on';
my $buf = "m=1\r\n";
my $cnt= length ($buf);
syswrite($hash->{CD}, $buf, $cnt);
} else {
my $buf = "m=0\r\n";
my $cnt= length ($buf);
syswrite($hash->{CD}, $buf, $cnt);
#fileaccess($hash, "value", "0");
#$hash->{STATE} = 'off';
}
}
} else {
if(!defined($setsinpt{$cmd})) {
return 'Unknown argument ' . $cmd . ', choose one of ' . join(' ', keys %setsinpt)
} else {
}
}
}
if ($cmd eq 'readValue') {
updatevalue($hash);
}
}
sub NetzerUtils_Attr(@) {
my (undef, $name, $attr, $val) = @_;
my $hash = $defs{$name};
my $msg = '';
if ($attr eq 'direction') {
if (!$val) { #$val nicht definiert: Einstellungen löschen
$msg = "$hash->{NAME}: no direction value. Use input output";
} elsif ($val eq "input") {
fileaccess($hash, "direction", "in");
#Log 1, "$hash->{NAME}: direction: input";
} elsif( ( AttrVal($hash->{NAME}, "interrupt", "none") ) ne ( "none" ) ) {
$msg = "$hash->{NAME}: Delete attribute interrupt or set it to none for output direction";
} elsif ($val eq "output") {
fileaccess($hash, "direction", "out");
#Log 1, "$hash->{NAME}: direction: output";
} else {
$msg = "$hash->{NAME}: Wrong direction value. Use input output";
}
}
return ($msg) ? $msg : undef;
}
sub NetzerUtils_Read($) {
Log 1, "Read gestartet";
my ($hash) = @_;
my $name = $hash->{NAME};
my $buf;
my $ret = sysread($hash->{CD}, $buf, 1024*1024);
if(!defined($ret) || $ret <= 0) {
NetzerUtils_conn($hash);
return;
} else {
readingsBeginUpdate($hash);
readingsBulkUpdate($hash, 'zeichenmenge', $ret);
#for ( $i = 1; $i <= 5; $i++) {
#}
readingsBulkUpdate($hash, 'inhalt', $buf);
readingsEndUpdate($hash, 1);
}
}
sub NetzerUtils_Undef($$) {
my ($hash, $arg) = @_;
if ( defined (AttrVal($hash->{NAME}, "poll_interval", undef)) ) {
RemoveInternalTimer($hash);
}
NetzerUtils_disconn($hash,0);
return undef;
}
sub NetzerUtils_conn($) {
my ($hash) = @_;
my $name = $hash->{NAME};
NetzerUtils_disconn($hash,0);
my $conn = IO::Socket::INET->new(PeerAddr=>"$hash->{Host}:$hash->{Port}");
if($conn) {
$hash->{STATE} = "Connected";
$hash->{FD} = $conn->fileno();
$hash->{CD} = $conn; # sysread / close won't work on fileno
$hash->{CONNECTS}++;
$selectlist{$name} = $hash;
Log(GetLogLevel($name,3), "$name: updater connected to $hash->{Host}:$hash->{Port}");
#iTunes_requestUpdate($hash);
} else {
NetzerUtils_disconn($hash, 1);
}
}
sub NetzerUtils_disconn($$) {
my ($hash, $connect) = @_;
my $name = $hash->{NAME};
return if( !$hash->{CD} );
close($hash->{CD}) if($hash->{CD});
delete($hash->{FD});
delete($hash->{CD});
delete($selectlist{$name});
$hash->{STATE} = "Disconnected";
if($connect) {
Log3 $name, 4, "$name: updater Connect failed.";
} else {
Log3 $name, 4, "$name: updater Disconnected";
}
}
1;
bei der "if($conn)" Schleife springe ich nach else, da eine Verbindung nicht zustande gekommen ist, richtig?
Dort gehts dann ins sub "NetzerUtils_disconn"
Der wiederum wird verlassen, da "$hash->{CD}" nicht definiert ist.
Aber dann bin ich doch durch. Ich erkenne keine Schleife.
Grüße
Klaus
prüf mal nach dem erzeugen des sockets noch ob es auch verbunden ist. das geht mit $conn->connected.
es kann sein das das socket zwar angelegt wird aber trotzdem die verbindung nicht funktioniert. dadurch landest du dann im read obwohl keine daten da sind. da wird dann die verbindung neu aufgebaut und das ist deine schleife.
ach ja noch was: leg deine readings mit readingsSingleUpdate oder readingsBeginUpdate/readingsBulkUpdate/readingsEndUpdate und nicht direkt selber in den hash.
gruss
andre
ja das mit den readings ist alter kram der noch wegkommt
juhu ich habs:
ein timeout wirkt wunder :)
my $timeout = $hash->{TIMEOUT} ? $hash->{TIMEOUT} : 3;
my $conn = IO::Socket::INET->new(PeerAddr=>"$hash->{Host}:$hash->{Port}", Timeout => $timeout);
und schon gehts
$conn->connected ist nicht notwendig da im fehlerfall $conn nicht definiert
Zitat von: justme1968 am 16 Dezember 2013, 22:15:02
wenn du so weit bist das du lesen und schreiben kannst schau dir mal readingsProxy an damit kannst du ein device das alle ports kombiniert hat auf mehrere fhem devices aufsplitten die dann getrennt in fhem darstellbar sind. also mit eigenen icon, eigenen webCmd, ....
in dem thread hier: http://forum.fhem.de/index.php/topic,16374.msg109816.html#msg109816 (http://forum.fhem.de/index.php/topic,16374.msg109816.html#msg109816) findest du weiter unten ein paar beispiele.
gruss
andre
Das funktioniert super. Mit nur 2 Zeilen (define und attr valueFn) pro Kanal habe ich ein separates Device erzeugt.
Danke für den Tip :)