(gelöst) Raspberry: Interrupt am GPIO mit MCP23017 - wie longpress auswerten?

Begonnen von ThomasRamm, 24 Februar 2015, 11:40:54

Vorheriges Thema - Nächstes Thema

ThomasRamm

Ich hatte die Frage zuerst im falschen Bereich gestellt, hier passt er besser hin

Hallo,
ich habe einen MCP23017 und den INTA an GPIO21 um den Interrupt auszuwerten.
Der Eingang wird auch imer korrekt auf on/off gesetzt.
Was nicht funktioniert ist das auswerten von longpress, da dieser Wert immer off ist.
Beim drücken des Tasters wird der state ganz kurz auf high und dann immer sofort wieder auf off gesetzt, beim loslasen des tasters das gleiche.
Damit funktioniert zwar der Taster (status wird ja korrekt ausgelesen), aber der Longpress ist halt auch immer off.
Wie muss ich meine Konfiguration ändern um den Longpress auswerten zu können?

#####RPI_GPIO#####
define INTA RPI_GPIO 21
attr INTA active_low no
attr INTA direction input
attr INTA interrupt both
attr INTA pud_resistor off
attr INTA userReadings test {fhem ("get myMcp20")}

#####RPII2C#####
define myI2C RPII2C 1

#####Modul MYMCP23017#####
define myMcp20 I2C_MCP23017 0x20
attr myMcp20 IODev myI2C
attr myMcp20 Interrupt A0,A1,A2,A3,A4,A5,A6,A7,B0,B1,B2,B3,B4,B5,B6,B7
attr myMcp20 InterruptOut connected_active-high
attr myMcp20 Pullup A0,A1,A2,A3,A4,A5,A6,A7,B0,B1,B2,B3,B4,B5,B6,B7
attr myMcp20 invert_input A0,A1,A2,A3,A4,A5,A6,A7,B0,B1,B2,B3,B4,B5,B6,B7


Gruß
Thomas

klausw

Hallo Thomas,

longpress ist eine Funktion des GPIO Moduls.
Du kannst sie also nur verwenden, wenn du den GPIO direkt als Eingang verwendest.
In Deiner Konfiguration nutzt Du den GPIO aber lediglich um im Interruptfall des MCP23017 dessen Register auszulesen.
Der INTx Ausgang des MCP23017 beleibt nur gesetzt, bis die Portregister ausgelesen werden.

Lösung 1:
Direkt den GPIO von Raspberry als Eingang nutzen.

Lösung 2:
Ein notify für dein myMcp20 anlegen, in dem im Fall eines on ein Timer (z.B. 2s) gesetzt wird und im Fall eines off dieser Timer gelöscht wird falls er existiert.
Der Timer erzeugt bei Ablauf ein Reading Longpress (z.B. in einem Dummy)

define act_on_myMcp20 notify myMcp20 {\
my $var =  ReadingsVal("myMcp20","PortA0",off);;\
if ($var eq "off") {\
fhem ("delete lpress_timerA0") if (Value("lpress_timerA0") ne "");;\
    fhem "setreading Longpressdummy longpress off";;\
} elsif ($var eq "on") {\
fhem "define lpress_timerA0 at +00:00:02 setreading Longpressdummy longpress on";;\
}\
}

Ist nicht getestet, sollte aber so gehen ... den Longpressdummy noch anlegen


PS: userReadings kannte ich noch gar nicht, kann man es wie ein notify verwenden?

RasPi B v2 mit FHEM 18B20 über 1Wire, LED PWM Treiber über I2C, Luchtdruck-, Feuchtesensor und ein paar Schalter/LED\'s zum testen
Module: RPI_GPIO, RPII2C, I2C_EEPROM, I2C_MCP23008, I2C_MCP23017, I2C_MCP342x, I2C_PCA9532, I2C_PCF8574, I2C_SHT21, I2C_BME280

ThomasRamm

Danke für deine Antwort,
der Denkanstoß hatte mir wohl gefehlt.
Im nachhinein ist es mir auch absolut logisch das der Interrupt beim auslesen des GPIO wieder zurückgesetzt wird um für den nächsten Eingang "bereit" zu sein.

Werde deinen Vorschlag so umsetzen.

zu den userReadings:
Ja ist ein "notify" ohne das man ein weiteres "Device" anlegen muss.
Ich finde es kompakter und übersichtlicher als einen weiteren notify in einem extra Raum zu haben (Auf der standard Oberfläche will ich den nicht sehen, ist nicht Familientauglich  ;) ).
Zitat aus der commandref: 
Zitatbenutzerdefinierte Readings werden bei jeder Aktualisierung der Gerätereadings gesetzt, indem das spezifizierte perl code { <perl code> } ausgeführt wird, und dessen Wert dem Reading zugewiesen wird. Falls <trigger> spezifiziert ist, dann findet diese Ausführung nur dann statt, falls einer der aktualisierten Readings dem regexp <trigger> entspricht (matched).

f.f

hallo, ich schieb das hier noch mal hoch, da ich gerade eine ähnliche Situation realieren will und nicht richtig weiter komme.

ich habe inputs (2 Taster) und Outputs (Jalousie relais rechts und Jalousie relais links) links via mcp23017 und Relaisplatine angeschlossen und
ich will folgende Situation nachbilden, die ich bisher über SPS realisiert habe.

drücke ich einen der Taster für weniger als 2s folgen die Ausgänge 1:1, d.h. die Eingänge sind direkt "durchgeschleift". ich nutzte dass um die Lamellenstellung einzustellen.
drücke ich länger als 2s, soll die Jalousie komplett für in die jeweilige Endstellung (da es hier keinen Sensor gibt, soll einfach die Ausgang 60s gehalten werden)
Es gibt auch noch eine Komfortfunktion beim Zufahren: drückt man den selben Taster nochmal, wenn die Jalousie schon im automatischen Zulasuf ist (also nachdem die Taste bereits länger als 2s gedrücktwar und wieder losgelassen wurde), wird nochmals ein kurzer Puls (1s) in die Gegenrichtung ausgelöst, damit die Jalousien zwar unten sind aber nicht "zu" sondern aufgeklappt.
Beim Hochfahren braucht man diese Funktion natürlich nicht.
Die Autofahrten soll natürlich jederzeit mit Druck auf den anderen Richtungstaster gesoppt werden können und die Timer ggf. zurückgesetzt.

in der SPS konnte ich das damals relativ leicht realisieren und auch mit C oder .net ist das kein Problem, aber mit den verschachtelten DOIFs  und den mir noch nicht ganz klaren Scope von Variablen in fhem tue ich mir noch verdammt schwer. Da es hier ja vor Kommas, Doppelkommas etc. nur so wimmelt verlier ich gerade völlig den überblich welche unterschachtel in welcher unterschachtel von welcher unterschachtel liegt....
gibt es nicht die  Möglichkeit mit einem Ereignis (Tastendruck) einen globalen timer zu Starten, den ich dann einfach von einem komplett anderen Stelle (Subroutine/Event) auslesen und vergleichen kann. Auch für andere Variablen wäre das extrem nützlich.

Gruss

klausw

Zitat von: f.f am 18 September 2017, 16:57:52
ich habe inputs (2 Taster) und Outputs (Jalousie relais rechts und Jalousie relais links) links via mcp23017 und Relaisplatine angeschlossen und
ich will folgende Situation nachbilden, die ich bisher über SPS realisiert habe.

...
in der SPS konnte ich das damals relativ leicht realisieren und auch mit C oder .net ist das kein Problem, aber mit den verschachtelten DOIFs  und den mir noch nicht ganz klaren Scope von Variablen in fhem tue ich mir noch verdammt schwer. Da es hier ja vor Kommas, Doppelkommas etc. nur so wimmelt verlier ich gerade völlig den überblich welche unterschachtel in welcher unterschachtel von welcher unterschachtel liegt....
gibt es nicht die  Möglichkeit mit einem Ereignis (Tastendruck) einen globalen timer zu Starten, den ich dann einfach von einem komplett anderen Stelle (Subroutine/Event) auslesen und vergleichen kann. Auch für andere Variablen wäre das extrem nützlich.

Zu DOIF kann ich nichts sagen. Allerdings vermute ich, das notify die direktere Lösung ist.
Dort kannst du direkt perl Code schreiben, der nicht soo verschieden zu C ist.
Semikolons gibts da nur am Zeilenende  8)

Ein notify sollte reichen. Dieses kann zum einen die Longpress Erkennung erledigen.
Weiterhin kannst du über das notify im jeweiligen Tasterdevice ein Reading mit dem aktuellen Zustand ablegen (also Stillstand, hochfahren, runterfahren)
Abhänging von diesem Reading und der Länge des Tastendrucks führst du dann die passenden Aktionen aus.
Das sollte mit einer if elsif ... else Schleife lösbar sein, ohne tiefere Veschachtelungen.
Einen globalen Timer (at) kannst du direkt aus dem notify generieren.

Wenn du noch die Position haben möchtest gibt es im Forum wohl ein Modul. Darauf wurdest du ja schon im anderen Thread aufmerksam gemacht  ;). Alternativ sollte das auch über das notify gehen (die Readings haben auch Zeitstempel mit denen man arbeiten kann). Das ist halt alles nicht allzu genau. Da solltest du bei auf/zu regelmäßig automatisch resetten.



RasPi B v2 mit FHEM 18B20 über 1Wire, LED PWM Treiber über I2C, Luchtdruck-, Feuchtesensor und ein paar Schalter/LED\'s zum testen
Module: RPI_GPIO, RPII2C, I2C_EEPROM, I2C_MCP23008, I2C_MCP23017, I2C_MCP342x, I2C_PCA9532, I2C_PCF8574, I2C_SHT21, I2C_BME280

ThomasRamm

Ich habe für mich ein neues Modul geschrieben TASTER.
Das kümmert sich um die Zeitmessungen etc.
4 Aktionen können pro Taste ausgewertet werden:

Tastendruck
Langer Tastendruck
Doppelter Tastendruck
gedrückt

Ich nutze insbesondere den Tastendruck und den doppelten Tastendruck. gedrückt werte ich gar nicht aus (nur auf der Oberfläche wird das Symbol rot so lange ein Taster gedrückt ist).
Die Zeiten ab wieviele sekunden eine Taste als langer Tastendruck ausgewertet wird, wird als Attribut festgelegt, das gleiche für den doppelten Tastendruck.
Als Attribut wird dann auch die aktion hinterlegt die bei einem Tastendruck ausgeführt werden soll.

Beispiel von mir, ich habe zwei Taster nebeneinander, mit einem einfachen Click wird das Licht geschaltet, mit einem doppelten mein Rollo.
Vereinfacht habe ich geschrieben

taste1_click_define = Licht1 toggle
taste1_doppelclick_define = "wenn rollo status = drive_up dann rollo stop sonst rollo drive_up
taste2_click_define = Licht2 toggle
taste2_doppelklick_define = "wenn rollo status = drive_down dann rollo stop sonst rollo drive_down

Natürlich muss ich auch noch if Befehle schreiben, evtl sogar verschachteln, aber alle Bedingungen die mit dem Taster zu tun haben sind dabei schon mal weg, so das das ganze wesentlich einfacher ist.

eine genaue Beschreibung findest du auf GitHub

installieren kannst du das Modul über update add https://raw.githubusercontent.com/ThomasRamm/fhem-taster/master/controls_taster.txt

f.f

Danke für die Tipps. Ich hab mich gestern mal an eine eigene "Bastelllösung" gemacht, da ich zwar für jede Lösung "von der Stange" dankbar bin, aber gerade jetzt am Anfang gerne alles auch vertstehen würde, damit ich bei späteren und komplexeren Sachen nicht Tage brauche um eine Zeile zu schreiben.

Ich habe es also so realisiert, wie ich es auch in anderen Sprachen programmiert hätte. Die logik ist ja trivial, nur mit der (für mich extrem) ungewohneten Schreibweise habe ich noch meine Problem.
Des Weiteren musste ich oft lange "probieren" wie ich welche variablen in notify Prozeduren von Devices abfrage, damit rechne und sie dann wieder z.b. über fhem{..} zurückschreibe.
Ich habe hier mir die unix zeit  {time} in sekunden bei verschiedenne Ereignissen über notify in die dummys state geschrieben und dann mit den dummys gerechnet. Das hat zwar funktioniert, allerdings brauche ich das ganze irgendwann (wenn um dimmer geht) im Subsekundenbereich. Wie bekomme ich das mit Millisekunden hin? Bzw. wie kann ich in FHEM mit Zeitsempeln rechnen? Ich habe irgendwo gelesen, dass man den normalen Timestamp auf Millisekunden erweitern kann, aber wie rechne ich mit timestamp, bzw. wie/wo in einer dummy lege ich den hin, damit er "rechenbar" ist und ich nicht erst einen string parsen muss (dazu brauch ich nämlich mit meinen momentanen perl Kenntnissen Ewigkeiten).
In .net geht das ja spielend einfach, wenn man zwei  system.datetime variablen mit "now" zum jeweiligen ereigniszeitpunkt belegt, lässt sich ein typ timespan deklarieren und man bildet einfach die differenz aus den zeitvariablen in diese ab. Es muss doch hier was ähnliches geben, oder?

Gruss