SOFTWARE


Een volledig functionele versie van het PC-programma kun je hier downloaden (ebrew_Qt_exe.zip file, v3.20). Voor een goede werking heb je uiteraard ook nog de hardware met de juiste firmware nodig.

Github: Alle source-files van het PC-programma zijn beschikbaar op Github, zie github/Emile666/ebrew_Qt

Op de hardware / elektronica pagina heb je kunnen zien, dat er een behoorlijke hoeveelheid elektronica ontwikkeld is om de brouwinstallatie te automatiseren. Maar deze hardware is nutteloos zonder bijbehorende software, die een en ander controleert en bestuurt. Toen ik startte met het ontwerp van mijn systeem, was flexibiliteit een belangrijke voorwaarde. Ik kon een ontwerp maken op basis van een microcontroller, waarin ik embedded software plaatste, zodat je geen PC nodig had. Maar omdat ik toch verschillende PCs in mijn huis heb, is het wel net zo makkelijk om een PC te gebruiken voor het besturen van je brouwerij. Op deze manier is het makkelijk om nieuwe functionaliteit toe te voegen. Als ik een nieuwe functie wil toevoegen aan de besturing, dan maak ik een update van het bestaande programma, ik test het en daarna installeer ik het op de PC in de brouwerij (via het netwerk).

Deze opzet heeft vele jaren succesvol gewerkt. De aangesloten hardware was via de parallelle poort aangesloten. Met de overgang naar nieuwe hardware werd ook dit bijgewerkt naar een (meer gebruikelijke) USB verbinding. En dan wordt toch een ontwerp m.b.v. een microcontroller weer interessant. De laatste versie van de hardware maakt gebruik van een Ethernet verbinding en wordt de USB verbinding alleen nog voor debugging gebruikt.

In de nieuwe opzet, die hier beschreven is, is dit dan ook gerealiseerd. Veel oude functionaliteit, bijvoorbeeld het opwekken van een PWM signaal, is standaard aanwezig in zo'n microcontroller. Dus kon ook de hoeveelheid hardware componenten verminderd worden. Aan de ene kant heb je dus het PC-programma dat via Ethernet communiceert met een Arduino Nano met een ATmega328 microcontroller, waar ook weer software op draait. Die laatste noemen we echter firmware, om een onderscheid te maken met de PC-software. We hebben dus twee soorten software: het PC-programma en de firmware op de microcontroller.

Welke programmeertaal kan nu het beste gebruikt worden voor beide systemen? Voor de firmware is dat duidelijk: ik maakte tot 2022 gebruik van een Arduino Nano, die ik via Atmel Studio in de C programmeertaal aanstuur, zie github.com/Emile666/Brew_Arduino. De laatste versies van de brouwhardware hebben geen Arduino Nano meer aan boord, maar een STM microcontroller, de STM8S207, een 64 pins IC. Om de firmware hiervoor te schrijven, gebruik ik IAR Embedded Workbench - STM8 series, uiteraard ook weer in de C programmeertaal, zie github.com/Emile666/Brew_HW_Stm8s207. In Embedded land is C de standaard en zijn zelfs vrijwel geen andere programmeertalen met een goede ontwikkelomgeving voorhanden.

De keuze voor een programmeeromgeving en -taal voor het PC-programma is minder duidelijk, door het grote aanbod op PC gebied. Ik ben niet echt een voorstander van moderne programmeertalen zoals Java en C#/.Net. Normaliter schrijf ik mijn programma's in the programmeertaal C, maar voor een programma onder Windows heb ik een programmeertaal nodig waarmee ik ook redelijk makkelijk een front-end GUI (windows, menus, muis etcetera) kan maken. Op zich is de taal C daar minder goed geschikt voor. De oplossing lag echter voor de hand: gebruik de programmeertaal C++ (object georiënteerd). Hiermee kun je ook je bestaande routines uit de taal C gebruiken en tegelijkertijd toch een mooi Windows programma maken.

Voor de opzet van het PC-programma, zoals dat tot 2017 gebruikt is, is Borland's C++ Builder gebruikt. Dit is inmiddels achterhaald en er zijn dan ook betere ontwikkelomgevingen beschikbaar. Voor de huidige versie van het PC-programma wordt de Qt omgeving gebruikt (zie www.qt.io) met C++ als programmeertaal.

Oke, de configuratie is gekozen, de programmeertaal en omgeving zijn ook geselecteerd. Wat kunnen we nog meer doen? Welnu, ik zou wat kunnen vertellen over de interne structuur van mijn brouwprogramma (die ik ebrew! gedoopt heb, hetgeen staat voor elektronisch brouwen). Deze pagina bestaat dan ook uit de volgende paragrafen:

1. GRAFISCHE USER INTERFACE (GUI)

Om de complexiteit van de software voor een groot deel af te kunnen schermen van de gebruiker, is het van belang om een goede gebruikersinterface (Graphical User Interface of GUI) te realiseren, die ervoor zorgt dat tijdens het brouwen alles zo makkelijk en efficiënt mogelijk verloopt. Na opstarten van het PC-programma verschijnt het hoofdscherm, waarvan hierboven een screenshot gegeven is.

Er is het nodige op te zien, wat ideaal is wanneer je aan het brouwen bent en je snel even bepaalde waarden wilt zien of controleren:

Als een van de ingelezen waarden vanuit de brouw-hardware niet goed is, of er is geen communicatie, dan kleuren de bijbehorende waarden rood. In het screenshot hierboven worden alle temperaturen correct ingelezen en worden de flows van flowmeter 2 en 3 ook ingelezen.

1.1 FUNCTIES VAN HET PC BROUWPROGRAMMA

De menu-balk van het brouwprogramma bevat de volgende menu items:

Terug naar boven

2. SOFTWARE ARCHITECTUUR

Het PC-programma bevat een aantal belangrijke componenten die gezamenlijk een geheel volgen. Omdat de taal C++ object georiënteerd is, is het logisch om een aantal objecten of classes tegen te komen. De belangrijkste onderdelen zijn:

Het PC-programma verzorgt met name allerlei mogelijke instellingen van het brouwprogramma (tijden, temperaturen, parameters) en verstuurt op vaste tijdstippen commando's naar de hardware via een standaard communicatieprotocol. De reacties van de hardware worden weer op het scherm zichtbaar gemaakt. Een overzicht van alle bestanden en schermen ziet er als volgt uit:

Alleen al het hoofdobject (heet MainEbrew) bevat al ongeveer 2200 Lines of Code (LOC). De totale PC-applicatie omvat ongeveer 5600 regels C++ code.

Naast het PC-programma is er uiteraard nog een tweede belangrijke deelcomponent in het totale systeem en dat is de firmware die op de ATmega328 van de Arduino Nano draait. Ook dit softwaresysteem omvat weer de nodige regels code: bij R1.45 waren dit ongeveer 3550 regels C code. Alles bij elkaar dus ruim 9000 regels C en C++ code!

Terug naar boven

3. SCHEDULER

De e-brew! applicatie vraagt strakke timingseisen en er moet veel gebeuren op vaste tijdstippen. Er moeten commando's naar de hardware gestuurd worden, er worden temperaturen en volumes ingelezen, het scherm moet bijgewerkt worden, etcetera. Het zou heel mooi zijn om de beschikking te hebben over een real-time embedded operating systeem (RTOS genoemd), maar Windows komt bij zoiets nog niet eens in de buurt. Om de 100 milliseconden iets uitvoeren, wordt al erg lastig. Gelukkig zijn temperaturen geen snel veranderende grootheden en hoeft er niet zo snel geregeld en gestuurd te gaan worden. Maar er is wel iets nodig om ervoor te zorgen dat alle verschillende taken ook aan bod komen. Hiervoor is een aparte scheduler geschreven. Een scheduler is software die ervoor zorgt dat de verschillende taken in een software programma op het juiste tijdstip en met de juiste frequentie uitgevoerd worden. De gebruikte scheduler is non pre-emptive, hetgeen wil zeggen dat als een taak gestart is, deze niet meer onderbroken kan worden door een andere taak. Een volgende taak kan pas starten als de huidige taak is afgelopen. Voor ons brouwsysteem is dit voldoende en met wat aandacht voor de duur van een taak levert dit geen problemen op.

Taken zijn op deze manier makkelijk toe te voegen, om dit te illustreren onderstaande stukje C code (zoals dit ook in het PC-programma aanwezig is):


	//-----------------------------------------
	// Now add all the tasks for the scheduler
	//-----------------------------------------
    scheduler->add_task("aliveLed"  ,   0,TS_LED_MSEC  ,Ebrew,SLOT(task_alive_led()));      // TASK 0
    scheduler->add_task("readTemps" , 100,TS_TEMPS_MSEC,Ebrew,SLOT(task_read_temps()));     // TASK 1
    scheduler->add_task("pidControl", 300,TS_PID_MSEC  ,Ebrew,SLOT(task_pid_control()));    // TASK 2
    scheduler->add_task("updateStd" , 400,TS_STD_MSEC  ,Ebrew,SLOT(task_update_std()));     // TASK 3
    scheduler->add_task("readFlows" , 600,TS_FLOWS_MSEC,Ebrew,SLOT(task_read_flows()));     // TASK 4
    scheduler->add_task("wrLogFile" ,1600,TS_WR_LOGFILE,Ebrew,SLOT(task_write_logfile()));  // TASK 5
	

Het toevoegen van taken is op deze manier eenvoudig en overzichtelijk. De taak task_update_std() voert bijvoorbeeld iedere seconde (1000 msec.) het toestandsdiagram uit. Je kunt ook nog een offset meegeven: in het geval van task_update_std() start deze op tijdstip 0.4 (400 msec.) en daarna weer op tijdstip 1.4, 2.4, 3.4 etcetera. Bij de PID regelaar, task_pid_ctrl(), is iets bijzonders aan de hand: de aanroepfrequentie is afhankelijk van de parameter Ts, die in seconden weergegeven is. Als in het PC-programma deze tijd bijvoorbeeld op 20 seconden ingesteld staat, dan zorgt deze aanroep ervoor dat de PID regelaar iedere 20 seconden aangeroepen wordt. Het belangrijkste is nu nog om ervoor te zorgen dat de taken niet teveel tijd in beslag nemen. De scheduler meet daarom ook de tijdsduur en maximale tijdsduur van iedere taak, dat kan er als volgt uitzien (in een log-file):


	Task-Name      T(ms) Stat  T(μs) M(μs)
--------------------------------------
aliveLed 500 0x02 407 1339 readTemps 2000 0x02 15236 15699 pidControl 1000 0x02 1421 1767 updateStd 1000 0x02 681 1040 readFlows 2000 0x02 55684 55684 wrLogFile 5000 0x02 167 167

Uit dit overzicht blijkt dat updateStd() maximaal 681 usec. geduurd heeft. Samen met het hierboven gedefinieerde overzicht levert dit een goed inzicht op in de diverse taken op. Foutzoeken is op deze manier eenvoudig te doen. De scheduler zelf wordt vanuit een Timer routine aangeroepen, dit gebeurt iedere 100 milliseconden. Belangrijkste functie van deze routine is het bijhouden van alle timers en het inschakelen en uitschakelen van taken. De feitelijke aanroep van de taken gebeurt hier niet in!

Terug naar boven

4. TOESTANDSDIAGRAM

Volgens de free online dictionary of Computing is een toestandsdiagram (state transition diagram, of STD) "een diagram die uit cirkels bestaat, die toestanden voorstellen. Ook bestaat zo'n diagram uit gerichte pijlen tussen die toestanden. Deze stellen de overgangen voor. Een of meerdere acties (outputs) kunnen gekoppeld worden aan zo'n overgang.". Kijkend naar de e-brew! applicatie, dan is een STD een centrale besturingsfunctie, die de verschillende fasen van het brouwproces coördineert. Een toestand kan zoiets zijn als 'maisch rust' of 'voorverwarmen'. Een overgang van de ene toestand naar de andere toestand wordt uitgevoerd wanneer de bijbehorende conditie WAAR is (bijv. de 'temperatuur is ok'). Condities zijn in het toestandsdiagram (zie plaatje hieronder) met GROEN aangegeven. Wanneer een overgang naar een andere toestand uitgevoerd wordt, dan kan er ook een bijbehorende actie uitgevoerd worden. (bijv. 'zet pomp aan'). Acties zijn met ROOD aangegeven. Samengevat: er zijn toestanden, overgangen tussen die toestanden, condities en acties. Het toestandsdiagram behorende bij het totale brouwproces bestaat uit 19 unieke toestanden, waarvan een aantal toestanden cyclisch doorlopen kunnen worden. Het ziet er als volgt uit:

Al met al een complex toestandsdiagram, die de totale besturing voor zijn rekening neemt. Een korte uitleg per toestand is dan ook wel handig om hier te geven:

De tabel (rechter bovenhoek) laat zien welke kleppen in welke toestand open (1) of gesloten (0) zijn, maar ook of de pomp aan (1) of uit (1) is. Door op deze manier je ontwerp te maken kun je het gedrag van de verschillende kleppen goed definiëren. Het is altijd duidelijk in welke toestand een bepaalde klep open of gesloten is. Er zijn nog heel wat meer details te vertellen over dit toestandsdiagram, maar het bovenstaande verhaal is hopelijk voldoende om een beeld te geven van de werking. Voor de volledigheid wordt nog opgemerkt dat het toestandsdiagram 1 keer per seconde uitgevoerd wordt.

Terug naar boven

5. FIRMWARE VOOR DE ARDUINO NANO / ATmega328

De firmware omvat de C code voor de ATmega328 microcontroller en staat dus qua opzet geheel los van het PC-programma dat hiervoor beschreven is. Beide programma's communiceren echter wel met elkaar, via een netwerkkabel of de virtuele COM poort van de USB verbinding. Het grote voordeel van het gebruik van een microcontroller is dat allerlei hardware gerelateerde en vaak tijdkritische zaken niet meer door PC-Windows afgehandeld hoeven te worden. Een microcontroller is hier veel beter voor geschikt. Aan de microcontroller kant wordt dankbaar gebruik gemaakt van de Arduino hardware opzet. Deze Arduino omgeving zal dan ook als eerste geïnstalleerd moeten worden. Hiermee worden de juiste drivers aan de PC kant geïnstalleerd. Aan de microcontroller kan is het een ander verhaal. Gelukkig hoeven we hier niet direct met USB routines aan de slag (complex!), maar kunnen we ook hier gebruik maken van standaard routines voor seriële communicatie. De microcontroller bevat een component die USART heet (Universal Synchronous/Asynchronous Receiver/Transmitter) en hiermee kan, met de juiste routines, seriële communicatie gerealiseerd worden. De gebruikte instellingen zijn 38400,N,8,1, dat wil zeggen: 38400 Baud,geen pariteitbit, 8 databits en 1 stopbit. Dit is uiteraard identiek aan de instellingen in het PC-programma.

Terug naar de overall opzet van de software voor de microcontroller. Ik maak geen gebruik van Arduino sketches, omdat dit te beperkt is voor mijn doeleinden. Van sommige sketches heb ik C libraries gemaakt, anderen heb ik zelf geschreven. Zoals gebruikelijk bij een C project, wordt van bijna iedere component een C file gemaakt met een bijbehorende header file (.h). Tezamen omvatten deze alle routines voor zo'n component. Voorbeeld: de I2C module (met files i2c.c en i2c.h) omvat alle routines voor de I2C communicatie, inclusief specifieke routines voor de gebruikte ICs, zoals de DS2482.

Low-level routines:Zoals in het plaatje te zien is, zijn er libraries voor I2C, SPI, ADC, PWM, One-Wire en Usart. In feite dus voor alle low-level hardware van de microcontroller zelf. Om de werking hiervan goed te begrijpen, zul je toch echt in de datasheet van de ATmega328P microcontroller moeten duiken. Zeker als je aanpassingen hierop wilt maken. Gelukkig zijn er veel goede voorbeelden op het Internet te vinden, het is namelijk ook een van de meest gebruikte microcontrollers.

Interrupt routines:De interrupt routines zijn tijd-kritische stukken code die niet kunnen wachten totdat er tijd voor ze is. Het is dus functionaliteit die direct uitgevoerd moet worden. Voor de ontwerper is het dus van belang om deze routines zo klein mogelijk te houden: je kunt niet even gaan wachten totdat er iets gaat gebeuren. Een mooi voorbeeld hiervan zijn de flowsensoren die aangesloten zitten op poortpinnen van de microcontroller. Als er een puls langs komt van zo'n flowsensor, dan wordt er nu een interrupt gegenereerd die niet veel meer doet dan een teller met 1 verhogen. Alle pulsen van de vier flowsensoren worden in zo'n interrupt afgehandeld. Hiernaast is nog een interrupt gedefinieerd voor de scheduler die iedere milliseconde aangeroepen moet worden. Als laatste zijn er nog twee interrupts actief voor de seriële communicatie. Als de USART hardware van de microcontroller een byte ontvangen heeft, dan wordt een interrupt gegenereerd. In deze interrupt wordt dat byte opgeslagen in een ringbuffer. Iets vergelijkbaars gebeurt er wanneer er data in een ringbuffer aanwezig is om verstuurd te worden.

Naast deze low-level libraries is er ook nog een gemengde library, waar enkele functies inzitten die te maken hebben met het filteren van de ingelezen signalen. Zo is er een moving-average filter (een laagdoorlaatfilter) gedefinieerd en een hellingsbegrenzer functie. Verder een ringbuffer routine voor de seriële communicatie en een RS232 command handler. Ieder commando van de seriële poort wordt door deze routine afgehandeld.

Ethernet/Wiz550io: de brouw-hardware heeft een WIZ550io Ethernet module, die via SPI aangestuurd wordt. De C-routines hiervoor zijn afgeleid van de Arduino sketches (die erg rommelig in elkaar zaten). Met deze toevoeging wordt het voor de brouw-hardware mogelijk om via UDP te werken, in plaats van via de USB verbinding. Bij opstarten van de brouwhardware wordt via de USB poort de status weergegeven, waaronder het gevonden IP-adres van de brouwhardware. Dit ziet er als volgt uit:

De scheduler van het PC-programma zien we ook hier weer terug. Mooi staaltje van hergebruik van code op verschillende platforms.

Terug naar boven