Als je een drukknop gebruikt zoals in 'Les2 Knop' dan gaat het ledje branden als je op de knop drukt en weer uit als je de knop loslaat (of andersom). Het kan ook handig zijn om de knop als schakelaar te gebruiken zodat het ledje aangaat als je op de knop drukt, aanblijft als je de knop loslaat en weer uit gaat als je nogmaals op de knop drukt.
Hieronder het schema. De drukknop is aan een kant verbonden met 5v. Aan de andere kant met pin 2 en via een 10kohm naar GND.
Het ledje is verbonden met pin 13 en via een 330 Ohm weerstand met gnd.
Er zijn meerdere mogelijkheden om een knop als schakelaar te laten werken. Een van de dingen waar je rekening mee moet houden is de mechanische onnauwkeurigheid van de knoppen. Door het 'trillen' of 'bumpen' (bouncen in het Engels) bij het indrukken kan de knop ongewild heel snel aan/uit schakelen zodat je schakeling niet goed werkt. Dit kan je verhelpen door een tijdvertraging in te bouwen. Het eenvoudigst is om er een delay (van b.v. 100 milliseconden) bij te plaatsen. Dit kan wel je programma erg langzaam maken of andere fouten opleveren.
Bestudeer de varianten en kijk welke voor jou doel het meest geschikt is.
1. Hieronder een eenvoudige versie met anti-bump.
// Kortste knop code. Met debounce.
const int knopPin = 2;
const int ledPin = 13;
int knopStand = LOW; // variabele voor de stand van de knop
int ledStaat = 0; // variabele voor de staat van de led (0 = uit)
void setup() {
pinMode(ledPin, OUTPUT);
pinMode(knopPin, INPUT);
}
void loop() {
knopStand = digitalRead(knopPin); // knoppin uitlezen
if (knopStand == HIGH) {
digitalWrite(ledPin, ledStaat);
delay(100); // anti-bump vertraging
ledStaat = 1 - ledStaat; // de staat van de led omkeren.
}
}
Hiernaast zie je dat in de 'loop' eerst wordt gezegd dat de knopStand is wat er op de knopPin uitgelezen wordt: HIGH of LOW, 0 of 1. Dan wordt, als de knop ingedrukt wordt (knopStand = HIGH), met digitalWrite de ledPin in de ledStaat gezet (0 of 1).
De ledStaat is afhankelijk van de vorige ledStaat want met de regel 'ledStaat = 1 - ledStaat' veranderd de ledStaat steeds.
Als de ledStaat aan het begin van de loop 0 is (led uit)dan wordt die met ledStaat=1-ledStaat (=1-0 =1) 1 gemaakt. Als er nu weer op de knop gedrukt wordt is de ledStaat 1 en gaat de led aan.
Deze code heeft een debounce (delay(100)). Het nadeel van het gebruiken van de delay functie is het programma tijdens de delay niets anders kan doen dan alleen wachten. Er kan niet intussen bij voorbeeld ook een sensor worden uitgelezen. Als dit wel moet gebeuren kan je een tijdinterval met de millis() functie gebruiken. Kijk hiervoor naar het derde voorbeeld (vooral de regel: if (toestandKnop == HIGH && millis() - tijd > interval) ).
2. Hieronder een programma dat wel werkt maar, door de onnauwkeurigheid van de knop, met fouten.
// Schakelaar met vlag zonder debounce
const int knopPin = 3;
const int ledPin = 13;
int toestandKnop = 0;
int vlag = 0;
void setup() {
pinMode(ledPin, OUTPUT);
pinMode(knopPin, INPUT);
}
void loop() {
toestandKnop = digitalRead(knopPin);
if (toestandKnop == HIGH) {
if (vlag == 0) {
digitalWrite(ledPin, HIGH);
vlag = 1;
}
else if ( vlag == 1) {
digitalWrite(ledPin, LOW);
vlag = 0;
}
}
}
Hiernaast zie je code die zou kunnen werken.
Wij willen dat als er op de knop gedrukt wordt het ledje aangaat als het uit was en andersom. We maken daarvoor twee variabelen aan: de variabele 'toestandKnop' en de variabele 'vlag'.
'toestandKnop' is datgene dat er gelezen wordt op de knopPin (pin 3), dat is als de knop ingedrukt wordt 1 en als de knop niet ingedrukt wordt 0.
De variabele vlag kan je zien als een vlaggetje dat omhoog of omlaag gezet kan worden.
Kijk in de loop hiernaast wat er achtereenvolgend gebeurd:
Eerst wordt naar toestandKnop gekeken. Wordt de knop ingedrukt, is die HIGH of LOW?
Dan komt een 'genest if statement': Als toestandKnop HIGH is (de knop is ingedrukt) dan moet als de vlag 0 is ('vlag omlaag') het ledje aan. Als de vlag 1 is ('vlag omhoog') dan moet het ledje uit zijn.
Kijken vanaf de start van het programma, dan beginnen we met vlag = 0 (regel 4). Als er op de knop gedrukt wordt dan is de vlag dus 0 en gaat het ledje aan maar op de regel eronder wordt de vlag op 1 gezet. Dus als er nu weer op de knop gedrukt wordt is de vlag 1 en gaat het programma naar de regel : else if (vlag == 1) en wordt het ledje uit gezet. Op de volgende regel wordt de vlag weer 0 gezet, zodat bij de volgende keer op de knop drukken de ledPin weer hoog gezet wordt etc..
3. Hieronder een programma met een 'debouncer' dat wel goed werkt.
// Schakelaar met vlag met debounce.
const int knopPin = 2;
const int ledPin = 13;
int toestandKnop = 0;
int vlag = 0;
long tijd = 0;
long interval = 200;
void setup() {
pinMode(ledPin, OUTPUT);
pinMode(knopPin, INPUT);
}
void loop() {
toestandKnop = digitalRead(knopPin);
if (toestandKnop == HIGH && millis() - tijd > interval) {
if (vlag == 0) {
digitalWrite(ledPin, HIGH);
vlag = 1;
}
else if ( vlag == 1) {
digitalWrite(ledPin, LOW);
vlag = 0;
}
tijd = millis();
}
}
Het bovenstaande programma werkt wel maar als je het uitprobeert zul je zien dat het ledje niet altijd netjes aan en uit gaat als er op de knop gedrukt wordt. Dat komt door de mechanische eigenschappen van de knop. Als je erop drukt heb je kans dat er heel even contact gemaakt wordt, dan weer heel even niet en dan weer wel. Je drukt dus eigenlijk heel snel de knop ongewild meerdere keren achter elkaar in. Deze fout kan verholpen worden door een 'debounce', een 'anti-bump' voorziening in het programma.
We hebben daarvoor nog twee extra variabelen nodig: tijd (long tijd = 0) en interval (long interval = ...) ('long' in plaats van 'int' omdat het tellen van tijd een heel groot getal kan worden).
Wat we nu willen is dat als de knop 'bibbert' en twee keer heel snel achter elkaar contact maakt (en de led dus aan en uit gaat) die tweede keer negeert. We kunnen zeggen dat het ledje alleen aan of uit mag gaan als er een bepaalde tijd verstreken is sinds het het laatst aan of uit gegaan is.
Dit doen we met de regel:
if (toestandKnop == HIGH && millis() - tijd > interval)
De led mag alleen veranderen als er zowel op de knop gedrukt wordt en ook (&&) 'millis()-tijd>interval'.
Hierbij is millis() een functie die de tijd bijhoud (in milliseconden) vanaf het starten van het programma. Dus na b.v. 5 seconden is millis() 5000. 'Tijd' wordt steeds opnieuw vastgesteld als het ledje aan of uit gegaan is (de laatste regel: tijd = millis() ). Na die laatste regel is tijd dus hetzelfde getal als millis(), maar millis() loopt door en tijd blijft een vast getal (tot de led opnieuw aan of uit gezet wordt). De grootte van het interval bepalen we zelf en is hier op 200 (0,2 sec.) gezet. Wordt er nu na b.v. 50 milliseconden nadat er na 5 seconden gedrukt was weer op de knop gedrukt, dan is millis()-tijd=5000-5050=50. 50 is niet groter dan de intervaltijd (200) dus aan de voorwaarde voor het eerste if statement is niet voldaan dus de led blijft zoals hij was.
4. De code hieronder is de veelgebruikte voorbeeldcode van Arduino die uiteraard goed werkt.
//In de praktijk kan je meerdere varianten van zo'n code met debounce tegenkomen. Hieronder de 'switch' //voorbeeldcode van Arduino.
/* switch
*
* Each time the input pin goes from LOW to HIGH (e.g. because of a push-button
* press), the output pin is toggled from LOW to HIGH or HIGH to LOW. There's
* a minimum delay between toggles to debounce the circuit (i.e. to ignore
* noise).
*
* David A. Mellis
* 21 November 2006
*/
int inPin = 2; // the number of the input pin
int outPin = 13; // the number of the output pin
int state = HIGH; // the current state of the output pin
int reading; // the current reading from the input pin
int previous = LOW; // the previous reading from the input pin
// the follow variables are long's because the time, measured in miliseconds,
// will quickly become a bigger number than can be stored in an int.
long time = 0; // the last time the output pin was toggled
long debounce = 200; // the debounce time, increase if the output flickers
void setup()
{
pinMode(inPin, INPUT);
pinMode(outPin, OUTPUT);
}
void loop()
{
reading = digitalRead(inPin);
// if the input just went from LOW and HIGH and we've waited long enough
// to ignore any noise on the circuit, toggle the output pin and remember
// the time
if (reading == HIGH && previous == LOW && millis() - time > debounce) {
if (state == HIGH)
state = LOW;
else
state = HIGH;
time = millis();
}
digitalWrite(outPin, state);
previous = reading;
}