Tasten über Interrupt und "Drückdauer"

Vielen Dank für die nähere Darstellung. Ein paar Dinge davon waren mir bekannt ... 1 Byte = 8 Bit, von rechts zählen ... aber ich habe es halt noch nie angewendet und bin sehr froh, dass ich hier nun die Gelegenheit bekomme, diese Lücken zu füllen.
Konnte es nicht abwarten und habe gerade mal das Board angeschlossen (Im Büro :astonished:) und geladen. Ne, funzt nicht, Rechte LED ist Dauerlicht und der Monitor meldet Garagentorfunktion und 1. Hier habe ich nun keine Gelegenheit mich dran zu machen, am Wochenende wird es hoffentlich passen .....

odiug:
Rechte LED ist Dauerlicht und der Monitor meldet Garagentorfunktion und 1. Hier habe ich nun keine Gelegenheit mich dran zu machen, am Wochenende wird es hoffentlich passen .....

Thank god it's friday!

Die Fehlfunktion könnte durch einen falsch gesetzten pinMode für die Buttons bedingt sein.

Grundsätzlich können Buttons ja auf zwei Arten angeschlossen werden:

Button mit PullDown-Widerstand ==> LOW bei nicht gedrücktem Button, HIGH bei gedrücktem Button
Button mit PullUp-Widerstand ==> HIGH bei nicht gedrücktem Button, LOW bei gedrücktem Button

Mein Sketch ist auf beide Anschlussfälle vorbereitet, aber die Definition von INPUTTYPE muß ggf. geändert werden, damit die Schaltlogik stimmt.

Für Vewendung von PullUp-Widerständen beim Anschließen der Buttons:
#define INPUTTYPE INPUT_PULLUP

Für Vewendung von PullDown-Widerständen beim Anschließen der Buttons:
#define INPUTTYPE INPUT

Ich schätze mal, Du verwendest PullDown-Widerstände und hast es im Quellcode nicht geändert.

In dem Fall erkennt das Programm nämlich alle Buttonzustände genau vertauscht, d.h. bei unbetätigten Buttons werden alle Buttons als gleichzeitig gedrückt erkannt, was bedeutet, dass auch der Links- und der Rechtsbutton geleichzeitig als gedrückt erkannt werden, was dann sofort beim Programmstart die Garagentoröffnerfunktion auslöst. Ändere also auf:
#define INPUTTYPE INPUT
wenn Deine Buttons tatsächlich mit PullDown-Widerständen beschaltet sind.

Ja ich verwende Pull-Down Widerstände. Hatte dies dann aber auch im Sketch geändert und siehe da, die Garagentormeldung blieb aus. Die Rechte Lampe ist aber immer noch im Betrieb (egal welche Taster) . Kann mich aber im Büro nicht voll auf das Thema stürzen ... Der Monitor lässt auf jeden Fall nun die 0 schön durchlaufen.

odiug:
Der Monitor lässt auf jeden Fall nun die 0 schön durchlaufen.

Wenn Du den Sketch in Betrieb nimmst, gehst Du am besten so vor:

Zunächst mal von der aukommentierten Zeile die Kommentarstriche entfernen, also statt:
// Serial.println(keystate,BIN);
setze:
Serial.println(keystate,BIN);

Dann den Sketch mit dem seriellen Monitor laufen lassen und sehen, welche Werte durchlaufen.

Falls kein Button gedrückt, sollten nur einzelne Nullen durchlaufen.
0
0
0
0

Falls BUTTONLEFT gedrückt ist, sollten sich die durchlaufenden Werte ändern in:
100010
100010
100010
100010

Falls BUTTONRIGHT gedrückt ist, sollten sich die durchlaufenden Werte ändern in:
10001
10001
10001
10001

Falls BUTTONSTOP gedrückt ist:
1000100
1000100
1000100
1000100

Falls BUTTONWARN gedrückt ist:
10001000
10001000
10001000
10001000

Wenn das nicht in Ordnung ist: Verkabelung der Buttons prüfen!
Wenn es in Ordnung ist: Die Kommentarstriche wieder vor die Zeile setzen, Sketch neu kompilieren und hochladen.

Ja, die Printfunktion habe ich zur Zeit aktiviert. Die 0 läuft auch durch und die Tasten werden auch richtig gemeldet.

Das Prinzip 4 Bits nach links schieben und dann die die 4 rechten Bits neu einlesen (setCurrent Keystate) auch klar. Dann gehts ab in die handleStateChange-Routine und mal schauen welches Bit sich geändert hat .. Soweit habe ich dies schon verstanden.
ShortButtonPress muss ich noch einmal nachvollziehen, da hat es noch nicht klick gemacht ...

Aber: Wenn ich das Bord einschalte / resette, dann bltzt kurz die linke LED auf und die recht geht auf Dauerlicht. Der Monitor behinnt mit einer 0, dann kommt eine 1 und dann läuft er mit 0 endlos weiter und meldet eventuell gedrückte Tasten wie beschrieben.
Wenn ich richtig verstande habe, bedeutet die ausgegebene 1, dass das erste - also rechte Bit - beim zweiten Durchlauf (erst 0 dann 1) den Wert 1 erhält. Wert 1 für Bit 0 bedeutet rechtet LED ein.

Soweit bin ich schon mal. Morgen werde ich mich mal ganz intensiv damit beschäftigen und den Ablauf verlangsamen und an diversen Stellen Monitorausgaben einbauen, damit der Ablauf deutlicher wird ...

Macht echt Spass und ich danke Dir recht herzlich für Deine "Betreuung".

odiug:
Das Prinzip 4 Bits nach links schieben und dann die die 4 rechten Bits neu einlesen (setCurrent Keystate) auch klar. Dann gehts ab in die handleStateChange-Routine und mal schauen welches Bit sich geändert hat .. Soweit habe ich dies schon verstanden.
ShortButtonPress muss ich noch einmal nachvollziehen, da hat es noch nicht klick gemacht ...

Das mit dem kurzen Tastendruck funktioniert im Prinzip so:
Die Blinkfunktion fängt beim Drücken des Buttons sofort an zu blinken und merkt sich die Zeit, zu dem die Blinkfunktion eingeschaltet wurde. Dadurch braucht das Ende eines Tastendrucks gar nicht abgewartet werden, also ob es ein kurzer oder langer Tastendruck ist, es blinkt einfach beim Drücken der Taste sofort los.
Erst während des Blinkens wird dann regelmäßig nachgeschaut, ob die Taste, die für das Starten des Blinkens verantwortlich war, innerhalb einer bestimmten Frist nach Blinkbeginn wieder losgelassen wurde. Wenn das der Fall ist, wird das Blinken nach einer bestimmten Blinkdauer automatisch beendet.

odiug:
Soweit bin ich schon mal. Morgen werde ich mich mal ganz intensiv damit beschäftigen und den Ablauf verlangsamen und an diversen Stellen Monitorausgaben einbauen, damit der Ablauf deutlicher wird ...

Das mit der "1" am Anfang des Sketches ist merkwürdig.
Vielleicht solltest Du als letzte Zeile in der setup() Funktion noch ein kurzes delay() einfügen, also

void setup
{
....
delay(25); // letzte Zeile im setup
}

Ansonsten brauchst Du, wenn die Tastenfunktion wie gewünscht funktioniert, nur diese Zeile wieder auskommentieren, indem Du die Kommentarstriche wieder davorsetzt und den Sketch dann hochlädst:
// Serial.println(keystate,BIN);

Dann sollten nur noch die Blinkaktionen als Debug-Meldungen erscheinen, dabei gilt:
r - rechte Blinkerlampe aus
R- rechte Blinkerlampe an
l - linke Blinkerlampe aus
L - linke Blinkerlampe an

Das kann man eigentlich per Auge nachverfolgen, ggf. die Blinkdauer etwas verlängern.
Und zum Anhalten der Ausgabe einfach zeitweise das "automatisch Scrollen" im seriellen Monitor abhaken.

Ich selbst habe den Sketch nur mit dem seriellen Monitor getestet, nicht mit Blinklampen.
Wenn sich tatsächlich eine Diskrepanz zeigt zwischen den mit r,R,l,L angezeigten Blinkaktionen und der Lampenschaltung, müßte ich mal selbst probeweise LEDs dranhängen und nachsehen, ob da noch ein Kinken drin ist.

Wenn Du die Debug-Ausgaben für den "keystate" mal auskommentiert hast, siehst Du dann die Blinkaktionen, z.B. für Linksblinken dann so im seriellen Monitor:
L
l
L
l
L
l
oder Rechtsblinken so
R
r
R
r
R
r
oder Warnblinken so
L
R
l
r
L
R
l
r
etc. und das ganze im Takt der Blinkfrequenz fortlaufend?

Also ein delay() im setup hat nichts bewirkt. Ich hatte es sogar auf 1250 gesetzt ... egal, nur mal am Rande!

Habe die von Dir genannte Zeile auskommentiert und die Tasten probiert. Die Linke funktioniert wie dargestellt l, L, l, L ....
Bei der rechten Taste kommen nur RRRRRRRRRR... und zwar rasend schnell. Bei der Warntaste kommt L, l, RRRRRRRRRRRRRR, L,l, ...
Irgendwas ist bei rechter Taste also noch nicht ok .... Habe jetzt ein halbes Stündchen Zeit, mal sehen ob ich dahinter komme.

odiug:
Irgendwas ist bei rechter Taste also noch nicht ok

Hast Du die Schaltung erstmal auf einem Steckbrett aufgebaut?
Oder verwendest Du gleich die Schalter am Motorrad?

Prüfe mal den PullDown-Widerstand (Widerstandswert, Anschluss, Spannungspegel) am rechten Taster!
Der rechte Taster ist bestimmt nicht sauber auf LOW, wenn der gleich am Start als HIGH ausgewertet wird!

Ja, auf einem Steckbrett. Habe die Schalter einfach mal getauscht (PIN am Board), es ändert sich nichts.

Ich habe nun mal "Meldungen" und Zähler in die Routinen eingebaut. Wenn loop einmal läuft, dann läuft die Routine "handleBlinkState" 2x. Und beim ersten Durchgang nach reset gibt der Monitor mir die Meldung "l" (.. soll der linke Blinker an sein ...) aus, dann nicht mehr.
Beim Start - mit einem delay 3000 - extrem verzögert, sind auch zunächst beide Leds an. Die linke geht dann nach dem ersten loop aus, die Rechte nicht. Jetzt werde ich mir mal die Werte in der Routune ausgeben lassen (wenn die Frau das Essen noch nicht fertig hat :slight_smile:

Durch diverse delays() und printeln() kann ich nun folgendes Verhalten beschreiben:

Beim Start des Sketch sind beide LEDs High.
Dann werden die Routinen "blinkTask()", "setCurrentKeystate()", "handleStateChange" durchlaufen. Bis hier leuchten
immer noch beide LEDs. Dann gehts in die Routine "handleBlinkState". Hier schaltet sich die Linke LED ab, die Rechte leuchtet weiter. Der Zähler in dieser Routine erhöht sich immer um 2, in den anderen Routinen um 1.
Morgen dann mehr ...

odiug:
Beim Start des Sketch sind beide LEDs High.

Schaltest Du die LEDs über Relais, die auf "ative LOW" schalten (z.B, mechanische Relais mit Optokopplern)?

In dem Fall sollte aber eigentlich auch nur die Blinklogik vertauscht sein, d.h. zu Anfang wären dann beide Blinker auf Dauerlicht an. Beim Einschalten blinkt es und beim Ausschalten wären wieder beide Blinker auf Dauerlicht an. Aber das ist es ja auch nicht.

Ich kann das von Dir beschriebene Verhalten hier nicht nachvollziehen und müßte mir bei Gelegenheit mal die Bauteile in der Grabbelkiste zusammensuchen und so zusammenstecken wie Du den Aufbau hast, mit PullDown-Widerständen an den Buttons.

Nachtrag vom 13.10.: Auch nachdem ich die Schaltung mit Tastern und PullDown-Widerständen auf Steckbrett aufgebaut habe, kommt nichts anderes dabei heraus als vorher: Das gepostete Programm funktioniert völlig einwandfrei. Einzige notwendige Änderung ist der INPUTTYPE auf "INPUT" statt auf "INPUT_PULLUP", wenn man PullDown-Widerstände an den Buttons verwendet statt PullUp-Widerstände.

Deshalb bleibt es bei meinem oben geposteten Ratschlag:
Prüfe mal den PullDown-Widerstand (Widerstandswert, Anschluss, Spannungspegel) am rechten Taster!
Der rechte Taster ist bestimmt nicht sauber auf LOW, wenn der gleich am Start als HIGH ausgewertet wird!
Schaltung überprüfen! Eventuell mal den Pulldown-Widerstand genau ausmessen oder den Button austauschen.
Oder testen, ob der rechte Button einen Wackelkontakt hat. Wenn man den "keystate" mal durchlaufen läßt, bei unbetätigter Taste und auch mal längere Zeit bei gedrückter Taste, dann sollte man "Ausreißer" im keystate eigentlich im seriellen Monitor leicht identifizieren können.

Sorry, aber ich widerspreche Dir. Der Grund muss ein anderer sein ....

Ich habe heute deinen Sketch mit weiteren Ausgaben und delays versehen.
Mit Start des Programmes leuchten beide LEDs. Das Programm kommt dann zur Routine "handleBlinkState". Bis zum if-Aufruf "Soll der linke Blinker gerade an sein?" leuchten beide LEDs. Nach Abarbeitung dieses If's geht die linke LED aus, es wird am Bildschirm ein " l" ausgegeben und der zweite Durchlauf beginnt. Dann wird keine Meldung mehr ausgegeben. Ich kann gerne eine Video bei YouTube posten ...

Ich habe heute dann meinen Interrupt-Sketch geladen, da werden alle Taster erkannt ohne dass der rechte Taster irgendwie anders als die anderen reagiert. Auch die LEDs gehen nicht beide direkt an.

Sorry, aber es muss was anderes sein.

Verwendest Du ein Due-Board?

odiug:
Sorry, aber es muss was anderes sein.

Verwendest Du ein Due-Board?

Ich habe noch nie einen DUE gehabt, zum Testen habe ich einen UNO und einen MEGA.

Lasse bei Dir mal folgenden Test-Sketch laufen:

#define LAMPELINKS 22
#define LAMPERECHTS 23

void setup() {
  Serial.begin(9600);
  pinMode(LAMPELINKS,OUTPUT);  
  pinMode(LAMPERECHTS,OUTPUT);  
  Serial.println("Sketch gestartet.");
  delay(10000);
  Serial.println("Nach 10 Sekunden:");
  digitalWrite(LAMPELINKS,HIGH);  
  digitalWrite(LAMPERECHTS,HIGH);  
}

void loop() {
}

Pin-Nummern für die beiden LEDs ggf. anpassen.

Nachdem die Meldung "Sketch gestartet" im seriellen Monitor angezeigt wird: Sind Deine LEDs dann an oder aus?

Und 10 Sekunden danach: Sind Deine LEDs dann an oder aus?

Die LEDs sind die ganze Zeit AN.
Direkt mit Start AN und nach 10 Sekunden auch noch AN.
Also scheint es so, dass die LEDs mit der Initalisierung direkt ein HIGH bekommen. Ich habe Deinen Test-Sketch geändert und aus dem HIGH ein LOW gemacht. Folge: Nach10 Sekunden schalten sich die LEDs auch AUS.

Ich habe jetzt im BLINKER Sketch diese beiden Zeilen im void setup() eingefügt:
digitalWrite( BLINKPINLEFT, LOW);
digitalWrite( BLINKPINRIGHT, LOW);
Nun bleiben beide LEDs beim Start aus. Die linke LED funktioniert mit der Linken-, der Warn- und der Stopp-Taste wie gewünscht.
Drücke ich die rechte Taste oder die Warn-Taste, dann geht die rechte LED an, aber blinkt nicht und geht auch nicht mehr aus. Egal welche Taste (Stopp, Warn etc.) ich dann drücke, ein Dauerlicht halt ... bis zu einem Reset.

odiug:
Die LEDs sind die ganze Zeit AN.
Direkt mit Start AN und nach 10 Sekunden auch noch AN.
Also scheint es so, dass die LEDs mit der Initalisierung direkt ein HIGH bekommen.

OK, also macht der DUE einiges anders, denn alle übrigen Arduinos sind nach dem Starten LOW an den Pins.

odiug:
Ich habe Deinen Test-Sketch geändert und aus dem HIGH ein LOW gemacht. Folge: Nach10 Sekunden schalten sich die LEDs auch AUS.

Ich habe jetzt im BLINKER Sketch diese beiden Zeilen im void setup() eingefügt:
digitalWrite( BLINKPINLEFT, LOW);
digitalWrite( BLINKPINRIGHT, LOW);
Nun bleiben beide LEDs beim Start aus. Die linke LED funktioniert mit der Linken-, der Warn- und der Stopp-Taste wie gewünscht.

Das hört sich schon mal besser an als vorher.

Da die Betaversion der Arduino-Softare für den DUE immer noch einige Bugs enthält (und gerade deshalb noch "BETA" ist), mußt Du bei Verwendung des DUE offenbar einige Bugs umschiffen. Ein Bug der DUE-BETA-Version betrifft auch den von mir geposteten Sketch und ist hier mit Workaround beschrieben:
http://forum.arduino.cc/index.php?topic=185291.0

Man kann offenbar den Status von als OUTPUT gesetzten Pins beim DUE nur dann zuverlässig auslesen, wenn der Pin vorher einmal als INPUT gesetzt war.

Mein Vorschlag wäre also, den im Link genannten Workaround zu verwenden, in der setup-Funktion die Ausgänge erstmal auf INPUT und danach erst auf OUTPUT und dann am Ende nochmal LOW, damit die Ausgänge LOW und die LEDs bei Programmstart aus sind:

void setup() 
{
#ifdef DEBUG  
  Serial.begin(9600);
#endif
  pinMode(BUTTONLEFT, INPUTTYPE);
  pinMode(BUTTONRIGHT, INPUTTYPE);
  pinMode(BUTTONSTOP, INPUTTYPE);
  pinMode(BUTTONWARN, INPUTTYPE);
  // Die beiden LED-Pins auf digitalen Output schalten
  pinMode(BLINKPINLEFT, INPUT);
  pinMode(BLINKPINLEFT, OUTPUT);
  digitalWrite(BLINKPINLEFT, LOW);
  pinMode(BLINKPINRIGHT, INPUT);
  pinMode(BLINKPINRIGHT, OUTPUT);
  digitalWrite(BLINKPINRIGHT, LOW);
}

Irgendwelche Änderungen im Ablauf?

TREFFER UND VERSENKT :smiley:

Jau, das war es .... Nun funz't es ....
Ich denke, Du hast schon an mir gezweifelt - vielen Dank für Deinen unermüdlichen Einsatz.
Der Hinweis mit dem DUE-Board war dann ja doch nicht so ganz verkehrt.

Jetzt werde ich mich noch etwas mit dem "&variable" (Pointer) beschäftigen, damit dann alles sitzt.
Da ich ja auch die Byte-Schieberei bislang vermieden habe :roll_eyes: muss ich noch einmal fragen:
Wenn ich einByte abfrage, dann wird mir immer ein "0b" mitgeliefert?

Dann werde ich mich nun mal an den Gyrosensor und das Display machen .... 8)

odiug:
Jau, das war es .... Nun funz't es ....

Na bravo!

odiug:
Ich denke, Du hast schon an mir gezweifelt - vielen Dank für Deinen unermüdlichen Einsatz.
Der Hinweis mit dem DUE-Board war dann ja doch nicht so ganz verkehrt.

Ja, zeitweise schon. Aber dass der DUE doch so große Unterschiede zu den Standard-Arduinos aufweist, wenn man "nur" die Befehle der Arduino-Software verwendet, war mir vollkommen neu. Z.B. dass Pins nach dem Setzen auf OUTPUT im setup() HIGH statt LOW sind, oder dass man den Zustand von als OUTPUT gesetzten Pins nur dann sicher abfragen kann, wenn der Ausgang vorher einmal explizit als INPUT gesetzt war. Na ja, hinterher ist man schlauer.

odiug:
Jetzt werde ich mich noch etwas mit dem "&variable" (Pointer) beschäftigen, damit dann alles sitzt.

Du meinst mit dem &-Adressoperator bei den Parametern in den Funktionsdeklarationen?

Der Unterschied ist der:
Wenn die Variablen "normal" deklariert sind, sind sie "call-by-value", die Funktion erhält eine Kopie der originalen Variablen übergeben. Die Funktion kann innerhalb der Funktion diese Variablen ändern, aber an der Stelle wo die Funktion aufgerufen wurde, wird das Original des Parameters NICHT geändert. Es wird ja in der Funktion tatsächlich mit einer Kopie des Parameters gearbeitet und nicht mit dem Original-Parameter.

Wenn die Variable in der Parameterliste mit dem &-Adressoperator deklariert ist, ist es "call-by-reference". Die Funktion bekommt dann keine Kopie des Parameters übergeben, sondern einen Referenz-Verweis auf den originalen Parameter. Wenn dieser Parameter innerhalb der Funktion verändert wird, wird der tatsächliche originale Parameter gleichzeitig an der Stelle geändert, von wo aus der Funktionsaufruf mit diesem Parameter erfolgte. Das benutzt man für Parameter "die innerhalb einer Funktion von der Funktion änderbar" sein sollen.

odiug:
Da ich ja auch die Byte-Schieberei bislang vermieden habe :roll_eyes: muss ich noch einmal fragen:
Wenn ich einByte abfrage, dann wird mir immer ein "0b" mitgeliefert?

Nein, es wird nichts "geliefert". Eine Zahl ist eine Zahl und kann auf verschiedene Arten dargestellt werden. Man kann z.B. eine Zahl als normale Dezimalzahl schreiben:
byte zahl=255;
Oder dieselbe Zahl als Hexedezimalzahl:
byte zahl=0xFF;
Oder dieselbe Zahl als Binärzahl:
byte zahl=0b11111111;

Das "0b" ist ein Präfix (quasi "Vorsilbe") und bedeutet, dass die nachfolgende Zahl als Binärzahl interpretiert werden soll.
Also genau so wie das Präfix "0x" bedeutet, dass die nachfolgende Zahl als Hexadezimalzahl interpretiert werden soll.

Es handelt sich als nur um einen Hinweis für den Compiler: Steht eine Zahl ohne Präfix da, soll er sie als Dezimalzahl behandeln, mit Präfix "0x" vor der Zahl als Hexadezimalzahl und mit Präfix "0b" als Binärzahl.

Die Darstellung als Binärzahl im Quelltext habe ich nur gewählt, weil man bei dieser Darstellung sehr gut die gesetzten und nicht gesetzten Bits in der Zahl erkennen kann.

Was ich durch dieses Projekt schon von Dir lernen konnte .... einfach genial und vielen, vielen Dank für Deine geduldigen Erklärungen.
Wenn Du mal Langweile haben solltest, kannst Du ja mal 2 Tasten programmieren, die eine blinkende LED durch Interrupt erzeugen bzw. unterbrechen. Wenn DU das machst, bin ich mir sicher es auch hinterher zu verstehen.

@Jurs
Ich bin gerade über Deinen Sketch gestolpert - und der erfüllt genau das, was ich suche. Ich habe in in die Arduino Ide kopiert beim kompilieren kommt die Fehlermeldung, dass blinkPhase nicht definiert ist. Da ich die Funktion Garagentor nicht benötige, habe ich die ausgeklammert ohne dass es etwas gebracht hätte. Kann es an der Verwendung der neuesten IDE liegen?

Danke und Grüße aus Hamburg

Jörg