Handboek · hoofdstuk 40Integratieproject: de klimaatmodule
Ontwerpen, dimensioneren, programmeren, testen en verdedigen: je team brengt 39 hoofdstukken samen in de tweede laag van Habitat
Negenendertig hoofdstukken lang leerde je telkens één stuk van de klimaat- en energiemodule berekenen: het warmteverlies, de panelen, de pomp, de pH, een programma met modi. In dit integratieproject zet je team ze samen tot één module die zichzelf van energie, water, warmte en licht voorziet, en die elke minuut noteert wat ze doet. Je legt eerst de lat (het programma van eisen), rekent elke balans na, voegt vijf versies van de code samen tot één programma, laat de module een week alleen draaien, analyseert de logbestanden in Python en verdedigt het resultaat voor een jury. Het handboek is je projectgids, het werkboek je projectmap.

- Je kan een project van twaalf weken plannen met een team, met vaste eigenaars per deelsysteem, mijlpalen, een logboek en een integrator.
- Je kan een programma van eisen voor een samengesteld systeem opstellen: meetbaar, met een vooraf vastgelegd criterium en een toets per eis, en met verwijzing naar de berekening waar de eis vandaan komt.
- Je kan de interfaces tussen deelsystemen benoemen en vastleggen (N²-diagram, interfacetabel) en integratieproblemen opsporen door budget, schema en code naast elkaar te leggen.
- Je kan de energie-, water- en warmtebalans van de module narekenen met de waarden uit de vorige hoofdstukken, per dag, per maand en per jaar.
- Je kan meetketens van sensor tot getal narekenen, sensoren in twee punten ijken en de voedingsoplossing bijsturen.
- Je kan een programma van meer dan 500 regels lezen dat modi, regelingen, energiebeheer en een logger samenbrengt, en het uitbreiden en testen met een naspeeltabel.
- Je kan een testplan opstellen met deeltests, storingstests en een testweek, en de logbestanden in Python toetsen aan het programma van eisen.
- Je kan systematisch foutzoeken met de logbestanden en meldingen: van symptoom naar oorzaak, met een test die telkens één ding controleert.
- Je kan een technisch rapport schrijven en je ontwerp verdedigen voor een jury, en je eigen werk en dat van je teamgenoten eerlijk beoordelen.
Twee filters die niet in elkaar pasten
Op 13 april 1970, twee dagen na de lancering van Apollo 13, ontplofte een zuurstoftank in de servicemodule. De drie astronauten moesten de reis rond de maan en terug maken in de maanlander, die gebouwd was voor twee mensen en een kort verblijf. Daar liep al snel een tweede probleem op: de koolstofdioxide die ze uitademden, stapelde zich op. De maanlander had te weinig patronen met lithiumhydroxide om CO2 te binden. In de commandomodule lagen er genoeg, maar die waren vierkant, en de houder in de maanlander was rond.
Twee deelsystemen die elk perfect werkten, gebouwd door verschillende teams, pasten niet op elkaar. In Houston zochten ingenieurs een oplossing met alleen wat er aan boord was: plastic zakken, karton, slangen van de ruimtepakken en tape. Ze bouwden de adapter eerst zelf, testten hem, en lazen de bemanning daarna de bouwinstructies voor over de radio. De CO2 daalde weer, en op 17 april landde de capsule veilig.
Dat is integratie: nagaan of delen die elk werken, ook samen werken. De plaats waar ze elkaar raken, heet een interface: een maat, een stekker, een pin, een eenheid, een tijdzone. In de meetkas (Fundamental, hoofdstuk 33) had je er tientallen. De klimaat- en energiemodule heeft er meer, en ze zijn lastiger: een stroommeter die tot 20 A meet naast panelen die met 74 A laden, twee taken die dezelfde pomp schakelen, een energiebudget in UTC en een klok op UTC+1. In dit hoofdstuk zoek je ze op, voor ze in een testweek of in een ruimtevaartverslag opduiken.
Het handboek is je projectgids: per fase wat je doet en waarom, met de waarden uit de vorige hoofdstukken, uitgewerkte berekeningen en de resultaten van een voorbeeldteam (Emma, Jonas, Sara en Wout). Het werkboek is je projectmap: per fase de opdrachten, met ruimte om in te vullen en een rubric. Hoofdstuknummers zonder letter verwijzen naar Intermediate; F33 is de meetkas van Fundamental, E-nummers zijn Expert.

De klimaat- en energiemodule: alle deelsystemen
Zo ziet de afgewerkte module eruit. De kweekruimte is binnen 2,40 m lang en 2,00 m diep, met een kasdak onder 25°: de lage wand (zuid) is 1,93 m hoog, de hoge wand (noord) 2,87 m (hoofdstuk 19). Het rek staat tegen de hoge wand, de gang van 0,80 m ligt langs de lage wand. De oostwand grenst aan de woonmodule van het station: daar zitten de deur, het bedieningspaneel en de warmwaterleiding. De zuid- en westwand zijn doorzichtig getekend. Bij elk nummer staat het hoofdstuk waar het onderdeel gekozen of berekend werd.
- 1Kasdak
Meerwandig polycarbonaat 16 mm, U = 2,0, 5,30 m²: meer dan de helft van het warmteverlies (hoofdstuk 19). Het schuifpaneel van 10 kg glijdt 0,60 m omlaag en opent de nok (hoofdstuk 7 en 8).
- 2Dakmotor
Wormwielmotor 12 V met tandriem, H-brug op D6-D8, eindeloop op D23 en D24; sluit in 6,8 s, na 12 s zonder eindpunt volgt een alarm (hoofdstuk 26 en 32).
- 3Groeilampen
2 × 95 W, dimbaar met PWM op D5: 200 µmol/(m²·s) op het bed, van 6 tot 22 uur. Ze vullen het daglicht aan (hoofdstuk 25 en 36).
- 4NFT-rek
Vier goten van 2,4 m in trappen, elk 12 plaatsen om de 20 cm: 48 kroppen op 2,88 m². Helling 1 : 50, 1,2 L/min per goot (hoofdstuk 36).
- 5Reservoir en pomp
60 L, lichtdicht, 65 kg vol, op de vloer binnen de toegelaten zone (hoofdstuk 10 en 11). Pomp 12 W, vlotter, druksensor, NTC en de pH- en EC-sonde.
- 6Buffervat
Donker vat van 200 L onder het rek: samen met het reservoir 1,09 MJ per graad. Het houdt de module 's nachts warm (hoofdstuk 18 en 31).
- 7Convector
Ventiloconvector op de warmwaterleiding van het station, ±800 W; klep en ventilator via D12 (hoofdstuk 31).
- 8Ventilator
Afzuiging hoog in de westwand: 11 m³/h bij PWM 90 voor de CO₂ (hoofdstuk 14), vol boven 25 °C (hoofdstuk 31).
- 9CO₂-fles
Fles met een klep op D10: 30 s per halfuur, alleen overdag met het dak dicht (hoofdstuk 14 en 32).
- 10Sensorkast SK-1
SHT31 op I²C (0x44) aan een kabel van 3 m, midden in de ruimte op planthoogte: niet in de zon en niet boven de convector (hoofdstuk 30 en 31).
- 11Lichtsensor
LDR onder een kapje op de zuidrand van het dak: meet het daglicht, niet de lampen (hoofdstuk 22 en 25).
- 12Regelkast RK-1
Geprinte kast in PETG met de Mega, de klok en de SD-kaart (hoofdstuk 37). Het vermogensdeel zit apart.
- 13Energiekast
Twee LiFePO4-batterijen van 120 Ah met BMS, de MPPT-regelaar, de zekeringen en de twee meetshunts (hoofdstuk 16, 20 en 30).
- 14Bedieningspaneel
Knop S1 naast de deur (kort: dak, lang: onderhoud) en de statusled (hoofdstuk 24 en 32).
- 15Inspectierobot
Rijdt een lus door de gang langs drie meetpunten, ±50 s per ronde, en laadt aan zijn station (hoofdstuk 33).
- 16Zonnepanelen
4 × 350 Wp = 1,40 kWp onder 60° op het zuiden, in twee rijen op het platte dak van de woonmodule, met ballast (hoofdstuk 6, 10 en 20).
- 17Wanden en vloer
Houtskelet met minerale wol: U = 0,22 (wand) en 0,21 (vloer). Samen met het dak en de ventilatie H = 20,2 W/K (hoofdstuk 19).
De module als één systeem
In hoofdstuk 1 van Fundamental beschreef je de meetkas als een systeem met input, proces en output. De module is hetzelfde, maar groter. Het systeemschema toont zeven soorten input (zonlicht, warmte van het station, regenwater, zouten, CO2, plantjes en instellingen), acht deelsystemen en zes soorten output. Drie stromen houden alles samen:
- energie: zonnestroom via de batterijen naar lampen, pomp, ventilatoren en besturing; warmte van het station via de convector naar de lucht en de buffer;
- materie: water en zouten in een kringloop van reservoir, goten en planten; CO2 uit de fles en de lucht; sla en plantenresten naar buiten;
- informatie: metingen naar de Mega, beslissingen naar de actuatoren, een regel per minuut naar de logger.
Een module die zichzelf in leven houdt, moet elke dag drie balansen sluiten. Energie: wat de panelen leveren, moet het verbruik dekken, met de batterijen als buffer voor de nacht en voor grijze dagen. Water: wat de planten verdampen, moet bijgevuld worden voor het reservoir onder 10 cm zakt. Warmte: wat door de schil verdwijnt, moet de convector, de zon of de lampen aanvullen, met de buffer voor de nacht. De fasen van dit project rekenen elke balans na, eerst op papier, daarna in een testweek.
Loopt er iets mis, volg dan de pijlen terug. Te weinig licht op het bed? Kijk bij licht (lampen, dimmen), bij besturing (energiebeheer, grens) en bij energie (laadtoestand). De foutzoektabel verderop werkt precies zo.
Het project: fasen, eigenaars en planning
Een module van 2,4 bij 2 m bouwt niet elke school. Daarom kies je bij de start een spoor. Beide sporen doorlopen dezelfde fasen en leveren hetzelfde op: een programma van eisen, berekeningen, een programma, logbestanden, een testrapport, een rapport en een verdediging.
| spoor A: de echte module | spoor B: tafelopstelling en simulatie | |
|---|---|---|
| wat staat er | de kweekruimte met rek, dak, panelen en batterijen | regelkast RK-1 met de Mega, sensorkast SK-1, één goot met pomp en reservoir, één lamp, één batterij |
| wat je bouwt | 12,8 V-net, regelkast, hydroponie, sensoren | dezelfde regelkast en sensoren, op een plank; de rest is model |
| testweek | de logbestanden van de module zelf | de simulatie van dit hoofdstuk, plus een week met de echte sensoren op tafel |
| wat je zeker leert | bouwen, keuren, alles samen laten werken | integreren, testen, data lezen: de module verandert niet van plaats |
Eigenaars. Elk teamlid is eigenaar van één deelsysteem en van de interfaces die erbij horen: hij rekent het na, bouwt het, test het en verdedigt het. Eén van de vier is ook integrator: die bewaakt het programma van eisen, de interfacetabel en de planning. Iedereen soldeert, programmeert en test mee.
| rol | eigenaar van | vooral in fase | steunt op hoofdstuk |
|---|---|---|---|
| energie | panelen, batterijen, 12,8 V-net, zekeringen, energiebeheer, interfaces I1, I2, I8 | 2, 3 | 16, 20, 21, 23 |
| water en voeding | pomp, goten, reservoir, EC en pH, bijsturen, interfaces I5, I6, I12, I13, I16 | 2, 3 | 9, 11-15, 36 |
| klimaat en licht | warmte, ventilatie, dak, CO₂, lampen, interfaces I3, I9, I11, I14, I15, I17 | 2, 3 | 14, 18, 19, 25, 26, 31 |
| besturing en data | klimaat.ino versie 5, logger, analyse in Python, interfaces I4, I7, I10, I18 | 3, 4 | 22, 24, 28-32, 39 |
| integrator (één van de vier) | programma van eisen, interfacetabel, planning, testrapport, logboek | alle | 2, 30 (F), 40 |
Planning. Het voorbeeldteam kreeg twaalf weken van drie lestijden. Fase 1 legt de lat, fase 2 rekent per deelsysteem, fase 3 bouwt en programmeert parallel, fase 4 integreert en test, fase 5 schrijft en verdedigt. Zes mijlpalen zijn momenten waarop iets af moet zijn en getoetst wordt: het programma van eisen ligt vast (week 2), de berekeningen doorstaan een ontwerpreview met de leraar (week 4), de 12,8 V-kant is gekeurd (week 6), versie 5 draait in de simulatie (week 8), de testweek is voorbij (week 10, na de integratietests van week 9) en het rapport is ingediend (week 11).
Zonder gekeurde 12,8 V-kant geen sensoren in de module, zonder werkende code geen integratietest, zonder integratietest geen testweek, zonder testweek geen testrapport. Die keten bepaalt het tempo. De hydroponie start daarom al in week 4, parallel: de planten groeien tijdens het programmeren.
Fase 1: het programma van eisen van de module
Het programma van eisen is de lat waarmee je de module beoordeelt. Voor de meetkas stelde je het op voor je rekende (Fundamental, hoofdstuk 30). Voor de module ligt al veel vast: 39 hoofdstukken berekenden en beslisten. Het werk van fase 1 is die beslissingen verzamelen, meetbaar maken en elk een toets geven. Drie regels:
- Meetbaar: een grootheid, een grens met een eenheid, en hoe je het nagaat. 'Warm genoeg' wordt 'overdag minstens 17,5 °C' (K1).
- Vooraf: het criterium ligt vast voor je test, anders ga je het resultaat goedpraten.
- Herkomst: bij elke eis staat het hoofdstuk waar de waarde vandaan komt. Verandert daar iets, dan weet je welke eis mee verandert.
Het voorbeeldteam kwam op 54 eisen in twaalf groepen, waarvan 47 moet-eisen. De criteria zijn soms net iets ruimer dan de ontwerpwaarde: de verwarming springt aan onder 18 °C, maar de eis vraagt 17,5 °C, omdat een aan/uit-regeling altijd een beetje doorschiet (hoofdstuk 31) en de sensor ±0,2 °C meet.
De twaalf groepen
| letter | groep | moet | wens | vooral uit hoofdstuk |
|---|---|---|---|---|
| K | klimaat | 7 | 1 | 14, 31, 34 |
| L | licht | 3 | 0 | 25, 36 |
| W | water | 5 | 0 | 11, 23, 36 |
| H | voedingsoplossing | 4 | 1 | 13, 15, 36 |
| E | energie | 7 | 1 | 16, 20, 21 |
| B | besturing | 7 | 0 | 29, 31, 32 |
| D | data | 5 | 0 | 28, 30 |
| V | veiligheid | 4 | 0 | 5, 15, 26 |
| C | constructie | 2 | 0 | 8, 10, 26 |
| G | gebruik | 2 | 2 | 4, 24, 32 |
| P | teelt | 1 | 1 | 36 |
| O | omgeving | 0 | 1 | 38 |
Alle eisen
| code | eis | soort | criterium (vooraf vast) | hoofdstuk |
|---|---|---|---|---|
| K1 | overdag (7-22 h) minstens 17,5 °C bij de planten | moet | laagste minuut ≥ 17,5 °C | 31, 34 |
| K2 | 's nachts (22-6 h) minstens 15,5 °C | moet | ≥ 15,5 °C | 31, 34 |
| K3 | hoogstens 30 °C | moet | ≤ 30 °C | 31, 34 |
| K4 | ventilatoren vol boven 25 °C, dak open boven 27 °C; terug onder 24 en 25 °C | moet | drempels ± 0,3 °C | 31, 32 |
| K5 | het dak haalt zijn eindpunt binnen 12 s, anders alarm | moet | alarm na 12 s | 26, 32 |
| K6 | verwarming hoogstens 6 keer aan per uur | wens | ≤ 6 | 31 |
| K7 | basisventilatie overdag minstens 11 m³/h | moet | ≥ 11 m³/h | 14 |
| K8 | CO₂-klep alleen in DAG met het dak dicht | moet | nooit met het dak open | 14, 32 |
| L1 | lichtsom op het bed minstens 11,0 mol/(m²·dag) | moet | elke dag | 25, 36 |
| L2 | lampen vullen het daglicht aan tot 200 µmol/(m²·s), hoogstens 1 stap per 200 ms | moet | ± 10 µmol zolang SoC ≥ 40 % | 25 |
| L3 | volle lampen: elk van 9 meetpunten ≥ 180 µmol/(m²·s) | moet | ≥ 180 | 25, 36 |
| W1 | 1,2 L/min per goot (± 20 %) | moet | 0,96-1,44 L/min | 11, 36 |
| W2 | pomp in minuut 0-9 van elk uur, extra beurt als slappe sla verwacht wordt | moet | juist | 30, 39 |
| W3 | de pomp draait nooit droog | moet | pomp uit na 10 s droog | 11, 23, 32 |
| W4 | minstens 5 dagen zonder bijvullen bij 6 L per dag | moet | ≥ 5 dagen | 11, 13 |
| W5 | reservoir lichtdicht: algen onder 5 · 10⁵ per mL | moet | < 5 · 10⁵ per mL | 5 |
| H1 | EC tussen 1,2 en 1,8 mS/cm (bij 25 °C) | moet | altijd | 13 |
| H2 | pH tussen 5,5 en 6,5 | moet | altijd | 15 |
| H3 | voedingsoplossing nooit boven 24 °C | moet | ≤ 24 °C | 34, 36 |
| H4 | voedingsoplossing tussen 18 en 22 °C | wens | 18-22 °C | 34, 36 |
| H5 | pH-sensor om de twee weken in twee punten geijkt, EC met ijkvloeistof | moet | helling −0,18 V/pH ± 10 %; EC ± 0,05 | 13, 15 |
| E1 | van maart tot oktober genoeg zonnestroom: saldo per maand ≥ 0 | moet | ≥ 0 | 20 |
| E2 | de batterijen overbruggen de nacht in de slechtste maand | moet | uit de batterij ≤ 2,46 kWh | 16, 20 |
| E3 | laadtoestand nooit onder 20 % | moet | ≥ 20 % | 20 |
| E4 | energiebeheer: onder 40 % lampen op zonnestroom, onder 20 % uit, onder 10 % alarm | moet | lampen volgen de grens | 20, 29 |
| E5 | laadtoestand elk uur in het EEPROM, na een stroomuitval hersteld | moet | verschil ≤ 2 %-punt | 16, 29 |
| E6 | 5 V-rail aan de sensorkast minstens 4,75 V | moet | ≥ 4,75 V | 21 |
| E7 | batterijen niet laden onder 0 °C | moet | module ≥ 0 °C | 16 |
| E8 | jaarverbruik hoogstens 700 kWh | wens | ≤ 700 kWh | 20, 25 |
| B1 | na een stroomuitval: alles uit, OPSTART 5 s, dan de gewone modus | moet | juist | 29, 32 |
| B2 | alarm bij: geen temperatuur 30 s, vlotter droog 10 s, reservoir onder 10 cm, dak-time-out, batterij onder 10 % | moet | alarm, statusled snel | 32, 40 |
| B3 | zonder temperatuur verwarmt de module met de gemiddelde inschakelduur | moet | juist | 31 |
| B4 | onderhoud: lange druk van 3 s; pomp uit, dak geblokkeerd, werklicht; na 60 min terug | moet | juist | 32 |
| B5 | waakhond: een hangende taak herstart de Mega binnen 2 s | moet | herstart | 32 |
| B6 | langste ronde van loop() hoogstens 100 ms | moet | ≤ 100 ms | 29 |
| B7 | één taak per uitgang | moet | geen tweede eigenaar | 40 |
| D1 | elke minuut een CSV-regel, één bestand per dag | moet | 1440 regels per dag | 30 |
| D2 | een ontbrekende meting geeft een lege cel | moet | lege cel | 30 |
| D3 | minstens een jaar op de kaart | moet | ≥ 1 jaar | 30 |
| D4 | klok op UTC+1, hoogstens 1 min afwijking per maand | moet | ≤ 1 min | 30 |
| D5 | dag- en weekrapport in Python, gekoppeld aan de eisen | moet | draait | 28, 30 |
| V1 | in de kweekruimte alleen 12,8 V en 5 V | moet | in orde | 5, 20 |
| V2 | zekering per tak en bij elke batterijpool | moet | in orde | 20, 23, 26 |
| V3 | niemand onder een bewegend dak | moet | dak staat stil | 26, 32 |
| V4 | pH-min (salpeterzuur 0,50 mol/L) gelabeld, met bril en handschoenen | moet | in orde | 15 |
| C1 | reservoir (65 kg vol) met zijn midden tussen 0,65 en 1,31 m van ligger A | moet | in de zone | 10 |
| C2 | dakmotor trekt 120 N zonder te blokkeren | moet | ≥ 120 N | 8, 26 |
| G1 | statusled: rustig normaal, snel bij alarm, vast bij onderhoud | moet | juist | 32 |
| G2 | kasdak met de hand: korte druk op S1 naast de deur | moet | juist | 24, 32 |
| G3 | wekelijks onderhoud (bijvullen, EC en pH) hoogstens 20 min | wens | ≤ 20 min | 40 |
| G4 | hydroponie hoogstens € 800 | wens | ≤ € 800 | 4 |
| P1 | 48 plantplaatsen, om de 8,75 dagen een goot oogsten | moet | in orde | 36 |
| P2 | minstens 100 kg sla per jaar | wens | ≥ 100 kg | 36 |
| O1 | hoogstens 2 kg CO₂-eq per kg sla | wens | ≤ 2 | 38 |
Interfaces: waar de deelsystemen elkaar raken
Een interface is een afspraak tussen twee deelsystemen. Het N²-diagram toont ze in één oogopslag: de deelsystemen staan op de diagonaal, en het vakje in rij A en kolom B zegt wat A aan B levert. Een lege rij of kolom is verdacht: een deelsysteem dat niets krijgt of niets geeft, hoort er waarschijnlijk niet bij. De interfacetabel schrijft elke afspraak uit: de waarde met eenheid, de pin of het adres, de eigenaar en de test. Twee interfaces komen van buiten de module: het station (warmte, deur) en de bewoner (de knop).
| code | van → naar | afspraak | eigenaar | test |
|---|---|---|---|---|
| I1 | energie → water, klimaat, licht | 12,8 V-rail, elke tak een eigen zekering (hoofdstuk 20, 23, 26) | energie | spanning aan de verbruiker bij volle last |
| I2 | energie → besturing | 5 V-rail (omvormer 1 A): minstens 4,75 V aan de sensorkast (hoofdstuk 21) | energie | meten terwijl de pomp aanslaat |
| I3 | besturing → licht | D5, PWM 490 Hz: 0-255 = 0-190 W, hoogstens 1 stap per 200 ms (hoofdstuk 25) | besturing | lampstand in de logger |
| I4 | besturing → water | D9 (IRLZ44N): minuut 0-9 en de extra beurt; één eigenaar (hoofdstuk 23, 39) | besturing | naspelen |
| I5 | water → besturing | vlotter D2 (LOW = water), druk A5: niveau alleen bij stilstaande pomp (hoofdstuk 11) | water | vlotter optillen |
| I6 | voeding → besturing | pH A2 (2,50 V bij 7,00; −0,18 V per pH), EC A6 (0,50 V per mS/cm), NTC A0 (hoofdstuk 13, 15, 22) | voeding | ijken |
| I7 | klimaat → besturing | SHT31 op I²C 0x44, sensorkast op 3 m, pull-ups samen 1,8 kΩ (hoofdstuk 30) | klimaat | I²C-scanner |
| I8 | energie → besturing | INA226 0x40 (zon) en 0x41 (verbruik), shunt 100 A / 75 mV (hoofdstuk 30) | energie | stroomtang ernaast |
| I9 | besturing → klimaat | D10 CO₂-klep, D11 ventilatoren (PWM), D12 convector, D6-D8 dak, D23/D24 eindeloop (hoofdstuk 26, 31) | besturing | naspelen |
| I10 | besturing → data | één CSV-regel per minuut, 17 kolommen, UTC+1, lege cel = geen meting (hoofdstuk 30) | data | logbestand lezen |
| I11 | licht → besturing | LDR A1 onder een kapje: daglicht in lux, gedeeld door 54 = µmol/(m²·s) (hoofdstuk 22, 25) | licht | luxmeter ernaast |
| I12 | water → teelt | 1,2 L/min per goot, een film van enkele mm (hoofdstuk 36) | water | maatbeker |
| I13 | voeding → teelt | EC 1,4 mS/cm, pH 5,8, 18-22 °C (hoofdstuk 13, 15, 36) | voeding | logger |
| I14 | klimaat → teelt | 18-27 °C overdag, CO₂ ≥ 350 ppm (hoofdstuk 14, 31, 34) | klimaat | logger |
| I15 | licht → teelt | 200 µmol/(m²·s) van 6 tot 22 uur: 11,5 mol per dag (hoofdstuk 25, 36) | licht | PAR-meter |
| I16 | teelt → water | transpiratie 4 à 6 L per dag: bijvullen met regenwater en stamoplossing (hoofdstuk 13, 36) | water | niveau in de logger |
| I17 | station → klimaat | warmwaterleiding naar de convector (±800 W), de deur, één gedeelde wand (hoofdstuk 19, 31) | klimaat | convector aan, temperatuur |
| I18 | bewoner → besturing | S1 naast de deur op D22 (RC + 74HC14): kort = dak, lang (3 s) = onderhoud (hoofdstuk 24, 32) | besturing | knoptest |
Integratieproblemen opsporen. De meeste problemen vind je niet door één deel te testen, maar door twee documenten naast elkaar te leggen: een budget naast de code, een schema naast een ander hoofdstuk, de code van twee versies naast elkaar. Het voorbeeldteam vond er zes, allemaal vóór de testweek:
| gevonden bij | probleem | oplossing in versie 5 |
|---|---|---|
| interface I8 naast hoofdstuk 20 | de ACS712 op A4 meet tot 20 A, maar de panelen laden met tot ±74 A: de coulombteller telt fout | de laadtoestand telt met de twee INA226 (zon min verbruik); A4 blijft vrij |
| code naast code | taakExtraPomp() (hoofdstuk 39) zet pin 9 aan, acties() (versie 4) zet ze om de 2 s weer uit: de pomp flikkert | één taak beslist over de pomp: minuut 0-9 of de extra beurt |
| takenlijst van versie 2 en 4 | modi.ino (hoofdstuk 32) liet het dimmen, het energiebeheer en de logger weg | alles terug in één programma, met de modi als kader |
| budget naast code | hoofdstuk 20 rekende 's nachts 2 W voor de ventilatoren; versie 4 liet ze op PWM 90 (5 W) draaien | 's nachts PWM 36 (2 W): het budget en de code zeggen hetzelfde |
| integratietest, reservoir bijna leeg | met de pomp aan staan er ±3,9 L in slang en goten: het niveau zakt 1,6 cm; bij 11 cm gaf dat elk uur een vals alarm | het niveau telt pas 2 min nadat de pomp gestopt is |
| budget naast logger | hoofdstuk 20 rekende de lampen van 4 tot 20 uur UTC; de klok van de module loopt op UTC+1 | één afspraak: alles in UTC+1, lampen van 6 tot 22 uur |
Elke rij in de tabel hierboven gaat over delen die apart perfect werkten. De ACS712 meet juist; de extra pompbeurt van hoofdstuk 39 werkt; het budget van hoofdstuk 20 klopt met zijn eigen aannames. Het probleem zit telkens in de afspraak ertussen.
Fase 2 · energie: het budget na veertig hoofdstukken
Hoofdstuk 20 stelde het energiebudget op: 3,27 kWh per dag, waarvan 93 % voor de lampen, en besloot tot een tweede batterij. Sindsdien kwamen er dingen bij: het dimmen met het daglicht (hoofdstuk 25), de convector (hoofdstuk 31), de modi en de logger. Reken het budget na zoals de module nu is. Volstaan de panelen en de batterijen, per maand? En wat betekent dat voor een heel jaar?
- PVGIS Brussel, 60° zuid: de gemiddelde dag van elke maand, per uur (hoofdstuk 20); 1,40 kWp
- lampen van 6 tot 22 uur (UTC+1), met het daglicht in µmol/(m²·s)
- daglicht op het bed: ±50 lx per W/m² op het paneel (rekenwaarde van dit project), 54 lx per µmol/(m²·s) (hoofdstuk 25)
- batterijen: 2 × 1,23 = 2,46 kWh bruikbaar; laadrendement 95 %
- het budget van een gemiddelde maartdag
- saldo en nachtbuffer per maand
- het jaar
- 1Het budget. De lampen verbruiken niet meer 3040 Wh per dag, maar alleen wat het daglicht niet levert. De rest verandert weinig; twee posten komen erbij en één wordt zuiniger.
verbruiker hoofdstuk 20 (Wh/dag) versie 5, gemiddelde maartdag (Wh/dag) waarom anders groeilampen 2 × 95 W 3040 1206 gedimd met het daglicht (hoofdstuk 25): de lampen vullen aan tot 200 µmol ventilatoren 96 95 overdag PWM 90, 's nachts PWM 36 (nieuw in versie 5) microcontroller, sensoren, logger 72 72 budget 3,0 W; hoofdstuk 21 rekende 0,96 W, de logger 0,07 W pomp (10 min per uur) 48 48 en ±1 Wh per extra beurt van 5 min (hoofdstuk 39) laadregelaar en BMS 12 12 CO₂-klep 1,5 1,6 32 keer 30 s in DAG, alleen met het dak dicht dakmotor 0,2 1,0 zonnige dag: ±24 bewegingen van ±7 s (testweek) convector: klep en ventilator 0,0 30 nieuw: 12 W zolang er verwarmd wordt totaal 3270 1465 met gedimde lampen: 55 % minder - 2Per maand. Zelfde methode als hoofdstuk 20, uur per uur, maar met gedimde lampen:
Alleen december (−0,44 kWh) en januari (−0,12 kWh) komen tekort; november en februari sluiten nu, zonder dimmen kwamen ze 1,0 en 0,4 kWh per dag tekort.maand jan feb mrt apr mei jun jul aug sep okt nov dec zon (kWh/dag) 1,97 2,95 4,19 5,17 5,01 4,96 4,83 4,63 4,61 3,50 2,32 1,75 verbruik, gedimd 2,01 1,70 1,44 1,22 1,14 1,09 1,11 1,20 1,33 1,58 1,90 2,11 uit de batterij 1,73 1,44 1,16 0,92 0,74 0,66 0,71 0,86 1,07 1,32 1,64 1,84 saldo −0,12 1,12 2,55 3,70 3,64 3,64 3,50 3,22 3,06 1,75 0,32 −0,44 - 3De nacht. Uit de batterijen komt hoogstens (december): van wat bruikbaar is. Eén batterij (1,23 kWh) volstaat van oktober tot februari niet: de tweede blijft nodig.
- 4Het jaar. De panelen kunnen leveren; de module gebruikt er ± van, waarvan voor de lampen. Zonder dimmen was dat . Blijft de oogst 100 kg sla, dan kost een kilo nog lampenergie in plaats van 11,1 (hoofdstuk 36).
Het 12,8 V-net: van paneel tot pin
Het schema toont de pluskant van het net als één lijn per tak. Het brengt samen wat hoofdstuk 16 (batterij), 20 (panelen en regelaar), 21 (5 V-rail en sterpunt), 23 (MOSFETs), 25 (PWM), 26 (dakmotor) en 30 (INA226) apart vastlegden.
- Bron. De MPPT-regelaar zet de 30 à 40 V van de panelen om naar de laadspanning van de batterijen (hoofdstuk 20; hoe hij dat doet, zie je in E24). Op een zonnige middag loopt er tot ±74 A naar de rail.
- Twee meetshunts. De INA226 op 0x40 meet de zonnestroom naar de rail, die op 0x41 de stroom naar de verbruikers. Het verschil is de batterijstroom: daarmee telt versie 5 de laadtoestand. De ACS712 op A4 (hoofdstuk 16) meet maar tot 20 A en zou op een zonnige middag verzadigen.
- Zekeringen. Een zekering bij elke batterijpool (een batterij levert bij kortsluiting honderden ampère) en één per tak, net boven de stroom die de tak normaal vraagt.
- Het sterpunt. Elke tak heeft een eigen retourdraad naar de minpool. Een gedeelde massadraad liet de pH 0,48 verspringen als de pomp aansloeg (hoofdstuk 21).
| tak | stroom (rekenwaarde) | zekering | schakelaar en pin | hoofdstuk |
|---|---|---|---|---|
| groeilampen 2 × 95 W | 14,8 A | 20 A | IRLZ44N op koelplaat, D5 (PWM 490 Hz) | 23, 25 |
| pomp 12 W | 1,0 A (aanloop meer) | 2 A | IRLZ44N, D9, vrijloopdiode 1N5819 | 9, 23 |
| dakmotor | 1,84 A bij 60 N, 10 A blokkeren | 5 A traag | H-brug 2 × BTS7960, D6-D8, eindeloop D23/D24 | 26 |
| ventilatoren | 0,39 A bij PWM 90 | 1 A | IRLZ44N, D11 (PWM) | 23, 31 |
| CO₂-klep | 0,47 A | 1 A | IRLZ44N, D10, vrijloopdiode | 14, 23 |
| convector: klep en ventilator | ±0,9 A (rekenwaarde 12 W) | 2 A | IRLZ44N, D12 | 31, 40 |
| omvormer 12 → 5 V | 0,08 A gemiddeld (0,16 A aan de 5 V-kant) | 1 A | altijd aan | 21 |
De grootste stroom tegelijk: lampen, pomp, een bewegende dakmotor en de ventilatoren vol, plus de omvormer: . Verwarmen en koelen gaan nooit samen, en de CO2-klep gaat niet open met een bewegend dak. De kabels van de rail naar de lampen rekent E5 na met matrices: hoogstens 3 % spanningsval tot elke verbruiker.
Fase 2 · water: pomp, goten en reservoir
Reken de waterkringloop van de module na. Haalt de pomp het debiet dat de goten nodig hebben? Hoeveel water staat er onderweg als de pomp draait, en wat ziet de druksensor dan? Hoe lang houdt het reservoir het vol zonder bijvullen, en hoeveel water gebruikt de module per jaar?
- pomp B: werkpunt 4,8 L/min bij 1,56 m met een slang van 16 mm en 2,5 m (hoofdstuk 11), 12 W elektrisch
- 4 goten van 2,4 m, 10 cm breed, film van ±3 mm, 1,2 L/min per goot (hoofdstuk 36)
- reservoir binnen 60 × 40 cm, vol bij 25 cm; alarm onder 10 cm (hoofdstuk 11)
- verbruik tot 6 L op een warme dag (hoofdstuk 13); 16 L water per kg sla, 100 kg per jaar (hoofdstuk 36)
- regen in Ukkel 837 mm per jaar (F9); kasdak in bovenaanzicht 2,40 × 2,00 m
- debiet en rendement van de pomp
- water onderweg
- dagen zonder bijvullen
- de waterbalans van een jaar
- 1Debiet. Vier goten van 1,2 L/min vragen : precies het werkpunt. Het hydraulische vermogen is , van de 12 W: een rendement van ±10 % (hoofdstuk 9). In 10 minuten gaat er 48 L rond.
- 2Water onderweg. Slang: ; film in vier goten: ; verdeler en verzamelbuis ±0,5 L. Samen , dus zakt het reservoir zolang de pomp draait.
- 3Dagen zonder bijvullen. Tussen 25 en 10 cm zit : dagen op warme dagen. In de testweek verdampten de planten gemiddeld per dag.
- 4Het jaar. Transpiratie , het reservoir om de 5 weken verversen: . Samen . Op het kasdak valt regen.
Fase 2 · warmte: verlies, buffer en verwarming
Reken de warmtebalans van de module na, voor een koude winternacht en voor een zonnige lentemiddag. Volstaat de convector van 800 W? Wat doet de buffer? En volstaan de ventilatoren en het dak om de module onder 27 °C te houden?
- H = 20,2 W/K (hoofdstuk 19); ontwerptemperatuur buiten −8 °C (Ukkel)
- overdag verwarmen onder 18 °C, 's nachts onder 16 °C; convector 800 W (hoofdstuk 31)
- buffer: 200 L vat + 60 L reservoir; lucht met alles wat snel meewarmt 80 kJ/K (hoofdstuk 31)
- zon door het kasdak op een heldere lentemiddag tot ±1,4 kW; buiten 14 °C
- ventilatoren vol +40 W/K, dak open +60 W/K (hoofdstuk 31)
- vermogen bij −8 °C
- de buffer
- koelen op een zonnige dag
- 1Winter. Overdag (18 °C) bij −8 °C: ; hoofdstuk 19 rekende 567 W voor 20 °C. De convector heeft ±275 W over om op te warmen. Schakelen de lampen aan, dan komt er tot 190 W bij.
- 2De buffer. : veertien keer de snelle knoop. Koelt de buffer 's nachts 2 °C af, dan geeft hij 2,2 MJ = 0,6 kWh warmte af. Hij remt het afkoelen en vlakt de zaagtand van de verwarming af (hoofdstuk 18 en 31).
- 3Zonnige lentemiddag. Om 1,4 kW zonnewarmte bij 14 °C buiten kwijt te raken zonder boven 27 °C te gaan, moet . De schil alleen haalt 20 W/K; met de ventilatoren vol en het dak open is het : genoeg, net.
Fase 2 · licht: lichtsom, lampen en dimmen
Planten tellen fotonen, geen watt (hoofdstuk 36). Het ontwerp vraagt 200 µmol/(m²·s) op het bed, 16 uur per dag. Dat is de lichtsom (daglichtsom, DLI):
Twee lampen van 95 W moeten dus op het bed krijgen: 3,0 µmol per joule (hoofdstuk 36 rekende 3,0, een zuinige lamp met reflecterende wanden).
Dimmen. De LDR meet het daglicht in lux; gedeeld door 54 wordt dat µmol/(m²·s) (hoofdstuk 25). De lampen vullen het verschil aan tot 200: bij 100 µmol daglicht branden ze op de helft, vanaf 200 zijn ze uit. De grafiek toont twee dagen uit de testweek. Op de zonnige woensdag is het daglicht van ongeveer 9 tot half zes ruim genoeg; de lampen helpen alleen 's morgens en 's avonds. De lichtsom wordt 21,4 mol: veel meer dan nodig. Op de grijze dinsdag komt het daglicht nauwelijks boven 100.
Energiebeheer. Dinsdag lag de laadtoestand onder 40 %. Dan krijgen de lampen alleen het zonne-overschot: de grens stijgt als de batterijen laden en daalt als ze leveren (hoofdstuk 20 en 29). 's Morgens tot 8 uur bleven de lampen uit, 's middags brandden ze op wat de panelen over hadden, en rond 18 uur gingen ze uit. De lichtsom zakte naar 6,0 mol. Dat is een keuze van het ontwerp: bij een lege batterij gaan eerst de lampen uit, niet de pomp of de besturing. De prijs is eis L1 (verderop).
Boven ±250 à 300 µmol/(m²·s) groeit sla bij deze temperatuur en CO2 nauwelijks sneller, maar ze verdampt meer (hoofdstuk 36). De module dimt daarom naar 200 en laat de middagzon ongebruikt over het bed vallen. Extra licht is pas nuttig als de lichtsom onder het doel blijft.
Fase 2 · voedingsoplossing: ijken, bewaken en bijsturen
Meten de pH- en de EC-sonde in het reservoir juist, en hoe stuur je de oplossing bij als de logger een waarschuwing geeft? (Samenstelling: hoofdstuk 12; EC: hoofdstuk 13; pH: hoofdstuk 15.)
- pH-module en sonde, EC-module en sonde, de Mega met een testprogramma dat de spanning toont
- bufferoplossingen pH 7,00 en 4,00, EC-ijkvloeistof 1,413 mS/cm, gedemineraliseerd water, tissues
- maatbeker 1 L, spuitjes van 5 en 20 mL, stamoplossingen A en B, pH-min (salpeterzuur 0,50 mol/L)
- veiligheidsbril, nitrilhandschoenen, het logboek
- 1Spoel de pH-sonde, zet ze in buffer 7,00 en wacht tot de spanning 30 s stabiel is. Noteer de spanning. Spoel, en herhaal in buffer 4,00.
- 2Bereken de helling in V per pH en de spanning bij pH 7. Zet beide in de code (hoofdstuk 15 rekende 2,50 V en −0,18 V per pH).
- 3Zet de EC-sonde in de ijkvloeistof van 1,413 mS/cm (25 °C) en vergelijk met wat de module toont na de omrekening naar 25 °C.
- 4Bijvullen: meet het niveau, vul met regenwater bij tot 25 cm, en voeg per liter bijgevuld water 10 mL A en 10 mL B toe, telkens apart en goed gemengd.
- 5pH te hoog: neem 1 L uit het reservoir en titreer met pH-min tot 5,8 (proeftitratie). Vermenigvuldig met het volume in het reservoir en geef de dosis in twee helften, met 15 minuten rondpompen ertussen.
- 6Noteer alles in het logboek: tijd, niveau, EC en pH voor en na, dosis.
| meting | voorbeeldteam |
|---|---|
| buffer pH 7,00 | 2,49 V |
| buffer pH 4,00 | 3,02 V |
| helling | −0,177 V per pH (−0,18 ± 10 %: in orde) |
| EC-ijkvloeistof 1,413 mS/cm | module toont 1,42 mS/cm |
| woensdag: bijvullen | 13 L regenwater + 130 mL A + 130 mL B; niveau 18,9 → 24,3 cm; EC 1,39 → 1,38 |
| maandag na de testweek | pH 6,6 in ±35 L; proeftitratie: 1,6 mL pH-min per L; dosis 56 mL in twee keer 28 mL |
De spanningen wijken 0,01 V af van hoofdstuk 15: elke sonde is een beetje anders, en ze verandert met de tijd. Daarom ijkt het team om de twee weken (eis H5).
De pH-module geeft . Bij pH 5,8 is dat , of ADC-waarde 556 op de 10-bits ADC van de Mega. Eén stap van de ADC (4,9 mV) is pH: ruim fijn genoeg voor een band van 5,5 tot 6,5. De EC-module geeft 0,50 V per mS/cm (rekenwaarde van dit project): één ADC-stap is . Versie 5 rekent de EC om naar 25 °C met de temperatuur van de NTC (2 % per graad, hoofdstuk 13).
Bijvullen met regenwater alleen zou de EC doen zakken: 13 L water op 45 L oplossing verdunt met ±23 %. Met 10 mL A en 10 mL B per liter krijgt het nieuwe water dezelfde samenstelling. De pH stijgt ondertussen elke dag met ±0,1 à 0,15 (hoofdstuk 15), omdat de planten meer nitraat opnemen dan ze afgeven: in de testweek van 5,86 naar 6,59.
De module in kerngetallen
Na fase 2 vat het voorbeeldteam de module samen op één blad: de technische fiche. Ze komt vooraan in het rapport en naast de module aan de muur. Elk getal heeft een herkomst: het hoofdstuk waar het berekend of beslist werd.
| deelsysteem | kerngetallen | hoofdstuk |
|---|---|---|
| kweekruimte | binnen 2,40 × 2,00 m, wanden 1,93 en 2,87 m hoog (11,5 m³); vloer 0,40 m boven het terrein; één wand grenst aan het station | 6, 14, 19 |
| kasdak | polycarbonaat 16 mm (U = 2,0), 5,30 m² onder 25°; schuifpaneel 10 kg op rails, slag 0,60 m, opening aan de nok | 7, 8, 19 |
| schil | wanden U = 0,22, vloer U = 0,21; H = 20,2 W/K (16,3 met nachtscherm): 567 W bij −8 °C buiten en 20 °C binnen | 19 |
| kweekbed | 4 NFT-goten van 2,4 m, 48 plaatsen, 2,88 m², helling 1 : 50; om de 8,75 dagen een goot oogsten | 4, 36 |
| water | reservoir 60 L (25 cm, 2,4 L per cm), alarm onder 10 cm; pomp 12 W, 4,8 L/min bij 1,56 m, 10 min per uur; 1,2 L/min per goot | 9, 10, 11, 36 |
| voedingsoplossing | 1,30 g/L zouten; EC 1,4 (1,2-1,8 mS/cm), pH 5,8 (5,5-6,5), 18-22 °C; stamoplossing A en B: 10 mL per L | 12, 13, 15, 36 |
| warmte | buffer 200 L + 60 L (1,09 MJ/K), lucht 80 kJ/K; convector 800 W; 18/19 °C overdag, 16/17 °C 's nachts | 18, 19, 31 |
| ventilatie en dak | 11 m³/h (PWM 90), 's nachts PWM 36; vol boven 25 °C (+40 W/K); dak open boven 27 °C (+60 W/K) | 14, 26, 31 |
| licht | 2 × 95 W, 200 µmol/(m²·s) van 6 tot 22 uur (UTC+1): 11,5 mol per dag; 3,0 µmol/J op het bed; gedimd met het daglicht | 23, 25, 36 |
| zon | 4 × 350 Wp = 1,40 kWp, 60° zuid, op het dak van de woonmodule; MPPT 100 A; ±1397 kWh per jaar mogelijk | 6, 20 |
| batterijen | 2 × LiFePO4 12,8 V 120 Ah: 2,46 kWh bruikbaar, BMS, in de verwarmde module; laadtoestand met twee INA226 | 16, 20, 30 |
| energiebeheer | vanaf 40 % volgens schema; 20-40 % lampen alleen op zonne-overschot; onder 20 % lampen uit; onder 10 % alarm | 20, 29 |
| verbruik | zonder dimmen 3,27 kWh per dag; met dimmen 1,1 tot 2,1 kWh per dag; ±540 kWh per jaar | 20, 25, 40 |
| besturing | Arduino Mega 2560, klimaat.ino versie 5 (510 regels) met pinnen.h en dak.h/.cpp; regelkast RK-1; sensorkast SK-1 op 3 m | 29-32, 37, 40 |
| data | CSV per minuut, 17 kolommen, ±121 kB per dag, klok op UTC+1 | 30, 40 |
| teelt | 48 kroppen van ±200 g in 35 dagen; ±100 kg sla per jaar | 34, 36 |
| voetafdruk | ±164 kg CO₂-eq per jaar, ±1,64 kg per kg sla | 38 |
Fase 3 · de besturing: vijf versies worden één programma
De code van de module groeide over vier hoofdstukken, telkens met iets erbij en soms met iets eruit om het nieuwe duidelijk te tonen. Versie 4 (hoofdstuk 32) liet het dimmen, het energiebeheer en de logger weg. In fase 3 worden ze samengevoegd tot versie 5.
| versie | hoofdstuk | wat erbij kwam | wat er nog ontbrak |
|---|---|---|---|
| 1 | 29 | pinnen.h, zeven taken met millis(), dimmen, pomp 10 min per uur, dak met de knop, laadtoestand in het EEPROM, energiebeheer | echte klok, logger, regeling |
| 2 | 30 | klok DS3231 (UTC+1), logger op SD en USB, pomp in minuut 0-9, meldingen met #, INA226 | verwarming, ventilatie, modi |
| 3 | 31 | verwarming en ventilatoren met hysterese, noodprogramma bij een sensorfout | modi, dak als toestandsmachine |
| 4 | 32 | modi OPSTART, DAG, NACHT, ALARM, ONDERHOUD; dak.h en dak.cpp; alarmbits; CO₂-klep | dimmen, energiebeheer en logger (weggelaten) |
| taak | 39 | slapVerwacht(): een extra pompbeurt van 5 min om het halfuur | een eigen taak die ook pin 9 stuurt |
| 5 | 40 | alles in één programma; laadtoestand met twee INA226; pH, EC en watertemperatuur; één eigenaar per uitgang; waakhond | zie 'wat de module nog niet kan' |
Samenvoegen is meer dan knippen en plakken. Vier regels hielden het programma overzichtelijk:
- Eén klok. millis() voor de intervallen (om de 2 s meten, om de 200 ms de lampen), de DS3231 voor het uur van de dag, en alles in UTC+1 (hoofdstuk 30).
- Nergens delay(). Elke taak kijkt of het haar beurt is en keert meteen terug (hoofdstuk 29). Dan blijft elke ronde kort, en kan de waakhond na 2 s ingrijpen als er toch iets blijft hangen (hoofdstuk 32).
- Eén eigenaar per uitgang. Alleen acties() beslist over de pomp; de extra beurt van hoofdstuk 39 is een voorwaarde in die beslissing, geen tweede taak die op pin 9 schrijft. Zo ook: alleen taakLampen() schrijft naar D5, alleen dak.cpp naar de H-brug.
- De modi als kader. Wat een taak doet, hangt af van de modus (volgende blad). Een nieuwe functie krijgt dus altijd de vraag: wat doet ze in OPSTART, in ALARM, in ONDERHOUD?
De modi en het dak als toestandsmachines
De modi komen ongewijzigd uit hoofdstuk 32: OPSTART na elke herstart, DAG en NACHT volgens de klok, ALARM bij een oorzaak die meteen ingrijpen vraagt, ONDERHOUD met een lange druk op S1. Versie 5 voegt er één alarm aan toe (batterij onder 10 %) en drie waarschuwingen die geen modus zijn: pH buiten 5,5-6,5, EC buiten 1,2-1,8 en een voedingsoplossing boven 24 °C. Een waarschuwing vraagt actie vandaag, geen actie nu; ze komt als melding en in de logger. Waarom niet gewoon alarm? Een pH van 6,6 schaadt de sla niet in een uur, en een module die om de haverklap in ALARM staat, leert zijn bewoners het alarm te negeren.
Wat elke taak doet, hangt af van de modus:
| modus | verwarming | ventilatoren | lampen | pomp | dak | CO₂ |
|---|---|---|---|---|---|---|
| OPSTART | uit | laag | uit | uit | dakStart: eindeloop lezen | dicht |
| DAG | 18/19 °C | 90; vol boven 25 °C | dimmen, grens | min. 0-9 + extra | volgt 27/25 °C | 30 s per halfuur, dak dicht |
| NACHT | 16/17 °C | 36; vol boven 25 °C | uit | min. 0-9 | dicht | dicht |
| ALARM | zoals DAG of NACHT | zoals DAG of NACHT | zoals DAG | niet bij vlotter of niveau | overdag automatisch | dicht |
| ONDERHOUD | zoals DAG of NACHT | vol (verse lucht) | werklicht 60 | uit | geblokkeerd | dicht |
Het dak blijft een eigen toestandsmachine in dak.h en dak.cpp (hoofdstuk 32): DICHT, OPENEN, OPEN, SLUITEN, GESTOPT en DAK_FOUT. De klimaatregeling vraagt alleen 'open' of 'dicht'; dak.cpp beslist wanneer de motor draait, stopt op de eindeloop en geeft DAK_FOUT als het eindpunt na 12 s niet bereikt is. Bij het binnenkomen van DAG volgt het dak meteen de temperatuur: na een herstart overdag sluit het dus eerst, en gaat het opnieuw open zodra het warmer is dan 27 °C.
klimaat.ino versie 5
Het volledige programma, 510 regels. pinnen.h en dak.h/dak.cpp zijn dezelfde bestanden als in hoofdstuk 32 (hieronder te downloaden). Lees het van boven naar onder: de instellingen verwijzen naar de eisen (K1, L1, ...), elk blok begint met een kopregel, en de volgorde in loop() is de volgorde van het blokschema.
// klimaat.ino - de sturing van de klimaatmodule, versie 5 (Arduino Mega 2560, hoofdstuk 40)// Samengevoegd: klok, logger, dimmen en energiebeheer (versie 2, hoofdstuk 30), de klimaatregeling (hoofdstuk 31),// de modi en het kasdak als toestandsmachines (versie 4, hoofdstuk 32) en de extra pompbeurt (hoofdstuk 39).// Nieuw: de laadtoestand telt met de twee INA226, pH, EC en de temperatuur van de voedingsoplossing worden bewaakt,// één taak beslist over de pomp, 's nachts draaien de ventilatoren trager, het niveau telt alleen als de pomp// stilstaat, en een waakhond van 2 s. I2C: INA226 0x40 en 0x41, SHT31 0x44, DS3231 0x68 (wintertijd).#include <Wire.h>#include <SPI.h>#include <SD.h>#include <EEPROM.h>#include <RTClib.h>#include <Adafruit_SHT31.h>#include <avr/wdt.h>#include "pinnen.h"#include "dak.h" // ---------------------------------------------------------------- instellingen (programma van eisen, hoofdstuk 40)const unsigned long MEET_MS = 2000; // meten en beslissenconst unsigned long LAMP_MS = 200; // lampen: hoogstens één stap per 200 ms (L4)const unsigned long LANG_MS = 3000; // lange druk op S1: onderhoud aan of uit (B4)const unsigned long ONDERHOUD_MAX_MS = 3600000UL;const unsigned long BEWAAR_MS = 3600000UL; // laadtoestand elk uur in het EEPROM (B1)const unsigned long POMP_RUST_MS = 120000UL; // niveau pas 2 min na het stoppen van de pomp (W3)const float DAG_LAAG = 18.0, NACHT_LAAG = 16.0; // verwarming aan onder LAAG, uit boven LAAG + 1 (K1, K2)const float KOEL_AAN = 25.0, KOEL_UIT = 24.0; // ventilatoren vol (K4)const float DAK_OPEN = 27.0, DAK_DICHT = 25.0; // kasdak (K4)const int VENT_DAG = 90, VENT_NACHT = 36, VENT_VOL = 255, WERKLICHT = 60;const float DOEL = 200.0, LAMPEN_VOL = 200.0, LUX_PER_UMOL = 54.0; // umol/(m2 s) op het kweekbed (L1, L2)const float SOC_VOL = 40.0, SOC_ZON = 20.0, SOC_ALARM = 10.0; // energiebeheer (L3, E5)const float CAPACITEIT_AH = 240.0; // twee LiFePO4 van 120 Ah (hoofdstuk 20)const float PH_LAAG = 5.5, PH_HOOG = 6.5, EC_LAAG = 1.2, EC_HOOG = 1.8, WATER_WARM = 24.0; // H1-H3const float SLAP_LICHT = 25.0, SLAP_VOCHT = 60.5; // extra pompbeurt: klx en % (hoofdstuk 39)const int SD_CS = 53;const byte INA_ZON = 0x40, INA_VERBRUIK = 0x41;const float R_SHUNT = 0.00075; // shunt 100 A / 75 mVconst int ADRES_SOC = 0;const char KOP[] = "tijdstip,temperatuur_C,vocht_pct,verbruik_Wh,zon_Wh,licht_lx,batterij_V,soc_pct,lampen_pwm," "verw_pct,dak,water_C,pH,EC_mScm,niveau_cm,modus,alarm"; enum Modus { OPSTART, DAG, NACHT, ALARM, ONDERHOUD };const char* NAAM[] = {"OPSTART", "DAG", "NACHT", "ALARM", "ONDERHOUD"};const byte A_SENSOR = 1, A_WATER = 2, A_NIVEAU = 4, A_DAK = 8, A_BATTERIJ = 16; // alarm: meteen ingrijpenconst byte W_PH = 1, W_EC = 2, W_WARM = 4, W_SD = 8; // waarschuwing: vandaag bijsturen // ---------------------------------------------------------------- toestandAdafruit_SHT31 sht;RTC_DS3231 rtc;DateTime klok, minuut;Modus modus = OPSTART;byte alarmen = 0, waarschuwingen = 0;unsigned long modusSinds = 0, vorigeMeting = 0, vorigeLamp = 0, vorigeBewaring = 0, ledSinds = 0;unsigned long knopSinds = 0, droogSinds = 0, pompSinds = 0;float luchtT = NAN, luchtRV = NAN, lux = 0, uBat = 0, uBus = NAN, iZon = NAN, iVerbruik = NAN;float soc = 50.0, waterT = NAN, ph = NAN, ec = NAN, niveau = 0, gemAan = 0.5;int geenMeting = 0, grens = 255, stand = 0;bool verwarmen = false, koelen = false, warm = false, laden = false, pompAan = false;bool extraBeurt = false, extraBeslist = false, niveauLaag = false, batLeeg = false;bool knopWas = false, langGemeld = false, ledAan = false, kaartOk = false, bezig = false;float somT, somRV, somLux, somZon, somVerbruik, somU, somWT, somPH, somEC, somNiveau, somStand; // loggerint nT, nMeting, nVermogen, nWT, nPH, nVerw;byte eindModus = OPSTART, eindAlarm = 0, eindDak = 0;float eindSoc = 50.0;char opdracht[32];int lengte = 0; // ---------------------------------------------------------------- meldingenvoid printTwee(int x) { if (x < 10) Serial.print('0'); Serial.print(x);} void melding(const char* tekst) { // "# 14:02:31 tekst" (ook dak.cpp gebruikt ze) Serial.print("# "); printTwee(klok.hour()); Serial.print(':'); printTwee(klok.minute()); Serial.print(':'); printTwee(klok.second()); Serial.print(' '); Serial.println(tekst);} // ---------------------------------------------------------------- sensoren (hoofdstuk 11, 13, 15, 22, 30)int gemiddeld(int pin) { // 16 metingen middelen tegen ruis long som = 0; for (int i = 0; i < 16; i++) som += analogRead(pin); return som / 16;} float daglichtLux() { // LDR onder een kapje, 1,0 kOhm naar GND int n = gemiddeld(LDR); if (n < 1) n = 1; float r = 1000.0 * (1024.0 - n) / n; return 10.0 * pow(r / 15000.0, -1.0 / 0.7) / 0.10;} float batterijspanning() { // deler 100 kOhm / 33 kOhm return gemiddeld(BATTERIJ) * 5.0 / 1024.0 * (133.0 / 33.0);} float waterTemperatuur() { // NTC in het reservoir, B = 3913 K int n = gemiddeld(NTC); if (n < 5 || n > 1018) return NAN; // draad los of kortsluiting float r = 10000.0 * (1024.0 - n) / n; return 1.0 / (1.0 / 298.15 + log(r / 10000.0) / 3913.0) - 273.15;} float phWaarde() { // 2,50 V bij pH 7,00, -0,18 V per pH (hoofdstuk 15) float u = gemiddeld(PH) * 5.0 / 1024.0; if (u < 0.1 || u > 4.9) return NAN; return 7.0 + (2.50 - u) / 0.18;} float ecWaarde(float t) { // 0,50 V per mS/cm, omgerekend naar 25 graden (hoofdstuk 13) float e = gemiddeld(EC) * 5.0 / 1024.0 / 0.50; if (isnan(t)) return e; return e / (1.0 + 0.02 * (t - 25.0));} float niveauCm() { // MPX5010 onderaan het reservoir (hoofdstuk 11) float u = gemiddeld(DRUK) * 5.0 / 1024.0; float p = (u / 5.0 - 0.04) / 0.09 * 1000.0; // Pa return p / (1000.0 * 9.81) * 100.0;} long inaLees(byte adres, byte reg) { // INA226: 16 bits uit een register, of -1 Wire.beginTransmission(adres); Wire.write(reg); if (Wire.endTransmission() != 0) return -1; if (Wire.requestFrom(adres, (byte)2) != 2) return -1; byte hoog = Wire.read(); byte laag = Wire.read(); return ((long)hoog << 8) | laag;} float inaStroom(byte adres) { // A: shuntspanning (2,5 uV per stap) gedeeld door 0,75 mOhm long s = inaLees(adres, 0x01); if (s < 0) return NAN; return (int16_t)s * 0.0000025 / R_SHUNT;} float inaSpanning(byte adres) { // V: busspanning, 1,25 mV per stap long b = inaLees(adres, 0x02); if (b < 0) return NAN; return b * 0.00125;} byte band(byte w, byte bit, float x, float laag, float hoog, float marge) { // waarschuwing met hysterese if (isnan(x)) return w; if (x < laag || x > hoog) return w | bit; if (x > laag + marge && x < hoog - marge) return w & ~bit; return w;} // ---------------------------------------------------------------- meten, laadtoestand, alarmen (om de 2 s)void meten(unsigned long nu) { float t, rv; if (sht.readBoth(&t, &rv)) { luchtT = t; luchtRV = rv; geenMeting = 0; } else { luchtT = luchtRV = NAN; geenMeting++; } lux = daglichtLux(); uBat = batterijspanning(); iZon = inaStroom(INA_ZON); iVerbruik = inaStroom(INA_VERBRUIK); uBus = inaSpanning(INA_VERBRUIK); if (!isnan(iZon) && !isnan(iVerbruik)) { // coulombteller (hoofdstuk 16) met de twee INA226 float iLaad = iZon - iVerbruik; // A, positief = de batterijen laden soc += iLaad * (MEET_MS / 3600000.0) / CAPACITEIT_AH * 100.0; if (uBat >= 14.2 && iLaad > 0.0 && iLaad < 2.0) soc = 100.0; // vol: opnieuw ijken soc = constrain(soc, 0.0, 100.0); laden = iLaad > 0.0; } if (soc >= SOC_VOL) grens = 255; // energiebeheer (hoofdstuk 20) else if (soc < SOC_ZON) grens = 0; else if (laden) grens = min(grens + 5, 255); // er is zon over: meer licht mag else grens = max(grens - 5, 0); // de batterij levert: minder licht waterT = waterTemperatuur(); ph = phWaarde(); ec = ecWaarde(waterT); if (digitalRead(VLOTTER) == LOW) droogSinds = nu; // LOW = genoeg water; een losse draad = droog if (!pompAan && nu - pompSinds >= POMP_RUST_MS) niveau = niveauCm(); // water in de goten telt niet mee if (!niveauLaag && niveau < 10.0) niveauLaag = true; // 10 en 12 cm else if (niveauLaag && niveau > 12.0) niveauLaag = false; if (!batLeeg && soc < SOC_ALARM) batLeeg = true; // 10 en 15 % else if (batLeeg && soc > SOC_ALARM + 5.0) batLeeg = false; byte nieuw = 0; if (geenMeting >= 15) nieuw |= A_SENSOR; // 30 s geen luchttemperatuur if (nu - droogSinds >= 10000) nieuw |= A_WATER; // 10 s droog if (niveauLaag) nieuw |= A_NIVEAU; if (dak == DAK_FOUT) nieuw |= A_DAK; if (batLeeg) nieuw |= A_BATTERIJ; if (nieuw & ~alarmen & A_SENSOR) melding("oorzaak: geen temperatuur"); if (nieuw & ~alarmen & A_WATER) melding("oorzaak: vlotter droog"); if (nieuw & ~alarmen & A_NIVEAU) melding("oorzaak: reservoir onder 10 cm"); if (nieuw & ~alarmen & A_DAK) melding("oorzaak: dak haalt zijn eindpunt niet"); if (nieuw & ~alarmen & A_BATTERIJ) melding("oorzaak: batterij onder 10 %"); alarmen = nieuw; byte w = band(waarschuwingen, W_PH, ph, PH_LAAG, PH_HOOG, 0.05); w = band(w, W_EC, ec, EC_LAAG, EC_HOOG, 0.05); w = band(w, W_WARM, waterT, 0.0, WATER_WARM, 0.5); if (w & ~waarschuwingen & W_PH) melding("waarschuwing: pH buiten de band, bijsturen"); if (w & ~waarschuwingen & W_EC) melding("waarschuwing: EC buiten de band, bijsturen"); if (w & ~waarschuwingen & W_WARM) melding("waarschuwing: voedingsoplossing te warm"); waarschuwingen = w | (kaartOk ? 0 : W_SD);} // ---------------------------------------------------------------- de knop: kort of lang (hoofdstuk 24, 32)void leesKnop(unsigned long nu, bool& kort, bool& lang) { bool in = digitalRead(KNOP) == HIGH; // na de 74HC14: hoog = ingedrukt kort = lang = false; if (in && !knopWas) { knopSinds = nu; langGemeld = false; } if (in && !langGemeld && nu - knopSinds >= LANG_MS) { lang = true; langGemeld = true; } if (!in && knopWas && !langGemeld) kort = true; knopWas = in;} // ---------------------------------------------------------------- de modi (hoofdstuk 32)bool isDag() { return klok.hour() >= 6 && klok.hour() < 22; }Modus gewoon() { return isDag() ? DAG : NACHT; } void naar(Modus nieuw, unsigned long nu) { Modus oud = modus; modus = nieuw; modusSinds = nu; char tekst[32] = "modus "; strcat(tekst, NAAM[nieuw]); melding(tekst); if (nieuw == ONDERHOUD) dakBlokkeer(true, nu); // niemand onder een bewegend dak if (oud == ONDERHOUD) dakBlokkeer(false, nu); if (nieuw == NACHT) dakVraag(false); if (nieuw == DAG) dakVraag(warm);} void taakModus(unsigned long nu, bool kort, bool lang) { switch (modus) { case OPSTART: // 5 s: sensoren en klok lezen if (nu - modusSinds >= 5000) naar(gewoon(), nu); break; case DAG: case NACHT: if (alarmen) naar(ALARM, nu); else if (lang) naar(ONDERHOUD, nu); else if (gewoon() != modus) naar(gewoon(), nu); else if (kort) dakKnop(nu); // korte druk: het dak met de hand (G2) break; case ALARM: if (kort) { // bevestigen if (alarmen & A_DAK) { dakHerstel(nu); alarmen &= ~A_DAK; } if (alarmen == 0) naar(gewoon(), nu); else melding("alarm blijft: oorzaak nog aanwezig"); } else if (lang) naar(ONDERHOUD, nu); break; case ONDERHOUD: if (lang || nu - modusSinds >= ONDERHOUD_MAX_MS) naar(gewoon(), nu); break; }} // ---------------------------------------------------------------- klimaat, pomp en CO2 (om de 2 s)void acties(unsigned long nu) { bool dag = isDag(); float laag = dag ? DAG_LAAG : NACHT_LAAG, hoog = laag + 1.0; if (isnan(luchtT)) { // noodprogramma: blind met de gemiddelde inschakelduur verwarmen = (nu / 1000UL) % 600UL < (unsigned long)(gemAan * 600); koelen = false; } else { if (!verwarmen && luchtT < laag) verwarmen = true; else if (verwarmen && luchtT > hoog) verwarmen = false; if (!koelen && luchtT > KOEL_AAN) koelen = true; else if (koelen && luchtT < KOEL_UIT) koelen = false; if (verwarmen) koelen = false; gemAan += ((verwarmen ? 1.0 : 0.0) - gemAan) * (MEET_MS / 3600000.0); bool automatisch = dag && (modus == DAG || (modus == ALARM && !(alarmen & A_DAK))); if (!warm && luchtT > DAK_OPEN) { warm = true; if (automatisch) dakVraag(true); } else if (warm && luchtT < DAK_DICHT) { warm = false; if (automatisch) dakVraag(false); } } if (modus == ONDERHOUD) koelen = true; // verse lucht voor wie binnen werkt bool verw = verwarmen && modus != OPSTART; digitalWrite(VERWARMING, verw ? HIGH : LOW); if (verw) nVerw++; analogWrite(VENTILATOREN, koelen ? VENT_VOL : (dag ? VENT_DAG : VENT_NACHT)); // de pomp: één taak beslist over pin 9 (minuut 0-9 van elk uur, extra beurt om het halfuur) int m = klok.minute(); if (m == 30 && !extraBeslist) { extraBeslist = true; extraBeurt = dag && lux / 1000.0 > SLAP_LICHT && luchtRV <= SLAP_VOCHT; if (extraBeurt) melding("extra pompbeurt: slappe sla verwacht"); } if (m < 30 || m >= 35) extraBeurt = extraBeslist = false; bool pomp = (modus == DAG || modus == NACHT || modus == ALARM) && (m < 10 || extraBeurt) && !(alarmen & (A_WATER | A_NIVEAU)); if (pomp != pompAan) { pompAan = pomp; pompSinds = nu; } digitalWrite(POMP, pomp ? HIGH : LOW); // CO2: alleen in DAG, met het dak dicht en zonder koelen: om het halfuur 30 s (K8) bool co2 = modus == DAG && dak == DICHT && !koelen && m % 30 == 0 && klok.second() < 30; digitalWrite(CO2_KLEP, co2 ? HIGH : LOW);} // ---------------------------------------------------------------- groeilampen: dimmen en energiebeheervoid taakLampen(unsigned long nu) { if (nu - vorigeLamp < LAMP_MS) return; vorigeLamp += LAMP_MS; int doel = 0; if (modus == ONDERHOUD) doel = WERKLICHT; else if ((modus == DAG || modus == ALARM) && isDag()) { float f = constrain((DOEL - lux / LUX_PER_UMOL) / LAMPEN_VOL, 0.0, 1.0); doel = min((int)(f * 255 + 0.5), grens); } if (doel > stand) stand++; else if (doel < stand) stand--; analogWrite(LAMPEN, stand);} // ---------------------------------------------------------------- logger (hoofdstuk 30): elke minuut één regelvoid wisSommen() { somT = somRV = somLux = somZon = somVerbruik = somU = somWT = somPH = somEC = somNiveau = somStand = 0; nT = nMeting = nVermogen = nWT = nPH = nVerw = 0;} size_t schrijfRegel(Print& uit) { size_t n = 0; char tijd[17]; snprintf(tijd, sizeof(tijd), "%04d-%02d-%02d %02d:%02d", minuut.year(), minuut.month(), minuut.day(), minuut.hour(), minuut.minute()); n += uit.print(tijd); n += uit.print(','); if (nT > 0) n += uit.print(somT / nT, 1); n += uit.print(','); if (nT > 0) n += uit.print(somRV / nT, 1); n += uit.print(','); if (nVermogen > 0) n += uit.print(somVerbruik / nVermogen / 60.0, 2); n += uit.print(','); if (nVermogen > 0) n += uit.print(somZon / nVermogen / 60.0, 2); n += uit.print(','); if (nMeting > 0) n += uit.print(somLux / nMeting, 0); n += uit.print(','); if (nVermogen > 0) n += uit.print(somU / nVermogen, 2); n += uit.print(','); n += uit.print(eindSoc, 1); n += uit.print(','); if (nMeting > 0) n += uit.print(somStand / nMeting, 0); n += uit.print(','); if (nMeting > 0) n += uit.print(100.0 * nVerw / nMeting, 0); n += uit.print(','); n += uit.print(eindDak); n += uit.print(','); if (nWT > 0) n += uit.print(somWT / nWT, 1); n += uit.print(','); if (nPH > 0) n += uit.print(somPH / nPH, 2); n += uit.print(','); if (nPH > 0) n += uit.print(somEC / nPH, 2); n += uit.print(','); if (nMeting > 0) n += uit.print(somNiveau / nMeting, 1); n += uit.print(','); n += uit.print(eindModus); n += uit.print(','); n += uit.print(eindAlarm); n += uit.println(); return n;} void opslaan() { size_t lang = schrijfRegel(Serial); if (!kaartOk) return; char naam[20]; snprintf(naam, sizeof(naam), "LOG/%04d%02d%02d.CSV", minuut.year(), minuut.month(), minuut.day()); bool nieuw = !SD.exists(naam); File f = SD.open(naam, FILE_WRITE); if (!f) { melding("waarschuwing: SD, kan het bestand niet openen"); kaartOk = false; return; } if (nieuw) f.println(KOP); if (schrijfRegel(f) < lang) { melding("waarschuwing: SD-kaart vol"); kaartOk = false; } f.close();} void loggen() { // om de 2 s, na meten() en acties() DateTime begin(klok.year(), klok.month(), klok.day(), klok.hour(), klok.minute(), 0); if (!bezig) { minuut = begin; bezig = true; } else if (begin.unixtime() != minuut.unixtime()) { opslaan(); wisSommen(); minuut = begin; } if (!isnan(luchtT)) { somT += luchtT; somRV += luchtRV; nT++; } if (!isnan(iZon) && !isnan(iVerbruik) && !isnan(uBus)) { somZon += iZon * uBus; somVerbruik += iVerbruik * uBus; somU += uBus; nVermogen++; } if (!isnan(waterT)) { somWT += waterT; nWT++; } if (!isnan(ph) && !isnan(ec)) { somPH += ph; somEC += ec; nPH++; } somLux += lux; somStand += stand; somNiveau += niveau; nMeting++; eindModus = modus; eindAlarm = alarmen; eindDak = dak; eindSoc = soc;} void leesOpdracht() { // T2021-03-08 09:00:00 + Enter: de klok zetten while (Serial.available() > 0) { char c = Serial.read(); if (c == '\n' || c == '\r') { opdracht[lengte] = '\0'; int j, mo, d, u, mi, s; if (lengte > 0 && sscanf(opdracht, "T%d-%d-%d %d:%d:%d", &j, &mo, &d, &u, &mi, &s) == 6) { rtc.adjust(DateTime(j, mo, d, u, mi, s)); klok = rtc.now(); melding("klok gezet"); wisSommen(); bezig = false; } lengte = 0; } else if (lengte < (int)sizeof(opdracht) - 1) { opdracht[lengte] = c; lengte++; } }} // ---------------------------------------------------------------- statusled en EEPROMvoid taakStatusled(unsigned long nu) { // normaal 500 ms, alarm 100 ms, onderhoud vast aan (G1) if (modus == ONDERHOUD) { digitalWrite(STATUSLED, HIGH); return; } unsigned long periode = (modus == ALARM) ? 100 : 500; if (nu - ledSinds < periode) return; ledSinds = nu; ledAan = !ledAan; digitalWrite(STATUSLED, (ledAan && modus != OPSTART) ? HIGH : LOW);} void taakBewaren(unsigned long nu) { // de laadtoestand overleeft een stroomonderbreking if (nu - vorigeBewaring < BEWAAR_MS) return; vorigeBewaring += BEWAAR_MS; EEPROM.put(ADRES_SOC, soc); // schrijft alleen de bytes die veranderen} // ---------------------------------------------------------------- setup en loopvoid setup() { wdt_disable(); // misschien zijn we net door de waakhond herstart const int UIT[] = {LAMPEN, PWM_DICHT, PWM_OPEN, DAK_ENABLE, POMP, CO2_KLEP, VENTILATOREN, VERWARMING, STATUSLED}; for (int p : UIT) { pinMode(p, OUTPUT); digitalWrite(p, LOW); } // veilig starten: alles uit digitalWrite(DAK_ENABLE, HIGH); pinMode(VLOTTER, INPUT_PULLUP); pinMode(KNOP, INPUT); pinMode(EIND_OPEN, INPUT_PULLUP); pinMode(EIND_DICHT, INPUT_PULLUP); Serial.begin(115200); Wire.begin(); rtc.begin(); sht.begin(0x44); // antwoordt ze niet, dan volgt het alarm na 30 s klok = rtc.now(); byte ina[] = {INA_ZON, INA_VERBRUIK}; for (int i = 0; i < 2; i++) { // INA226: 128 metingen middelen (282 ms) Wire.beginTransmission(ina[i]); Wire.write(0x00); Wire.write(0x49); Wire.write(0x27); Wire.endTransmission(); } kaartOk = SD.begin(SD_CS); if (kaartOk && !SD.exists("LOG")) SD.mkdir("LOG"); EEPROM.get(ADRES_SOC, soc); if (isnan(soc) || soc < 0.0 || soc > 100.0) soc = 50.0; // leeg EEPROM: een ruwe schatting eindSoc = soc; niveau = niveauCm(); unsigned long nu = millis(); droogSinds = nu; melding("klimaatmodule v5 (hoofdstuk 40)"); Serial.print("# laadtoestand uit het EEPROM: "); Serial.print(soc, 1); Serial.println(" %"); if (!kaartOk) melding("waarschuwing: geen SD-kaart"); Serial.println(KOP); naar(OPSTART, nu); dakStart(nu); wdt_enable(WDTO_2S); // komt loop() 2 s niet langs wdt_reset(): herstart (B5)} void loop() { wdt_reset(); unsigned long nu = millis(); leesOpdracht(); bool kort, lang; leesKnop(nu, kort, lang); if (nu - vorigeMeting >= MEET_MS) { vorigeMeting += MEET_MS; klok = rtc.now(); meten(nu); acties(nu); loggen(); } taakModus(nu, kort, lang); taakDak(nu); taakLampen(nu); taakStatusled(nu); taakBewaren(nu);}Wat er nieuw is tegenover versie 2 en 4:
- De laadtoestand telt nu met de twee INA226:
iLaad = iZon - iVerbruik, om de 2 s opgeteld (een coulombteller, hoofdstuk 16). Bij 14,2 V met een kleine laadstroom zijn de batterijen vol en wordt de teller opnieuw op 100 % gezet. - Het energiebeheer zit in meten(): boven 40 % mogen de lampen alles, onder 20 % niets, daartussen schuift de grens 5 stappen op of af naargelang de batterijen laden of leveren (hoofdstuk 29).
- band() geeft een waarschuwing met hysterese: aan buiten de band, pas weer uit als de waarde 0,05 (of 0,5 °C) binnen de band zit. Zonder hysterese zou een pH die rond 6,50 schommelt tientallen meldingen geven (hoofdstuk 31).
- De pomp wordt in acties() beslist: minuut 0-9, of minuut 30-34 als de beslisboom om 30 minuten slappe sla verwachtte, nooit bij een water- of niveau-alarm en niet in ONDERHOUD. Het niveau telt pas 2 minuten na de pomp.
- De logger schrijft 17 kolommen: de 7 van hoofdstuk 28 en 30, en 10 nieuwe achteraan, zodat oude Python-scripts blijven werken (hoofdstuk 30).
Getest. Het programma compileert zonder waarschuwingen met g++ tegen een nagebootste Arduino, ook met int = 16 bit zoals op de Mega. In een gesloten lus met een model van de module draaide het een week: 59 miljoen rondes van loop(), de langste 48,8 ms (eis B6: hoogstens 100 ms; de SHT31 alleen wacht al 20 ms), 432 schrijfbeurten in het EEPROM (alleen bytes die veranderen) en acht dagbestanden op de kaart. De testweek van dit hoofdstuk komt uit dezelfde simulatie, in Python nagebouwd: per dag verschillen verbruik, zon en warmte minder dan 0,5 %.
Het logformaat van versie 5
Eén regel per minuut, op de SD-kaart in LOG/JJJJMMDD.CSV en tegelijk over USB (hoofdstuk 30). Een lege cel betekent: geen geldige meting in die minuut. Nieuwe kolommen komen achteraan, zodat een script dat de eerste zeven kolommen leest, blijft werken.
| kolom | eenheid | wat erin staat | sinds |
|---|---|---|---|
| tijdstip | jjjj-mm-dd uu:mm | begin van de minuut, UTC+1 | 28 |
| temperatuur_C | °C | gemiddelde van de SHT31 (leeg zonder meting) | 28 |
| vocht_pct | % | relatieve vochtigheid van de lucht | 28 |
| verbruik_Wh | Wh | energie naar de verbruikers in die minuut (INA226 0x41) | 28 |
| zon_Wh | Wh | geleverde zonnestroom in die minuut (INA226 0x40) | 28 |
| licht_lx | lx | daglicht op de LDR | 30 |
| batterij_V | V | busspanning van de rail | 30 |
| soc_pct | % | laadtoestand aan het einde van de minuut | 40 |
| lampen_pwm | 0-255 | gemiddelde lampstand | 40 |
| verw_pct | % | deel van de minuut met de verwarming aan | 40 |
| dak | 0-5 | DICHT, OPENEN, OPEN, SLUITEN, GESTOPT, DAK_FOUT | 40 |
| water_C | °C | voedingsoplossing (NTC) | 40 |
| pH | gemiddelde van de minuut | 40 | |
| EC_mScm | mS/cm | omgerekend naar 25 °C | 40 |
| niveau_cm | cm | reservoir, gemeten bij stilstaande pomp | 40 |
| modus | 0-4 | OPSTART, DAG, NACHT, ALARM, ONDERHOUD | 40 |
| alarm | bits | 1 temperatuur, 2 vlotter, 4 niveau, 8 dak, 16 batterij | 40 |
Vijf regels uit de testweek (donderdag, de sensorkast is van 14:02 tot 14:07 losgekoppeld)
tijdstip,temperatuur_C,vocht_pct,verbruik_Wh,zon_Wh,licht_lx,batterij_V,soc_pct,lampen_pwm,verw_pct,dak,water_C,pH,EC_mScm,niveau_cm,modus,alarm 2021-03-11 14:01,25.4,49.6,0.49,0.58,39142,14.30,100.0,0,0,2,21.5,6.21,1.36,21.5,1,0 2021-03-11 14:02,,,0.35,0.43,39112,14.30,100.0,0,0,2,21.5,6.21,1.36,21.5,3,1 2021-03-11 14:03,,,0.34,0.42,39082,14.30,100.0,0,0,2,21.5,6.21,1.36,21.5,3,1 2021-03-11 14:07,26.2,47.6,0.49,0.57,38961,14.30,100.0,0,0,2,21.6,6.21,1.36,21.5,3,0 2021-03-11 14:08,26.2,47.8,0.49,0.58,38930,14.30,100.0,0,0,2,21.6,6.21,1.36,21.5,3,0
Om 14:02 zijn temperatuur en vocht leeg, springt de modus naar 3 (ALARM) en staat alarmbit 1 (geen temperatuur) aan. Om 14:07 is de meting terug en het alarmbit uit, maar de modus blijft 3 tot iemand kort op S1 drukt (14:09). Het verbruik zakt in die minuten van 0,49 naar 0,35 Wh per minuut: zonder meting koelt het noodprogramma niet, en de ventilatoren vallen terug.
| grootte | waarde |
|---|---|
| regel | ±84 tekens |
| dagbestand | ±121 kB (hoofdstuk 30, met 7 kolommen: 68,5 kB) |
| kaart van 1 GB | ±23 jaar |
Fase 4 · testen: van deeltest tot testweek
Testen volgt de rechterkant van het V-model: van de delen naar het geheel, en elke test toetst de ontwerpstap die ertegenover staat.
- Deeltests, zodra een onderdeel klaar is: elke sensor naast een referentie, elke tak met de stroomtang, de pomp met een maatbeker per goot, het dak met een blokje in de rail.
- Integratietests: werkt elke interface zoals de tabel zegt? De I²C-scanner vindt vier adressen, de logger schrijft 17 kolommen, de pomp slaat aan in minuut 0 en niet in minuut 11.
- Storingstests: je veroorzaakt een fout en kijkt of het systeem reageert zoals ontworpen. Wat er moet gebeuren, schrijf je vooraf op.
- De testweek: de module draait zeven dagen alleen. Elke dag kijkt één teamlid in de logbestanden, zonder in te grijpen tenzij de planten of de veiligheid het vragen.
| storingstest | wanneer | wat moet er gebeuren (vooraf) | wat de logger toonde | eis |
|---|---|---|---|---|
| stekker van de sensorkast los (5 min) | do 14:02 | na 30 s alarm, noodprogramma voor de verwarming, lege cellen | alarm om 14:02:28, 5 regels zonder temperatuur | B2, B3, D2 |
| vlotter met de hand omhoog (1 min) | do 14:15 | na 10 s alarm, pomp uit | alarm om 14:15:08 | W3, B2 |
| stekker van de regelkast uit (2 min) | do 15:56 | alles uit; daarna OPSTART, laadtoestand uit het EEPROM | OPSTART 15:58:00, DAG 15:58:06; 3 regels ontbreken | B1, E5, D1 |
| lange druk op S1: onderhoud | wo 10:12 | pomp uit, dak geblokkeerd, werklicht, ventilatoren vol | modus 4 van 10:12 tot 10:29 | B4 |
| blokje in de rail van het dak | deeltest, vr | na 12 s DAK_FOUT en alarm; kort drukken: opnieuw sluiten | zie K5 (voor de testweek) | K5 |
| een taak die blijft hangen | deeltest (hoofdstuk 32) | de waakhond herstart de Mega na 2 s | herstart | B5 |
De simulatie als generale repetitie. Voor de module een week alleen blijft, draait het voorbeeldteam versie 5 in een simulatie: het programma krijgt nagebootste sensoren, en een model van de module (warmte zoals hoofdstuk 31, zon zoals hoofdstuk 20) reageert op wat het programma schakelt. Zo vind je fouten in de code voor ze in de echte module zitten. De testweek van dit hoofdstuk is zo'n simulatie; wie de module echt heeft (spoor A), vervangt ze door de eigen logbestanden.
Een simulatie toont wat het programma doet met de module zoals je ze gemodelleerd hebt. Klopt het model niet (een dak dat minder koelt, een sla die meer verdampt), dan klopt de uitkomst ook niet. Schrijf daarom bij elk resultaat of het uit een meting, een berekening of een simulatie komt, en vergelijk het model met echte metingen zodra je ze hebt (E17, E36).
De testweek: 8 tot 14 maart 2021
De testweek begon op maandag met een grijze start: laadtoestand 55 %, reservoir op 22 cm. Het logboek van het team:
| wanneer | wat | gevolg in de logbestanden |
|---|---|---|
| ma 8 maart 0:00 | start van de testweek; laadtoestand 55 %, reservoir 22 cm, EC 1,40, pH 5,86 | — |
| ma en di | grijs (zonfactor 0,35 en 0,25), 3 tot 9 °C | laadtoestand onder 40 %: de lampen krijgen alleen zonne-overschot |
| wo 10:12-10:29 | onderhoud: 13 L regenwater en 130 mL A + 130 mL B bijgevuld | modus 4; niveau van 18,9 naar 24,3 cm |
| wo en do | zonnig, tot 14 °C buiten | dak 10 en 14 keer open; batterijen vol, zonnestroom afgeknepen |
| do 14:00-16:00 | storingstests: sensorkast, vlotter, stroom | alarmen, lege cellen, 3 ontbrekende regels |
| za | heel grijs (zonfactor 0,20) | laadtoestand van 77 naar 38 %; lichtsom 9,2 mol |
| zo 10:12 | pH boven 6,5 | waarschuwing in de meldingen; niemand in de module |
| ma 15 maart | pH bijgestuurd met pH-min, reservoir bijgevuld | (na de testweek) |
Wat de vier grafieken tonen:
- Temperatuur. 's Nachts de zaagtand van de verwarming rond 16-17 °C, overdag 18-19 °C op grijze dagen. Woensdag en donderdag stijgt de lucht tot 27,1 °C: dan gaat het dak 10 en 14 keer open. De verwarming vroeg 14,0 kWh warmte van het station, van 0,8 tot 4,3 uur per dag.
- Energie. De panelen hadden 23,9 kWh kunnen leveren, maar de regelaar nam er maar 11,4 af: op de zonnige dagen waren de batterijen voor de middag vol, en 12,4 kWh bleef ongebruikt. Het verbruik was 10,2 kWh, waarvan 8,2 kWh voor de lampen.
- Laadtoestand. Tussen 35 en 100 %. Onder 40 % van maandagavond tot dinsdagmiddag, van dinsdagavond tot woensdagochtend en van zaterdagavond tot zondagochtend: dan kregen de lampen alleen zonne-overschot. Nooit onder 20 %.
- Voedingsoplossing. De pH stijgt gestaag, van 5,86 naar 6,59; zondag om 10:12 passeert ze 6,5. De EC zakt langzaam, van 1,40 naar 1,31: de sla neemt verhoudingsgewijs meer zouten op dan water. Het niveau zakt van 21,9 naar 14,6 cm, met het bijvullen van woensdag ertussen.
De testweek analyseren in Python
Zeven dagbestanden van 1440 regels lees je niet met het oog. Het script leest ze met het csv-moduul (hoofdstuk 28 en 30), maakt per dag een rapport en toetst een deel van de eisen. Twee keuzes: een lege cel wordt None en telt niet mee in een minimum; en 'overdag' begint voor K1 pas om 7 uur, omdat de module om 6 uur van de nacht- naar de daginstelling springt en even moet opwarmen (dat staat zo in het programma van eisen, niet achteraf bedacht).
# testweek.py - de logbestanden van de klimaatmodule toetsen aan het programma van eisenimport csv DAGEN = ["20210308", "20210309", "20210310", "20210311", "20210312", "20210313", "20210314"]NAAM = ["ma", "di", "wo", "do", "vr", "za", "zo"] def getal(tekst): """Tekst naar kommagetal; een lege cel (geen meting) wordt None.""" return float(tekst) if tekst != "" else None def lees(dag): with open("LOG/" + dag + ".CSV", newline="") as f: return [{k: (v if k == "tijdstip" else getal(v)) for k, v in r.items()} for r in csv.DictReader(f)] def uur(r): return int(r["tijdstip"][11:13]) def dagrapport(rijen): dag = [r for r in rijen if 7 <= uur(r) < 22 and r["temperatuur_C"] is not None] nacht = [r for r in rijen if not 6 <= uur(r) < 22 and r["temperatuur_C"] is not None] par = [r["licht_lx"] / 54 + 200 * r["lampen_pwm"] / 255 for r in rijen if 6 <= uur(r) < 22] paren = list(zip(rijen, rijen[1:])) # (deze minuut, volgende minuut) return { "regels": len(rijen), "T_dag": (min(r["temperatuur_C"] for r in dag), max(r["temperatuur_C"] for r in dag)), "T_nacht": min(r["temperatuur_C"] for r in nacht), "zon": sum(r["zon_Wh"] for r in rijen) / 1000, # kWh "verbruik": sum(r["verbruik_Wh"] for r in rijen) / 1000, "soc": min(r["soc_pct"] for r in rijen), "lichtsom": sum(p * 60 for p in par) / 1e6, # mol/(m² dag) "verwarming": sum(r["verw_pct"] for r in rijen) / 100 / 60, # uur "starts": sum(1 for a, b in paren if a["verw_pct"] == 0 and b["verw_pct"] > 0), "dak": sum(1 for a, b in paren if a["dak"] == 0 and b["dak"] > 0), "pH": max(r["pH"] for r in rijen), "EC": min(r["EC_mScm"] for r in rijen), "niveau": min(r["niveau_cm"] for r in rijen), } week = [dagrapport(lees(dag)) for dag in DAGEN]print("dag regels T overdag nacht zon verbr SoC licht verw starts dak pH EC niveau")for naam, d in zip(NAAM, week): print(f"{naam} {d['regels']:5d} {d['T_dag'][0]:4.1f}-{d['T_dag'][1]:4.1f} {d['T_nacht']:4.1f} " f"{d['zon']:4.2f} {d['verbruik']:4.2f} {d['soc']:3.0f} {d['lichtsom']:4.1f} {d['verwarming']:3.1f}" f" {d['starts']:3d} {d['dak']:3d} {d['pH']:4.2f} {d['EC']:4.2f} {d['niveau']:4.1f}") def toets(code, tekst, fout): print(f"{code} {tekst:38s} {'gehaald' if not fout else 'NIET: ' + ', '.join(fout)}") print()toets("K1", "overdag (7-22 h) nooit onder 17,5 C", [n for n, d in zip(NAAM, week) if d["T_dag"][0] < 17.5])toets("K2", "'s nachts nooit onder 15,5 C", [n for n, d in zip(NAAM, week) if d["T_nacht"] < 15.5])toets("K3", "nooit boven 30 C", [n for n, d in zip(NAAM, week) if d["T_dag"][1] > 30])toets("L1", "lichtsom minstens 11,0 mol per dag", [n for n, d in zip(NAAM, week) if d["lichtsom"] < 11.0])toets("E3", "laadtoestand nooit onder 20 %", [n for n, d in zip(NAAM, week) if d["soc"] < 20])toets("H1", "EC nooit onder 1,2 mS/cm", [n for n, d in zip(NAAM, week) if d["EC"] < 1.2])toets("H2", "pH nooit boven 6,5", [n for n, d in zip(NAAM, week) if d["pH"] > 6.5])toets("D1", "1440 regels per dag", [n for n, d in zip(NAAM, week) if d["regels"] != 1440])De uitvoer:
dag regels T overdag nacht zon verbr SoC licht verw starts dak pH EC niveau ma 1440 18.0-21.4 16.0 1.47 1.94 39 10.6 2.8 48 0 5.94 1.38 20.0 di 1440 18.0-20.2 16.0 1.05 1.08 36 6.0 4.3 70 0 6.00 1.38 19.0 wo 1440 17.9-27.0 16.0 2.54 1.09 35 21.4 3.0 43 10 6.16 1.37 18.6 do 1437 18.0-27.1 16.0 1.46 1.36 67 23.6 1.7 30 14 6.29 1.35 20.1 vr 1440 18.0-25.0 16.0 1.56 1.58 65 13.4 0.8 16 0 6.40 1.34 18.0 za 1440 18.0-20.1 16.0 0.84 2.05 38 9.2 2.3 41 0 6.48 1.33 16.6 zo 1440 18.0-25.2 16.0 2.50 1.13 35 15.1 2.6 40 0 6.59 1.31 14.6 K1 overdag (7-22 h) nooit onder 17,5 C gehaald K2 's nachts nooit onder 15,5 C gehaald K3 nooit boven 30 C gehaald L1 lichtsom minstens 11,0 mol per dag NIET: ma, di, za E3 laadtoestand nooit onder 20 % gehaald H1 EC nooit onder 1,2 mS/cm gehaald H2 pH nooit boven 6,5 NIET: zo D1 1440 regels per dag NIET: do
Lees ze eerst per kolom. regels: donderdag ontbreken er drie (de stroomtest). licht: maandag, dinsdag en zaterdag onder 11,0 mol. starts: dinsdag 70 keer verwarmen op één dag, maar in één uur nooit meer dan 6 keer (eis K6, een aparte telling per uur). dak: alleen woensdag en donderdag. pH: zondag boven 6,5.
Twee eisen zijn niet gehaald (L1 en H2), en D1 geeft 'NIET' voor donderdag: dat is geen fout van de module maar van de toets, want de stroomtest stond in het testplan. Het testrapport noteert daarom 'ja' met een verklaring. Een toets in code is streng en eerlijk; het oordeel blijft werk voor mensen.
Het testrapport van het voorbeeldteam
Per eis de toets, het resultaat en het besluit. Van de 47 moet-eisen zijn er 43 gehaald, één voorlopig (K3: de zomer komt nog), één kan pas na vijf weken getoetst worden (W5) en twee zijn niet gehaald. Van de 7 wensen zijn er 5 gehaald, één niet (H4) en één pas na een jaar te toetsen (P2).
L1, de lichtsom. Op drie grijze dagen bleef de lichtsom onder 11,0 mol. De oorzaak is geen fout maar een botsing tussen twee eisen: E3 (nooit onder 20 %) en E4 (eerst de lampen afknijpen) winnen van L1. Opvallend: dezelfde week werd 12,4 kWh zonnestroom afgeknepen. Een grotere buffer of stroom van het station zou beide eisen laten samengaan (werkboek).
H2, de pH. De waarschuwing kwam op zondag om 10:12, maar niemand zag ze tot maandag. De module deed wat het ontwerp vroeg; het ontwerp vroeg te weinig. Twee oplossingen: vrijdag de pH preventief naar 5,8 brengen (bij +0,1 à 0,15 per dag blijft ze dan tot maandag onder 6,5), of de melding naar een telefoon sturen (E37).
| code | eis | toets | resultaat (voorbeeldteam) | besluit |
|---|---|---|---|---|
| K1 | overdag (7-22 h) minstens 17,5 °C bij de planten | logger, testweek | laagste 17,9 °C | ja |
| K2 | 's nachts (22-6 h) minstens 15,5 °C | logger, testweek | laagste 16,0 °C | ja |
| K3 | hoogstens 30 °C | logger, zonnige dagen | hoogste 27,1 °C (do) | voorlopig |
| K4 | ventilatoren vol boven 25 °C, dak open boven 27 °C; terug onder 24 en 25 °C | naspelen, logger | 24 keer open (wo, do), drempels juist | ja |
| K5 | het dak haalt zijn eindpunt binnen 12 s, anders alarm | deeltest: blokje in de rail | sluit in 6,8 s; met blokje alarm na 12 s | ja |
| K6 | verwarming hoogstens 6 keer aan per uur | logger: starts per uur | hoogstens 6 (wo 5-6 h) | ja, net |
| K7 | basisventilatie overdag minstens 11 m³/h | anemometer aan het rooster | 11,9 m³/h | ja |
| K8 | CO₂-klep alleen in DAG met het dak dicht | naspelen, code | in orde | ja |
| L1 | lichtsom op het bed minstens 11,0 mol/(m²·dag) | logger: daglicht + lampen | ma 10,6, di 6,0, za 9,2 | nee |
| L2 | lampen vullen het daglicht aan tot 200 µmol/(m²·s), hoogstens 1 stap per 200 ms | logger, code | in orde | ja |
| L3 | volle lampen: elk van 9 meetpunten ≥ 180 µmol/(m²·s) | PAR-meter, 's nachts | 186 tot 214 | ja |
| W1 | 1,2 L/min per goot (± 20 %) | maatbeker, 1 min per goot | 1,08 tot 1,31; na bijstellen 1,19 tot 1,21 | ja |
| W2 | pomp in minuut 0-9 van elk uur, extra beurt als slappe sla verwacht wordt | logger, meldingen | 7 extra beurten | ja |
| W3 | de pomp draait nooit droog | vlotter met de hand omhoog | alarm na 8 s, pomp uit | ja |
| W4 | minstens 5 dagen zonder bijvullen bij 6 L per dag | berekening, logger | 36 L / 6 L = 6,0 dagen | ja |
| W5 | reservoir lichtdicht: algen onder 5 · 10⁵ per mL | telling na 5 weken | pas na 5 weken | later |
| H1 | EC tussen 1,2 en 1,8 mS/cm (bij 25 °C) | logger | 1,31 tot 1,40 | ja |
| H2 | pH tussen 5,5 en 6,5 | logger | 5,86 tot 6,59; zo 10:12 boven 6,5 | nee |
| H3 | voedingsoplossing nooit boven 24 °C | logger | hoogste 23,0 °C | ja |
| H4 | voedingsoplossing tussen 18 en 22 °C | logger | 17,1 tot 23,0 °C | nee |
| H5 | pH-sensor om de twee weken in twee punten geijkt, EC met ijkvloeistof | ijkverslag | −0,177 V/pH; 1,42 in 1,413 | ja |
| E1 | van maart tot oktober genoeg zonnestroom: saldo per maand ≥ 0 | berekening PVGIS, testweek | maart +2,6, oktober +1,8 kWh/dag | ja |
| E2 | de batterijen overbruggen de nacht in de slechtste maand | berekening | 1,84 kWh (december) | ja |
| E3 | laadtoestand nooit onder 20 % | logger | laagste 35 % | ja |
| E4 | energiebeheer: onder 40 % lampen op zonnestroom, onder 20 % uit, onder 10 % alarm | logger (dinsdag) | dinsdag: lampen begrensd | ja |
| E5 | laadtoestand elk uur in het EEPROM, na een stroomuitval hersteld | stekker van de regelkast uit | 100,0 → 100,0 % | ja |
| E6 | 5 V-rail aan de sensorkast minstens 4,75 V | meten: pomp aan, SD schrijft | 4,91 V | ja |
| E7 | batterijen niet laden onder 0 °C | BMS-instelling, logger | laagste 16,0 °C | ja |
| E8 | jaarverbruik hoogstens 700 kWh | berekening | ±540 kWh | ja |
| B1 | na een stroomuitval: alles uit, OPSTART 5 s, dan de gewone modus | stekker van de regelkast uit | 15:58:00 OPSTART, 15:58:06 DAG | ja |
| B2 | alarm bij: geen temperatuur 30 s, vlotter droog 10 s, reservoir onder 10 cm, dak-time-out, batterij onder 10 % | storingstests | sensor na 28 s, vlotter na 8 s | ja |
| B3 | zonder temperatuur verwarmt de module met de gemiddelde inschakelduur | storingstest sensor | in orde | ja |
| B4 | onderhoud: lange druk van 3 s; pomp uit, dak geblokkeerd, werklicht; na 60 min terug | woensdag | wo 10:12 tot 10:29 | ja |
| B5 | waakhond: een hangende taak herstart de Mega binnen 2 s | deeltest (hoofdstuk 32) | in orde | ja |
| B6 | langste ronde van loop() hoogstens 100 ms | simulatie, micros() | 48,8 ms | ja |
| B7 | één taak per uitgang | code nalezen | in orde (pomp samengevoegd) | ja |
| D1 | elke minuut een CSV-regel, één bestand per dag | logbestanden | 1440; do 1437 (stroomtest) | ja |
| D2 | een ontbrekende meting geeft een lege cel | storingstest sensor | 5 regels | ja |
| D3 | minstens een jaar op de kaart | berekening | ±121 kB per dag: 1 GB ≈ 23 jaar | ja |
| D4 | klok op UTC+1, hoogstens 1 min afwijking per maand | referentieklok | ±5 s per maand (±2 ppm) | ja |
| D5 | dag- en weekrapport in Python, gekoppeld aan de eisen | testweek.py | in orde | ja |
| V1 | in de kweekruimte alleen 12,8 V en 5 V | checklist | in orde | ja |
| V2 | zekering per tak en bij elke batterijpool | schema, keuring | in orde | ja |
| V3 | niemand onder een bewegend dak | test in onderhoud | in orde | ja |
| V4 | pH-min (salpeterzuur 0,50 mol/L) gelabeld, met bril en handschoenen | checklist | in orde | ja |
| C1 | reservoir (65 kg vol) met zijn midden tussen 0,65 en 1,31 m van ligger A | rolmeter | 0,98 m | ja |
| C2 | dakmotor trekt 120 N zonder te blokkeren | trekproef met veerunster | 120 N bij 3,4 A | ja |
| G1 | statusled: rustig normaal, snel bij alarm, vast bij onderhoud | test | in orde | ja |
| G2 | kasdak met de hand: korte druk op S1 naast de deur | test | in orde | ja |
| G3 | wekelijks onderhoud (bijvullen, EC en pH) hoogstens 20 min | tijd opnemen | 17 min | ja |
| G4 | hydroponie hoogstens € 800 | kostenraming | € 788 | ja |
| P1 | 48 plantplaatsen, om de 8,75 dagen een goot oogsten | teeltschema | in orde | ja |
| P2 | minstens 100 kg sla per jaar | wegen, een jaar lang | model: 100 kg | later |
| O1 | hoogstens 2 kg CO₂-eq per kg sla | levenscyclusanalyse | 1,64 | ja |
Foutzoeken met de logger: de klep klikt voortdurend
Een ander team hing de sensorkast SK-1 op 40 cm boven de convector: daar kwam de kabel van 3 m net toe. De eerste ochtend hoort een bewoner de klep van de convector aan één stuk klikken. De sla staat er goed bij, de statusled knippert rustig. Het logbestand van maandag, 7:00 tot 7:08:
tijdstip,temperatuur_C,vocht_pct,verbruik_Wh,zon_Wh,licht_lx,batterij_V,soc_pct,lampen_pwm,verw_pct,dak,water_C,pH,EC_mScm,niveau_cm,modus,alarm 2021-03-08 07:00,18.7,83.5,3.51,0.04,107,12.95,46.5,252,0,0,16.9,5.86,1.40,21.8,1,0 2021-03-08 07:01,18.3,83.7,3.47,0.04,110,12.95,46.4,252,0,0,16.9,5.86,1.40,21.8,1,0 2021-03-08 07:02,18.1,83.9,3.53,0.04,114,12.94,46.3,252,37,0,16.9,5.86,1.40,21.8,1,0 2021-03-08 07:03,18.9,83.2,3.58,0.05,117,12.94,46.2,252,47,0,16.9,5.86,1.40,21.8,1,0 2021-03-08 07:04,19.0,83.3,3.47,0.05,121,12.95,46.0,252,0,0,16.9,5.86,1.40,21.8,1,0 2021-03-08 07:05,18.5,83.5,3.47,0.05,124,12.95,45.9,252,0,0,16.9,5.86,1.40,21.8,1,0 2021-03-08 07:06,18.2,83.8,3.47,0.05,128,12.95,45.8,252,0,0,16.9,5.86,1.40,21.8,1,0 2021-03-08 07:07,18.2,83.7,3.60,0.05,131,12.94,45.7,252,70,0,16.9,5.86,1.40,21.8,1,0 2021-03-08 07:08,19.2,83.1,3.51,0.05,135,12.94,45.6,252,13,0,16.9,5.86,1.40,21.8,1,0
Waar zit de fout, hoe bevestig je ze, en hoe los je ze op?
- het logbestand hierboven (kolommen: blad 'Het logformaat van versie 5')
- verwarming aan onder 18 °C, uit boven 19 °C (hoofdstuk 31); eis K6: hoogstens 6 keer aan per uur
- een referentiethermometer bij de planten, op 1,2 m hoogte
- wat het logbestand zegt
- een hypothese en een test die één ding verandert
- de oplossing en de hertest
- 1Lees wat de module zelf vertelt. Geen alarm (kolom alarm 0, modus 1): de module denkt dat alles in orde is. Maar verw_pct springt van minuut tot minuut: 0, 37, 47, 0, 70, 13 %. De verwarming gaat binnen één minuut aan en weer uit. Geteld over een uur: 14 keer aan tussen 7 en 8 uur, tegenover ±4 op dezelfde ochtend in de testweek. Eis K6 is ruim overschreden.
- 2Vergelijk met een referentie. Tussen 7 en 9 uur meet de SHT31 gemiddeld 18,6 °C, de referentie bij de planten 18,0 °C. De sensor meet warmer dan de lucht waarin de planten staan, en de temperatuur in het logbestand springt tot 1 °C per minuut: veel sneller dan een ruimte met 80 kJ/K kan opwarmen (hoofdstuk 31).
- 3Hypothese. De sensor zit in de warme luchtstroom van de convector. Zodra die blaast, meet hij binnen een minuut 19 °C en schakelt de verwarming uit; de luchtstroom stopt, de sensor koelt af tot onder 18 °C, en de klep gaat weer open. De regeling regelt de lucht rond de sensor, niet de module.
- 4Test die één ding verandert. Hang de referentiethermometer naast de SHT31, boven de convector. Zijn ze het daar eens, dan meet de sensor juist en is de plaats fout. Hang daarna alleen de sensorkast om, midden in de ruimte op planthoogte, en laat de rest zoals het was.
- 5Oplossen en hertesten. Na het verplaatsen: een nacht loggen en tellen. In het model valt de verwarming terug naar ±4 starts per uur en verdwijnt het verschil met de referentie. Noteer de verplaatsing in de interfacetabel (I7: de plaats van de sensor hoort bij de afspraak).
De foutzoektabel van de module
Per symptoom de mogelijke oorzaken, de waarschijnlijkste eerst, met een test die telkens één ding controleert. Begin altijd bij wat de module zelf vertelt: de statusled, de meldingen en de logbestanden. Voeg de fouten toe die jullie zelf vinden: zo groeit de tabel mee met de module.
| symptoom | mogelijke oorzaak (waarschijnlijkste eerst) | test (telkens één ding) | oplossing |
|---|---|---|---|
| alles donker, statusled uit | zekering van de omvormer (1 A); BMS schakelde af; stekker van de regelkast | 12,8 V op de rail? 5 V na de omvormer? | oorzaak van de zekering zoeken, dan vervangen; batterijen laden |
| statusled knippert snel | een alarm: geen temperatuur, vlotter, niveau, dak of batterij | de meldingen (#) en de kolom alarm lezen | de oorzaak oplossen, dan kort drukken op S1 |
| lampen zwak op een grijze dag | laadtoestand onder 40 %: het energiebeheer; LDR ziet een lamp; lamp of MOSFET defect | logger: soc_pct, lampen_pwm, licht_lx naast elkaar | bij een lage laadtoestand is het een ontwerpkeuze (eis L1, werkboek) |
| pomp slaat om de paar seconden aan en uit | twee taken schrijven naar pin 9 | elke plaats in de code die de pomp schakelt opzoeken | één eigenaar (versie 5) |
| een goot krijgt weinig water | kraantje dicht of verstopt; wortels in de verzamelbuis; pomp zuigt lucht | maatbeker aan het einde van elke goot, 1 min | kraantje bijstellen, goot reinigen bij het oogsten |
| elk uur een niveau-alarm in minuut 1 tot 9 | niveau gemeten terwijl er water in de goten staat | niveau_cm per minuut rond minuut 0 | niveau pas 2 min na de pomp (versie 5); reservoir bijvullen |
| pH springt als de pomp aanslaat | gedeelde massadraad (hoofdstuk 21) | pH-kolom rond minuut 0 en 10 | eigen retourdraad per tak naar het sterpunt |
| pH loopt op tot boven 6,5 | normale stijging (0,1 à 0,15 per dag); niemand stuurde bij | pH per dag in de logger | pH-min na een proeftitratie; vrijdag preventief |
| EC zakt na het bijvullen | alleen regenwater bijgevuld | EC voor en na het bijvullen | 10 mL A en 10 mL B per liter bijgevuld water |
| temperatuur leeg in de logger | SHT31 los, kabel van 3 m, pull-ups | I²C-scanner: staat 0x44 erbij? | stekker, kabel, pull-ups nakijken (hoofdstuk 30) |
| klep van de convector klikt tot 14 keer per uur | sensorkast in de warme lucht van de convector; hysterese te smal | starts per uur; referentiethermometer bij de planten | sensorkast midden in de ruimte (hoofdstuk 31); eventueel een minimum aantijd |
| dak blijft half open, alarm | blokje of ijs in de rail; eindeloop defect; zekering 5 A | melding 'dak haalt zijn eindpunt niet'; eindeloop met de hand | vrijmaken, dan kort drukken: het dak sluit opnieuw |
| laadtoestand 100 % terwijl de spanning zakt | de teller is lang niet opnieuw geijkt | rustspanning 's morgens tegenover soc_pct | de batterijen één keer laten vollopen tot 14,2 V |
| logbestand ontbreekt of is leeg | kaart vol of weg; naam niet 8.3; stroom viel weg voor close() | melding 'SD'; kaart in de laptop | kaart vervangen; bestandsnaam nakijken (hoofdstuk 30) |
| module te warm met het dak open | zon op een zomerdag groter dan dak en ventilatoren aankunnen | temperatuur tegenover zonnestroom | schaduwdoek of nachtscherm als zonnescherm (versie 2) |
Fase 5 · het technisch rapport
Een technisch rapport is geschreven voor iemand die het project niet volgde, maar er verder mee moet: een volgend team, een ingenieur van het station, de jury. Die lezer leest eerst de samenvatting, dan de figuren, en pas daarna de tekst. De opbouw volgt de fasen:
| onderdeel | wat erin staat | bij het voorbeeldteam |
|---|---|---|
| titelblad | titel als boodschap, team, datum, versie | Eigen stroom, eigen sla: de klimaatmodule van Habitat in een testweek (versie 1.0) |
| samenvatting | een halve bladzijde: doel, aanpak, belangrijkste getallen, besluit | zie hieronder |
| inleiding | probleem, doel, afbakening, spoor | alleen de kweekruimte, niet de woonmodule; spoor B |
| programma van eisen | de eisen per groep, met herkomst; de volledige tabel in een bijlage | 54 eisen, 47 moet |
| ontwerp per deelsysteem | berekening, keuze en schema, per eigenaar | energiebudget, waterkringloop, warmtemodel, licht, voeding |
| integratie | N²-diagram, interfacetabel, de problemen die je vond | 18 interfaces, 6 problemen |
| besturing | versies, blokschema, toestandsdiagram; de code in een bijlage | klimaat.ino versie 5 |
| testen | testplan, storingstests, testweek, testrapport | 43 van 47 moet-eisen gehaald |
| besluit en aanbevelingen | antwoord op het doel; wat beter moet, gekoppeld aan eisen | L1 en H2: twee voorstellen |
| bronnen | alles wat je gebruikte, zo dat een ander het terugvindt | PVGIS, datasheets, hoofdstukken |
| bijlagen | code, logbestanden, tekeningen, ijkverslagen | klimaat.ino, zeven logbestanden |
De samenvatting van het voorbeeldteam (één alinea, alle getallen erin):
De klimaat- en energiemodule van Habitat moet 48 kroppen sla telen op eigen zonnestroom, met een geregeld klimaat en een logger. We stelden 54 eisen op, waarvan 47 moet-eisen, rekenden de energie-, water- en warmtebalans na, voegden vijf versies van de besturing samen tot klimaat.ino versie 5 en lieten de module een week alleen draaien in een simulatie met hetzelfde programma (8-14 maart 2021). 43 moet-eisen zijn gehaald, één voorlopig (de zomer komt nog) en één kan pas na vijf weken getoetst worden. Twee zijn niet gehaald: op drie grijze dagen bleef de lichtsom onder 11,0 mol/(m²·dag), omdat het energiebeheer de lampen afknijpt onder 40 % laadtoestand, terwijl op de zonnige dagen 12,4 kWh zonnestroom ongebruikt bleef; en zondag steeg de pH boven 6,5 zonder dat iemand de waarschuwing zag. We stellen een grotere buffer of stroom van het station voor, en meldingen die een telefoon bereiken.
Zinnen die iets bewijzen. Een rapport overtuigt met getallen, eenheden en criteria:
| zwak | sterk | waarom |
|---|---|---|
| De batterij is groot genoeg. | In december leveren de batterijen 's nachts 1,84 kWh van de 2,46 kWh die bruikbaar is (75 %). | getal, eenheid en de grens ernaast |
| De test was geslaagd. | Met de vlotter omhoog volgde na 8 s een alarm en stopte de pomp (eis W3: binnen 10 s). | wat, wanneer en tegen welk criterium |
| We denken dat de sensor slecht hing. | Boven de convector las de SHT31 0,5 °C hoger dan de referentie bij de planten; op de nieuwe plaats verdween het verschil. | een meting in plaats van een mening |
| Zie grafiek. | Figuur 7 toont dat de laadtoestand dinsdag onder 40 % bleef: de lampen kregen alleen zonne-overschot. | de tekst zegt wat de figuur bewijst |
| Alles werkte goed. | 43 van de 47 moet-eisen zijn gehaald; L1 en H2 niet (zie 6.3). | eerlijk, ook over wat niet lukte |
Bronnen. Vermeld alles wat je gebruikte, zo dat een ander het terugvindt: wie, wat, welke versie, wanneer geraadpleegd (hoofdstuk 2). Bijvoorbeeld: Europese Commissie, Joint Research Centre. PVGIS, versie 5.3. Brussel, 60° zuid, 1,40 kWp. Documentatie gecontroleerd op 30 september 2026; de getallen zijn rekenwaarden uit het cursusmodel. Voor een datasheet: de fabrikant, het onderdeel en de revisie. Voor dit boek: StemExpert Intermediate, hoofdstuk 20: Zonne-energie dimensioneren.
Een rapport vertelt wat er is, waarom, en hoe goed het werkt. Het logboek bewaart het verhaal van het werk; het rapport bewaart het resultaat. Wat niet lukte, hoort er wel in: met de oorzaak en een voorstel.
Fase 5 · de verdediging voor een jury
De verdediging duurt twintig minuten: tien minuten presenteren, tien minuten vragen. De jury (de leraar, een collega, iemand van buiten de school) kent het programma van eisen en heeft het rapport gelezen. Ze wil drie dingen weten: begrijpen jullie je eigen ontwerp, zijn jullie keuzes onderbouwd, en zijn jullie eerlijk over de grenzen?
- Het storyboard legt de dia's vast voor je ze maakt: één boodschap per dia, als titel geschreven ('De batterijen dekken de nacht, niet de grijze week'), met één figuur die ze bewijst.
- Iedereen spreekt, elke eigenaar over zijn deelsysteem. De integrator opent en sluit.
- De demo toont de module aan het werk: een hand op de SHT31 en de ventilatoren slaan aan, de vlotter omhoog en de statusled knippert snel, de logger die live een regel schrijft. Houd een plan B klaar (een filmpje, een logbestand).
Vragen die een jury stelt, en wat een sterk antwoord bevat:
| vraag van de jury | waar het echt over gaat | wat een sterk antwoord bevat |
|---|---|---|
| Waarom twee batterijen en geen drie? | dimensioneren en afwegen | de nacht in december (1,84 kWh van 2,46); een derde zou de afgeknepen zonnestroom van een zonnige dag bewaren voor een grijze, maar kost geld en CO₂ (hoofdstuk 38) |
| Hoe weet je dat je simulatie klopt? | model tegenover werkelijkheid | de waarden komen uit hoofdstuk 19, 20 en 31; C++ en Python geven dezelfde week; valideren doe je met echte logbestanden (E36) |
| Wat gebeurt er als de SHT31 uitvalt? | fail-safe ontwerp | alarm na 30 s, noodprogramma met de gemiddelde inschakelduur (eis B2, B3), getest op donderdag |
| Waarom aan/uit en geen PID? | regeltechniek | de convector kan alleen aan of uit; met 1 °C hysterese hoogstens 6 starts per uur; PID past bij een traploze verwarming (E39) |
| Kan de module zonder het station? | systeemgrens | energie: van februari tot november wel, in december en januari niet; warmte: nee; water: het kasdak vangt 4,0 m³ tegenover 2,2 m³ nodig |
| Wat hebben jullie niet getest? | eerlijkheid | de zomer (K3), de algen (W5), de opbrengst van een jaar (P2); en de testweek was een simulatie |
| Wie had zondag de pH moeten zien? | eigenaarschap | de eigenaar van water en voeding; het ontwerp stuurde de melding naar niemand: voorstel in de aanbevelingen |
Zeg wat je wel weet, wat je niet weet, en hoe je het zou uitzoeken. 'Dat hebben we niet getest; de logger zou het tonen als we de sensorkast een week in de zon hangen' is een sterk antwoord. Raden is dat niet.

Beoordeling: eindrubric, peer- en zelfevaluatie
De jury en de leraar beoordelen het project met de eindrubric; het team gebruikt dezelfde rubric voor de zelfevaluatie (werkboek). Per criterium kies je het niveau dat het best past, met bewijs: een berekening, een logbestand, een regel in het logboek. De eindscore is de som van niveau × gewicht, gedeeld door 4. Het voorbeeldteam haalde niveau 3 op alles en niveau 4 voor testen en data-analyse: 80 op 100.
Daarnaast beoordeelt elk teamlid zichzelf en de anderen op vijf punten (peer-evaluatie), van 1 (zelden) tot 4 (altijd). Grote verschillen tussen zelf- en peer-evaluatie bespreek je in een kort teamgesprek.
| criterium | gewicht | 1 · begint | 2 · op weg | 3 · goed | 4 · uitstekend |
|---|---|---|---|---|---|
| programma van eisen en interfaces | 10 % | eisen vaag, zonder toets | meetbaar, maar herkomst of criterium ontbreekt vaak | alle eisen meetbaar, met criterium, toets en hoofdstuk; interfacetabel volledig | ook: botsende eisen opgemerkt en beslist |
| dimensionering | 15 % | berekeningen ontbreken | berekeningen met fouten in eenheden of aannames | energie, water, warmte, licht en voeding juist nagerekend, met herkomst | ook: gevoeligheid getoond (wat als het grijs is, wat als het vriest) |
| bouw en veiligheid | 10 % | niet volgens het schema | werkt, maar pas na ernstige herstellingen gekeurd | volgens het schema, zekering per tak, sterpunt, in één keer gekeurd | ook: nette bekabeling, alles gemeten en genoteerd |
| besturing en code | 15 % | versie 5 draait niet | draait, maar niet alle situaties getest | draait, alle naspeeltests en storingstests in orde | ook: een eigen uitbreiding, getest en uitgelegd |
| testen en data-analyse | 20 % | geen testplan | testplan, maar criteria achteraf of analyse met de hand | testplan vooraf, storingstests, testweek met een script getoetst | ook: niet-gehaalde eisen tot op de oorzaak ontleed |
| technisch rapport | 15 % | onvolledig | alle delen, maar zonder getallen of bronnen | volledig, met getallen, figuren die in de tekst uitgelegd worden, bronnen | ook: kort, helder, klaar voor een volgend team |
| verdediging | 10 % | geen presentatie of demo | presentatie, maar vragen ontweken | storyboard gevolgd, iedereen spreekt, vragen met getallen beantwoord | ook: eerlijk over grenzen, sterke demo met plan B |
| samenwerking en logboek | 5 % | logboek leeg | logboek onvolledig | logboek elke les, eigenaars kennen elkaars interfaces | ook: conflicten opgelost en genoteerd |
Peer- en zelfevaluatie
| criterium | 1 · zelden | 4 · altijd |
|---|---|---|
| komt afspraken en deadlines na | levert te laat of niet | op tijd, en meldt vooraf als het niet lukt |
| kwaliteit in het eigen deelsysteem | berekeningen of bouw met fouten | juist, gemeten, gedocumenteerd |
| bewaakt de eigen interfaces | verandert een pin of waarde zonder te melden | overlegt met de andere eigenaar, past de tabel aan |
| helpt anderen | werkt alleen | legt uit, test mee, deelt wat hij kan |
| houdt het logboek mee bij | nooit | elke les: wie, wat, probleem, volgende stap |
Wat de module nog niet kan, en wat Expert toevoegt
De module werkt: ze teelt, regelt, beheert haar energie en houdt alles bij. Maar de testweek toonde ook haar grenzen. Ze reageert op wat er nu is, ze voorspelt niet; ze meldt, maar alleen aan wie in de module staat; ze vertrouwt op één sensor per grootheid; en haar gedrag is getest in een model dat nog niet op echte metingen geijkt is.
| wat de module nog niet kan | gevolg (testweek of berekening) | wat Expert toevoegt |
|---|---|---|
| een melding bereikt niemand buiten de module | de pH-waarschuwing van zondag werd pas maandag gezien | sensornetwerk en MQTT naar een centrale computer en een telefoon (E37) |
| aan/uit-regeling | zaagtand van 1 °C, tot 6 starts per uur | PID-regeling met een traploze verwarming (E39) |
| het energiebeheer reageert, maar voorspelt niet | dinsdag lampen afgeknepen, woensdag ±4 kWh zonnestroom ongebruikt | verbruik en zon voorspellen (E41, E43), opslag en het 48 V-net van het station (E19-E24, E27) |
| één temperatuursensor | sensor los: blind verwarmen | sensorfusie en foutdetectie (E40), redundantie en FMEA (E46) |
| het model is niet geijkt op metingen | de testweek is een simulatie | parameters schatten uit logbestanden (E17, E36), de digitale tweeling (E49) |
| geen noodstop in hardware | hangt de Mega, dan beslist alleen de waakhond | noodstoplogica met poorten (E23) |
| CO₂ niet gemeten | de klep gaat open op tijd, niet op concentratie | een CO₂-sensor en minuutgemiddelden (E6, E14) |
| de MPPT is een zwarte doos | je weet niet waarom hij afknijpt | buck- en boostomzetters en MPPT zelf (E24) |
In Expert bouw je het brein van het station: een centrale computer die de meetkas (laag 1) en de klimaatmodule (laag 2) verbindt met de woonmodule, metingen verzamelt, een digitale tweeling meedraait (een simulatie zoals de testweek van dit hoofdstuk, maar geijkt op de echte module) en setpoints terugstuurt. De Mega blijft de module regelen, met versie 5 als basis; het brein zegt haar wat het beste setpoint is. Het ontwerpproces wordt het V-model in het groot, met een eisenboom en een FMEA (E45, E46). Het echte station EDEN ISS in Antarctica werkte zo: een serre die zichzelf regelde, bewaakt door een controlecentrum op afstand (hoofdstuk 36).
De tweede laag is af
De klimaat- en energiemodule is de tweede laag van Habitat. Elk deel van Intermediate leverde er iets aan, en in dit hoofdstuk kwam alles samen: meten met een onzekerheid (I1), modellen met hun geldigheidsgebied (I2), krachten op dak en vloer (I3), de chemie van de voedingsoplossing en de batterij (I4), de drie balansen (I5), de elektronica van sensor tot MOSFET (I6), Python en C++ (I7), regelen en toestanden (I8), de plant zelf (I9), en ontwerpen met data (I10).
Waar het vandaan komt. De meetkas van Fundamental (F33) was hetzelfde project in het klein: 36 eisen, één programma met tellers, een testweek met een laptop. Nu: 54 eisen, vijf versies samengevoegd, een logger en een analyse in Python, en een verdediging voor een jury.
Waar het naartoe gaat. Expert neemt de module over als bouwsteen van het brein van het station. Het warmtemodel met twee knopen wordt de kern van de digitale tweeling (E17), de aan/uit-regeling een PID-regeling (E39), het energiebeheer een voorspellend beheer met het 48 V-net van het station (E24, E41), de meldingen een sensornetwerk (E37), en het programma van eisen een eisenboom in het V-model (E45). Het integratieproject van Expert (E49) volgt dezelfde fasen, voor het hele station.
Het eerste integratieproject: dezelfde fasen, in het klein.
Het verslag en de bronvermelding van het technisch rapport.
Het werkpunt van de pomp en de druksensor in het reservoir.
De EC van de voedingsoplossing en de temperatuurcompensatie.
De pH, de tweepuntsijking en het bijsturen met pH-min.
De LiFePO4-batterijen en de coulombteller.
Het warmteverlies van 20,2 W/K.
Panelen, batterijen en energiebeheer: het budget dat hier herrekend is.
Dimmen met PWM en het daglicht in µmol.
Versie 1, pinnen.h en taken met millis().
De logger, I²C en de klok van versie 2.
Hysterese, het warmtemodel met twee knopen en het noodprogramma.
De modi en het dak als toestandsmachines: het kader van versie 5.
Het NFT-rek, de lichtsom en de opbrengst.
De voetafdruk van de module en de keuze van de batterijen.
De extra pompbeurt uit de beslisboom, en grafieken die niet misleiden.
Het warmtemodel van het station: de kern van de digitale tweeling.
De MPPT-regelaar van binnen.
Meldingen die iemand bereiken: een sensornetwerk met MQTT.
Van aan/uit naar PID.
Het verbruik en de zon van morgen voorspellen.
Het V-model en de eisenboom voor het hele station.
Het integratieproject van Expert: het brein van het station.
In het kort
- Integreren is nagaan of delen die elk werken, ook samen werken. De plaats waar ze elkaar raken, heet een interface: een waarde, een pin, een adres, een eenheid, een tijdzone.
- Een project met vier eigenaars en een integrator loopt in vijf fasen: eisen, dimensioneren, bouwen en programmeren, integreren en testen, rapporteren en verdedigen, met mijlpalen en een logboek.
- Het programma van eisen van de module telt 54 eisen (47 moet), elk meetbaar, met een vooraf vastgelegd criterium, een toets en het hoofdstuk waar de waarde vandaan komt.
- Het N²-diagram en de interfacetabel leggen 18 interfaces vast; door budget, schema en code naast elkaar te leggen vond het voorbeeldteam zes integratieproblemen vóór de testweek.
- Met dimmen daalt het verbruik van 3,27 naar ±1,5 kWh op een gemiddelde maartdag en naar ±540 kWh per jaar; alleen december en januari komen tekort, en de twee batterijen blijven nodig voor de nacht.
- Water: 4,8 L/min over vier goten, ±3,9 L onderweg als de pomp draait, 6 warme dagen zonder bijvullen, ±2,2 m³ per jaar. Warmte: 20,2 W/K, convector 800 W, buffer 1,09 MJ/K; op zonnige dagen dak en ventilatoren samen.
- Klimaat.ino versie 5 voegt vijf versies samen met vier regels: één klok, nergens delay(), één eigenaar per uitgang, de modi als kader; ze schrijft elke minuut 17 kolommen.
- Testen gaat van deeltest over integratie- en storingstest naar een testweek; de logbestanden worden in Python getoetst aan het programma van eisen.
- In de testweek werden 43 van de 47 moet-eisen gehaald; L1 (lichtsom op grijze dagen) en H2 (pH op zondag) niet: de oorzaken zijn een botsing tussen eisen en een melding die niemand bereikte.
- Het technisch rapport begint met een samenvatting vol getallen; de verdediging volgt een storyboard; de eindrubric en de peer-evaluatie beoordelen resultaat en samenwerking.
Wat je nu kent
- integrator
- Het teamlid dat bewaakt dat de deelsystemen op elkaar passen: programma van eisen, interfaces, planning en testrapport.
- interfacetabel
- Lijst van alle plaatsen waar twee deelsystemen elkaar raken, met per interface de afspraak (waarde, eenheid, pin, adres), de eigenaar en de test.
- N²-diagram
- Vierkante tabel met de deelsystemen op de diagonaal; het vakje in rij A en kolom B zegt wat A aan B levert.
- deeltest
- Test van één onderdeel of deelsysteem apart, zodra het klaar is (een sensor, een tak, de pomp).
- integratietest
- Test van de samenwerking tussen deelsystemen: werkt de interface zoals afgesproken?
- storingstest
- Test waarbij je bewust een fout veroorzaakt (sensor los, vlotter omhoog, stroom eraf) om na te gaan of het systeem zoals ontworpen reageert.
- testweek
- Systeemtest waarin het systeem een week alleen draait, terwijl de logger alles bijhoudt.
- lichtsom
- Het aantal mol lichtdeeltjes (PAR) dat op één dag op een vierkante meter valt, daglicht en lampen samen: DLI, in mol/(m²·dag).
- afknijpen
- Wat een laadregelaar doet als de batterij vol is: hij neemt minder zonnestroom af dan de panelen kunnen leveren.
- één eigenaar per uitgang
- Ontwerpregel: één enkele taak in het programma beslist over een pin; andere taken vragen, ze schakelen niet zelf.
- technisch rapport
- Verslag van een ontwerp: van eisen over berekeningen en keuzes tot tests en besluit, zo dat een ander het kan nagaan en verderzetten.
- verdediging
- Presentatie van een ontwerp voor een jury, gevolgd door vragen, waarin je je keuzes met getallen onderbouwt.