Intermediate · Werkboek · 29. Microcontrollers programmeren in C++ StemExpert · Brecht Corbeel · schoolium.me
SE
StemExpert
I29. Microcontrollers programmeren in C++ — werkboek
IIntermediate · deel I7 · Programmeren
Werkboek · hoofdstuk 29Microcontrollers programmeren in C++
Van losse schetsen naar één programma dat nooit stilstaat: de sturing van de klimaatmodule
Je begint met types, voorspellen en fouten zoeken, schrijft dan zelf taken met millis() en zoekt fouten met Serial, en eindigt met een eigen bibliotheek, een uitbreiding van klimaat.ino en een tijdbudget. Alle codeoplossingen zijn gecompileerd en getest in een simulatie. Opdracht 29.14 is een bouwsteen voor de klimaatmodule van Habitat: bewaar ze in je portfolio.
De Mega in de kast van de klimaatmodule: één programma voor sensoren, pomp, lampen en dak.
Hoe lees je een opdracht?
Drie tredenOefenenToepassenUitdagen
Elk hoofdstuk bouwt op: eerst oefenen (één stap, direct toepassen), dan toepassen (meerdere stappen, in een context), dan uitdagen (transfer, open problemen, andere leerlijnen erbij).
De StemExpert-schaal5
Eén moeilijkheidsschaal van 1 tot 9 over de drie niveaus heen: Fundamental loopt van 1 tot 4, Intermediate van 3 tot 7, Expert van 6 tot 9. Zo zie je dat de uitdaging van het ene niveau de oefening van het volgende is.
Oefenen
Oefenen
één stap, direct toepassen
29.1
Welk type past?
Oefenen3Begrijpen12 min
Kies voor elke variabele van de klimaatmodule het kleinste type dat altijd volstaat, en zeg waarom. Gebruik de tabel met types uit het handboek (Mega: int = 16 bit).
a
Vul de tabel in.
variabele
type
waarom
(a) de ADC-waarde van de LDR
(b) het moment van de vorige meting (ms)
(c) de PWM-waarde van de lampen, 0 tot 255
(d) staat de pomp aan?
(e) de batterijspanning, bv. 13,2 V
(f) het aantal pompbeurten sinds de start (één per uur, 5 jaar lang)
(g) de som van 100 ADC-waarden
(h) het I²C-adres 0x44 van de SHT31
b
Een klasgenoot stelt voor om gewoon alles float te maken, 'dan past alles'. Geef twee redenen waarom dat geen goed idee is.
29.2
Voorspel de uitvoer
Oefenen3Programmeren12 min
Wat toont de seriële monitor als dit programma op een Arduino Mega draait? Schrijf per println één regel, zonder het programma uit te voeren.
Schrijf de zeven regels die op de monitor verschijnen.
b
Welke twee regels zouden op je computer (int = 32 bit) anders zijn? Waarom?
Hint
Rekent C++ in int of in float? Kijk naar de types van de getallen, niet naar het type links van =.
29.3
Welke functie, welke pin?
Oefenen3Begrijpen10 min
Vul voor elk onderdeel van de klimaatmodule in hoe je de pin instelt en leest of stuurt. Gebruik de pintabel.
a
Vul de tabel in.
onderdeel
pin
pinMode
functie
wat betekent HIGH / welk bereik?
vlotter
D2
knop S1
D22
eindeloop dak dicht
lichtsensor
groeilampen
D5
pomp
statusled
b
Waarom staat de vlotter op INPUT_PULLUP en de knop S1 op INPUT?
29.4
Vind de vijf fouten
Oefenen4Programmeren20 min
Een leerling test de pomp met dit programma: om de 5 s aan of uit, en uit als de vlotter te weinig water meldt. Er zitten vijf fouten in. Drie hoort de compiler, twee niet.
// pomp_test.ino - de pomp om de 5 s aan of uit, en de vlotter bewaken (met vijf fouten)constintPOMP=9;constintVLOTTER=2;boolaan=false;unsignedlongvorige=0;voidsetup(){pinMode(VLOTTER,INPUT_PULLUP);Serial.begin(9600);}voidloop(){unsignedlongnu=millis()if(nu-vorige>=5000){vorige+=5000;aan=!aan;}if(digitalRead(VLOTTER)=HIGH){aan=false;}digitalwrite(POMP,aan?HIGH:LOW);Serial.println(aan);}
a
Zoek de vijf fouten. Welke drie meldt de compiler? Op welke regel verwacht je elke melding?
b
Welke twee fouten merk je pas als het programma draait, en wat zie je dan?
c
Schrijf de verbeterde versie. Zorg er ook voor dat de monitor niet overspoeld wordt.
29.5
Rekenen met tijd en geheugen
Oefenen4Rekenen15 min
Gebruik: int 16 bit met teken, unsigned int 16 bit, unsigned long 32 bit; het EEPROM verslijt na ongeveer 100 000 schrijfbeurten per byte.
a
Hoeveel ms zijn 10 minuten, 1 uur en 1 dag? Past elk getal in een int? In een unsigned long?
b
Na hoeveel tijd loopt millis() over? En na hoeveel tijd wordt een int waarin je millis() bewaart negatief? Een unsigned int weer 0?
c
Hoeveel uurcycli van de pomp passen in één periode van millis()?
d
klimaat.ino bewaart de laadtoestand elk uur in het EEPROM. Hoe lang houdt het EEPROM dat vol? En als je het om de 2 s zou doen?
Toepassen
Toepassen
meerdere stappen, in een context
29.6
De seriële plotter: temperatuur en vocht
Toepassen4Programmeren20 min
De SHT31 op I²C-adres 0x44 meet de temperatuur en de relatieve vochtigheid van de kaslucht. Je wil ze in de seriële plotter zien, samen met een vaste lijn op 26 °C (boven 26 °C wordt het te warm voor sla, hoofdstuk 28).
a
Schrijf een programma dat elke seconde, zonder delay(), de drie waarden in het formaat van de plotter stuurt.
b
De vochtigheid schommelt tussen 60 en 85 %, de temperatuur tussen 18 en 28 °C. Waarom is het lastig om beide in één plotter te bekijken, en hoe los je dat op?
29.7
Trace-tabel: wanneer meet het programma?
Toepassen5Analyseren20 min
Twee versies van een meettaak met INTERVAL = 2000. Beide beginnen met vorige = 0. Versie A doet na een meting vorige += INTERVAL;, versie B vorige = nu;. De lus komt langs op deze momenten (ms): 1500, 2150, 3900, 4020, 5990, 6300, 8100.
a
Vul de trace-tabel in.
nu
A: nu − vorige
A: meten?
A: vorige na
B: nu − vorige
B: meten?
B: vorige na
1500
2150
3900
4020
5990
6300
8100
b
Hoeveel metingen doet elke versie? Wat gebeurt er met versie B op lange termijn?
c
Na 8100 blijft de lus hangen tot 15 000 ms, daarna komt ze weer vlot langs (15 000, 15 001, 15 002 …). Wat doen A en B? Welke is beter voor de coulombteller, die bij elke meting 2 s stroom optelt?
29.8
Water geven zonder delay()
Toepassen5Programmeren35 min
In F25 gaf de meetkas water met dit programma (Uno; SENSOR A0, VLOTTER 3, POMP 6, rode led 10, groene led 11; water geven onder 30 %):
voidloop(){intruw=analogRead(SENSOR);intvocht=map(ruw,DROOG,NAT,0,100);boolwater=(digitalRead(VLOTTER)==LOW);if(vocht<DREMPEL&&water){digitalWrite(ROOD,HIGH);digitalWrite(GROEN,LOW);digitalWrite(POMP,HIGH);delay(30000);// 30 s = 240 mLdigitalWrite(POMP,LOW);}elseif(vocht<DREMPEL){digitalWrite(ROOD,HIGH);digitalWrite(GROEN,LOW);}else{digitalWrite(ROOD,LOW);digitalWrite(GROEN,HIGH);}delay(900000UL);// 15 min: het water moet eerst inzakken}
a
De grond wordt droog vlak nadat de lus aan delay(900000UL) begon. Hoe lang duurt het voor het lampje rood wordt? En wat als de vlotter opengaat terwijl de pomp 30 s draait?
b
Herschrijf het programma zonder delay(). Gebruik een variabele fase (KLAAR, POMPEN, WACHTEN) en millis(). Het lampje moet meteen reageren en de pomp moet stoppen zodra de vlotter opengaat.
c
Waarom is er nog altijd een wachttijd van 15 minuten na een beurt, ook zonder delay()?
29.9
De CO₂-klep als taak
Toepassen5Programmeren30 min
De CO₂-klep op D10 (6 W, hoofdstuk 14 en 20) moet tijdens de lichtperiode (6 tot 22 uur) om het halfuur 30 s opengaan, maar niet als het dak openstaat. klimaat.ino heeft al secondenVanDeDag(nu), START_UUR en de globale dakOpen.
a
Schrijf de functie void taakCo2(unsigned long nu) met millis(), en een melding bij elke wissel.
b
Waarom mag de klep niet open als het dak openstaat?
c
Hoeveel minuten per dag staat de klep open, en hoeveel energie kost dat? Vergelijk met het budget van hoofdstuk 20 (15 minuten, 1,5 Wh).
29.10
Debuggen: altijd 25,00 °C
Toepassen5Programmeren25 min
Dit programma meet de temperatuur van het reservoir met de NTC op A0 (hoofdstuk 22). De monitor toont 25.00, 25.00, 25.00, en als het water opwarmt ineens -273.15.
// ntc_fout.ino - de temperatuur van het reservoir: waarom altijd 25.00?#include"pinnen.h"floattemperatuur(){intn=analogRead(NTC);longr=10000L*(1024-n)/n;// weerstand van de NTC (ohm)floatinvT=1.0/298.15+log(r/10000)/3950;return1.0/invT-273.15;}voidsetup(){Serial.begin(115200);}voidloop(){Serial.println(temperatuur());delay(2000);}
a
Welke tussenresultaten laat je afdrukken om de fout te vinden? Schrijf de extra Serial-regels.
b
Wat is de fout? Verklaar zowel de 25.00 als de −273.15.
c
Verbeter de functie.
29.11
Labo: hoe snel draait loop()?
Toepassen5Labo45 min
Met dit programma tel je hoeveel keer per seconde loop() rondgaat. Je zet telkens één van de regels in commentaar aan.
// lusteller.ino - hoeveel keer per seconde loopt loop()?unsignedlongvorige=0;unsignedlongrondes=0;voidsetup(){Serial.begin(115200);// proef (c): 9600}voidloop(){rondes++;// hier komt wat je wil testen, bv.:// analogRead(A1); // proef (b)// Serial.println(123); // proef (c) en (d)// delay(1); // proef (e)unsignedlongnu=millis();if(nu-vorige>=1000){vorige+=1000;Serial.print("rondes per seconde: ");Serial.println(rondes);rondes=0;}}
a
Voorspel eerst het aantal rondes per seconde voor (b) met analogRead (±0,11 ms), (c) met Serial.println(123) op 9600 baud, (d) hetzelfde op 115200 baud, (e) met delay(1). Eén teken kost 10 bits.
b
Meet (a) zonder extra regel en (b) tot en met (e).
proef
voorspeld
gemeten
(a) leeg
(b) analogRead
(c) println, 9600
(d) println, 115200
(e) delay(1)
c
Wat leer je hieruit voor klimaat.ino? Welke regel zou je nooit in elke ronde van loop() zetten?
Zo wordt dit beoordeeld
Voorspelling
Berekend uit de tijd per bewerking, met eenheden; bij Serial het aantal tekens en 10 bits per teken.
Metingen
Vijf metingen, elk een paar seconden stabiel afgelezen; baudrate van de monitor aangepast bij (c).
Vergelijking
Verschil tussen voorspeld en gemeten verklaard (de lus zelf kost ook tijd, de buffer van Serial).
Besluit
Serial in elke ronde en delay() vertragen de hele lus; analogRead is snel genoeg voor tientallen metingen per taak.
29.12
De overloop narekenen
Toepassen6Analyseren25 min
Een unsigned long rekent modulo 232=4294967296: een negatief resultaat krijgt er 232 bij. Het interval is 2000 ms.
a
Bereken nu - vorige zoals de Mega het doet, en zeg of de taak loopt.
vorige
nu
nu − vorige
taak loopt?
(a)
4 294 960 000
4 294 962 000
(b)
4 294 966 000
704
(c)
4 294 966 000
100
(d)
4 294 966 000
4 294 967 000
b
Een leerling schrijft unsigned long volgende = vorige + 2000; en if (millis() > volgende). Wat gebeurt er in geval (d)?
c
Je wil testen of je programma de overloop overleeft, zonder 49,7 dagen te wachten. Hoe pak je dat aan?
Uitdagen
Uitdagen
transfer, open problemen, leerlijnen combineren
29.13
Debat: mag delay() nog?
Uitdagen5Argumenteren20 min
Een klasgenoot zegt: 'Na dit hoofdstuk mag je delay() nooit meer gebruiken.' Een ander antwoordt: 'Onzin, zelfs de bibliotheek van de SHT31 gebruikt delay(20), en klimaat.ino ook: in batterijstroom() wacht het programma 20,4 ms.'
a
Geef twee situaties waarin delay() (of een korte wachtlus) geen probleem is, en twee waarin het wel een probleem is. Gebruik voorbeelden uit de klimaatmodule.
b
Formuleer een regel van één zin die beide klasgenoten aanvaarden.
29.14
Een eigen bibliotheek: sensoren.h en sensoren.cpp
Uitdagen6Programmeren40 min
klimaat.ino wordt lang. De sensorfuncties (gemiddeld, daglichtLux, batterijspanning, batterijstroom) horen in een eigen 'bibliotheek' in de schetsmap, samen met een nieuwe functie reservoirTemperatuur() voor de NTC op A0 (10 kΩ bij 25 °C, B = 3913 K na ijking, hoofdstuk 22).
a
Schrijf sensoren.h: de declaraties van de vijf functies, met een include-bewaking.
b
Schrijf sensoren.cpp met de definities. Welke twee bestanden moet het zelf met #include invoegen, en waarom?
c
In pinnen.h staat const int LDR = A1;. Wat zou er gebeuren als daar int LDR = A1; stond, nu pinnen.h in twee .cpp-bestanden terechtkomt?
Zo wordt dit beoordeeld
sensoren.h
Alleen declaraties met de juiste types; include-bewaking met een unieke naam.
sensoren.cpp
Definities identiek aan klimaat.ino; Arduino.h, pinnen.h en sensoren.h ingevoegd; reservoirTemperatuur met foutcontrole.
Test
Een testschets die alle vijf functies aanroept en de waarden plausibel toont.
Uitleg
Verschil tussen declaratie en definitie; waarom const in een headerbestand mag.
29.15
Habitat: een niveaualarm voor het reservoir
Uitdagen6Habitat50 min
Het voedingsreservoir (60 × 40 cm, vol bij 25 cm) heeft onderaan een druksensor MPX5010 op A5 (hoofdstuk 11): U=VS⋅(0,09⋅p+0,04) met p in kPa en VS = 5,0 V. Onder 10 cm (24 L) moet de pomp geblokkeerd worden en moet de statusled snel knipperen, met één melding op de monitor. Het alarm gaat pas weg boven 12 cm.
a
Bereken de ADC-waarde bij 10 cm en bij 12 cm water, en hoeveel millimeter één ADC-stap is (dichtheid 1000 kg/m³).
b
Schrijf float niveauCm() en void taakNiveau(unsigned long nu) (om de 2 s). Welke twee regels van klimaat.ino pas je aan?
c
Waarom twee grenzen (10 en 12 cm) en niet één?
d
Hoe test je het alarm zonder het reservoir leeg te laten lopen?
Zo wordt dit beoordeeld
Berekening
Druk, spanning en ADC-waarde juist bij 10 en 12 cm; resolutie in mm per stap.
Code
niveauCm met de formule van de datasheet; taakNiveau met millis(), twee grenzen, één melding per wissel; pomp en statusled aangepast.
Test
Een testplan zonder het reservoir te legen, en controle in de seriële monitor.
Habitat
In het portfolio: de alarmgrens, de nieuwe taak en het testresultaat.
29.16
Het tijdbudget van loop()
Uitdagen7Ontwerpen40 min
De meettaak van klimaat.ino doet elke 2 s: een SHT31-meting (readBoth: 20 ms wachten plus ±0,9 ms I²C-verkeer), 100 metingen van de stroom met 204 µs tussen twee metingen, en twee keer 16 gewone ADC-metingen van 0,112 ms. Eens per minuut stuurt taakMelden een regel van ongeveer 110 tekens op 115 200 baud; de zendbuffer van Serial is 64 tekens groot.
a
Hoe lang houdt taakMeten de lus bezig? Vergelijk met de 44,8 ms uit de simulatie.
b
Hoe lang duurt het versturen van de statusregel, en hoe lang blijft print() daarvan wachten?
c
Wat is in het slechtste geval de reactietijd op de knop (ontdenderen 20 ms)? Hoe ver schuift het dak bij 0,10 m/s voor een eindeloopschakelaar gezien wordt? En bij de kruipsnelheid van 30 %?
d
Ontwerp een verbetering waardoor de langste ronde onder 25 ms zakt, zonder minder te meten. Beschrijf de nieuwe taakverdeling.
Zo wordt dit beoordeeld
Tijdbudget
Elke bijdrage apart berekend en opgeteld; vergelijking met de simulatie.
Gevolgen
Reactietijd knop en overshoot van het dak juist berekend, met eenheden.
Ontwerp
Een concrete opsplitsing in fasen met millis(), die de werking niet verandert.
Afweging
Voor- en nadelen genoemd (complexere code, gegevens van verschillende momenten).
ZelfevaluatieKleur per doel: lukt al / bijna / nog niet
Ik kan uitleggen wat setup() en loop() doen, wat er bij het compileren en uploaden gebeurt en welk geheugen (flash, SRAM, EEPROM) waarvoor dient.
Ik kan voor elke grootheid het juiste C++-type kiezen (bool, byte, int, unsigned int, long, unsigned long, float) en weet dat een int op de Mega 16 bit is.
Ik kan overloop, gehele deling en te kleine types herkennen, voorspellen wat er gebeurt en de code verbeteren (L, UL, 5.0).
Ik kan functies schrijven met parameters en een returnwaarde, en het verschil uitleggen tussen globale, lokale en static variabelen.
Ik kan de in- en uitgangen van de klimaatmodule instellen en gebruiken met pinMode, digitalRead, digitalWrite, analogRead en analogWrite.
Ik kan met Serial.print en de seriële plotter tussenresultaten zichtbaar maken en zo een fout vinden.
Ik kan meerdere taken tegelijk laten lopen met millis() in plaats van delay(), en uitleggen waarom nu − vorige ≥ interval ook na de overloop van 49,7 dagen werkt.
Ik kan een bibliotheek installeren en gebruiken, en een eigen .h-bestand maken.
Ik kan een groter programma in stappen opbouwen en elke stap testen.