Les 1, Weg met delay!

Vaak wil je de Simple Walker meerdere taken laten uitvoeren. Bijvoorbeeld de sonar uitlezen, de servo motoren bewegen en tegelijk een LED laten knipperen. Het probleem is, de Simple Walker kan (net als elke Arduino) maar één ding tegelijk. Hoe lossen we dit op?

De truc is om de Simple Walker niet daadwerkelijk alles tegelijk te laten doen, maar hem zo snel tussen de verschillende taken te laten wisselen dat het lijkt of het tegelijk gaat.

We gaan een sketch maken waarmee we een lampje heel langzaam laat knipperen (2 seconden aan, 2 secondeden uit), maar als er een object voor de afstandssensor staat, dan gaat het lampje snel knipperen (50 milliseconden aan, 50 milliseconden uit). Dit lijkt heel eenvoudig, we hebben een programma gezien om de LED te laten knipperen, en een programma om de objecten te detecteren met de sonar. Als we die twee combineren dan zijn we klaar!

Toch is het ietsjes ingewikkelder dan dat. Het probleem zit hem, zoals de titel van deze les al een beetje doet vermoeden, in de functie delay().

In deze les gaan we stap voor stap kijken hoe je zo’n sketch kan maken:

1
De volgende sketch laat LED13 heel langzaam knipperen:


In principe is er niets mis met deze sketch. Maar we zullen zien dat de delay()-functie voor problemen gaat zorgen.
2
De volgende sketch laat de LED branden, maar als de sonar een object detecteert gaat de LED uit:


Met de regel int afstand = meetAfstand(); gebruiken we de sonar om de afstand te meten tot het dichtstbijzijnde object. De regel if (afstand < 10) { test of deze afstand kleiner is dan 10 centimeter. Als dat zo is wordt de LED aangezet (digitalWrite(13, LOW);), zo niet dan wordt de LED uitgezet (digitalWrite(13, HIGH);).

3
Nu willen we beide bovenstaande programma's combineren. We willen de sonar uitlezen zoals in stap 2, maar in plaats van de LED simpelweg aan en uit te zetten willen we hem sneller laten knipperen. We doen een eerste poging:


Deze sketch lijkt heel erg op de sketch van stap 2, maar waar in de oorspronkelijke sketch de volgende code stond:


hebben we deze vervangen voor:


Dus in plaats van dat we de LED uitzetten als de afstand kleiner is dan 10 cm laten we de LED heel snel knipperen. (Eerst gaat de LED 50 milliseconden aan en daarna 50 milliseconden uit.)

Hetzelfde doen we daarna voor als de afstand niet kleiner is dan 10 cm:


Maar dan is de delay 2000 milliseconden (2 seconden) in plaats van 50 milliseconden (en knippert de LED dus veel langzamer).

Zet dit programma op de Simple Walker en kijk wat er gebeurt. Als het goed is gaat de LED heel langzaam knipperen, totdat je je hand voor de sonar houdt, dan gaat hij sneller knipperen.

Het valt je misschien op dat het wel werkt, maar maar niet heel goed: Als je je hand ervoor houdt dan kan het een paar seconden duren voor de LED reageert. Waarom?

4
Het probleem in de vorige stap is de delay. Elke keer als we delay aanroepen, dan doet de Simple Walker helemaal niets meer. Hij leest dus ook de afstandssensor niet uit.

Als in de bovenstaande sketch de gemeten afstand groter is dan 10 cm doet de Simple Walker het volgende:

  1. Zet de LED aan.
  2. Wacht 2 seconden.
  3. Zet de LED uit.
  4. Wacht weer 2 seconden

Al die tijd (in totaal 4 seconden) doet de Simple Walker niets, en kijkt hij dus ook niet of er inmiddels wel een object voor de sensor staat. Hij kan dus in het slechtste geval met een vertraging van 4 seconden reageren.

We kunnen dit al iets verbeteren door het aantal delay's in de code te verminderen:


Deze code is iets veranderd. Als je deze code op de Simple Walker zet zal je zien dat hij al iets sneller reageert. Waarom?

We hebben een functie toggleLED() gemaakt. De eerste keer dat deze functie wordt aangeroepen zet hij de LED uit, de volgende keer weer aan, de keer erna weer uit, etc.. De volgende code zou de LED snel laten knipperen:

Met de functie toggleLED() kunnen we de code al flink verbeteren. Het if (afstand < 10) stuk van de bovenstaande code is een beetje aangepast:


Dankzij de functie toggleLED() kunnen we de helft van de delay-regels weglaten. Als er geen object voor de sonar staat toggled de Simple Walker de LED, vervolgens wacht hij 2 seconden. Dat is al een stuk beter dan de 4 seconden van de vorige stap!

Maar het is nog niet goed genoeg. De code bevat nog steeds delay-statements. Het zou mooi zijn als we daar helemaal vanaf kunnen.

5
Een laatste poging:

Als je deze code op de Simple Walker zet, zal je zien dat hij een stuk beter reageert. Het zal je misschien ook opvallen dat deze code helemaal geen delay meer bevat (zoals belooft!).

Hoe werkt het? De code ziet er misschien wat ingewikkelder uit, maar het valt wel mee.

Het principe is simpel. In plaats van de Simple Walker steeds een paar seconden niets te doen, houden we hem aan het werk. We laten hem continue de afstand meten en tussendoor met regelmaat op de klok kijken of het al tijd is om de LED aan of uit te zetten.

De Simple Walker doet het volgende:

  1. Meet de afstand.
  2. Als de afstand kleiner is dan 10 cm, zet dan de toggleTijd op 50 milliseconden, anders op 2000 milliseconden.
  3. Kijk hoelang geleden de LED voor het laatst is getoggeld. Als dat meer is dan de toggleTijd is het tijd om de LED te toggelen.
  4. Begin weer bij stap 1.

Om dit in de code te bewerkstelligen hebben we eerst een aantal variabelen gedefinieerd:

Met toggleTijd kunnen we beïnvloeden hoe snel de LED knippert. In laatsteTijdsMeting houden we bij op welk tijdstip we de laatste keer de LED aan of uit hebben gezet. In nu houden we de huidige tijd bij.

In het if-stuk van de sketch veranderen we nu alleen maar de variabele toggleTijd en doen we verder niets. De waarde van toggleTijd is 50 milliseconden als de gemeten afstand kleiner is dan 10 cm, en anders is de waarde 2000 milliseconden.

En tot slot is dit het stukje code waar de magie plaatsvindt. Eerst meten we de huidige tijd (het aantal verstreken milliseconden sinds het starten van de Simple Walker) met nu = millis();. Vervolgens bepalen we met nu - laatsteTijdsMeting de verstreken tijd sinds de laatste keer dat we de LED aan of uit hebben gezet. Dan testen we of die groter of gelijk is aan de toggleTijd (if (nu - laatsteTijdsMeting >= toggleTijd)). Als dat zo is dan is het tijd om de LED te toggelen toggleLed();. In dat geval moeten we natuurlijk ook de laatsteTijdsMeting aanpassen. Die maken we gelijk aan de huidige tijd ( laatsteTijdsMeting = nu;).

Opdrachten

Opdracht 1, Paniek!

De Simple Walker is erg snel bang. Als hij opeens merkt dat er een object voor hem staat dan raakt hij in paniek. De enige manier die hij heeft om dat te laten blijken is met behulp van de LED.

Pas de bovenstaande sketch (die van stap 5) zo aan dat als er een object te dichtbij komt (de gemeten afstand kleiner is dan 10 cm) de LED een SOS-signaal in morsecode knippert. Ter herinnering het SOS signaal is ...---... (kort-kort-kort-lang-lang-lang-kort-kort-kort).