Hallo,
ich arbeite gerade an einem Zahlenschloss und habe leider bereits einen
Fehler im Code den ich einfach nicht verstehe..
In dem Code sollen einfach die gedrückten Taster abgefragt werden.
1
intPasswortArray[5]={5,4,3,2,1};
2
intPasswortArray2[5];
3
4
5
intzahlhalter1=0;
6
intzahlhalter2=0;
7
intzahlhalter3=0;
8
intzahlhalter4=0;
9
intzahlhalter5=0;
10
intzahlhalter6=0;
11
12
intzaehler2=0;
13
intzaehler=0;
14
inttest=0;
15
intpineins=7;
16
intpinzwei=6;
17
intpindrei=5;
18
intpinvier=4;
19
intpinfuenf=3;
20
intpinsechs=2;
21
22
intprelltime=300;
23
intprogram=0;
24
25
intledgruen=12;
26
intledrot=11;
27
28
booleanresult;
29
30
31
voidsetup(){
32
33
pinMode(pineins,INPUT_PULLUP);
34
pinMode(pinzwei,INPUT_PULLUP);
35
pinMode(pindrei,INPUT_PULLUP);
36
pinMode(pinvier,INPUT_PULLUP);
37
pinMode(pinfuenf,INPUT_PULLUP);
38
pinMode(pinsechs,INPUT_PULLUP);
39
pinMode(ledrot,OUTPUT);
40
pinMode(ledgruen,OUTPUT);
41
Serial.begin(9600);
42
43
}
44
45
voidloop(){
46
47
48
//Delay um Taster zu entprellen
49
50
51
do
52
{
53
delay(100);// warte darauf das Taster gedrückt wird
Ich bitte falsch deklarierte bzw. unnötige Variablen zu ignoieren ist ja
alles noch in der Testphase :)
Das Problem im Code ist nun das als gedrückter Taster immer eins
anzeigt, außer ich halte den Taster, dann zählt er brav bis vier hoch.
Woran liegts?
Paul schrieb:> Woran liegts?
Am "else if"...
Wie sieht deine Hardware aus? Wie sind die Taster angeschlossen? Welches
Signal gibt ein betätigter Taster ab?
Das mit dem Entprellen ist Murks. Und delay() ist MurksQuadrat.
Siehe den Beitrag "Arduino UNO | IF Schleife |"
Warum am else if?
Also die Taster sind an die Pins 7-4 mit einem Pullup Widerstand
angeschlossen. Taster LOW -->Aktion.
Wieso? Eingabe ist nicht zeitkritisch also warum soll ich die
Debounce-Libary verwenden?
Lese den Link gerade.
Paul schrieb:> Wieso? Eingabe ist nicht zeitkritisch also warum soll ich die> Debounce-Libary verwenden?
Weil dein Zeugs ja wohl recht offensichtlich nicht vernünftig
funktioniert. Sonst müsstest du ja nicht fragen.
Eine ordentlich und vernünftige Entprellung bzw. Erkennung eines
Tastendrucks ist Voraussetzung für alles weitere. Egal wie die
Flankenerkennung bzw. Entprellung dann tatsächlich realisiert ist. Aber
funktionieren muss sie.
Und sowas
1
do
2
{
3
delay(100); // warte darauf das Taster gedrückt wird
ist noch viel größerer Murks. Selbst wenn der Kommentar zum Code stimmen
würde.
Es wird nicht gewartet!
Deine loop() sieht als Pseudocode so aus
1
void loop()
2
{
3
if Taste 1 gedrückt
4
dann hänge eine 1 an die angegebene Kombination an
5
6
else if Taste 2 gedrückt
7
dann hänge eine 2 an die angegebene Kombination an
8
9
else if Taste 3 gedrückt
10
dann hänge eine 3 an die angegebene Kombination an
11
12
13
if angegebene Kombination ist gleich der vorgegebenen Kombination
14
dann öffne das Schloss
15
}
da wird nirgends gewartet. Alles was passiert ist: wenn ein Tastendruck
festgestellt wird, dann wird dieser Tastendruck 'ausgewertet', indem die
Zahl für die er steht an die bisherige Eingabe angehängt wird. Ist keine
Taste gedrückt, dann passiert auch nichts weiter, bis loop() das nächste
mal aufgerufen wird.
Weder muss es hier eine for-Schleife geben, die auf 6 Tasten wartet,
noch muss man auf das Loslassen einer Taste warten. Denn letzteres macht
die Erkennung eines Tastendrucks.
Und daher muss das Erkennen eines Tastendrucks (das Pedant zur
Fragestellung_ ist jetzt gerade eine Taste gedrückt) absolut zuverlässig
funktionieren. Und zwar auch für mehrere Tasten. Vorher hat es keinen
Sinn darauf aufbauend irgendetwas weiterführendes zu programmieren.
Was hat das delay mit dem restlichen Code zu tun? Nur weil er 300 ms
wartet bis der Taster nicht mehr prellt...
Karl Heinz schrieb:> Und sowas do> {> delay(100); // warte darauf das Taster gedrückt wird> }> while((digitalRead(pineins) == LOW) && (digitalRead(pinzwei) ==> LOW) && (digitalRead(pindrei) == LOW) && (digitalRead(pinvier) == LOW));> ist noch viel größerer Murks. Selbst wenn der Kommentar stimmen würde.>> Es wird nicht gewartet!
Warum soll nicht gewartet werden? Solange kein Taster gedrückt wird
kommt er ja nicht weiter sondern wiederholt die Schleife solange bis ein
Taster gedrückt wird..?
Okey, dann lasse ich die for-Schleife weg, aber wie überprüfe ich dann
ob der Code welcher Eingegeben wurde auch der ist welcher im Array
steht?
Der Code sollte später ebenfalls über die Taster neu programmiert werden
können.
Paul schrieb:> Warum soll nicht gewartet werden?
Weil damit Rechenzeit verplempert wird, die man irgendwann bitter nötig
braucht. Oder frage dich mal andersrum: warum sollte gewartet werden?
Lässt dich diese Aufgabe nicht ohne delay() lösen? Und bestenfalls "ganz
unten", direkt an der Hardware kann es sein, dass man mal eine oder zwei
us verplempern muss. Aber ein Delay im ms Bereich ist NIE nötig.
> Solange kein Taster gedrückt wird
Du bekommst es ja 300ms lang gar nicht mit, wenn da ein Taster gedrückt
wird (nicht mal ein Wagnerscher Hammer prellt so lange). Du machst
deinen uC absichtlich langsam und träge.
Du brauchst übrigens einfach eine "Flankenerkennung": ist eine Taste
gedrückt worden, dann gibt es genau einen Durchlauf der Auswertung.
Ich werde das Morgen am Rechner mal kurz runterschreiben, hier am Handy
ist mir das zu mühsam... ;-)
Den Code würde ich erst nach Eingabe aller Zeichen vergleichen.
sonst sieht man sofort, wenn man einen Fehler gemacht hat...
Normalerweise hätte man die Sicherheit von 1 000 000 Möglichkeiten...
Signalisiert man den Fehler sofort, hat man nur noch max. 60
Varianten...
Ansonsten sieht der Code für mich (als Anfänger) gut aus.
johann schrieb:> Den Code würde ich erst nach Eingabe aller Zeichen vergleichen.> sonst sieht man sofort, wenn man einen Fehler gemacht hat...
Richtig. Kleine Anpassung nötig... ;-)
1
intCode[5]={5,4,3,2,1};
2
intCodeeingabe[5];
3
intSchritt=0;
4
intTreffer=0;
5
intTaste=0;
6
7
8
...
9
10
// Umrechnen Taste in Zahl
11
Taste=0;
12
if(T1)Taste=1;
13
if(T2)Taste=2;
14
if(T3)Taste=3;
15
if(T4)Taste=4;
16
if(T5)Taste=5;
17
if(T6)Taste=6;
18
19
// Code abspeichern
20
Codeeingabe[Schritt]=Taste);
21
Schritt++;
22
23
}
24
}// tiAkt>tiEntprellen
25
26
if(Schritt==5){// geschafft: Code prüfen
27
for(Treffer=0,Schritt=0;Schritt<5;Schritt++)// einfach mal ansehen, wann was wie in der for-Schleife gemacht wird. Es lohnt sich... ;-)
28
if(Code[i]=Codeeingabe[i])Treffer++;
29
30
if(Treffer==5){// alle Zahlen richtig
31
Led=1;// grüne LED für 500ms einschalten
32
tiLED=tiAkt+500;
33
}else{// falsche Zahl
34
Schritt=0;
35
Led=2;// rote LED für 500ms einschalten
36
tiLED=tiAkt+500;
37
}
38
}
39
40
if(tiAkt>tiLED){// Zeit für LEDs abgelaufen?
41
...
42
}
Ich würde das Prüfen aber in der Realität mit einem memcmp() statt der
for-Schleife machen...
johann schrieb:> Den Code würde ich erst nach Eingabe aller Zeichen vergleichen.
Man kann auch sofort vergleichen und merkt sich einfach nur das
Falschbit bis zur letzten Taste.
Peter Dannegger schrieb:> Man kann auch sofort vergleichen und merkt sich einfach nur das> Falschbit bis zur letzten Taste.
Tausende von Möglichkeiten... ;-)
Da nehme ich wieder mal den ersten Code:
Paul schrieb:> Wieso? Eingabe ist nicht zeitkritisch also warum soll ich die> Debounce-Libary verwenden?
Eine separate Tastenfunktion mit Entprellung und Flankenerkenung macht
aber das Programmieren erheblich einfacher.
Man braucht dann nur noch den eigentlichen Ablauf zu programmieren.
Ich würde ehrlich gesagt gar keine Möglichkeit zum Abbruch der
Codeeingabe vorsehen.
Der Benutzer tippt einfach dahin. Die letzten 5 eingegebenen Ziffern
gelten (werden mit jeder Benutzereingabe um 1 Stelle weitergeshiftet,
die am 5-weitesten zurück liegende Eingabe fällt raus).
Stimmen zu irgendeinem Zeitpunkt alle 5 Ziffern, wird die LED grün,
ansonsten rot.
D.h. wenn sich ein Benutzer mal vertippt, dann fängt er einfach erneut
mit der Eingabe der 6 Ziffern an, egal wieviele Stellen er schon
eingegeben hat. Ist das Schloss geöffnet, dann braucht er einfach nur
irgendeine Ziffer tippen (vernünftiger Code vorausgesetzt) und das
Schloss ist wieder geschlossen.
Ein 'Einbrecher' erhält dadurch keinerlei Information. Weder über
gültige Ziffern noch über die Codelänge.
1
voidloop()
2
{
3
....Tastenerkennung....
4
5
if(Tastedruckregistriert){
6
for(i=0;i<4;i++)
7
Codeeingabe[i]=Codeeingabe[i+1];
8
Codeeingabe[4]=nächsterTastendruck;
9
10
Treffer=TRUE;
11
for(i=0;i<5;i++){
12
if(Codeeingabe[i]==Code[i])
13
Treffer=FALSE;
14
}
15
16
digitalWrite(ledrot,Treffer?HIGH:LOW);
17
digitalWrite(ledgruen,Treffer?LOW:HIGH);
18
}
19
}
Ich hätte auch die Tastenerkennung/Enprellung einfacher gemacht. Da hier
wohl niemand nur ganz kurz antippen wird, hätte ich einfach eine
Flankenerkennung alle 50ms gemacht. 50ms dürfte ausreichen, um
eventuelles Prellen zu eliminieren und wenn nicht, dann geht man eben
mit der Zeit noch etwas hoch.
So ungefähr
1
...
2
uint8_toldState[10];
3
uint8_tstateNow[10];
4
uint8_tkeyPressed;
5
uint8_tisValid;
6
unsignedlonglastCheckedTime;
7
8
uint8_tpinNr[10]={pinEins,pinZwei,pinDrei,....};
9
10
#define CODE_LEN 5
11
uint8_tCode[CODE_LEN]={5,4,3,2,1};
12
uint8_tCodeeingabe[CODE_LEN];
13
14
....
15
16
voidloop()
17
{
18
keyPressed=-1;
19
20
// alle 50ms den Zustand der Tasten checken und auswerten
21
if(millis()-lastCheckedTime>50){
22
lastCheckedTime=millis();
23
24
// 10 Tasten abfragen
25
// gab es an irgendeinem Pin eine Veränderung?
26
// Bei exakt gleichzeitiger Betätigung ist die Priorität
Karl Heinz schrieb:> Ich würde ehrlich gesagt gar keine Möglichkeit zum Abbruch der> Codeeingabe vorsehen.>> Der Benutzer tippt einfach dahin. Die letzten 5 eingegebenen Ziffern> gelten (werden mit jeder Benutzereingabe um 1 Stelle weitergeshiftet,> die am 5-weitesten zurück liegende Eingabe fällt raus).> Stimmen zu irgendeinem Zeitpunkt alle 5 Ziffern, wird die LED grün,> ansonsten rot.
Das würde ich nicht machen. Ist der Code bspw. 00001 und der Einbrecher
probiert zuerst 10000, dann braucht er danach nur noch einmal die Eins
drücken (will z.B. als nächstes 10001 probieren) und schon hat er den
Code.
JH schrieb:> Karl Heinz schrieb:>> Ich würde ehrlich gesagt gar keine Möglichkeit zum Abbruch der>> Codeeingabe vorsehen.>>>> Der Benutzer tippt einfach dahin. Die letzten 5 eingegebenen Ziffern>> gelten (werden mit jeder Benutzereingabe um 1 Stelle weitergeshiftet,>> die am 5-weitesten zurück liegende Eingabe fällt raus).>> Stimmen zu irgendeinem Zeitpunkt alle 5 Ziffern, wird die LED grün,>> ansonsten rot.>> Das würde ich nicht machen. Ist der Code bspw. 00001
Ich vergass:
vernünftigen Code vorausgesetzt
> danach nur noch einmal die Eins drücken (will z.B. als nächstes> 10001 probieren) und schon hat er den Code.
Ja, wenn.
Dazu muss er aber wissen, dass der Code 5 stellig ist. Da ihm aber das
Gerät diese Information nicht verrät, ergibt sich da ein zusätzlicher
Schutz, den es mit der Alternative nicht gibt.
Karl Heinz schrieb:> digitalWrite(ledrot, isValid ? HIGH : LOW);
Den Fragezeichenoperator wollte ich erst in der nächsten Stunde
drannehmen... ;-)
> isValid
Das allein wäre mir zu wenig. Da bleibt der Türöffner ja bis zur
nächsten Eingabe betätigt...
> Ich würde ehrlich gesagt gar keine Möglichkeit zum Abbruch der> Codeeingabe vorsehen.
Ich würde im Gegenteil die Codeeingabe frühestmöglich abbrechen: nur ein
zügig eingegebener Code ist ein gültiger Code. Wenn so eine zögerliche
Eingabe erkannt und gespeichert, aber nicht angezeigt wird, dann
probiert man sogar mit der richtigen Nummer herum, ohne dass die Tür
aufgeht: nach holpriger Eingabe der richtigen Zahl leuchtet die rote
LED... :-o
Lothar Miller schrieb:> Karl Heinz schrieb:>> digitalWrite(ledrot, isValid ? HIGH : LOW);> Den Fragezeichenoperator wollte ich erst in der nächsten Stunde> drannehmen... ;-)
:-)
>>> isValid> Das allein wäre mir zu wenig. Da bleibt der Türöffner ja bis zur> nächsten Eingabe betätigt...
OK. Häng noch ein Software Monoflop hinten nach.
Ausserdem: Es ist das Projekt des TO. Was bleibt denn noch für ihn zu
tun, wenn wir ihm alles erledigen :-)
Ich hab in letzter Zeit sowieso schon so meine persönlichen Probleme mit
Projekten der Marke "Ich will ein Space Shuttle bauen ... aber ich kann
noch nicht mal einen Papierflieger falten ... ich will es auch nicht
lernen .... könnt ihr nicht mal .... guck mal Mama, was ICH gebaut habe"
>>> Ich würde ehrlich gesagt gar keine Möglichkeit zum Abbruch der>> Codeeingabe vorsehen.> Ich würde im Gegenteil die Codeeingabe frühestmöglich abbrechen: nur ein> zügig eingegebener Code ist ein gültiger Code. Wenn das nicht angezeigt> wird, dann probiert man sogar mit der richtigen Nummer herum, ohne> dass die Tür aufgeht...
Auch eine nette Alternative. Lässt sich (genauso wie bei dir) auch in
meinem Schema problemlos nachrüsten. 5 Sekunden nach dem letzten
Tastendruck, werden alle Stellen zwangsweise zb auf 0 gesetzt. Zeit des
letzten Tastendrucks merken, Differenzbildung von millis() und wenn
größer 5000 läuft die Rücksetzmaschinerie. Je mehr ich darüber
nachdenke, desto besser gefällt mir diese Zeitbegrenzung.
Karl Heinz schrieb:> Ja, wenn.> Dazu muss er aber wissen, dass der Code 5 stellig ist. Da ihm aber das> Gerät diese Information nicht verrät, ergibt sich da ein zusätzlicher> Schutz, den es mit der Alternative nicht gibt.Karl Heinz schrieb:> Ich vergass:> vernünftigen Code vorausgesetzt
Wenn man das Schloss möglichst sicher machen möchte, dann sollte es
keinen "vernünftigeren" Code geben :P
Karl Heinz schrieb:> Ja, wenn.> Dazu muss er aber wissen, dass der Code 5 stellig ist. Da ihm aber das> Gerät diese Information nicht verrät, ergibt sich da ein zusätzlicher> Schutz, den es mit der Alternative nicht gibt.
Wenn er nicht weiß, dass der wielang der Code ist, bleibt ihm ja nur
alle Längen durchzuprobieren. Angenommen der Code sei dreistellig ->
930. Er probiert also erstmall alle einstelligen Codes (0..9) ohne
Erfolg. Jetzt startet er mit der Eingabe aller zweistelligen Codes
(00..99) und öffnet das Schloss bereits bei der Eingabe von "30", da er
vorher "29" probiert hat.
Karl Heinz schrieb:> Auch eine nette ALternative.
Man könnte noch eins weiter gehen und die Zahlen komplett irrelevant
machen. Dann gilt nur der Rhythmus der Tastaturbetätigungen. Ist
natürlich nichts für Schlagzeuger, die schleppen gern... ;-)
JH schrieb:> Wenn er nicht weiß, dass der wielang der Code ist, bleibt ihm ja nur> alle Längen durchzuprobieren. Angenommen der Code sei dreistellig ->> 930. Er probiert also erstmall alle einstelligen Codes (0..9) ohne> Erfolg. Jetzt startet er mit der Eingabe aller zweistelligen Codes> (00..99) und öffnet das Schloss bereits bei der Eingabe von "30", da er> vorher "29" probiert hat.
Sperr ihm die Eingabe nach der 5-ten Falscheingabe für 2 Minuten und der
sitzt die ganze Nacht am Gerät um die 40 Eingaben zu machen. Ausserdem
ist der Code nicht 3-stellig sondern 5 stellig (was aber ein Einbrecher
nicht weiß).
Klar kann es passieren, dass er durch Zufall die richtige Kombination
bekommt. Das kann im anderen Fall aber genau so passieren. Nur mit dem
Unterschied, dass er mit dem Wissen des 5 stelligen Codes erst mal alle
Geburtsdaten durchprobiert, den Hochzeitstag, etc. etc.
Aber ok.
Ich sag ja nicht, dass er muss. So würde ich das machen. Wenn ihm das
nicht gefällt, kann er immer noch eine Rücksetztaste dazu bauen und die
Anzahl der Tastendrücke mitzählen. Schliesslich sind wir Programmierer.
Wir bestimmen, wie sich die Dinge verhalten sollen und so wie wir das
definieren ist es per Definition richtig :-)
Karl Heinz schrieb:> Sperr ihm die Eingabe nach der 5-ten Falscheingabe für 2 Minuten und der> sitzt die ganze Nacht am Gerät um die 40 Eingaben zu machen.
Ich würde in diesem Fall während der (unsichtbaren) Sperrzeit nach
irgendeinem Tastendruck beliebig mal die rote oder die grüne LED ein-
und wieder ausschalten (natürlich ohne den Türöffner zu betätigen)...
;-)
> so wie wir das definieren ist es per Definition richtig :-)
Und das ist gut so.
Der Trick dabei ist: der uC muss das machen, was wir wollen. Und das
können wir ihm mit einfachen und strukturierten Anweisungen sagen.
Ein delay() trägt aber niemals etwas zur Struktur bei. Ein delay()
verzögert bestenfalls irgendwelche Probleme, ist aber oft sogar die
Ursache dafür...
Wenn man einbaut, dass man immer zwei aufeinanderfolgende Ziffern
paarweise gleichzeitig drücken muss, kann man den Code sogar noch
ausdrucken und als Gedächtnisstütze direkt neben das Schloss kleben ;-)
kopfkratz
Man könnte natürlich auch eine Tastenkombination als Auslöser nehmen,
z.B. 0+9 gleichzeitig gedrückt testet ob der Code korrekt war ...
Oder man schafft sich ein paar nette Rottweiler an, die riechen's dann
:-P
Karl Heinz schrieb:> Ich würde ehrlich gesagt gar keine Möglichkeit zum Abbruch der> Codeeingabe vorsehen.>> Der Benutzer tippt einfach dahin. Die letzten 5 eingegebenen Ziffern> gelten (werden mit jeder Benutzereingabe um 1 Stelle weitergeshiftet,> die am 5-weitesten zurück liegende Eingabe fällt raus).> Stimmen zu irgendeinem Zeitpunkt alle 5 Ziffern, wird die LED grün,> ansonsten rot.
Der Nachteil dabei ist, daß der Angreifer mit jedem Tastendruck eine
neue Kombination testen kann, was das Durchprobieren beschleunigt.
Die einfachste Abhilfe wäre eine zusätzliche Taste, mit der der Benutzer
sagt, daß er jetzt fertig ist, und der Code geprüft werden soll.
Alternativ könnte man sich einen cleveren Sperrzeit-Algorithmus
ausdenken, aber man muß dabei berücksichtigen, daß jede korrekte Eingabe
eines fünfstelligen Codes erstmal mit vier falschen Eingaben anfängt.
Paul schrieb:> Habe nun durch die Tipps meine eigenen Code geschrieben
Trau dich doch einfach, den Code hier zu posten. Andere könnten etwas
davon lernen...