ECMD: Unterschied zwischen den Versionen
GRZBRZ (Diskussion | Beiträge) KKeine Bearbeitungszusammenfassung |
GRZBRZ (Diskussion | Beiträge) KKeine Bearbeitungszusammenfassung |
||
Zeile 1: | Zeile 1: | ||
ECMD bedeutet "Ethersex Command"<ref>[http://www.ethersex.de/index.php/Main_Page_%28Deutsch%29 Ethersex.de]</ref> und ist laut FHEM-Dokumentation | ECMD bedeutet "Ethersex Command"<ref>[http://www.ethersex.de/index.php/Main_Page_%28Deutsch%29 Ethersex.de]</ref> und ist laut FHEM-Dokumentation | ||
"Any physical device with request/response-like communication capabilities over a TCP connection"<ref>{{Link2CmdRef|Anker=ECMD|Label=FHEM Commandref}}</ref>, das heisst, irgendein physisches Gerät, welches request/response-artige Kommunikationsfähigkeiten aufweist, sei es über eine TCP-Verbindung (Netzwerk) oder eine serielle Schnittstelle. | |||
"Any physical device with request/response-like communication capabilities over a TCP connection"<ref>{{Link2CmdRef|Anker=ECMD|Label=FHEM Commandref}}</ref>, | |||
das heisst, irgendein physisches Gerät, welches request/response-artige Kommunikationsfähigkeiten aufweist, sei es über eine TCP-Verbindung (Netzwerk) oder eine serielle Schnittstelle. | |||
In FHEM sind enthalten: | In FHEM sind enthalten: | ||
Zeile 13: | Zeile 16: | ||
Siehe [[Radioaktivitätsmessung mit DIYGeigerCounter]] | Siehe [[Radioaktivitätsmessung mit DIYGeigerCounter]] | ||
=== DIY Sensor via HC-12 === | === Beispiel DIY Sensor via HC-12 === | ||
Ein BME280 Temperatur/Druck/Feuchte-Sensor liefert via eines HC-12 Pärchens seine Daten an der seriellen Schnittstelle eines RasPi ab. Der Sketch im Nano, an dem sowohl der BME280 als auch einer der HC-12 hängen, liefert alle 10 Minuten ein Datagramm ab und schickt seinen Wirt in den Tiefschlaf bis zum nächsten Datagramm, um mit den notwendigen Batterien über ein Jahr aushalten zu können. | Ein BME280 Temperatur/Druck/Feuchte-Sensor liefert via eines HC-12 Pärchens seine Daten an der seriellen Schnittstelle eines RasPi ab. Der Sketch im Nano, an dem sowohl der BME280 als auch einer der HC-12 hängen, liefert alle 10 Minuten ein Datagramm ab und schickt seinen Wirt in den Tiefschlaf bis zum nächsten Datagramm, um mit den notwendigen Batterien über ein Jahr aushalten zu können. | ||
Zeile 22: | Zeile 25: | ||
Daraus werden die notwendigen Definitionen für eine Klassendefinition abgeleitet. Die z.B. unter '/opt/fhem/BME280.classdef' abgelegte Datei erhält den Inhalt<syntaxhighlight line="1"> | Daraus werden die notwendigen Definitionen für eine Klassendefinition abgeleitet. Die z.B. unter '/opt/fhem/BME280.classdef' abgelegte Datei erhält den Inhalt<syntaxhighlight line="1"> | ||
params devId | params devId | ||
reading temperature match "%devId | reading temperature match "%devId[^\n]+\n" | ||
reading temperature postproc { /%devId | reading temperature postproc { /%devId[^T]+T([-+.,0-9]+).*/; $1 } | ||
reading airpressure match "%devId | reading airpressure match "%devId[^\n]+\n" | ||
reading airpressure postproc { /%devId | reading airpressure postproc { /%devId[^P]+P([-+.,0-9]+).*/; $1 } | ||
reading humidity match "%devId | reading humidity match "%devId[^\n]+\n" | ||
reading humidity postproc { /%devId | reading humidity postproc { /%devId[^H]+H([-+.,0-9]+).*/; $1 } | ||
state humidity | state humidity | ||
</syntaxhighlight>Die regex ist in allen Zeilen | </syntaxhighlight>Die regex für match ist in allen Zeilen gleich gestaltet. Alle Werte werden durch diese Vorgehensweise gleichzeitig und erst dann gelesen, wenn das gesamte Datagramm eingetroffen ist. Es ließe sich auch auf Teil-Strings abfragen, Konflikte bei mehreren Sensoren sind dann denkbar. | ||
Die letzte Zeile sorgt dafür, dass der state nur dann einen neuen Wert erhält, wenn (in diesem Fall) 'humidity' ein Update erfährt. Bei den gewählten regex trift das hier eigentlich für alle zu, aber da humidity der letzte übertragene Wert ist, ist die Wahl auf ihn gefallen. Außerdem steht nun nur ein Wert im state ohne einleitenden reading Namen, der wäre hier im Beispiel erwartungsgemäß 'humidity', was hier insofern irritiert, weil ja 3 Werte gelesen wurden und man sich unwillkürlich fragt, wo den die anderen geblieben sind. Zum state daher später unter Notify noch weiteres. | Die letzte Zeile sorgt dafür, dass der state nur dann einen neuen Wert erhält, wenn (in diesem Fall) 'humidity' ein Update erfährt. Bei den gewählten regex trift das hier eigentlich für alle zu, aber da humidity der letzte übertragene Wert ist, ist die Wahl auf ihn gefallen. Außerdem steht nun nur ein Wert im state ohne einleitenden reading Namen, der wäre hier im Beispiel erwartungsgemäß 'humidity', was hier insofern irritiert, weil ja 3 Werte gelesen wurden und man sich unwillkürlich fragt, wo den die anderen geblieben sind. Zum state daher später unter Notify noch weiteres. | ||
Zeile 75: | Zeile 78: | ||
</syntaxhighlight>① 'ECMDDevState' wird immer dann benachrichtigt und ausgeführt, wenn der Device 'BME280_42356' ein reading 'humidity' mit einem beliebigen (.*) Wert hat. ③ Dies wird zunächst im Log vermerkt und ④ als nächstes ein wenig gewartet, damit eine Änderung des state auf jeden Fall nach dem Befüllen des state durch das humidity reading stattfindet und auch der Luftdruck fertig berechnet ist. Dann wird ⑤ der state mit einer Verkettung der gerade gelesenen bzw. berechneten Daten aller Werte ⑥,⑦,⑧ versorgt. | </syntaxhighlight>① 'ECMDDevState' wird immer dann benachrichtigt und ausgeführt, wenn der Device 'BME280_42356' ein reading 'humidity' mit einem beliebigen (.*) Wert hat. ③ Dies wird zunächst im Log vermerkt und ④ als nächstes ein wenig gewartet, damit eine Änderung des state auf jeden Fall nach dem Befüllen des state durch das humidity reading stattfindet und auch der Luftdruck fertig berechnet ist. Dann wird ⑤ der state mit einer Verkettung der gerade gelesenen bzw. berechneten Daten aller Werte ⑥,⑦,⑧ versorgt. | ||
====== Log HISTORY ====== | |||
Die Werte sammeln sich nun im Log. Dort haben die Werte ebenfalls eine Einheit, in diesem Fall "°C" für reading-Namen, die mit "temperature" beginnen und "%" für reading-Namen, die mit "humidity" beginnen. Andere Einheiten oder andere reading-Namen lassen sich entweder über ein Attribut in der Log-Definition<syntaxhighlight> | |||
attr myDbLog valueFn { | |||
if ($DEVICE eq "BME280_42356"){ | |||
if ($READING=~ m(^airpressure)){ | |||
$UNIT = "hPa"; | |||
} | |||
if ($READING=~ m(^pressure)){ | |||
$UNIT = "Pa"; | |||
} | |||
} | |||
} | |||
</syntaxhighlight>oder direkt im Device ergänzen bzw. beeinflussen.<syntaxhighlight> | |||
attr BME280_42356 DbLogValueFn { | |||
if ($READING=~ m(^airpressure)){ | |||
$UNIT = "hPa"; | |||
} | |||
if ($READING=~ m(^pressure)){ | |||
$UNIT = "Pa"; | |||
} | |||
} | |||
</syntaxhighlight>Damit lassen sich freilich auch ungewöhnliche Einheiten festlegen, Blutdruck wird z.B. in "mmHg" gemessen. | |||
====== Weiterführend ====== | ====== Weiterführend ====== | ||
Zeile 92: | Zeile 118: | ||
"<IN>" using 1:2 axes x1y2 title 'Luftdruck' ls l1 lw 1 with lines,\ | "<IN>" using 1:2 axes x1y2 title 'Luftdruck' ls l1 lw 1 with lines,\ | ||
"<IN>" using 1:2 axes x1y1 title 'Feuchte' ls l2 lw 1 with lines | "<IN>" using 1:2 axes x1y1 title 'Feuchte' ls l2 lw 1 with lines | ||
</syntaxhighlight>Dabei wird der Luftdruck auf einen plausiblen Bereich eingegrenzt, Drücke außerhalb dieses Bereichs hat es noch nicht gegeben auf diesem Erdball. Der Wert der Feuchte wird dem zu erwartenden Temperaturbereich (-10 bis 40) angeglichen, das ergibt eine schönere Kurve (:$val=$val | </syntaxhighlight>Dabei wird der Luftdruck auf einen plausiblen Bereich eingegrenzt, Drücke außerhalb dieses Bereichs hat es noch nicht gegeben auf diesem Erdball (:$val=$val>1100?1100:$val<800?800:$val). Der Wert der Feuchte wird dem zu erwartenden Temperaturbereich (-10 bis 40) angeglichen, das ergibt eine schönere Kurve (:$val=$val/2-10). Eine Y-Achse für die Feuchte wäre ja die dritte Y-Achse, die haben wir also nicht. Also landet 100% Feuchte bei 40°C und 0% Feuchte bei -10°C. | ||
Dann wird der Plot definiert. In diesem Fall mit den Daten aus DbLog, für FileLog muss geringfügig angepaßt werden. Dann fehlten freilich auch die FileLog -Definitionen für 'BME280_42356'.<syntaxhighlight> | Dann wird der Plot definiert. In diesem Fall mit den Daten aus DbLog, für FileLog muss geringfügig angepaßt werden. Dann fehlten freilich auch die FileLog -Definitionen für 'BME280_42356'.<syntaxhighlight> |
Version vom 27. Juni 2020, 14:37 Uhr
ECMD bedeutet "Ethersex Command"[1] und ist laut FHEM-Dokumentation
"Any physical device with request/response-like communication capabilities over a TCP connection"[2],
das heisst, irgendein physisches Gerät, welches request/response-artige Kommunikationsfähigkeiten aufweist, sei es über eine TCP-Verbindung (Netzwerk) oder eine serielle Schnittstelle.
In FHEM sind enthalten:
- Modul ECMD für die Bearbeitung physischer I/O-Schnittstellengeräte und
- Modul ECMDDevice für einzelne logische Geräte, deren Kommunikation über ein ECMD-Gerät läuft
Beispiele & Links
Links
Siehe Thread im FHEM-Forum
Siehe Radioaktivitätsmessung mit DIYGeigerCounter
Beispiel DIY Sensor via HC-12
Ein BME280 Temperatur/Druck/Feuchte-Sensor liefert via eines HC-12 Pärchens seine Daten an der seriellen Schnittstelle eines RasPi ab. Der Sketch im Nano, an dem sowohl der BME280 als auch einer der HC-12 hängen, liefert alle 10 Minuten ein Datagramm ab und schickt seinen Wirt in den Tiefschlaf bis zum nächsten Datagramm, um mit den notwendigen Batterien über ein Jahr aushalten zu können.
Das Datagramm hat die Form
DevId T-3.45°C P995.31hPa H63.27%RH\r\n
Klassendefinition
Daraus werden die notwendigen Definitionen für eine Klassendefinition abgeleitet. Die z.B. unter '/opt/fhem/BME280.classdef' abgelegte Datei erhält den Inhalt
params devId
reading temperature match "%devId[^\n]+\n"
reading temperature postproc { /%devId[^T]+T([-+.,0-9]+).*/; $1 }
reading airpressure match "%devId[^\n]+\n"
reading airpressure postproc { /%devId[^P]+P([-+.,0-9]+).*/; $1 }
reading humidity match "%devId[^\n]+\n"
reading humidity postproc { /%devId[^H]+H([-+.,0-9]+).*/; $1 }
state humidity
Die regex für match ist in allen Zeilen gleich gestaltet. Alle Werte werden durch diese Vorgehensweise gleichzeitig und erst dann gelesen, wenn das gesamte Datagramm eingetroffen ist. Es ließe sich auch auf Teil-Strings abfragen, Konflikte bei mehreren Sensoren sind dann denkbar.
Die letzte Zeile sorgt dafür, dass der state nur dann einen neuen Wert erhält, wenn (in diesem Fall) 'humidity' ein Update erfährt. Bei den gewählten regex trift das hier eigentlich für alle zu, aber da humidity der letzte übertragene Wert ist, ist die Wahl auf ihn gefallen. Außerdem steht nun nur ein Wert im state ohne einleitenden reading Namen, der wäre hier im Beispiel erwartungsgemäß 'humidity', was hier insofern irritiert, weil ja 3 Werte gelesen wurden und man sich unwillkürlich fragt, wo den die anderen geblieben sind. Zum state daher später unter Notify noch weiteres.
Schnittstelle
Sollte bereits eine ECMD Schnittstelle bestehen, kann nun die Definition hinzugefügt werden. Z.B. die ECMD Schnittstelle namens 'SerDevDef' erhält eine zusätzliche Klasse 'BME280' mit den Definitionen aus der vorher generierten Datei, Beispiel siehe oben.
set SerDevDef classdef BME280 /opt/fhem/BME280.classdef
Ist noch keine ECMD Schnittstelle definiert, wird diese erstellt und gleich mit passenden Attributen versehen.
define SerDevDef ECMD serial /dev/ttyS0@9600
set SerDevDef classdef BME280 /opt/fhem/BME280.classdef
attr SerDevDef partial 2
Die letzte Zeile ist für die serielle Schnittstelle gerade bei längeren Datagrammen ein Muss. Sie sorgt dafür, dass die kleineren Informationsbröckchen zu einer vollständigen Einheit werden. Kommt das nächste Päckchen innerhalb von (hier) 2 Sekunden, wird das neue Päckchen an die bereits gelesenen Daten angehängt, ansonsten startet eine neue Aufzeichnung beginnend mit dem Päckchen. Für den Start kann noch zur Erleichterung der Fehlersuche folgendes ergänzt werden
attr SerDevDef logTraffic 5
attr SerDevDef verbose 5
Rückstelllen dieser Attribute nicht vergessen, so ist's reichlich gesprächig und füllt schnell das Log !!!
Device
Damit ist alles vorbereitet den ECMD Device zu erstellen.
define BME280_42356 ECMDDevice BME280 42356
attr BME280_42356 alias Gartenhaus
attr BME280_42356 room Grundstück
attr BME280_42356 IODev SerDevDef
attr BME280_42356 userReadings airpressure_sealevel:humidity:.* { sprintf("%.2f", ReadingsVal("BME280_42356","pressure",0)*1.023);; }
Der fragliche Sensor beginnt seine Datagramme immer mit '42356', das ist seine DevId, die über den Parameter 'devId' in der Klassendefinition (siehe oben) Eingang in die readings findet. Über 'BME280' wird die Klassendefinition festgelegt, über '42356' die devId. Sinnigerweise wird im Beispiel hier 'BME280_42356' als Devicename festgelegt, Systematik ist Trumpf.
Die letzte Zeile macht aus dem vom Sensor gemessenen Luftdruck noch den auf NormalNull Bezogenen. Nicht jeder wohnt direkt am Meer und kann auf diesen Schritt verzichten. Ansonsten weichen die Werte des Sensors immer von der Wettervorhersage ab, je höher der Sensor, um so markanter. Das Beispiel hier gilt für ca. 180m üNN. Die Erzeugung des Wertes für "airpressure_sealevel" ist übrigens an den Event "humidity geknüpft, sonst hat man im Event-Log in diesem Fall 3 Einträge, nämlich den beim Temperatur-, beim Druck- und beim Feuchte-Event (siehe auch beim folgenden Notify und dem linken Teil des Beispiel-Plots weiter unten).
Notify
Da der state nach einer Übermittlung aufgrund der Festlegungen aus der Klassendefinition nun nur den Wert der Luftfeuchte enthält, ist es hilfreich ein 'notify' für eine gefälligere Darstellung zu definieren.
define ECMDDevState notify BME280_42356:humidity:.* { Log 1, "$NAME has new values";;sleep 1;;fhem "setstate BME280_42356"." T:".ReadingsVal("BME280_42356","temperature",0)." P:".ReadingsVal("BME280_42356","airpressure_sealevel",0)." H:".ReadingsVal("BME280_42356","humidity",0) }
Für die Übersicht zerlegt:
define ECMDDevState notify BME280_42356:humidity:.*
{
Log 1, "$NAME has new values";;
sleep 1;;
fhem "setstate BME280_42356".
" T:".ReadingsVal("BME280_42356","temperature",0).
" P:".ReadingsVal("BME280_42356","airpressure_sealevel",0).
" H:".ReadingsVal("BME280_42356","humidity",0)
}
① 'ECMDDevState' wird immer dann benachrichtigt und ausgeführt, wenn der Device 'BME280_42356' ein reading 'humidity' mit einem beliebigen (.*) Wert hat. ③ Dies wird zunächst im Log vermerkt und ④ als nächstes ein wenig gewartet, damit eine Änderung des state auf jeden Fall nach dem Befüllen des state durch das humidity reading stattfindet und auch der Luftdruck fertig berechnet ist. Dann wird ⑤ der state mit einer Verkettung der gerade gelesenen bzw. berechneten Daten aller Werte ⑥,⑦,⑧ versorgt.
Log HISTORY
Die Werte sammeln sich nun im Log. Dort haben die Werte ebenfalls eine Einheit, in diesem Fall "°C" für reading-Namen, die mit "temperature" beginnen und "%" für reading-Namen, die mit "humidity" beginnen. Andere Einheiten oder andere reading-Namen lassen sich entweder über ein Attribut in der Log-Definition
attr myDbLog valueFn {
if ($DEVICE eq "BME280_42356"){
if ($READING=~ m(^airpressure)){
$UNIT = "hPa";
}
if ($READING=~ m(^pressure)){
$UNIT = "Pa";
}
}
}
oder direkt im Device ergänzen bzw. beeinflussen.
attr BME280_42356 DbLogValueFn {
if ($READING=~ m(^airpressure)){
$UNIT = "hPa";
}
if ($READING=~ m(^pressure)){
$UNIT = "Pa";
}
}
Damit lassen sich freilich auch ungewöhnliche Einheiten festlegen, Blutdruck wird z.B. in "mmHg" gemessen.
Weiterführend
Abschließend soll noch ein Plot der Messwerte gemacht werden, eine Kurve liest sich leichter. Als Erstes wird eine Datei z.B. '/opt/fhem/www/gplot/SVG_BME280_42356.gplot' mit folgendem Inhalt erstellt, der anzuzeigenden Linien und Beschriftungen des Plots beschreibt.
set terminal png transparent size <SIZE> crop
set output '<OUT>.png'
set xdata time
set timefmt "%Y-%m-%d_%H:%M:%S"
set xlabel "Verlauf"
set title 'Wetter'
set ylabel "Temperatur"
set y2label "Luftdruck"
#myDbLog BME280_42356:temperature:0:
#myDbLog BME280_42356:airpressure_sealevel:0::$val=$val>1100?1100:$val<800?800:$val
#myDbLog BME280_42356:humidity:0::$val=$val/2-10
plot "<IN>" using 1:2 axes x1y1 title 'Temperatur' ls l0 lw 1 with lines,\
"<IN>" using 1:2 axes x1y2 title 'Luftdruck' ls l1 lw 1 with lines,\
"<IN>" using 1:2 axes x1y1 title 'Feuchte' ls l2 lw 1 with lines
Dabei wird der Luftdruck auf einen plausiblen Bereich eingegrenzt, Drücke außerhalb dieses Bereichs hat es noch nicht gegeben auf diesem Erdball (:$val=$val>1100?1100:$val<800?800:$val). Der Wert der Feuchte wird dem zu erwartenden Temperaturbereich (-10 bis 40) angeglichen, das ergibt eine schönere Kurve (:$val=$val/2-10). Eine Y-Achse für die Feuchte wäre ja die dritte Y-Achse, die haben wir also nicht. Also landet 100% Feuchte bei 40°C und 0% Feuchte bei -10°C. Dann wird der Plot definiert. In diesem Fall mit den Daten aus DbLog, für FileLog muss geringfügig angepaßt werden. Dann fehlten freilich auch die FileLog -Definitionen für 'BME280_42356'.
defmod SVG_BME280_42356 SVG myDbLog:SVG_BME280_42356:HISTORY
Der Term 'SVG_BME280_42356' liest dabei die Definitionen aus der vorher angelegten Datei 'SVG_BME280_42356.gplot'.
FIN
Viel Spaß bei den eigenen Experimenten.