Server » Verwarming » Eigen installatie » Historiek » 2020 » Software
Verwarmingsinstallatie 2020
Gasconvector met Arduino sturing
Software
-

-

In de inleiding heb ik de redenen aangehaald om over te schakelen van een klassieke computersturing van de centrale verwarming naar een arduino sturing van de gasverwarming. De arduino software listing staat op een aparte pagina. Hier wordt er uitleg gegeven over de software. De listing die hier gegeven wordt komt ondertussen al niet meer overeen met de werkelijkheid. De listing wordt regelmatig bijgewerkt.

Seriële communicatie
wordt hier uitgelegd

Serial of SoftwareSerial?

Om te communiceren met een LCD paneel heeft de arduino een ingebouwde hardware seriële poort die verbonden is met de digitale port 1, maar het is ook mogelijk een software poort aan te maken via een #include <SoftwareSerial.h>. Dit kan bijvoorbeeld nodig zijn als je meerdere panelen wilt aansturen.

Een beter alternatief, als je echt veel displays moet aansturen is een switch IC te gebruiken die de TX lijn van de arduino met de corresponderende RX lijn van het display verbindt.

De software poort kan echter niet samenwerken met de servomotor sturing: beide routines gebruiken een tijdsvertraging, en blijkbaar heeft de arduino slechts één timer. Als er data naar de seriële poort verstuurd moet worden, dan wordt de servo-puls verlengt, waardoor dat de motor zich gaat verdraaien.

De oplossing is hier de ingebouwde poort te gebruiken (TX op digitale poort 1), dit is de hardwarematige oplossing die verder besproken wordt. Deze oplossing heeft ook nadelen.

Wat zijn dan de mogelijkheden?

  • Softwarematig (met SoftwareSerial)
    Door de communicatie met het LCD paneel te scheiden van de communicatie met de servo motor zorg je ervoor dat de communicatie met het LCD paneel de servomotor niet stoort. Er zijn hier verschillende mogelijkheden om dit op te lossen. De communicatie met de servo-motor kan gelukkig aan- en uitgezet worden (servomotor.attach en servomotor.detach). Hoe kan je dit praktisch doen?
    Met wachttijden (delay();
    Als je de servomotor wenst te verdraaien:
    • Je wacht 100ms om zeker te zijn dat alle data naar het scherm verstuurd is
    • Je stuurt een commando servomotor.attach om de servopulse te genereren
    • Je wacht 100ms zodat de servomotor op de pulsen kan locken,
    • Je stuurt de nieuwe positie met een write opdracht,
    • Je wacht opnieuw 100ms (zodat er voldoende pulsen gestuurd zijn om de servo naar zijn juiste positie te krijgen) en
    • Je geeft een command detach zodat de servopulsen onderbroken worden. Nu kan je opnieuw seriël zenden.

    Met een boolse variabele
    Je hoeft de software niet noodzakelijk te doen wachten, je kan bijvoorbeeld een paar schijfopdrachten naar het scherm onderdrukken (de loop wordt toch constant doorlopen en het scherm wordt regelmatig bijgewekt).
    • Als je in een lus naar de servo motor moet schrijven, dan zet je in de eerste lus de servo-variabele op true. Zolang de servo-variabele true is, wordt er niet naar het scherm geschreven.
    • In de tweede doorgang wordt er gecontroleerd of de servo variabele true is en wordt er naar de servo geschreven
    • In de derde doorgang wordt de variabele op false gezet als de positie van de servo niet meer gewijzigd moet worden
    • In de vierde doorgang is de servo variabele false en kan er opnieuw naar het scherm geschreven worden.
    Deze oplossing is ideaal voor grote lussen met veel complexe opdrachten, of als er een vaste wachttijd ingesteld is (de lus wordt bijvoorbeeld om de seconde doorlopen).

    Met een timer
    Een andere mogelijkheid om het programma niet te doen wachten is met een timer te werken. De arduino ondersteunt geen timers in hardware, je zal die dus moeten programmeren aan de hand van de millis() funktie.
    • Normaal staat de timer op nul en kan er naar het LCD paneel geschreven worden.
    • Als de servomotor verplaatst moet worden, wordt de timer gestart.
    • Vanaf nu (timer > 0) kan er niet meer gecommuniceerd worden met het LCD paneel.
    • Als de timer boven de 100ms komt, kan er een attach commando gegeven worden,
    • boven de 200 wordt de nieuwe positie doorgestuurd,
    • boven de 300 wordt er een detach opdracht gegeven en wordt de timer stilgelegd en op nul gezet.
    Deze mogelijkheid is aangeraden in strakke, korte lussen die snel doorlopen worden.
    Hoewel in de meeste toepassingen de sturing van de servo-motor belangrijker is dan de aanduiding op het LCD paneel, heeft de servomotor geen voorrang. Als er nog een seriële opdracht lopende is, dan wordt de positie van de servomotor verstoord.

  • Hardwarematig (via de seriële poort)
    Hier kan je data naar de servomotor en naar het scherm sturen in opeenvolgende opdrachten zonder dat ze elkaar storen. De hardwarepoort heeft een eigen processor zodt de hoofdprocessor ontlast wordt.

    Er wordt echter ook data over de poort verstuurd als er nieuwe software naar de arduino gestuurd wordt (via USB). Daardoor kan het gebeuren dat het display niet meer aanspreekbaar is (in het ergste geval is het display onbruikbaar geworden): de software voor de arduino komt immers terecht in het LCD panel. Bij de uno wordt alle USB communicatie gedupliceerd over de seriële interface (dus bijvoorbeeld ook als je gewoon de arduino met de computer verbindt of als de arduino opstart).

    Je moet de voeding van het LCD paneel onderbreken (of de datalijn) als je en update doet, of zelfs gewoon als je de module met de computer verbindt, of als de arduino opgestart wordt. De hardware-oplossing lijkt de eenvoudigste oplossing, maar de kans is groot dat je eens de schakelaar zal vergeten te verzetten.


Hierboven een voorbeeld als het LCD paneel op de verkeerde baudrate werkt. Je hebt geluk, een probleem met de baudrate is gemakkelijk op te lossen. Je moet de software aanpassen om op de correcte baudrate te werken (bijvoordeeld LCDSerial.begin(2400)) en alle baudrates overlopen totdat je opnieuw normale tekst hebt. Als dat lukt kan je een commando doorsturen naar het scherm om terug te keren naar 9600 baud. Omdat het scherm gereset is op de juiste baudrate is er nu geen communicatie meer mogelijk todat de baudrate in de software opnieuw aangepast is.


Een stukje van de software, ondertussen aangepast om niet tezelfdertijd met de servomotor en met het scherm te communiceren.


De servomotor (kan in theorie 25kg verzetten, maar zoveel is niet nodig). Om beschadinging van de thermostaat te voorkomen is de koppeling tussen servo en thermostaat soepel uitgevoerd.

Waarom zet je de baudrate beter terug op 9600? Het is een standaard-waarde: een nieuw scherm zal direct werken. Bij een te lage baudrate is de processor permanent bezig data door te sturen (om de inhoud van het scherm volledig te refreshen heb je een halve seconde nodig op 2400 baud, en gedurende die tijd is de processor bezig (die geldt natuurlijk niet als je de hardware poort gebruikt, maar we hebben gezien dat dit niet de beste oplossing is). Bij een te hoge baudrate kunnen er transmissiefouten ontstaan.

Op de softwarelisting zie je hoe ik dat aangepakt hebt, met een boolse variabele servodrive als de servomotor aangestuurd moet worden. Als de positie van de servomotor gewijzigd moet worden, dan wordt de variabele true gezet. In de volgende lusdoorgang zijn er geen LCD schrijfopdrachten mogelijk, maar kan de servomotor wel gebruikt worden.

Ik laat de servomotor kleine stapjes zetten om de thermostaat niet te forceren. Om toch output naar het scherm te hebben wordt er afwisselend naar het scherm geschreven in de ene lus en wordt de servomotor gestuurd in de andere lus (boolse variabele blinker die ook gebruikt wordt om leds te doen knipperen).

Constanten

De gebruikte poorten worden als constanten aangegeven. Als een poort defekt zou gaan (nooit gebeurt), dan moet je enkel de constante wijzigen.

Ik probeer het aantal constanten te beperken: de constanten worden in het begin van het programma gedefinieerd, en ergens gebruikt. Op de plaats waar de constante dan gebruikt wordt, weet je soms niet goed meer wat die voorstelt. In het algemeen is het beter geen constanten vooraf te definieren, als je die maar op één plaats in het programma gebruikt. Als tijdens de testfase of later een constante gewijzigd moet worden, dan moet je op 2 plaatsen kijken.

Variabelen

Voor de variabelen probeer ik integer-waarden te gebruiken, behalve voor variabelen die een continue waarde voorstellen zoals de temperaturen. De temperatuurwaarden worden trouwens als integer ingelezen (0..1023) maar verder verwerkt als floats zodat de PID-berekening nauwkeuriger is en een trend sneller opgemerkt kan worden.

PID parameters zijn eigenlijk integers met 2 cijfers na de komma. Als ze gebruikt moeten worden, worden ze omgezet naar een float en door 100 gedeeld.

Let op met de scope van de variabelen: variabelen gedefinieerd aan het begin van de code zijn geldig voor het volledig programma (globale variabelen), variabelen gedefinieerd in een routine zijn enkel geldig in de betreffende routine (ook de loop).

Het is beter de scope van een variabele beperkt te houden zodat je geen ongewenste invloeden hebt (een tel-variabele die ook in een andere routine gebruikt wordt en waarvan de waarde niet meer juist is in de aanroepende routine). Het maakt ook de code duidelijker: je weet welke variabelen hier gebruikt worden.

In routines definieer je de variabelen beter als static zodat ze hun waarde behouden tussen twee aanroepen van de routines. Als de routine later opnieuw uitgevoerd wordt, dan hebben de variabelen dezelfde waarbe behouden als bij het verlaten van de routine. Niet-static variabelen worden op nul geïnitialiseerd aan hetbegin van de routine. Dit maakt niet veel uit, de lokale variabelen krijgen toch meestal een nieuwe waarde bij de start van de routine, maar het is onnodig om daarom de variabele eerst op nul te zetten.

In sommige routines kan het nuttig zijn dat bepaalde variabelen hun waarde behouden tussen de aanroepen: daarvoor moet je geen globale variabelen definieeren, maar gewoon een locale static variabele.

Algemene routines

Dit zijn standaard-troutines om de led status te tonen, om de led status op nul te zetten, om waarden naar de display te schrijven, enz. Deze routines worden op verschillende plaatsen opgeroepen.

Initialisatie (setup)

De initialisatie wordt slechts éénmaal doorlopen na een reset. Dit deel wordt gebruikt om de digitale poorten te definieren (ingang of uitgang), om tabelwaarden een beginwaarde te geven, om gegevens die in EEPROM opgeslagen zijn te lezen,...

Als je een beginwaarde aan een variabele wenst te geven, dan kan dat beter direct bij de definitie van de variabele.

Hoofdprogramma (loop)

Na het uitvoeren van de initialisatie wordt de loop uitgevoerd. Is de loop ten einde, dan wordt die opnieuw uitgevoerd. Let hier dus op met het verschil in variabelen: globale variabelen behouden hun waarde, locale static variabelen behouden ook hun waarde, niet static variabelen worden terug geïnitialiseerd.

Publicités - Reklame

-