Pagina1 van 1

Arduino Programmeren voor Beginners – Deel 4: Beslissingen

Arduino Programmeren voor Beginners – Deel 4: Beslissingen
   15

Dit is het vierde deel van Arduino Programmeren voor Beginners.
In dit deel gaan we kijken naar het maken van beslissingen in ons programma.

Dit zijn de zogenaamde “if…then..,” (als…dan…) situaties, en omdat we in ons programma informatie vergelijken om vervolgens beslissingen te maken, gaat dit een belangrijk onderdeel worden. Deze beslissingen gaan de zogenaamde “Control Flow” van ons programma bepalen – oftewel, bepalen in welke situatie, welk deel van het programma uitgevoerd gaat worden.

Deze reeks, richt zich hoofdzakelijk op het Arduino Programmeren voor beginners, en dan specifiek voor m’n neefjes Bram en Max – gebrek aan kennis voor wat betreft de Engelse taal en wiskundige achtergrond hoeft waarschijnlijk geen probleem te zijn. Het gebruik van extra electronika componenten blijft beperkt tot een minimum en bewaren we voor een volgende reeks.




Overzicht van dit Hoofdstuk

  Een volledig overzicht van de cursus vindt je hier: Cursus Overzicht.

Beslissingen maken in ons Arduino Programma

In het voorgaande hoofdstuk hebben we gekeken naar de zogenaamde Comparison Operators (vergelijking operators), waarmee we informatie (data) kunnen vergelijken. We weten dat een dergelijk vergelijking een boolean antwoord gaat geven, dus “true” (waar) of “false” (niet waar). Iets wat we in dit deel gaan gebruiken om beslissingen (decisions) te maken in ons programma.

We hebben ook naar de zogenaamde Boolean Operators gekeken en deze zijn een belangrijke aanvulling in dit deel.

Omdat onze programma’s vaak kijken naar verschillende situaties, is het van belang dat we vergelijkingen kunnen maken en aan de hand daarvan beslissingen kunnen maken. Dat uit zich in het bepalen wanneer, welk deel van onze code uitgevoerd gaat worden. Dit heet in het Engels “control flow” of in andere woorden: controleren hoe we door een programma gaan.

Als simpel voorbeeld zou ons programma lichten aan of uit kunnen zetten, afhankelijk van het feit of het nu buiten licht of donker is.
In ons programma moeten we dus eerst kijken of het buiten donker is, is dat het geval dan moeten we de code uitvoeren die de lampen aan gaan zetten. Als het echter niet donker is, dan moeten we de code uitvoeren die de lampen uit zetten.

Dit zou je kunnen lezen als:  als … dan …
Dus als iets waar is, doen dan dit, als iets niet waar is doe dan dat.
Het “als … dan …”  vertaald zich in het Engels naar “if … then …”, een constructie die je in heel veel programmeertalen terug gaat vinden.

If … then … of te wel “Als … dan …”

Dit is echt een van de meest gebruikte manieren om beslissingen in jouw programma te maken.
Een stukje van jouw programma wordt alleen maar uitgevoerd als een bepaalde stelling waar is – dit heet conditionele instructies of in het Engels een “Conditional Statements“. Dus instructies die alleen worden uitgevoerd onder bepaalde condities.

Laten we eens gaan kijken naar 2 eenvoudige voorbeelden.

Stel we hebben lampen die automatisch aan gaan als het buiten donker wordt. De conditionele instructie wordt dan:

als BuitenDonker dan ZetLichtenAan

We zagen al dat “als … dan …” in het Engels “if …. then …” is, en om daar aan te wennen gaan we de Engelse variant gebruiken in onze tekst:

if GeldInPortemonnee=0 then GeldGaanPinnen

Dus als onze Portemonnee leeg is (GeldInPortemonnee=0) dan moeten we even gaan pinnen om weer geld in onze Portemonnee te krijgen.

Ik denk dat je nu wel situaties kunt bedenken waarbij een “if … then …” in jouw programma handig kan of zelfs nodig kan zijn. Je wilt uiteindelijk dat jouw programma correct reageert op een mogelijk veranderende situatie, of klaar zijn voor gebruik in meerdere situatie. Het deel dat anders kan zijn of kan veranderen kan een waarde of variabele zijn (van bijvoorbeeld een lichtsensor of knop druk).

De juiste notatie (“notatie” in het Engels noemen we “syntax”) is:


1
2
3
4
5
6
if ( conditie ) {
  // wat gedaan moet worden als de conditie waar (true) is
}
else {
  // wat gedaan moet worden als de conditie niet waar (false) is
}

We zien weer de zogenaamde code blokken die we eerder hebben gezien – tussen de accolades. Elk blok hoort bij een conditie.

Het “else” statement wordt altijd aan het einde van een “if … then …” geplaatst, dus als de laatste “conditie” die wordt uitgevoerd als alle condities falen …

Wat verder opvalt is het “else” woord in regel 4. Het woord “else” is het Engelse woord voor “anders”. Dus “als iets waar is dan doe dit anders doe dat”. Het blok dat volgt na “else” is het stukje code wat uitgevoerd als alle andere de conditie(s) falen.
Dit deel (regels 4, 5, en 6) is echter geheel optioneel en hoeft dus niet gebruikt te worden, wat de notatie kan vereenvoudigen naar:


1
2
3
if ( conditie ) {
  // doe wat nodig is als de conditie klopt
}

De “conditie” is over het algemeen het resultaat van een comparison (vergelijking) operator wat, zoals we al weten, altijd een true of false oplevert. Kijk eventueel terug naar het voorgaande hoofdstuk. Voor het gemak heb ik de tabel hier nog eens geplaatst;

Verglijkingsoperators
 Symbool  Doel
==  is gelijk aan
!=  is niet gelijk aan
<  is kleiner dan
>  is groter dan
<=  is kleiner dan of gelijk aan
>=  is groter dan of gelijk aan

De meest voorkomende fout bij Vergelijkingsoperators is dat men
het symbool “=” gebruikt (toewijzing!) in plaats van het “==” teken als we kijken of twee waarden gelijk zijn.

Overigens, nog een tip die in de toekomst handig gaat zijn: een “if…then…” beslist op basis van een conditie die true of false kan zijn – dus een boolean! Dit wil dus ook zeggen dat we een boolean waarde kunnen gebruiken in plaats van een conditie waarin we een vergelijk zetten. Onderstaande voorbeeld zal bijvoorbeeld altijd de “if”-blok uitvoeren:


1
2
3
if ( true ) {
  // doen als de conditie waar is
}

OK, laten we eens door een werkend programma gaan, en in dit geval borduren we verder op ons geld probleem.

We gaan kijken hoeveel geld we op zak hebben en hoeveel geld we gespaard hebben. Als we minder dan € 5 hebben dan willen we een waarschuwing zien zodat we weten dat we niet al te veel geld meer hebben.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
void setup() {
  // set the speed for the serial monitor:
  Serial.begin(9600);
 
  // define our variables
  int ZakGeld;
  int SpaarGeld;
  int AlMijnGeld;

  // assign the values
  ZakGeld = 4;
  SpaarGeld = 12;
  AlMijnGeld = ZakGeld + SpaarGeld;

  // print the values to the serial monitor
  Serial.print("Geld op zak = ");
  Serial.println(ZakGeld);

  Serial.print("Spaargeld = ");
  Serial.println(SpaarGeld);

  Serial.print("Al mijn Geld = ");
  Serial.println(AlMijnGeld);

  if(AlMijnGeld<5) {
    Serial.println("Oh oh,... we hebben maar weinig geld!!");
  }
  else {
    Serial.println("Geen probleem, we hebben genoeg geld.");
  }
}

void loop() {
  // even leeg laten
}

Een deel van deze code hebben we al eerder gezien. Het beslissing-stuk vinden we vanaf regel 25 waar we gaan kijken of we nog genoeg geld hebben … dus we gaan kijken of de variabele “AlMijnGeld” minder dan (<) 5 euro is. Als we minder dan 5 Euro hebben, dan willen we een waarschuwing zien “Oh oh,… we hebben maar weinig geld!!“. Mochten we meer dan 5 Euro hebben, dan is alles OK en willen we de melding “Geen probleem, we hebben genoeg geld.” zien.

Kopieer de code en plak deze in de Arduino IDE, compileer het en stuur het naar de Arduino.

Omdat de conditie “false” is (4+12=16 en 16 is groter dan 5) zal het “if” code blokje overgeslagen worden en gaat de Arduino meteen verder met het “else” stuk:

Geld op zak = 4
Spaargeld = 12
Al mijn Geld = 16
Geen probleem, we hebben genoeg geld.

Laten we nu regel 12 veranderen en ons spaargeld op zetten: SpaarGeld = 0;.
Compileer en upload het programma opnieuw naar de Arduino.

De conditie in regel 25 is nu WEL WAAR, dus de “if” blok wordt uitgevoerd en het “else” blok wordt nu overgeslagen wat een output geeft als:

Geld op zak = 4
Spaargeld = 0
Al mijn Geld = 4
Oh oh,... we hebben maar weinig geld!!

We kunnen ook een voorbeeld bedenken waarbij we het “else” stuk niet gebruiken. Stel je wilt alleen een waarschuwing zien als het foute boel is met ons geld, maar als alles OK is, dan willen we niks zien. Zo’n voorbeeld zou er zo uit kunnen zien:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
void setup() {
 // set the speed for the serial monitor:
 Serial.begin(9600);
 
 // define our variables
 int ZakGeld;
 int SpaarGeld;
 int AlMijnGeld;

 // assign the values
 ZakGeld = 4;
 SpaarGeld = 12;
 AlMijnGeld = ZakGeld + SpaarGeld;

 // print the values to the serial monitor
 Serial.print("Geld op zak = ");
 Serial.println(ZakGeld);

 Serial.print("Spaargeld = ");
 Serial.println(SpaarGeld);

 Serial.print("Al mijn Geld = ");
 Serial.println(AlMijnGeld);

 if(AlMijnGeld<5) {
   Serial.println("Oh oh,... we hebben maar weinig geld!!");
 }
}

void loop() {
 // even leeg laten
}

Het “if … then …” statement heeft echter ook nog eens de mogelijkheid om meerdere condities samen te voegen.

Als voorbeeld:

als AlMijnGeld=0 dan paniek anders als AlMijnGeld<5 dan haal meer geld anders niks aan het handje.

We stapelen zeg maar meerdere “if .. then … else if … then … else if … then” op, en jouw Arduino zal netjes door alle condities lopen tot het een conditie vindt die waar is en voert dan de bijbehorende code uit en verlaat dan de hele “if .. then ..” constructie!

“if … then …” statements kunnen opgebouwd worden uit meerdere “if … then …” constructies.
Als in zo’n constructie een conditie gevonden wordt die WAAR is, dan wordt het bijbehorende code blok uitgevoerd en de gehele constructie verlaten. De andere “if … then …” of “else” worden dan dus niet meer bekeken!

Een voorbeeld:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
void setup() {
  // set the speed for the serial monitor:
  Serial.begin(9600);
 
  // define our variables
  int ZakGeld;
  int SpaarGeld;
  int AlMijnGeld;

  // assign the values
  ZakGeld = 4;
  SpaarGeld = 12;
  AlMijnGeld = ZakGeld + SpaarGeld;

  // print the values to the serial monitor
  Serial.print("Geld op zak = ");
  Serial.println(ZakGeld);

  Serial.print("Spaargeld = ");
  Serial.println(SpaarGeld);

  Serial.print("Al mijn Geld = ");
  Serial.println(AlMijnGeld);

  if(AlMijnGeld==0) {
    Serial.println("PANIEK!!");
  } else if(AlMijnGeld<5) {
    Serial.println("Oh oh,... we hebben maar weinig geld!!");
  } else if(AlMijnGeld>10) {
    Serial.println("Woohoo - we zijn rijk!!");
  } else {
    Serial.println("Geen probleem, we hebben genoeg geld.");
  }
}

void loop() {
  // leave empty for now
}

Deze code zal:
– “PANIEK!!” weergeven als AlMijnGeld = 0, OF
– “Oh oh,… we hebben maar weinig geld!!” weergeven als AlMijnGeld kleiner is dan 5, OF
– “Woohoo – we zijn rijk!!” weergeven als AlMijnGeld groter is dan 10, OF
– in alle andere situaties “Geen probleem, we hebben genoeg geld.” weergeven.

Meerdere “if … then … else if … then”

Als we naar het voorgaande voorbeeld kijken, dan moet je goed opletten wat hier gebeurt!

Een “if … then ..” statement wordt verlaten bij het vinden van een “true” conditie.

Alle volgende “else if ….” en “else” statements worden genegeerd !!!!

In on voorbeeld is AlMijnGeld = 16, dus de eerste 2 condities FALEN.
De derde conditie is echter WAAR, dus de conditie SLAAGT, en dus zal de melding “Woohoo – we zijn rijk!!” zichtbaar worden gemaakt.
Omdat deze conditie WAAR is, zal het “else” blok genegeerd worden. Logisch?

OK, laten we regels 11 en 12 aanpassen:


11
12
  ZakGeld = 0;
  SpaarGeld = 0;

Dus AlMijnGeld zal dus nul (0) gaan worden in ons programma.

Maar als we nu naar onze “if … then …” statements gaan kijken dan zien we iets interessants wat een fout (bug) in ons programma kan veroorzaken.

Kijk maar … dus AlMijnGeld = 0.
Dus de eerste conditie SLAAGT, en het bericht “PANIEK!!” verschijnt.
Echter,… conditie 2 (AlMijnGeld kleiner dan 5) is ook WAAR, maar zal nooit worden uitgevoerd.

Als namelijk een conditie wordt gevonden die WAAR is (true), dan wordt de rest van de “if … then …” gezien als “klaar” en zullen de andere condities niet eens getest worden en dat is express zo gedaan.

Als je dit niet zou weten, dan zou je ook de melding “Oh oh,… we hebben maar weinig geld!!” verwachten.

Dit komt omdat we condities hebben gemaakt die elkaar overlappen. Daarmee bedoelen we dat 2 of meer condities WAAR kunnen zijn onder bepaalde omstandigheden – en dat kan soms onwenselijk zijn.

Maar wat moeten we dan doen om beide meldingen toch zichtbaar te krijgen?
In zo’n geval moeten we de “if…then…” uit elkaar halen en afzonderlijk opzetten, maar dat kan weer problemen veroorzaken als je een “else” aan het einde hebt.

Hier een voorbeeld hoe dat er uit zou kunnen zien:


25
26
27
28
29
30
31
32
33
34
35
  if(AlMijnGeld==0) {
    Serial.println("PANIEK!!");
  }

  if(AlMijnGeld<5) {
    Serial.println("Oh oh,... we hebben maar weinig geld!!");
  }

  if(AlMijnGeld>10) {
    Serial.println("Woohoo - we zijn rijk!!");
  }

Dit werkt, maar … omdat we in onze oorspronkelijk code een “else” hebben voor de overige scenario’s, lopen we tegen een probleem aan. Waar zouden we deze “else” dan moeten zetten?

In ons oorspronkelijke programma zeiden we:

If AlMijnGeld == 0 then “Paniek”else if AlMijnGeld < 5 then “Oh Oh”else if AlMijnGeld > 10 then “Woohoo”else “Geen probleem”.

Maar door het opbreken van de “if … then …” constructie, hebben we alles kunnen vatten, behalve wat er na het laatste “else” statement gebeurt:

If AlMijnGeld == 0 then “Paniek”.
If AlMijnGeld < 5 then “Oh Oh”.
If AlMijnGeld > 10 then “Woohoo”.

We kunnen geen “else” toevoegen, bij welke van de 3 dan ook, anders zou deze namelijk uitgevoerd kunnen worden op ongepaste momenten. Kijk hier maar eens naar:

If AlMijnGeld == 0 then “Paniek” else “Geen probleem”.
If AlMijnGeld < 5 then “Oh Oh” else “Geen probleem”..
If AlMijnGeld > 10 then “Woohoo” else “Geen probleem”.

Als AlMijnGeld nou 8 zou zijn, dan zouden we drie keer de melding “Geen probleem” zien omdat alle drie de condities falen.
Als AlMijnGeld 0 (nul) zou zijn, dan zien we “Paniek” en twee keer “Geen Probleem” – en dat is ook niet correct.
Als AlMijnGeld 4 zou zijn, Dan zien we “Geen probleem” twee keer, en één keer “Oh Oh” – en dat klopt ook niet he?

Dus waar zit nou het gat waarbij de oorspronkelijke “else” wel uitgevoerd zou worden?

Als we naar onze condities gaan kijken dan zie we een bereik voor iedere conditie, waarbij een aantal daarvan elkaar zelf overlappen.
De waarde 0 (nul) en waarden kleiner dan 5 worden door de eerste 2 condities afgehandeld – en ze overlappen elkaar, nul is immers ook kleiner dan 5.
Waarden groter dan 10 worden in de derde conditie gevangen.

Dus waar zit nu het gat waar “else” de zaak afvangt?

Complexere “if…then…” condities

De waarden groter of gelijk aan (>=) 5 EN kleiner of gelijk aan (<=) 10 worden niet afgehandeld – dit is ons “else” gat.

Stap voor Stap, even uitgaande van hele nummers, dus zonder cijfers achter de komma:

Conditie 1 zorgt voor “0” (nul).
Conditie 2 zorgt voor “0, 1, 2, 3, 4” (kleiner dan 5 – dus dat wil zeggen dat 5 niet meetelt!).
Conditie 3 zorgt voor “11, 12, 13, … oneindig” (groter dan 10 – dus 10 telt hier niet mee!).

Zie je het gat nu? Het gat is “5 ,6 ,7 ,8 ,9 ,10”.

Dus alles groter of gelijk aan (>=) 5 EN kleiner of gelijk aan (<=) 10.

Dit is het punt waar onze Boolean Operators van pas komen:

Boolean Operator
 Symbool  Doel
 &&  AND (en)
 ||  OR (of)
 !  NOT (niet)

Het gat bestaat eigenlijk uit twee condities.
De eerste is conditie is dat het getal groter of gelijk aan 5 moet zijn, dus: AlMijnGeld >= 5 .
De tweede conditie is dat het getal kleiner of gelijk aan “10” moet zijn, dus: AlMijnGeld <= 10 .

Maar hoe plakken we beiden condities nou bij elkaar als een enkele conditie? Beiden condities moeten immers gelden.

De eerste optie is een “if…then…” in een “if…then”, bij voorbeeld:


if(AlmijnGeld>=5) {
  if(AlMijnGeld<=10) {
    // doe dit
  }
}

Uiteraard werkt dit, maar dit wordt erg snel erg rommelig.

Laten we even naar de Boolean Operator tabel kijken, en lees dan deze tekst eens: het gat geld voor getallen >=5 en (and) <=10.

Het lampje gaat branden? De oplossing van ons probleem ligt in het woord “en” (and). Kijk maar eens naar deze tabel van ons vorige hoofdstuk:

And Resultaten
 Conditie1  Conditie2  Resultaat van Conditie1 EN (and) Conditie2
 true  true  = true
 true  false  = false
 false  true  = false
 false  false  = false

Dus als beide condities waar zijn, hebben we ons gat, en in code met een boolean operator ziet dat er zo uit: AlMijnGeld >= 5 && AlMijnGeld <= 10.

Als je het zo schrijft kan dit wat problemen opleveren omdat we niet precies weten in welke volgorde dit wordt uitgevoerd. Eerst de “>=” en “<=” of eerst de “&&” (and)? De Arduino kan het wel goed afwerken hoor, maar je begrijpt dat leesbaarheid niet geweldig is. Overigens: andere programmeertalen kunnen hier ook problemen veroorzaken of dit zelfs niet een accepteren.

Om het leesbaar te houden, maar ook om er zeker van te zijn dat alles juist wordt uitgevoerd, gaan we haakjes gebruiken.
Code die we tussen ronde haakjes zetten wordt namelijk als eerste uitgevoerd en daarna pas de rest, in dit geval dus het “&&” (and) teken.

Code tussen ronde haakjes wordt als eerste uitgevoerd ...

De nette notatie is dus: (AlMijnGeld >= 5) && (AlMijnGeld <= 10)

Als het programma draait dan wordt eerst gekeken wat “AlMijnGeld >= 5” en “AlMijnGeld <= 10” oplevert. De uitkomst van ieder vervangt de individuele conditie tussen haakjes en daarna wordt de rest van de regel pas gedaan.

Ter illustratie, stel AlMijnGeld = 6:

Stap 1: (AlMijnGeld >= 5) && (AlMijnGeld <= 10) en we weten dat “AlMijnGeld=6”
Stap 2: (true) && (AlMijnGeld <= 10)
Stap 3: (true) && (true)

Wat resulteert in true && true wat dus “true” oplevert (zie tabel).

Onze aangepaste code wordt dus:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
void setup() {
  // set the speed for the serial monitor:
  Serial.begin(9600);
 
  // define our variables
  int ZakGeld;
  int SpaarGeld;
  int AlMijnGeld;

  // assign the values
  ZakGeld = 0;
  SpaarGeld = 4;
  AlMijnGeld = ZakGeld + SpaarGeld;

  // print the values to the serial monitor
  Serial.print("Geld op zak = ");
  Serial.println(ZakGeld);

  Serial.print("Spaargeld = ");
  Serial.println(SpaarGeld);

  Serial.print("Al mij geld = ");
  Serial.println(AlMijnGeld);

  if(AlMijnGeld==0) {
    Serial.println("PANIEK!!");
  }

  if(AlMijnGeld<5) {
    Serial.println("Oh oh,... we hebben maar weinig geld!!");
  }

  if(AlMijnGeld>10) {
    Serial.println("Woohoo - we zijn rijk!!");
  }

  if( (AlMijnGeld>=5) && (AlMijnGeld<=10) ){
    Serial.println("Geen probleem, we hebben genoeg geld.");
  }
}

void loop() {
  // leave empty for now
}

Ik raad zeker aan om hier mee te gaan spelen. Rommel wat met de waarden in regels 11 en 12, en pas de condities vanaf regel 25 eens een beetje aan.

Het gebruik van ronde haakjes

We gaan toch nog eens wat beter kijken naar het gebruik van haakjes, omdat ze een belangrijke rol spelen in complexe berekeningen en vergelijkingen, en dan met name de haakjes tussen haakjes. Dit noemt men “nested brackets”.

Als condities erg complex worden, dan kan het gebeuren dat we haakjes veel moeten gebruiken om ervoor te zorgen dat het e.e.a. in de juiste volgorde gedaan gaat worden door de computer. Uiteraard geldt dit ook voor berekeningen.

Laten we eens naar een voorbeeld gaan kijken: (  (  (c1) && (c2) ) || ( (c3) && (c4) ) )
Elk c-nummer representeert een conditie.

De computer (Arduino) zal eerst de condities of berekeningen uitvoeren die het “diepst” zitten – waar dus de grootste hoeveelheid haakjes omheen staan. In onderstaande tekening zijn dat de condities met 1 gemarkeerd. Dus de condities c1, c2, c3 en c4. Deze worden dan vervangen door de uitkomst van deze condities waardoor het zoiets wordt als dit: ( ( u1 && u2 ) || ( u3 && u4 ) )
(waarbij het u-nummer staat voor de uitkomst van een conditie)

Vervolgens gaat de computer een niveau naar buiten, dus weer waar (simpel gezegd) de meeste haakjes omheen staan en die in de tekening met 2 is gemarkeerd: ( u12 || u34 ).
(waar u12 de uitkomst is van u1 && u2, en u34 de uitkomst van u3 && u4).

En uiteindelijk zal de computer aankomen bij de laatste haakjes (3 in de tekening), wat resulteert in: u1234
(waarbij u1234 de uitkomst is van u12 || u34).

Visueel weergegeven zou dat er zo uit kunnen zien:

Haakjes gebruiken - Verwerkt van binnen naar buiten

Haakjes gebruiken – Verwerkt van binnen naar buiten

Nu snap ik helemaal dat dit misschien iets boven je pet gaat, laten we dus naar een rekenvoorbeeld gaan kijken.

N.b.: Het voorbeeld hieronder is slechts ter illustratie, de Arduino zou dit zonder haakjes ook goed doen.

A = 3 * 4 + 12 / 4 + 6 * 3

Als we geen prioriteiten geven aan de rekentekens, dan zou het antwoord hiervan kunnen zijn:

A = 12 + 12 / 4 + 6 * 3
A = 24 / 4 + 6 * 3
A = 6 + 6 * 3
A = 12 * 3
A = 36

Maar we kunnen het ook anders doen waardoor we een ander antwoord krijgen:

A = 12 + 12 / 4 + 6 * 3
A = 12 + 12 / 10 * 3
A = 12 + 12 / 30
A = 24 / 30
A = 0.8

Je raadt het al: beiden zijn fout. Bij normale berekeningen weet de computer natuurlijk wel in welke volgorde het e.e.a. gedaan moet worden. Laten we dat eens illustreren met haakjes.

Let op: bij rekenen gaat vermenigvuldigen en delen voor optellen en aftrekken!

A = ( (3 * 4) + (12 / 4) + (6 * 3) )

Als we nu de volgorde van haakjes afhandelen gebruiken, waarbij de binnenste of diepste haakjes eerst gedaan worden, dan krijgen we:

A = ( (12) + (12 / 4) + (6 * 3) )
A = ( (12) + (3) + (6 * 3) )
A = ( (12) + (3) + (18) )
A = ( 15 + 18 )
A = 33

En dit is het juiste antwoord – je ziet dus dat de volgorde van groot belang kan zijn.
Het gebruik van haakjes forceert dit zelfs en kan leesbaarheid bevorderen.

Code tussen haakjes, wordt eerst geëvalueerd. Van de diepste haakjes tot de uiterste haakjes, to er geen haakjes meer over zijn.

Switch … case …

Het “switch … case …” statement, is een soort super “if…then…” variant die niet vaak gebruikt wordt maar wel erg veel werk kan besparen.

Het vervangt een groep “if…then…” statements in een bepaald aantal situaties en heeft dus ook invloed op de control de flow van programma’s, waarbij bepaalde code wordt uitgevoerd onder bepaalde condities.

Het “if” statement staat toe dat we condities kunnen gebruiken m.b.v. vergelijkingen en booleans.
Het “switch” statement echter, werkt alleen maar met “equal to” of te wel “gelijk aan” situaties.

Laten we eens kijken naar een “if” voorbeeld, die we later kunnen vervangen met een switch statement.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
void setup() {
  // set the speed for the serial monitor:
  Serial.begin(9600);
 
  // define our variable
  int A = 5;
 
  if(A==1) {
    Serial.println("A = 1");
  }
  else if(A==2) {
    Serial.println("A = 2");
  }
  else if(A==3) {
    Serial.println("A = 3");
  }
  else if(A==4) {
    Serial.println("A = 4");
  }
  else if(A==5) {
    Serial.println("A = 5");
  }
  else if(A==6) {
    Serial.println("A = 6");
  }
  else if(A==7) {
    Serial.println("A = 7");
  }
  else if(A==8) {
    Serial.println("A = 8");
  }
  else if(A==9) {
    Serial.println("A = 9");
  }
  else if(A==10) {
    Serial.println("A = 10");
  }
  else {
    Serial.println("A is kleiner dan 1 of groter dan 10");
  }  
}

void loop() {
  // leave empty for now
}

Deze lange code geeft het antwoord: A = 5

Maar dit is wel een hoop “if” statements, niet waar?
En ja, dit kan efficiënter worden geschreven, en wel met het “switch” statement.

Het formaat hoe we “switch” gebruiken ziet er zo uit:


switch (variabele) {
    case waarde1:
      // doe dit als de variabele gelijk is aan waarde1
      break;
    case waarde2:
      // doe dit als de variabele gelijk is aan waarde2
      break;

    ... // etc.

    default:
      // optioneel: doe dit als geen van de stellingen waar is
    break;
  }

Dus het “switch” statement, neemt de huidige waarde van de “variabele” en vergelijkt het met de waarde die bij iedere “case” statement staat, om te bepalen of de daaropvolgende code moet uitvoeren. De optionele waarde “default:” (Engels voor “standaard”) geeft aan welke code wordt uitgevoerd als geen van de stellingen waar zijn. – dit hoeft dus niet gebruikt te worden in een switch statement.

Laten we de voorgaande code eens met een “switch” statement doen:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
void setup() {
  // set the speed for the serial monitor:
  Serial.begin(9600);
 
  // define our variable
  int A = 5;

  switch(A) {
    case 1: Serial.println("A = 1");
    case 2: Serial.println("A = 2");
    case 3: Serial.println("A = 3");
    case 4: Serial.println("A = 4");
    case 5: Serial.println("A = 5");
    case 6: Serial.println("A = 6");
    case 7: Serial.println("A = 7");
    case 8: Serial.println("A = 8");
    case 9: Serial.println("A = 9");
    case 10: Serial.println("A = 10");
    default: Serial.println("A is kleiner dan 1 of groter dan 10");
  }  
}

void loop() {
  // leave empty for now
}

In deze code heb ik expres een fout gemaakt, om een klein verschil met “if” aan tegeven!

De output is namelijk onverwacht:

A = 5
A = 6
A = 7
A = 8
A = 9
A = 10
A is kleiner dan 1 of groter dan 10

De reden dat dit fout gaat is omdat het “switch” statement zal zoeken naar een “case” die true is en zal de opvolgende code gaan uitvoeren.
Omdat A=5, worden alle statements vanaf “case 5:” uitgevoerd.

Dit kan zeer handig zijn in bepaalde situaties, maar in de meeste gevallen willen we dat de “switch” statement stopt nadat het “case 5:” heeft gevonden. Om dat te bewerkstelligen, hebben we het “break” statement nodig aan het einde van iedere “case”.

Vergeet het “break” statement niet aan het einde van elke “case” als je de code uitvoering wil beperken tot alleen deze case.

Onze verbeterde code ziet er daarom dan ook zo uit:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
void setup() {
  // set the speed for the serial monitor:
  Serial.begin(9600);
 
  // define our variable
  int A = 5;

  switch(A) {
    case 1: Serial.println("A = 1");
            break;
    case 2: Serial.println("A = 2");
            break;
    case 3: Serial.println("A = 3");
            break;
    case 4: Serial.println("A = 4");
            break;
    case 5: Serial.println("A = 5");
            break;
    case 6: Serial.println("A = 6");
            break;
    case 7: Serial.println("A = 7");
            break;
    case 8: Serial.println("A = 8");
            break;
    case 9: Serial.println("A = 9");
            break;
    case 10: Serial.println("A = 10");
            break;
    default: Serial.println("A is kleiner dan 1 of groter dan 10");
  }  
}

void loop() {
  // voorlopig even leeg laten
}

Om te zien hoe “default” werkt, zou je regel 6 kunnen aanpassen naar b.v. A = 11;  waarbij alle “case” statements falen, en “switch” uit gaat komen bij het “default” statement. Mochten we echter “default” niet hebben geplaatst, dan zal er gewoon niets gebeuren omdat er geen geldig statement is gevonden.

Gebruik van “default” in een”switch” statement is optioneel

Zoals gezegd, het “switch” statement wordt veel minder gebruikt dan het “if” statement.
Overigens is het sterk aan te raden om de “case” waarden in een logische volgorde te zetten waardoor code beter leesbaar wordt en je geen onverwachte resultaten tegen gaat komen.

Probeer, indien mogelijk, om de case waarden in een logische volgorde te plaatsen, voor betere leesbaarheid en ter voorkoming van onverwachte resultaten.

 

 

Als je vragen hebt: stel ze dan hieronder, en bedenk dat er geen domme vragen zijn, behalve dan natuurlijk de vraag die niet gesteld is. We zijn allemaal een keer bij nul begonnen!

Volgende hoofdstuk: Arduino Programmeren voor Beginners – Deel 5: Lussen

Ondersteun ons ...


Jouw ondersteuning wordt zeer gewaardeerd, en hoeft zelfs niets te kosten. Bijvoorbeeld door links naar ons te delen op social media, of andere websites.

Andere vormen kunnen ook gratis zijn (b.v. shoppen op Amazon).
Alle opbrengsten worden gebruikt voor web-hosting kosten, project hardware en software, koffie, etc.

Hartelijk dank voor wie al heeft bijgedragen!
Het is altijd geweldig om te zien hoe men mijn artikeltjes en applicaties weet te waarderen.

Merk op dat het klikken op affiliate links een kleine commissie voor ons kunnen genereren - dit wordt zeer gewaardeerd.

Reacties


Er zijn 15 reacties welke je hieronder kunt lezen.
Je kunt jouw eigen opmerkingen plaatsen m.b.v. dit formulier, of een reactie op een bestaande opmerking plaatsen door op de "Beantwoorden" knop te klikken.

  • 15 nov 2016 - 12:04 - Peter van Oosterom Reactie Link

    Hallo ??,

    Ik deel 4 moet je nog wel even het een en ander herstellen. Je gebruikt AlMijnGeld als integer en even later staat er in de if ..  else  ..  If AllMyMoney Deze integer heb je niet benoemd dat moet AlMijnGeld zijn.

    Dit gebeurd in de eerste broncode van deel 4. Ook daarna ga je Spaargeld op 0 zetten in de uitleg maar schrijf je Savings = 0 Ook in het voorbeeld erna staat If(AllMyMoney<5) dat gaat ook niet werken dat begrijp je zelf ook wel.

    Ik zeik je niet af hoor want ik vind dat je het prima uitlegt alleen even wat secuurder zijn met de namen van de integers. Ik snap wat je bedoeld maar voor een echte leek even netjes na kijken en herstellen. Want die snappen er niets meer van.

    Met vriendelijke groet,

    Peter van Oosterom

    Beantwoorden

    Peter van Oosterom

    • 15 nov 2016 - 14:50 - hans - Auteur: Reactie Link

      Hoi Peter,

      Hartelijk dank voor het vinden van deze fouten – gebeurt weleens als ik het e.e.a. moet vertalen, en dan vooral in code vergeet ik weleens te kijken naar de vertaling. Oops!! Dus ik lees het niet als iets negatiefs – eerder als iets positiefs! Ik ga zo meteen door de code lopen en de foto verbeteren!

      Dank je wel 

      Beantwoorden

      hans

    • 15 nov 2016 - 14:54 - hans - Auteur: Reactie Link

      OK, heb hopelijk alle fouten in de code/tekst gevonden en verbeterd! 

      Ik schrijf elke pagina eerst in het Engels en vertaal het daarna in het Nederlands (ik woon zelf in de VS dus ik denk makkelijker in het Engels). Vandaar soms ook de “stomme” taal fouten zoals “d”, “t” en “dt” …

      Mocht je meer fouten vinden; hou je vooral niet in en laat het me gerust weten! 

      Beantwoorden

      hans

  • 7 nov 2019 - 20:45 - Jordy Reactie Link

    Hallo 

    Je hebt wel slimme neefjes ik ben 32 en snap er werkelijk niets van deel 4 haha zal wel aan mij liggen. Wel een mooie site!

    Misschien verstandiger voor mij dat ik een boek voor dummies koop. 

    Beantwoorden

    Jordy

    • 8 nov 2019 - 11:01 - hans - Auteur: Reactie Link

      Hoi Jordy!

      Vervelend om te horen – als je interesse hebt, dan wil ik er best samen met jou doorheen lopen (dan kan ik Deel 4 misschien verbeteren).

      M’n neefjes zijn misschien slim (is nog maar de vraag), maar doorzettingsvermogen zit er bij hun niet in.
      Een van de twee is niet eens begonnen (was al moe bij de gedachte) en de tweede gaf het na de eerste les al op (te veel moeite) hahah 

      Beantwoorden

      hans

      • 8 nov 2019 - 11:36 - Jordy Reactie Link

        O haha dan heb je niet veel aan slim nee, ik wilde mezelf is verdiepen in programmeren en welke talen ik moet kiezen etc. Heb altijd wel de interessen ervoor gehad maar zag er zo moeilijk uit dat ik er niet aan wilde beginnen maar ik ga het toch maar gewoon aan want aan spijt heb je niets.

        Na het stukje tussen haakjes gaat het mis  de laatste berekening van ‘33’ snapte ik wel maar switch en case, kan gewoon niet volgen wat je uitlegt .

        Beantwoorden

        Jordy

      • 8 nov 2019 - 13:09 - hans - Auteur: Reactie Link

        Taal keuze

        Ik ken het probleem wat taal kiezen betreft maar al te goed, en bijna dagelijks kijk ik nog om me heen wat voor nieuwe opties er zijn  .
        De keuze hangt een beetje af van wat je doel is.
        Als je met Arduino aan de slag wilt dan is C eigenlijk de enige keuze.

        Wil je aan de slag op een Mac, Windows PC of Linux PC, dan heb je wat meer keuze.

        Voor PC’s heb je meer talen beschikbaar. Talen zoals C, C++, Pascal, C#, en Java. Deze talen vertonen grote gelijkenis, dus als je bekend wordt met C dan is overstappen naar een van de andere talen minder moeilijk.
        Ik zelf heb de voorkeur voor Pascal, omdat het erg structureel werkt, en je dwingt om dingen correct te doen.
        Talen zoals de C varianten laten “rommelen” toe, en dat komt met de nodige problemen als je een fout probeert te vinden in de code.
        Maar … dat is ieder voor zich – vaak is het gewenning, en als je eenmaal aan een taal gewend bent, of de tools die voor een taal beschikbaar zijn, dan is het lastig wennen aan iets anders.

        Talen zoals Python zijn populair op het moment, maar ik ben er niet zo kapot van. Het is meer een “script” taal.

        Het verschil: 

        Talen zoals C en Pascal worden vaak gecompileerd. D.w.z. als je klaar bent met het maken van jouw programma (of het programma wilt testen), dan wordt het meteen omgezet naar machinecode.
        Als de gebruiker het programma start, dan is het ook meteen beschikbaar.

        Een script taal daarentegen wordt vaak pas bij gebruik omgezet naar machinecode, dus pas als de gebruiker het programma start.
        Het resultaat is dus dat een script taal vaak slomer is. Op een snelle PC hoeft dat overigens geen probleem te zijn.

        Uiteraard hebben we dan nog de (oude) BASIC varianten, welke leuk zijn voor een programmeer intro, maar zelden toepasbaar zijn voor het maken van de betere applicaties.

        Voor web design hebben we dan nog JavaScript (lijkt ook erg veel op C).

        Structureel werken de meeste van deze talen hetzelfde, je moet soms alleen even wennen aan kleine verschil.
        Als voorbeeld:

        Pascal

        var
          counter:integer;
        begin
          for Counter:=0 to 10 do
            writeln(Counter);
        end.

        C varianten

        int Counter;
        for (Counter=0; Counter<=10; Counter++) 
        {
          printf(Counter);
        }

        Je ziet een vergelijkbare structuur.

        Maar, we zijn nu met de Arduino bezig, en ik ben al lekker overboard gegaan met de “taal keuze” hahah. Maar misschien toch leuk om te weten.

        Het “switch” verhaal

        Switch is eigenlijk gedacht als een kortere notatie voor een berg if-then statements (wat er slordig uit kan zien).
        Omdat ik niet precies weet waar je vastloopt, doe ik maar een beginnetje.

        Stel je wilt een waarde vergelijken met meerdere mogelijke uitkomsten bv. de evaluatie van variabele “A”.
        Afhankelijk van wat de waarde van A is, willen we een actie doen (dit stuk begreep je al vermoed ik):

        if(A==1) {
            Serial.println("A = 1");
          }
          else if(A==2) {
            Serial.println("A = 2");
          }
          else if(A==3) {
            Serial.println("A = 3");
          }

        Zoals je ziet; herhaling van if statements, en bij programmeren mag je altijd even het hoofd krabben als je herhaling ziet, omdat het vaak een indicatie is dat het e.e.a. efficienter gedan kan worden.
        De “Serial.println()” is natuurlijk een erg triviale actie, het is maar een voorbeeld.

        We kunnen deze code met het switch-case statement beschrijven als:
          switch(A) {
            case 1: Serial.println("A = 1"); break;
            case 2: Serial.println("A = 2"); break;
            case 3: Serial.println("A = 3");
          }  

        Initieel, in zo’n eenvoudig voorbeeld, lijkt het niet efficiënter – we moeten zelfs meer typen dan de if-then.
        Maar in complexere situatie kan het veel kopieer-en-plak-werk besparen, en is de code efficienter dan al de if-thens.

        Lees het als:

        Schakel (switch) met de waarde van A:
          indien (case) 1 dan (:) output A=1 en stop dan met evaluatie (break)
          indien (case) 2 dan (:) output A=2 en stop dan met evaluatie (break)
          indien (case) 3 dan (:) output A=3 en stop dan met evaluatie (break)

        De reden waarom er een “break” instaat is als volgt; als een waarde gevonden wordt, dan wordt alles daarna uitgevoerd, tot er ergens een “stop” ingezet wordt (break dus).
        Dus als A nu 1 had geweest, en we de “break” statements weg hadden gelaten, dan zouden we dus als output zien: 1, 2, 3.
        Niet wat we wilden, want we wilden alleen maar “1” zien in dat geval.

        Het plaatsen van de “case” waarden mag willekeurig zijn, maar over het algemeen is het beter leesbaar (en soms sneller – afhankelijk van de compiler) om de waarden in logische volgorde te zetten.

        Nu aan jou om te laten weten waar je vast loopt, dan kan ik daar antwoord op geven 

        Beantwoorden

        hans

  • 21 mrt 2020 - 18:22 - Edwin Reactie Link

    Leuke cursus in begrijpelijke jip en janneke taal. Ga weer verder met het volgende deel.

    Beantwoorden

    Edwin

  • 23 mrt 2020 - 21:13 - Henk te Kolstee Reactie Link

    Hallo Edwin,

    Fijn, dat je zo’n handige cursus geschreven hebt en fantastisch, dat je nog zo lang na de eerste publicatie aan “nazorg” doet! Daarom hoop ik, dat je mijn vraag nog wilt beantwoorden.

    In cursusdeel 4 in het onderdeel over het switch statement snap ik iets niet.
    In het eerste voorbeeld (met A=5 , switch(A) en dan dan case 1, case 2 enz. heb je een “foutje” ingebouwd, n.l. geen break.
    Pas als case 5 (regel 13) aan de beurt is, wordt A = 5 geprint. Snap ik.

    Het programma gaat dan verder met case 6. Uit je omschrijving voorgaand aan het voorbeeld begrijp ik, dat er nu weer een test volgt: is A gelijk aan 6 ?
    Omdat A gelijk is aan 5, zal toch die code daarachter (Serial.println …) niet worden uitgevoerd en ook de volgende testen vallen negatief uit.

    Waarom wordt dan toch dan toch “A = 6, A= 7, enz. afgedrukt?

    In de Arduino reference staat eigenlijk hetzelfde als jij zegt, namelijk, dat “alle statements vanaf “case 5:” worden uitgevoerd. Zonder nog te testen dan??

    Met vr. groet,
    Henk

    Beantwoorden

    Henk te Kolstee

    • 24 mrt 2020 - 10:49 - Hans - Auteur: Reactie Link

      Hoi Henk,

      Het “switch” statement werkt een beetje anders dan het “if” statement.

      Bij het “if” statement wordt de hele “lus” verlaten nadat een match werd gevonden.
      In andere woorden: in het eerste voorbeeld onder “Switch … case …”, worden de “if” statements getest tot een match wordt gevonden.
      Zodra een match wordt gevonden (A=5, dus regel 20), wordt het opvolgende code blok uitgevoerd (tussen de accolades, regel 21) en springt na het uitvoeren van het blok naar regel 40 (dus alle hierop volgende “else if” statements worden gewoon overgeslagen, we hebben immers al een antwoord gevonden).

      Het “switch” statement lijkt hierbij een beetje een vreemde constructie.
      Zodra een match wordt gevonden, wordt alle volgende code uitgevoerd tot een “break” wordt gevonden.

      Misschien is het eenvoudiger om het zo uit te leggen:

      Zie “switch” als een schakelaar die springt naar een positie in de code, en zie de “case xyz:” regels als markeer punten waar “switch” heen kan springen. Deze markeerpunten (ook wel labels genoemd, zoals men dat in het goto statement kan zien) doen op zichzelf niks, er wordt daar dus geen vergelijking gedaan. Het is een “eindbestemming”.

      Switch zoekt dus naar de “eindbestemming” (of “label”) welke voldoet aan de criteria en voert daarna alles uit tot het een “break” statement ziet (wat ook in andere loops gebruikt wordt om uit de “lus” te springen), OF tot het einde van het blok wordt bereikt.

      Hopelijk verduidelijkt dit het e.e.a. 

      Beantwoorden

      Hans

      • 24 mrt 2020 - 12:56 - Henk te Kolstee Reactie Link

        Aha, ik snap ‘m (denk ik!).
        In de switchroutine wordt er 1 keer een sprong naar een passend label gemaakt en dan is de routine afgelopen.

        Lijkt me vergelijkbaar met “On … goto” in good old Basic.

        Dank je voor het snelle antwoord!
        m.v.g.,
        Henk

        Beantwoorden

        Henk te Kolstee

      • 24 mrt 2020 - 13:27 - Hans - Auteur: Reactie Link

        Helemaal correct …  

        Graag gedaan 

        Beantwoorden

        Hans

  • 10 jul 2020 - 15:07 - Rob Wels Reactie Link

    Hoi Hans,

    ik wil graag met een pushbutton code laten starten en met nog een keer drukken deze laten beeindigen.

    Kun je me een voorzetje geven in deze?

    Gr,

    Rob

    Beantwoorden

    Rob Wels

    • 12 jul 2020 - 12:56 - Hans - Auteur: Reactie Link

      Hoi Rob,

      Geen probleem:

      1) Je begint met het aansluiten van een push button op een van de I/O pinnen van de Arduino.
      B.v. tussen Pin 2 en GND (Aarde). Hiervoor definieer ik normaal gesproken een waarde voor de PIN, helemaal aan het begin van de code, zoiets als:

      #define KnopPin 2

      2) In de code definieer je een globale boolean variable, zodat we onze “status” kunnen terugvinden en updaten.
      B.v. zoiets als dit, waarbij we er vanuit gaan dat onze actie “uit” (false) staat als de Arduino start.

      #define KnopPin 2
      bool KnopStatus = false; 
      ...
      void setup() {
      ... 
      }
      ...
      void loop() {
      ...
      }

      3) In de setup() moeten we aangeven dat pin 2 een input wordt voor een knop.
      Zoiets als hieronder (de interne pull-up weerstand wordt gebruikt als we PIN 2 en GND gebruiken – nog niet te hard over nadenken):

      void setup() {
      ... 
        pinMode(KnopPin,INPUT_PULLUP); // internal pull-up resistor
      }

      4) In loop() moeten we dan de knop status steeds opnieuw bekijken om onze actie te starten of the stoppen. Als de knop ingedrukt wordt, willen we de status dus omgooien (van false naar true, of van true naar false) en dat kunnen we met de “not” operator doen (not = uitroepteken):

      void loop() {
      ...
        if (digitalRead(KnopPin) == HIGH) {
          KnopStatus = !KnopStatus;
        }

      }

      Als je dit werkende hebt, dan komt de uitdaging, wat afhankelijk is van de actie die je gaat uitvoeren.
      De Arduino kent namelijk geen multitasking, of “events”, dus als de knop niet vaak genoeg wordt gecontroleerd, dan kun je het indrukken van de knop missen.
      Dus: regelmatig de knop status uitlezen met de if-then loop.

      Zoals gezegd: dit kan lastig worden als je een actie hebt die veel stappen moet uitvoeren of in een loop blijft rondgaan.
      Je kunt dan eventueel een zogenaamde interrupt gebruiken, maar dat is wat verder gevorderd. Als je wilt dan kan ik dat ook uitleggen (een werkent, maar misschien wat complex voorbeeld, kun je hier vinden). Ik zou wel eerst gaan spelen met een oplossing zonder interrupt. Dat kan namelijk al voldoende zijn, en zo niet dan heb je op z’n minst een beter gevoel gekregen voor dit soort probleem. 

      Op basis van de waarde van “KnopStatus” kun je dan iets wel of niet doen.

      Hopelijk helpt dit je op weg 

      Beantwoorden

      Hans



Jouw Opmerking ...

Plaats hier geen grote bestanden (zoals source codes, log files of config files). Gebruik hiervoor het Forum.

Delen:
*
*
Laat me per email weten als er nieuwe reacties zijn.
       Je kunt jouw RSS reader gebruiken om reacties te volgen.


Tweaking4All gebruikt de gratis Gravatar dienst voor Avatar weergave.